# Project-Manager — Design Spec

**Date:** 2026-05-29
**Author:** Gajeel (for Trường Giang)
**Status:** Approved (brainstorming complete)

---

## 1. Mục tiêu (Goal)

Xây dựng webapp **Project-Manager (PM)** chạy nội bộ trên LXC `192.168.40.20`, dùng để:

- Tracking toàn bộ project trong `/data/video-pipeline/LXC4103/project/` + project external add thủ công.
- Hiển thị trạng thái runtime, git state, build/test health, activity, tasks/notes, deploy info.
- Build & deploy mỗi project sang LXC target qua Docker (build → push registry → SSH pull → compose up).
- Receive webhook từ git hook (commit/push) để cập nhật version + commit info realtime.
- Stream realtime updates và logs về UI qua SSE.

---

## 2. Kiến trúc tổng thể (Architecture)

3 LXC:

1. **LXC PM** (`192.168.40.20`) — host chính:
   - Next.js (App Router) full-stack — UI + API + SSE
   - Worker process (BullMQ consumer) — chạy background jobs
   - Redis (BullMQ backend)
   - SQLite (Prisma)
   - Docker registry self-hosted (`registry:2`) cổng `:5000`
2. **LXC Builder** — dedicated build node:
   - Docker daemon + BuildKit
   - PM connect qua Docker remote context (TCP+TLS hoặc SSH)
3. **LXC Targets** (n máy) — nơi project được deploy:
   - Docker + docker compose
   - PM SSH vào để `docker pull` + `docker compose up -d`
   - Mỗi project có config target riêng (IP, SSH creds, paths)

```
┌────────────────────────────────────────────────────────────┐
│ LXC PM (192.168.40.20)                                    │
│                                                            │
│  Next.js App ──► SQLite (Prisma)                          │
│       │                                                    │
│       ├──► SSE channel ──► Browser UI                     │
│       │                                                    │
│       └──► BullMQ ──► Redis ──► Worker process            │
│                                     │                      │
│                                     ├──► Git scan/sync    │
│                                     ├──► Health check     │
│                                     ├──► Build job ───────┼──► LXC Builder (Docker BuildKit)
│                                     ├──► Push image ──────┼──► registry:5000 (local)
│                                     └──► Deploy job ──────┼──► LXC Target (SSH + compose)
│                                                            │
│  Webhook endpoint POST /api/webhook/git ◄──────── git hook in each project repo
└────────────────────────────────────────────────────────────┘
```

---

## 3. Tech Stack

| Layer | Choice |
|---|---|
| Frontend + Backend | Next.js 15 App Router + TypeScript |
| Database | SQLite + Prisma ORM |
| Queue | BullMQ + Redis (Docker container) |
| Realtime | SSE (Server-Sent Events) |
| UI library | shadcn/ui + Tailwind CSS, dark mode default |
| Auth | Single password, hash bcrypt, session cookie |
| Docker registry | `registry:2` self-hosted trên LXC PM |
| Build | Remote Docker context → LXC Builder (BuildKit) |
| Deploy | SSH (`node-ssh`) + Docker compose |
| Encryption | AES-256-GCM cho creds (key trong env var `PM_ENCRYPTION_KEY`) |

---

## 4. Data Model (Prisma schema khung)

