File size: 4,917 Bytes
75557db
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
33dc414
 
 
 
 
 
 
 
 
 
 
 
 
75557db
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
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
"""
app.py β€” Entry point: khởi tαΊ‘o Flask, Δ‘Δƒng kΓ½ blueprints, xα»­ lΓ½ lα»—i chuαΊ©n.

CαΊ₯u trΓΊc module:
    config.py           β€” BiαΊΏn mΓ΄i trường
    utils/image.py      β€” Tiện Γ­ch xα»­ lΓ½ αΊ£nh
    storage/
        mongo_storage   β€” MongoDB: raw_images, face_crops, unknown_faces, recognition_payloads
        pg_storage      β€” Supabase: persons, visits, face_embeddings, recognition_logs
    face_model/
        analyzer        β€” InsightFace singleton + RAM cache
    api/
        system          β€” GET /api/health, POST /api/database/reload
        recognize       β€” POST /api/recognitions  (+ legacy /api/recognize)
        faces           β€” POST /api/faces, GET /api/face-crops/<id>
        persons         β€” GET/PUT/DELETE /api/persons/<id>
        database        β€” GET /api/faces, /api/db, DELETE /api/db/<fn>, GET /api/unknown-faces
"""
import logging
import sys

from flask import Flask, jsonify
from flask_cors import CORS

import config
from face_model.analyzer import face_analyzer
from storage.pg_storage import pg_storage

# ── Logging ───────────────────────────────────────────────────────────────
logging.basicConfig(
    level=logging.INFO,
    format="%(asctime)s [%(levelname)s] %(name)s β€” %(message)s",
)
logger = logging.getLogger("app")

# ── Flask Init ────────────────────────────────────────────────────────────
app = Flask(__name__)
CORS(app, resources={r"/api/*": {"origins": "*"}}, supports_credentials=True)

# ── Register Blueprints ───────────────────────────────────────────────────
from api.system    import bp as system_bp
from api.recognize import bp as recognize_bp
from api.faces     import bp as faces_bp
from api.persons   import bp as persons_bp
from api.database  import bp as database_bp

app.register_blueprint(system_bp)
app.register_blueprint(recognize_bp)
app.register_blueprint(faces_bp)
app.register_blueprint(persons_bp)
app.register_blueprint(database_bp)

# ── Root Route ────────────────────────────────────────────────────────────
@app.route("/")
def index():
    return jsonify({
        "status": "success",
        "message": "FaceID AI Service is running successfully on HuggingFace Spaces!",
        "endpoints": {
            "health_check": "/api/health",
            "recognize": "/api/recognitions",
            "faces": "/api/faces"
        }
    }), 200

# ── Global Error Handlers ─────────────────────────────────────────────────
@app.errorhandler(404)
def not_found(e):
    return jsonify({"error": {"code": "NOT_FOUND", "message": str(e), "status": 404}}), 404

@app.errorhandler(405)
def method_not_allowed(e):
    return jsonify({"error": {"code": "METHOD_NOT_ALLOWED", "message": str(e), "status": 405}}), 405

@app.errorhandler(500)
def internal_error(e):
    return jsonify({"error": {"code": "INTERNAL_ERROR", "message": str(e), "status": 500}}), 500

# ── Startup: Model + DB Cache ─────────────────────────────────────────────
def startup():
    # 1. Khởi tẑo InsightFace
    try:
        face_analyzer.initialize()
    except Exception as e:
        logger.critical(f"Failed to initialize InsightFace: {e}", exc_info=True)
        sys.exit(1)

    # 2. Load embeddings tα»« Supabase vΓ o RAM
    try:
        records = pg_storage.load_all_embeddings()
        face_analyzer.reload_cache(records)
    except Exception as e:
        logger.warning(f"[Startup] Supabase load failed: {e}. Falling back to local folder...")
        try:
            face_analyzer.reload_from_local_folder(config.DB_FOLDER)
        except Exception as fe:
            logger.warning(f"[Startup] Local fallback also failed: {fe}")

    logger.info(
        f"[Startup] Ready. Model={config.MODEL_NAME}, "
        f"Embeddings={face_analyzer.total}, Port={config.PORT}"
    )

# Khởi chẑy startup khi import (cần thiết cho Gunicorn)
startup()

# ── Main ──────────────────────────────────────────────────────────────────
if __name__ == "__main__":
    logger.info(f"Starting Flask RESTful API on {config.HOST}:{config.PORT} ...")
    app.run(host=config.HOST, port=config.PORT, debug=False)