Explorar el Código

Add Docker setup for local development

- Add Dockerfiles for api-server, web-dashboard, worker
- Fix workspace protocol: convert 'workspace:*' to 'file:' for npm compatibility
- Add .dockerignore to exclude node_modules from build context
- Add .env.example as environment template
- Add DOCKER-SETUP.md with full setup instructions
- Update .gitignore for pnpm artifacts (lock, store, etc)

Note: .env with secrets is gitignored and not committed.
Timelapse Dev hace 2 meses
padre
commit
301c88d1df
Se han modificado 7 ficheros con 263 adiciones y 35 borrados
  1. 19 0
      .dockerignore
  2. 10 15
      .env.example
  3. 7 0
      .gitignore
  4. 189 0
      DOCKER-SETUP.md
  5. 13 8
      apps/api-server/Dockerfile
  6. 13 6
      apps/web-dashboard/Dockerfile
  7. 12 6
      apps/worker/Dockerfile

+ 19 - 0
.dockerignore

@@ -0,0 +1,19 @@
+.git
+*.md
+.gitignore
+.eslintrc.js
+.prettierrc
+.env
+.env.example
+pnpm-lock.yaml
+HEARTBEAT.md
+SOUL.md
+AGENTS.md
+BOOTSTRAP.md
+TOOLS.md
+IDENTITY.md
+USER.md
+**/node_modules
+**/.next
+**/dist
+**/.pnpm

+ 10 - 15
.env.example

@@ -1,27 +1,22 @@
-# Database
-DATABASE_URL=postgres://timelapse:timelapse_dev_password@localhost:5432/timelapse_dev
+# GOGS_TOKEN: Personal access token from your Gogs/Gitea instance
+GOGS_TOKEN=your_gogs_token_here
 
-# Redis
-REDIS_URL=redis://localhost:6379
+# Database connection (đặt trong docker-compose hoặc .env)
+DATABASE_URL=postgres://timelapse:timelapse_dev_password@postgres:5432/timelapse_dev
 
-# Auth
+# Redis connection
+REDIS_URL=redis://redis:6379
+
+# JWT secrets — GENERATE NEW VALUES FOR PRODUCTION!
 JWT_SECRET=change-this-to-a-long-random-string-in-production
 REFRESH_TOKEN_SECRET=change-this-to-another-long-random-string-in-production
 
-# CORS
+# CORS origin (địa chỉ web dashboard)
 CORS_ORIGIN=http://localhost:3000
 
-# Google OAuth (optional)
+# Google OAuth (tuỳ chọn — bỏ trống nếu không dùng)
 GOOGLE_CLIENT_ID=
 GOOGLE_CLIENT_SECRET=
 GOOGLE_CALLBACK_URL=http://localhost:3001/v1/auth/google/callback
 
-# S3 / Object Storage (optional)
-AWS_ACCESS_KEY_ID=
-AWS_SECRET_ACCESS_KEY=
-AWS_REGION=ap-southeast-1
-S3_BUCKET=
-
-# API Server
-PORT=3001
 NODE_ENV=development

+ 7 - 0
.gitignore

@@ -12,3 +12,10 @@ bun.lockb
 .claude/
 *.tsbuildinfo
 .env
+
+# pnpm
+pnpm-lock.yaml
+pnpm-workspace.yaml
+.npmrc
+.pnpm-store/
+next-env.d.ts

+ 189 - 0
DOCKER-SETUP.md

