Spaces:
Running
Running
File size: 4,999 Bytes
29bfc1f | 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 | import asyncio
import time
from fastapi import APIRouter, Form, HTTPException, Request, Depends
from src.core.config import (
DEFAULT_CLOUDINARY_URL, DEFAULT_PINECONE_KEY,
IDX_FACES, IDX_OBJECTS,
IDX_FACES_ARCFACE, IDX_FACES_ADAFACE,
USE_SPLIT_FACE_INDEXES,
)
from src.core.security import get_verified_keys
from src.services.db_client import (
cld_delete_all_paginated, cld_remove_folder, cld_root_folders,
delete_and_recreate_indexes, pinecone_pool,
)
from src.core.logging import log, warn
from src.common.utils import get_ip, is_default_key
router = APIRouter()
def _all_index_names() -> list[str]:
"""All possible index names across both modes — used for exhaustive cleanup."""
if USE_SPLIT_FACE_INDEXES:
return [IDX_FACES, IDX_OBJECTS, IDX_FACES_ARCFACE, IDX_FACES_ADAFACE]
return [IDX_FACES, IDX_OBJECTS]
@router.post("/api/reset-database")
async def reset_database(
request: Request,
user_id: str = Form(""),
keys: dict = Depends(get_verified_keys),
):
ip = get_ip(request)
start = time.perf_counter()
log("WARNING", "danger.reset_database.attempt",
user_id=user_id or "anonymous", ip=ip)
if is_default_key(keys["pinecone_key"], DEFAULT_PINECONE_KEY) or \
is_default_key(keys["cloudinary_url"], DEFAULT_CLOUDINARY_URL):
log("WARNING", "danger.reset_database.blocked",
user_id=user_id or "anonymous", ip=ip)
raise HTTPException(403, "Reset is not allowed on the shared demo database.")
try:
deleted = await asyncio.to_thread(
cld_delete_all_paginated, keys["cloudinary_creds"]
)
log("INFO", "danger.reset_database.cloudinary_wiped", deleted=deleted)
except Exception as e:
warn(f"Cloudinary wipe error: {e}")
try:
folders_res = await asyncio.to_thread(
cld_root_folders, keys["cloudinary_creds"]
)
folder_tasks = [
asyncio.to_thread(
cld_remove_folder, f["name"], keys["cloudinary_creds"]
)
for f in folders_res.get("folders", [])
]
if folder_tasks:
await asyncio.gather(*folder_tasks, return_exceptions=True)
except Exception as e:
warn(f"Cloudinary folder cleanup error: {e}")
try:
pc = pinecone_pool.get(keys["pinecone_key"])
await asyncio.to_thread(delete_and_recreate_indexes, pc)
except Exception as e:
log("ERROR", "danger.reset_database.pinecone_error",
user_id=user_id or "anonymous", ip=ip, error=str(e))
raise HTTPException(500, f"Pinecone reset error: {e}")
log("WARNING", "danger.reset_database.complete",
user_id=user_id or "anonymous", ip=ip,
duration_ms=round((time.perf_counter() - start) * 1000))
return {"message": "Database reset complete. All data wiped and indexes recreated."}
@router.post("/api/delete-account")
async def delete_account(
request: Request,
user_id: str = Form(""),
keys: dict = Depends(get_verified_keys),
):
ip = get_ip(request)
start = time.perf_counter()
log("WARNING", "danger.delete_account.attempt",
user_id=user_id or "anonymous", ip=ip)
if is_default_key(keys["pinecone_key"], DEFAULT_PINECONE_KEY) or \
is_default_key(keys["cloudinary_url"], DEFAULT_CLOUDINARY_URL):
log("WARNING", "danger.delete_account.blocked",
user_id=user_id or "anonymous", ip=ip)
raise HTTPException(403, "Account deletion is not allowed on the shared demo database.")
try:
deleted = await asyncio.to_thread(
cld_delete_all_paginated, keys["cloudinary_creds"]
)
log("INFO", "danger.delete_account.cloudinary_wiped", deleted=deleted)
except Exception as e:
warn(f"Account delete Cloudinary error: {e}")
try:
folders_res = await asyncio.to_thread(
cld_root_folders, keys["cloudinary_creds"]
)
folder_tasks = [
asyncio.to_thread(
cld_remove_folder, f["name"], keys["cloudinary_creds"]
)
for f in folders_res.get("folders", [])
]
if folder_tasks:
await asyncio.gather(*folder_tasks, return_exceptions=True)
except Exception as e:
warn(f"Account delete folders error: {e}")
try:
pc = pinecone_pool.get(keys["pinecone_key"])
def _delete_all_indexes():
existing = {idx.name for idx in pc.list_indexes()}
for name in _all_index_names():
if name in existing:
pc.delete_index(name)
await asyncio.to_thread(_delete_all_indexes)
except Exception as e:
warn(f"Account delete Pinecone error: {e}")
log("WARNING", "danger.delete_account.complete",
user_id=user_id or "anonymous", ip=ip,
duration_ms=round((time.perf_counter() - start) * 1000))
return {"message": "Account data deleted. Sign out initiated."} |