```prisma
model Project {
  id                String   @id @default(cuid())
  name              String   @unique
  path              String   // local path on PM (e.g. /data/video-pipeline/LXC4103/project/foo)
  source            String   // "auto-scan" | "manual"
  gitRemote         String?
  branch            String?
  version           String?
  lastCommitHash    String?
  lastCommitMessage String?
  lastCommitTime    DateTime?

  // runtime
  status            String   @default("unknown") // running|stopped|unknown
  port              Int?
  pid               Int?
  uptimeStart       DateTime?

  // health
  buildStatus       String?  // pass|fail|never
  testStatus        String?
  lintErrors        Int?
  outdatedDeps      Int?

  // deploy
  deployTargetId    String?
  deployTarget      DeployTarget? @relation(fields: [deployTargetId], references: [id])
  deployedVersion   String?
  deployedAt        DateTime?
  composeSource     String   @default("repo") // repo|template|inline
  composeInline     String?  // YAML if source=inline

  envVarsEncrypted  String?  // AES-256-GCM JSON blob

  tasks             Task[]
  notes             String?
  tags              String?  // CSV
  priority          Int      @default(0)

  logs              LogEntry[]
  deploys           DeployHistory[]
  commits           CommitLog[]

  createdAt         DateTime @default(now())
  updatedAt         DateTime @updatedAt
}

model DeployTarget {
  id          String   @id @default(cuid())
  name        String
  host        String   // IP / hostname
  sshUser     String   @default("root")
  authMode    String   // "password" | "key"
  authBlob    String   // encrypted password OR private key
  composePath String   @default("/opt/{name}/docker-compose.yml")
  projects    Project[]
}

model Task {
  id        String  @id @default(cuid())
  projectId String
  project   Project @relation(fields: [projectId], references: [id])
  title     String
  done      Boolean @default(false)
  createdAt DateTime @default(now())
}

model LogEntry {
  id         String  @id @default(cuid())
  projectId  String
  project    Project @relation(fields: [projectId], references: [id])
  type       String  // build|deploy|runtime|webhook|system
  level      String  // info|warn|error
  message    String
  timestamp  DateTime @default(now())
  @@index([projectId, type, timestamp])
}

model CommitLog {
  id         String  @id @default(cuid())
  projectId  String
  project    Project @relation(fields: [projectId], references: [id])
  hash       String
  message    String
  author     String
  timestamp  DateTime
  @@index([projectId, timestamp])
}

model DeployHistory {
  id          String  @id @default(cuid())
  projectId   String
  project     Project @relation(fields: [projectId], references: [id])
  version     String
  commitHash  String
  imageTag    String
  status      String  // success|failed|in_progress
  startedAt   DateTime
  finishedAt  DateTime?
  logBlob     String? // truncated build/deploy stdout
}

model Setting {
  key   String @id
  value String
}
```

---

## 5. Module Decomposition

| Module | Responsibility |
|---|---|
| `app/` | Next.js routes (UI pages + API routes) |
| `lib/db/` | Prisma client, helpers |
| `lib/crypto/` | AES-256-GCM encrypt/decrypt cho creds & env vars |
| `lib/auth/` | Single-password login, session cookie, middleware |
| `lib/git/` | Git scan: read commit, branch, version từ `package.json`/`pyproject.toml` |
| `lib/scanner/` | Auto-scan thư mục, detect project, sync vào DB |
| `lib/queue/` | BullMQ producer (enqueue jobs) |
| `lib/ssh/` | SSH client wrapper (node-ssh), pool connections |
| `lib/docker/` | Docker remote context, build, push, image tag mgmt |
| `lib/sse/` | SSE event bus (in-memory pub-sub, broadcast tới connected clients) |
| `worker/` | Standalone Node process, BullMQ consumer |
| `worker/jobs/scan.ts` | Quét git state, cập nhật Project |
| `worker/jobs/health.ts` | Run lint/test/build status check |
| `worker/jobs/build.ts` | Docker build → push registry |
| `worker/jobs/deploy.ts` | SSH target → pull + compose up |
| `worker/jobs/runtime.ts` | start/stop/logs project local process |
| `app/api/webhook/git/route.ts` | Receive git hook payload |
| `app/api/sse/route.ts` | SSE endpoint, subscribe to event bus |

---

## 6. Core Workflows

### 6.1 Initial seed (lần đầu chạy)
1. Migration Prisma → tạo schema.
2. Auto-scan `/data/video-pipeline/LXC4103/project/`:
   - Mỗi subdir có `.git/` → coi là project.
   - Đọc tên (basename), git remote, branch, last commit, version (`package.json`/`pyproject.toml`).
   - Insert `Project` với `source="auto-scan"`.
3. Cài git hook script vào mỗi repo (`.git/hooks/post-commit` + `post-receive`):
   ```bash
   #!/bin/bash
   curl -X POST http://192.168.40.20:3000/api/webhook/git \
     -H "X-PM-Token: $PM_WEBHOOK_TOKEN" \
     -d "{\"project\":\"$(basename $(git rev-parse --show-toplevel))\",\"hash\":\"$(git rev-parse HEAD)\",\"message\":\"$(git log -1 --pretty=%B | head -1)\",\"branch\":\"$(git branch --show-current)\"}"
   ```

### 6.2 Webhook flow
1. POST `/api/webhook/git` với token + project + hash + message + branch.
2. API verify token, update `Project` row + insert `CommitLog`.
3. Emit SSE event `project.updated` → UI refresh card.
4. Enqueue job `health-check` → worker re-run lint/test status.

### 6.3 Build & Deploy flow (UI bấm Deploy)
1. UI POST `/api/projects/:id/deploy`.
2. API enqueue job `build` (BullMQ).
3. Worker `build.ts`:
   - Connect Docker remote context → LXC Builder.
   - Resolve compose source: `repo` (đọc file trong repo) / `template` (PM gen từ metadata) / `inline` (DB).
   - `docker buildx build` → tag `registry:5000/{project}:{version}-{commit_short}`.
   - Push registry.
   - Insert `DeployHistory` status=`in_progress`.
   - Emit SSE log lines realtime.
   - On success, enqueue `deploy` job.