@@ -0,0 +1,189 @@
+# Construction Timelapse — Docker Setup Guide
+
+Document này ghi lại quy trình chuẩn để build và chạy toàn bộ hệ thống timelapse.
+
+## Cấu trúc hệ thống
+
+```
+timelapse-2/
+├── apps/
+│   ├── api-server/      # NestJS API (port 3001)
+│   ├── web-dashboard/   # Next.js frontend (port 3000)
+│   ├── worker/          # Background worker
+│   └── device-agent/    # Device agent (Python)
+├── packages/
+│   ├── shared-types/    # Shared TypeScript types
+│   └── config/          # Shared tsconfig templates
+├── docker-compose.yml
+├── .env                  # Secrets (KHÔNG commit)
+└── .env.example          # Template cho .env
+```
+
+## Các bước thiết lập
+
+### 1. Prerequisites
+
+```bash
+# Docker Engine (cần API version tương thích với docker compose plugin)
+docker --version          # >= 24.0
+docker compose version    # >= 2.20
+
+# Nếu dùng Raspberry Pi / Debian cũ (docker.io package)
+# Cần nâng cấp lên Docker CE:
+curl -fsSL https://download.docker.com/linux/debian/gpg \
+  | sudo gpg --dearmor -o /usr/share/keyrings/docker-archive-keyring.gpg
+echo "deb [arch=arm64 signed-by=/usr/share/keyrings/docker-archive-keyring.gpg] \
+  https://download.docker.com/linux/debian bullseye stable" \
+  | sudo tee /etc/apt/sources.list.d/docker.list
+sudo apt-get install -y docker-ce
+```
+
+### 2. Clone repo
+
+```bash
+git clone <repo-url> /opt/timelapse
+cd /opt/timelapse
+```
+
+### 3. Tạo file `.env`
+
+```bash
+cp .env.example .env
+# Hoặc tạo mới với nội dung:
+cat > .env << 'EOF'
+GOGS_TOKEN=<your-gogs-token>
+DATABASE_URL=postgres://timelapse:timelapse_dev_password@postgres:5432/timelapse_dev
+REDIS_URL=redis://redis:6379
+JWT_SECRET=<generate-a-long-random-string>
+REFRESH_TOKEN_SECRET=<generate-another-long-random-string>
+CORS_ORIGIN=http://localhost:3000
+GOOGLE_CLIENT_ID=
+GOOGLE_CLIENT_SECRET=
+GOOGLE_CALLBACK_URL=http://localhost:3001/v1/auth/google/callback
+NODE_ENV=development
+EOF
+```
+
+> **Lưu ý:** `.env` đã được gitignore. KHÔNG bao giờ commit file này.
+
+### 4. Build và chạy
+
+```bash
+cd /opt/timelapse
+
+# Build tất cả images (lần đầu mất ~5-10 phút)
+sudo docker compose up -d --build
+
+# Kiểm tra trạng thái
+sudo docker compose ps
+
+# Xem logs
+sudo docker compose logs --tail=30
+```
+
+### 5. Kiểm tra services
+
+```bash
+# API health check
+curl http://localhost:3001/v1/auth/health
+
+# Web dashboard
+curl http://localhost:3000/ | head -5
+```
+
+**Kết quả mong đợi:**
+- `api-server` — Up, healthy
+- `postgres` — Up, healthy
+- `redis` — Up, healthy
+- `web-dashboard` — Up
+- `worker` — Up (hiện là stub)
+
+### 6. Dừng hệ thống
+
+```bash
+sudo docker compose down
+# Thêm -v để xóa volumes:
+sudo docker compose down -v
+```
+
+## Dockerfiles — Tại sao khác biệt so với repo gốc
+
+Repo gốc dùng `pnpm` với `workspace:*` protocol. Docker build cần 2 thay đổi:
+
+### Thay đổi 1: `sed` convert workspace protocol
+
+```dockerfile
+# Trong mỗi Dockerfile (api-server, worker, web-dashboard):
+RUN sed -i 's|"@shared/types": "workspace:\*"|"@shared/types": "file:../../packages/shared-types"|g' \
+  apps/api-server/package.json \
+  apps/web-dashboard/package.json \
+  apps/worker/package.json
+```
+
+Vì `npm` (Alpine) không hiểu `workspace:*` protocol — chỉ pnpm mới hỗ trợ.
+
+### Thay đổi 2: npm thay vì pnpm
+
+```dockerfile
+FROM node:20-alpine
+RUN apk add --no-cache npm
+WORKDIR /app
+...
+RUN npm install
+RUN cd packages/shared-types && npm run build
+RUN cd apps/<app> && npm run build
+```
+
+## Troubleshooting
+
+### `FATAL: database "timelapse" does not exist`
+
+Kiểm tra biến môi trường trong container:
+```bash
+sudo docker exec timelapse-api-server-1 sh -c 'echo $DATABASE_URL'
+# Phải trỏ đến timelapse_dev KHÔNG phải timelapse
+# Đúng: postgres://timelapse:...@postgres:5432/timelapse_dev
+```
+
+### PostgreSQL health check fail liên tục
+
+Chờ postgres khởi động xong (~10s). Kiểm tra:
+```bash
+sudo docker exec timelapse-postgres-1 pg_isready -U timelapse
+```
+
+### Worker restart loop
+
+Worker hiện chỉ là stub. Kiểm tra logs:
+```bash
+sudo docker compose logs worker --tail=20
+```
+
+### Muốn rebuild sạch
+
+```bash
+sudo docker compose down
+sudo docker builder prune -f
+sudo docker compose up -d --build
+```
+
+## Cấu hình thêm
+
+### Redis memory overcommit warning
+
+Thêm vào `/etc/sysctl.conf` (cần reboot):
+```
+vm.overcommit_memory = 1
+```
+
+Hoặc chạy ngay:
+```bash
+sudo sysctl vm.overcommit_memory=1
+```
+
+### Google OAuth (tuỳ chọn)
+
+Điền `GOOGLE_CLIENT_ID`, `GOOGLE_CLIENT_SECRET` trong `.env` và restart:
+```bash
+sudo docker compose restart api-server
+```

