# Construction Timelapse — Session Memory ## Tổng quan dự án Hệ thống timelapse construction monitoring. - **Repo Gogs**: `https://git.k9tech.space/kingkong/timelapse-2` - **Token**: `a6224640fb6f576f6cba46f0e1646500c87226b6` (Personal Access Token) - **Stack**: Next.js 14 + NestJS + PostgreSQL (Drizzle) + Redis + Python (device-agent) - **Package manager**: Bun (KHÔNG dùng npm vì workspace protocol không hoạt động với nexus registry) - **Registry**: Luôn thêm `--registry https://registry.npmjs.org/` khi cài package --- ## Kiến trúc hiện tại ``` MacOS (code) → Gogs push → webhook → Pi 4 (deploy) ↓ ┌─ postgres (docker) ├─ redis (docker) ├─ api-server (NestJS) ├─ web-dashboard (Next.js) └─ worker (Node) ``` --- ## Workspace config - **KHÔNG** dùng `apps/*` trong `workspaces` — `device-agent` là Python - Workspace config đúng: ```json "workspaces": ["apps/api-server", "apps/web-dashboard", "apps/worker", "packages/*"] ``` - `@shared-types` **KHÔNG hợp lệ** — dùng `@shared/types` - Luôn `cd` vào workspace trước khi `bun add` --- ## Gogs API ```bash # List repos curl -H "Authorization: token " https://git.k9tech.space/api/v1/user/repos # Create repo curl -X POST -H "Authorization: token " \ -H "Content-Type: application/json" \ -d '{"name":"repo-name","private":false}' \ https://git.k9tech.space/api/v1/user/repos # Create webhook curl -X POST -H "Authorization: token " \ -H "Content-Type: application/json" \ -d '{"type":"gogs","active":true,"config":{"url":"http://:9000/webhook","content_type":"json"},"events":["push"]}' \ https://git.k9tech.space/api/v1/repos///hooks # Push với token git remote set-url origin "https://@git.k9tech.space/kingkong/timelapse-2.git" git push -u origin main ``` --- ## Pi 4 Deploy Setup (TODO) Trên Pi 4 cần: 1. Clone repo: `git clone https://git.k9tech.space/kingkong/timelapse-2.git /opt/timelapse` 2. Tạo `/opt/timelapse/.env` với: ``` GOGS_TOKEN= GOGS_REPO_URL=https://git.k9tech.space/kingkong/timelapse-2.git DATABASE_URL=postgres://user:pass@postgres:5432/timelapse REDIS_URL=redis://redis:6379 JWT_SECRET=... ``` 3. Chạy webhook server: `node scripts/webhook-server.js` 4. Update Gogs webhook URL với IP thật của Pi 4 5. Deploy script: `bash scripts/deploy.sh` --- ## Docker Compose (in repo) ```bash docker compose up -d postgres redis docker compose up -d --build api-server web-dashboard worker ``` --- ## Phase 1 — Trạng thái: ✅ HOÀN THÀNH ### Backend (apps/api-server) - `src/modules/auth/` — AuthController/Service/Strategies stubs - `src/modules/devices/` — heartbeat endpoint + API key guard + repo/service/controller - `src/common/` — guards, decorators - `src/modules/orgs|projects|captures|videos|alerts/` — stubs - `src/realtime/` — stub ### Dashboard (apps/web-dashboard) - Dashboard home, device list, device detail pages - Tailwind CSS v3 + React Query --- ## Database (Drizzle Schema) - File: `apps/api-server/src/db/schema.ts` - Enum: org_status, project_status, device_status, capture_status, video_status, alert_severity, alert_type, user_role, command_result_status - Tables: organizations, projects, users, memberships, sessions, magic_links, devices, device_heartbeats, captures, videos, video_jobs, commands, alert_rules, alerts, audit_logs, activity_logs --- ## Các lỗi đã gặp và cách fix 1. `@shared-types` → `@shared/types` 2. `device-agent` (Python) trong npm workspaces → loại khỏi workspaces 3. `npm install` không hoạt động → `bun install --registry https://registry.npmjs.org/` 4. `bun add` với nexus registry → luôn `--registry https://registry.npmjs.org/` 5. `db` không export được → import từ `database.module.ts` riêng 6. `experimentalDecorators` thiếu → thêm vào `packages/config/tsconfig.node.json` 7. TSC output vào `src/` → cleanup *.js/*.d.ts/*.js.map + fix outDir --- ## Realtime WebSocket — ✅ ĐÃ LÀM - Socket.io gateway tại `src/realtime/gateways/realtime.gateway.ts` - Events: `device.heartbeat`, `device.status.changed`, `capture.uploaded`, `alert.opened` - Dashboard tự refetch khi nhận WS event (useSocket hook) - Client subscribe theo project room: `subscribe:project` ## Device installer (Pi) — ✅ ĐÃ LÀM - `apps/device-agent/agent/main.py` - claim device, poll approval, save key, heartbeat loop - `apps/device-agent/systemd/timelapse-agent.service` - `apps/device-agent/scripts/install-agent.sh` - `apps/device-agent/requirements.txt` - `apps/device-agent/README.md` ## Device installer (Pi) — ✅ ĐÃ LÀM - `apps/device-agent/agent/main.py` - claim device, poll approval, save key, heartbeat loop - `apps/device-agent/systemd/timelapse-agent.service` - `apps/device-agent/scripts/install-agent.sh` - `apps/device-agent/requirements.txt` - `apps/device-agent/README.md` ## TODO — Phase tiếp theo 1. **DB migration**: drizzle-kit push + seed data (trên Pi) 2. **Role-based access control nâng cao** (org_admin/project_manager/viewer) 3. **Device-agent** (Python) heartbeat sender 4. **Realtime WebSocket** cho status update 5. **Google OAuth thật** (redirect + callback) 6. **Worker thực tế** (hiện vẫn restart vì stub) ## Đã hoàn thành gần đây - Auth thực sự: register/login/refresh/logout + JWT guard + /auth/me - Dashboard auth flow: /login, ProtectedRoute, logout, auto attach Bearer token - Orgs CRUD API (`/v1/orgs`) có JWT guard - RBAC theo role: - `org_admin`: full org/project/member management - `project_manager`: create/update project - `viewer`: read-only - Membership management endpoints: - `GET /v1/orgs/:id/members` - `POST /v1/orgs/:id/members` (org_admin) - `PATCH /v1/orgs/:id/members/:userId` (org_admin) - `DELETE /v1/orgs/:id/members/:userId` (org_admin) - Projects CRUD API: - `POST /v1/projects` - `GET /v1/projects?orgId=` - `GET /v1/projects/:id` - `PATCH /v1/projects/:id` - `DELETE /v1/projects/:id` (đều có JwtAuthGuard) --- ## Lệnh nhanh (MacOS dev) ```bash # Cài deps bun install --registry https://registry.npmjs.org/ # Dev cd apps/api-server && bun run dev cd apps/web-dashboard && bun run dev # Build (verify) cd apps/api-server && bun run build cd apps/web-dashboard && bun run build # Test heartbeat curl -X POST http://localhost:3001/v1/devices/test-device/heartbeat \ -H "Content-Type: application/json" \ -H "X-API-Key: dev-key-123" \ -d '{"deviceId":"test-device","apiKey":"dev-key-123","status":"online","storageFreeGb":50,"capturesToday":12,"firmwareVersion":"1.0.0"}' ```