""" api/database.py — Endpoints quản lý database face (danh sách & xóa theo tên) GET /api/faces — Danh sách khuôn mặt (RESTful) GET /api/db — Legacy alias DELETE /api/db/ — Legacy: xóa theo tên file (backward compat) GET /api/unknown-faces — Danh sách khuôn mặt chưa nhận diện """ import logging import os from flask import Blueprint, jsonify from face_model.analyzer import face_analyzer from storage.mongo_storage import mongo_storage from storage.pg_storage import pg_storage logger = logging.getLogger("api.database") bp = Blueprint("database", __name__, url_prefix="/api") @bp.get("/faces") def list_faces(): """ GET /api/faces Trả về danh sách khuôn mặt đã đăng ký từ Supabase + MongoDB. """ try: persons = pg_storage.list_persons_with_faces() faces = [ { "filename": f"{p['full_name']}.jpg", "name": p["full_name"], "person_id": p["person_id"], "status": p.get("status", "active"), "total_appearances": p.get("total_appearances", 0), "last_seen_at": p.get("last_seen_at"), "url": f"/api/face-crops/{p['face_crop_mongo_id']}" if p.get("face_crop_mongo_id") else None, } for p in persons ] return jsonify({ "total": len(faces), "faces": faces, }), 200 except Exception as e: logger.error(f"[list_faces] Error: {e}", exc_info=True) return jsonify({"error": { "code": "FETCH_FAILED", "message": f"Lỗi khi lấy danh sách khuôn mặt: {e}", "status": 500, }}), 500 @bp.get("/db") def list_faces_legacy(): """ Legacy alias GET /api/db (backward compatibility với frontend). Response format giữ nguyên để frontend không cần sửa. """ try: persons = pg_storage.list_persons_with_faces() faces = [ { "filename": f"{p['full_name']}.jpg", "name": p["full_name"], "url": f"/api/face-crops/{p['face_crop_mongo_id']}" if p.get("face_crop_mongo_id") else "", } for p in persons ] return jsonify({ "total": len(faces), "faces": faces, }), 200 except Exception as e: logger.error(f"[list_faces_legacy] Error: {e}", exc_info=True) return jsonify({"detail": f"Lỗi khi lấy danh sách khuôn mặt: {e}"}), 500 @bp.delete("/db/") def delete_face_legacy(filename: str): """ Legacy DELETE /api/db/ — Xóa theo tên file (backward compat với frontend). """ name = os.path.splitext(filename)[0] try: success, mongo_ids = pg_storage.delete_person_by_name(name) if not success: return jsonify({"detail": "Không tìm thấy người này trong database."}), 404 for mid in mongo_ids: mongo_storage.delete_face_crop(mid) # Xóa khỏi RAM cache (theo tên) to_remove = [ pid for pid, n in zip(face_analyzer.known_person_ids, face_analyzer.known_names) if n == name ] for pid in to_remove: face_analyzer.remove_from_cache(pid) return jsonify({ "success": True, "message": f"Đã xóa thành công {name} khỏi database.", "total_faces": face_analyzer.total, }), 200 except Exception as e: logger.error(f"[delete_face_legacy] Error: {e}", exc_info=True) return jsonify({"detail": f"Lỗi khi xóa khuôn mặt: {e}"}), 500 @bp.get("/unknown-faces") def list_unknown_faces(): """ GET /api/unknown-faces Trả về danh sách khuôn mặt chưa nhận diện từ MongoDB. """ try: unknowns = mongo_storage.list_unknown_faces(limit=50) return jsonify({ "total": len(unknowns), "unknown_faces": unknowns, }), 200 except Exception as e: logger.error(f"[list_unknown_faces] Error: {e}", exc_info=True) return jsonify({"error": { "code": "FETCH_FAILED", "message": f"Lỗi khi lấy danh sách unknown faces: {e}", "status": 500, }}), 500