+ 13 - 8
apps/api-server/Dockerfile

@@ -1,16 +1,21 @@
 FROM node:20-alpine
 
+RUN apk add --no-cache npm
+
 WORKDIR /app
 
-# Install deps
-COPY package.json pnpm-lock.yaml* package-lock.json* ./
-COPY packages/*/package.json packages/
-COPY apps/*/package.json apps/
-RUN npm install --workspaces --include-workspace-root --ignore-scripts
+COPY package.json ./
+COPY packages packages/
+COPY apps apps/
+COPY tsconfig.json ./
+
+# Convert pnpm workspace:* protocol to npm file: protocol
+RUN sed -i 's|"@shared/types": "workspace:\*"|"@shared/types": "file:../../packages/shared-types"|g' apps/api-server/package.json apps/web-dashboard/package.json apps/worker/package.json
+
+RUN npm install
 
-# Build shared-types then api-server
-RUN npm run build --workspace=packages/shared-types
-RUN npm run build --workspace=apps/api-server
+RUN cd packages/shared-types && npm run build
+RUN cd apps/api-server && npm run build
 
 EXPOSE 3001
 CMD ["node", "apps/api-server/dist/main.js"]

+ 13 - 6
apps/web-dashboard/Dockerfile

@@ -1,13 +1,20 @@
 FROM node:20-alpine
 
+RUN apk add --no-cache npm
+
 WORKDIR /app
 
-COPY package.json pnpm-lock.yaml* package-lock.json* ./
-COPY packages/*/package.json packages/
-COPY apps/*/package.json apps/
-RUN npm install --workspaces --include-workspace-root --ignore-scripts
+COPY package.json ./
+COPY packages packages/
+COPY apps apps/
+COPY tsconfig.json ./
+
+RUN sed -i 's|"@shared/types": "workspace:\*"|"@shared/types": "file:../../packages/shared-types"|g' apps/api-server/package.json apps/web-dashboard/package.json apps/worker/package.json
+
+RUN npm install
 
-RUN npm run build --workspace=apps/web-dashboard
+RUN cd packages/shared-types && npm run build
+RUN cd apps/web-dashboard && npm run build
 
 EXPOSE 3000
-CMD ["npm", "start", "--workspace=apps/web-dashboard"]
+CMD ["sh", "-c", "cd apps/web-dashboard && npm start"]

+ 12 - 6
apps/worker/Dockerfile

@@ -1,13 +1,19 @@
 FROM node:20-alpine
 
+RUN apk add --no-cache npm
+
 WORKDIR /app
 
-COPY package.json pnpm-lock.yaml* package-lock.json* ./
-COPY packages/*/package.json packages/
-COPY apps/*/package.json apps/
-RUN npm install --workspaces --include-workspace-root --ignore-scripts
+COPY package.json ./
+COPY packages packages/
+COPY apps apps/
+COPY tsconfig.json ./
+
+RUN sed -i 's|"@shared/types": "workspace:\*"|"@shared/types": "file:../../packages/shared-types"|g' apps/api-server/package.json apps/web-dashboard/package.json apps/worker/package.json
+
+RUN npm install
 
-RUN npm run build --workspace=packages/shared-types
-RUN npm run build --workspace=apps/worker
+RUN cd packages/shared-types && npm run build
+RUN cd apps/worker && npm run build
 
 CMD ["node", "apps/worker/dist/main.js"]