|
|
@@ -2,124 +2,147 @@
|
|
|
|
|
|
## Tổng quan dự án
|
|
|
Hệ thống timelapse construction monitoring.
|
|
|
-- **Repo**: `/Users/kingkong/Documents/code/timelaspe-2`
|
|
|
-- **Stack**: Next.js + NestJS + PostgreSQL (Drizzle) + Python (device-agent) + Redis
|
|
|
-- **Package manager**: Bun (KHÔNG dùng npm vì workspace protocol không hoạt động với nexus registry của môi trường)
|
|
|
-- **Registry**: Luôn thêm `--registry https://registry.npmjs.org/` khi cài package từ public npm
|
|
|
+- **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
|
|
|
|
|
|
```
|
|
|
-construction-timelapse/
|
|
|
-├── apps/
|
|
|
-│ ├── api-server/ # NestJS — API backend
|
|
|
-│ ├── web-dashboard/ # Next.js 14 — Dashboard UI
|
|
|
-│ ├── worker/ # (stub)
|
|
|
-│ └── device-agent/ # Python — Pi agent (chưa implement)
|
|
|
-├── packages/
|
|
|
-│ ├── shared-types/ # Shared TypeScript types/enums
|
|
|
-│ └── config/ # tsconfig.base
|
|
|
-└── docs/
|
|
|
- ├── api/ # (trống)
|
|
|
- └── firmware/ # (trống)
|
|
|
+MacOS (code) → Gogs push → webhook → Pi 4 (deploy)
|
|
|
+ ↓
|
|
|
+ ┌─ postgres (docker)
|
|
|
+ ├─ redis (docker)
|
|
|
+ ├─ api-server (NestJS)
|
|
|
+ ├─ web-dashboard (Next.js)
|
|
|
+ └─ worker (Node)
|
|
|
```
|
|
|
|
|
|
---
|
|
|
|
|
|
-## Workspace config (quan trọng!)
|
|
|
+## 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ệ** — đổi thành `@shared/types` ở mọi nơi (package.json + tsconfig.json)
|
|
|
+- `@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 <TOKEN>" https://git.k9tech.space/api/v1/user/repos
|
|
|
+
|
|
|
+# Create repo
|
|
|
+curl -X POST -H "Authorization: token <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 <TOKEN>" \
|
|
|
+ -H "Content-Type: application/json" \
|
|
|
+ -d '{"type":"gogs","active":true,"config":{"url":"http://<pi>:9000/webhook","content_type":"json"},"events":["push"]}' \
|
|
|
+ https://git.k9tech.space/api/v1/repos/<user>/<repo>/hooks
|
|
|
+
|
|
|
+# Push với token
|
|
|
+git remote set-url origin "https://<TOKEN>@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=<deploy_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)
|
|
|
-**Chạy dev**: `cd apps/api-server && bun run dev`
|
|
|
-**Build**: `cd apps/api-server && bun run build` ✅
|
|
|
-
|
|
|
-**Auth module** (`src/modules/auth/`):
|
|
|
-- `auth.module.ts`, `auth.controller.ts`, `auth.service.ts` — stub endpoints
|
|
|
-- `strategies/jwt.strategy.ts`, `strategies/google.strategy.ts` — OAuth stubs
|
|
|
-
|
|
|
-**Devices module** (`src/modules/devices/`):
|
|
|
-- `devices.controller.ts` — 4 endpoints:
|
|
|
- - `POST /v1/devices/:deviceId/heartbeat` — X-API-Key auth
|
|
|
- - `GET /v1/devices` — list
|
|
|
- - `GET /v1/devices/:id` — detail
|
|
|
- - `GET /v1/devices/:id/heartbeats` — history
|
|
|
-- `devices.service.ts` — heartbeat logic + API key verify
|
|
|
-- `devices.repository.ts` — Drizzle queries
|
|
|
-- `dto/heartbeat.dto.ts` — class-validator DTO
|
|
|
-
|
|
|
-**Common guards/decorators** (`src/common/`):
|
|
|
-- `guards/api-key.guard.ts` — X-API-Key header guard
|
|
|
-- `decorators/api-key.decorator.ts` — `@ApiKey()` decorator
|
|
|
-
|
|
|
-**Các module stub** (đều rỗng, chỉ để compile):
|
|
|
-- `modules/orgs/`, `modules/projects/`, `modules/captures/`,
|
|
|
- `modules/videos/`, `modules/alerts/`, `realtime/`
|
|
|
+- `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)
|
|
|
-**Chạy dev**: `cd apps/web-dashboard && bun run dev`
|
|
|
-**Build**: `cd apps/web-dashboard && bun run build` ✅
|
|
|
-
|
|
|
-- `pages/index.tsx` — Dashboard home (stats cards + recent devices)
|
|
|
-- `pages/devices/index.tsx` — Device list
|
|
|
-- `pages/devices/[id].tsx` — Device detail
|
|
|
-- `components/StatsCard.tsx`, `DeviceCard.tsx`
|
|
|
-- `hooks/useDashboardStats.ts`, `useDevices.ts`
|
|
|
-- Tailwind CSS v3 + PostCSS đã setup
|
|
|
+- 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, alert_state, user_role, command_result_status
|
|
|
+- 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
|
|
|
-- Lưu ý: table names trong Drizzle schema là **lowercase** (`devices`, `deviceHeartbeats`) nhưng khi truy vấn phải dùng `devices`, `deviceHeartbeats` đúng tên export
|
|
|
|
|
|
---
|
|
|
|
|
|
## Các lỗi đã gặp và cách fix
|
|
|
-1. `@shared-types` → đổi package name thành `@shared/types`
|
|
|
+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 → dùng `bun install --registry https://registry.npmjs.org/`
|
|
|
-4. `bun add` với nexus registry → luôn thêm `--registry https://registry.npmjs.org/`
|
|
|
+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. `rootDir` conflict → bỏ `rootDir` khỏi tsconfig base
|
|
|
+7. TSC output vào `src/` → cleanup *.js/*.d.ts/*.js.map + fix outDir
|
|
|
|
|
|
---
|
|
|
|
|
|
## TODO — Phase tiếp theo
|
|
|
-1. **Phase 2**: Auth thực sự (JWT login, refresh token, Google OAuth)
|
|
|
-2. **Phase 2**: Database migration + seed data (drizzle-kit push)
|
|
|
-3. **Phase 2**: Projects/Orgs CRUD endpoints
|
|
|
-4. **Phase 2**: Device-agent (Python) heartbeat sender
|
|
|
-5. **Phase 2**: Realtime WebSocket (Socket.io) cho device status
|
|
|
-6. **Phase 2**: Dashboard auth flow (login/logout)
|
|
|
+1. **Auth thực sự**: JWT login, refresh token, Google OAuth
|
|
|
+2. **DB migration**: drizzle-kit push + seed data
|
|
|
+3. **Pi 4 setup**: webhook server + deploy script + Gogs token
|
|
|
+4. **Device-agent** (Python) heartbeat sender
|
|
|
+5. **Projects/Orgs CRUD endpoints**
|
|
|
+6. **Dashboard login/logout**
|
|
|
|
|
|
---
|
|
|
|
|
|
-## Lệnh dev nhanh
|
|
|
+## Lệnh nhanh (MacOS dev)
|
|
|
```bash
|
|
|
-# Backend
|
|
|
-cd apps/api-server && bun run dev # http://localhost:3001
|
|
|
+# 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
|
|
|
|
|
|
-# Dashboard
|
|
|
-cd apps/web-dashboard && bun run dev # http://localhost:3000
|
|
|
+# Build (verify)
|
|
|
+cd apps/api-server && bun run build
|
|
|
+cd apps/web-dashboard && bun run build
|
|
|
|
|
|
-# API test (heartbeat stub)
|
|
|
-curl -X POST http://localhost:3001/v1/devices/test-device-1/heartbeat \
|
|
|
+# 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-1","apiKey":"dev-key-123","status":"online","storageFreeGb":50,"capturesToday":12,"firmwareVersion":"1.0.0"}'
|
|
|
+ -d '{"deviceId":"test-device","apiKey":"dev-key-123","status":"online","storageFreeGb":50,"capturesToday":12,"firmwareVersion":"1.0.0"}'
|
|
|
```
|