4. Worker `deploy.ts`:
   - SSH vào `DeployTarget.host`.
   - `docker login registry:5000` (nếu cần auth).
   - Push compose file (rsync hoặc scp) nếu compose source = template/inline.
   - Inject `.env` (decrypt envVars, scp file).
   - `docker compose pull && docker compose up -d`.
   - Update `Project.deployedVersion`, `deployedAt`.
   - Update `DeployHistory.status=success`.
   - Emit SSE event.

### 6.4 Background jobs (cron-like, BullMQ repeat)
- `scan-projects` — every 5 min: re-scan thư mục mặc định, detect project mới.
- `sync-git-state` — every 2 min: cho mỗi project, đọc git state, update DB.
- `health-check` — every 10 min: run `npm test`, `npm run lint`, `npm run build` (timeout 60s).
- `runtime-status` — every 30s: check process running không, port mở không.

---

## 7. UI Pages (sơ đồ)

| Route | Mục đích |
|---|---|
| `/login` | Single-password login |
| `/` | Dashboard: list projects (cards với status, version, deploy info) |
| `/projects/:id` | Detail view: tabs (Overview / Logs / Tasks / Deploy / Env / Settings) |
| `/projects/:id/logs` | Stream + history logs (filter by type, level, search) |
| `/deploy-targets` | Manage LXC targets (CRUD, test SSH connection) |
| `/settings` | Encryption key status, webhook token, scan paths, registry config |

UI design: shadcn/ui + Tailwind, dark mode default, palette + font pairing chọn từ `ckm-ui-styling` databases.

---

## 8. Security

- Single password hash bcrypt (cost 12) lưu `Setting` table.
- Session cookie HttpOnly, SameSite=Lax, signed JWT (HS256, secret env var).
- Webhook token (random 32 char) check qua header `X-PM-Token`.
- AES-256-GCM cho:
  - DeployTarget.authBlob (password / private key)
  - Project.envVarsEncrypted
- Encryption key đọc từ env `PM_ENCRYPTION_KEY` (32 bytes hex), không lưu trong DB.
- `.env` file inject vào target: scp với mode 0600, owner root.

---

## 9. Error Handling

- Build/deploy job fail → status=`failed`, logBlob lưu stderr, SSE emit error event, UI badge đỏ trên card project.
- SSH timeout (30s default) → mark target as unhealthy.
- DB write fail → log error, retry với exponential backoff.
- BullMQ job retry: 3 lần với backoff `2s, 8s, 32s`.

---

## 10. Testing Strategy

- **Unit:** Prisma helpers, crypto, git scanner, compose template generator (Vitest).
- **Integration:** API routes với SQLite test DB, BullMQ với Redis container (Vitest + supertest).
- **E2E:** Playwright — login, scan, deploy mock target.
- **Worker jobs:** Mock Docker + SSH clients, test job logic.

---

## 11. Out of Scope (YAGNI)

- Multi-user / RBAC.
- Notifications (chỉ in-app, không Telegram/email/webhook ngoài).
- Cloud registry (Docker Hub / GHCR / Harbor) — dùng self-hosted `registry:2`.
- Log aggregation (Loki/Promtail) — SQLite đủ.
- Scheduled deploys, auto-deploy on push.
- Metrics/Grafana — chỉ status checks cơ bản.

---

## 12. Roadmap (sub-projects nếu cần tách)

Nếu project quá lớn, tách thành các phase. Đề xuất tách 4 plan:

1. **Phase 1 — Core Skeleton:** Next.js setup, Prisma, auth, project model, dashboard, auto-scan, manual add. (Working artifact: list & view projects.)
2. **Phase 2 — Git & Webhook:** Git scanner, webhook endpoint, commit log, SSE updates. (Working artifact: realtime version display.)
3. **Phase 3 — Build & Deploy:** Docker registry setup, BullMQ + Redis, builder LXC, deploy targets, deploy flow. (Working artifact: bấm deploy chạy được end-to-end.)
4. **Phase 4 — Polish:** Logs viewer, tasks/notes, env vars manager, health checks, UI design pass với ckm-ui-styling.

Mỗi phase = 1 spec → 1 plan → 1 implementation cycle riêng.

---

## 13. Approval

Spec này được Trường Giang approve qua brainstorming dialogue ngày 2026-05-29 trên Telegram.
