# TrackingToolWeb — CLAUDE.md ## Tổng quan dự án Hệ thống điểm danh khuôn mặt (Face Check-in) tích hợp với Management System tại `ms.prology.net`. Camera nhận diện khuôn mặt → FastAPI backend so khớp → ghi log → đồng bộ sang hệ thống quản lý. --- ## Kiến trúc ``` Frontend (React/Vite) → Backend (FastAPI/Python) → MySQL ↓ External MS API (ms.prology.net) ``` **Backend**: `main.py` (FastAPI) + `api.py` (external calls) + `sync.py` (data sync) **Frontend**: `client/src/` — React 19, TypeScript, TailwindCSS, Zustand **Database**: MySQL — database `face_checkin` **Deployment**: Backend phục vụ luôn frontend build (`static/`) qua route `/` --- ## Commands ### Backend ```bash # Development uvicorn main:app --reload # Production nohup uvicorn main:app --host 172.16.6.38 --port 8080 > log.log 2>&1 & ``` ### Frontend ```bash cd client npm run dev # dev server (Vite HMR) npm run build # build to client/dist/ npm run lint # ESLint ``` ### Deploy frontend Sau khi build, copy `client/dist/` vào `static/`. Đảm bảo asset paths trong `index.html` dùng prefix `/camera/static/assets/`. --- ## Cấu hình ### Backend (hardcoded — cần đưa vào .env) | Biến | Giá trị hiện tại | File | |------|-----------------|------| | DB URL | `mysql+pymysql://root:123@localhost/face_checkin` | `database.py` | | MS API base | `https://ms.prology.net/api/v1` | `api.py` | | JWT token | hardcoded string | `api.py` | | Face threshold | `0.42` | `main.py:217` | | Ratio threshold | `0.85` | `main.py:286` | | Recent check window | 0.5 phút | `main.py` | ### Frontend (.env trong `client/`) ``` VITE_API_BASE_URL=/camera # production (proxy qua nginx) VITE_API_BASE_MS=https://ms.prology.net ``` --- ## API Endpoints | Method | Path | Mô tả | |--------|------|-------| | GET | `/` | Phục vụ `static/index.html` | | POST | `/register` | Đăng ký khuôn mặt (name, email, file ảnh) | | POST | `/register-simple` | Đăng ký/cập nhật user không cần ảnh | | POST | `/checkin` | Nhận diện & điểm danh (file ảnh, camera_id) | | GET | `/logs` | 20 log điểm danh gần nhất | | GET | `/users` | Danh sách users + 5 checkpoint gần nhất | --- ## Database Schema ```sql students (id, name, email UNIQUE, avatar) student_encodings (id, student_id FK, encoding BLOB[1024 bytes = 128 float64], created_at) checkin_logs (id, student_id FK, time, camera_id, status[check in/check out]) ``` **Encoding format**: `np.float64` array 128 chiều → `.tobytes()` → BLOB 1024 bytes **Giải mã**: `np.frombuffer(blob, dtype=np.float64)` — validate `enc.size == 128` --- ## Logic nhận diện khuôn mặt (`/checkin`) 1. Nhận ảnh JPEG → lưu tạm `uploads/checkin.jpg` 2. `face_recognition.face_encodings()` → encoding 128-dim 3. Load **tất cả** encodings từ DB → so khớp `face_recognition.face_distance()` 4. Chọn student có `min_dist` nhỏ nhất 5. Kiểm tra: `best_distance ≤ 0.42` **AND** `ratio = best/second_best ≤ 0.85` 6. Kiểm tra recent check (tránh điểm danh 2 lần trong 30 giây) 7. Ghi `checkin_logs` → `BackgroundTask`: gửi ảnh + tạo history trên MS API **Bottleneck chính**: Bước 3 — load toàn bộ encodings, giải mã numpy, so khớp tuần tự trong request. --- ## External API (ms.prology.net) - `POST /api/v1/admin/tracking/scan-create` — tạo history check-in - `POST /api/v1/admin/tracking/send-image` — upload ảnh check-in - `GET /api/v1/admin/timekeeping` — lấy dữ liệu chấm công (dùng trong `sync.py`) Token JWT được hardcode trong `api.py` — cần chuyển sang env variable. --- ## Frontend State Management **Zustand stores:** - `use-app-store.ts` — `isAutoChecking`, `isCountDown`, `refreshLog`, video/canvas refs - `use-user-store.ts` — `currentUser` (user được chọn cho checkpoint) **Auto check-in**: interval 3000ms, gọi `/checkin` liên tục khi `isAutoChecking = true` --- ## Các lưu ý quan trọng - `UPLOAD_DIR = ./uploads/` — lưu ảnh tạm check-in, bị ghi đè mỗi lần (`checkin.jpg`) - `images/{YYYY_MM_DD}/` — lưu ảnh vĩnh viễn theo ngày (tạo trong `sync.py`) - DB session trong `/checkin` dùng `Depends(get_db)`, các endpoint khác tạo `SessionLocal()` trực tiếp — cần thống nhất - Tối đa 10 encodings/user (giới hạn trong `sync.py`) - CORS `allow_origins=["*"]` — chấp nhận vì deploy nội bộ