jim800121chen fb7da5d180 chore(autoflow): migrate .autoflow/ 共享層文件至 docs/autoflow/
依 autoflow-agent workspace v2 設計把 PRD / 設計 / 架構 / 交付類
共享文件從個人層 .autoflow/(ignored)搬到 docs/autoflow/(進 git),
讓團隊可共享產品與架構文件,個人層只留 progress / review / testing 等
per-branch 筆記。

- 02-prd/        21 個檔(PRD、features、market-analysis 等)
- 03-design/     18 個檔(design-spec、wireframes、flows 等)
- 04-architecture/ 31 個檔(TDD、design-doc、ADR×14、API 規格等)
- 07-delivery/   3 個檔(project-summary、phase-0.6-handover、stage-deployment-setup)

合計 73 檔。原檔已從 .autoflow/ 移除(migration 工具執行 git mv,
但因 .autoflow/ 在 .gitignore 中、git 將此操作視為新增、無 rename history)。
2026-05-04 16:55:55 +08:00

344 lines
11 KiB
Markdown
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

# Build & Deploy
> 建置、本機開發、Docker 打包、部署的實務細節。
---
## 1. MakefilevisionA-backend
```makefile
.PHONY: build build-api build-proxy build-dev test clean docker-build docker-up docker-down run-dev
GO ?= go
GO_FLAGS ?= -ldflags="-s -w"
OUT_DIR ?= dist
VERSION ?= $(shell git describe --tags --always --dirty 2>/dev/null || echo "dev")
build: build-api build-proxy
build-api:
$(GO) build $(GO_FLAGS) -o $(OUT_DIR)/api-server ./cmd/api-server
build-proxy:
$(GO) build $(GO_FLAGS) -o $(OUT_DIR)/remote-proxy ./cmd/remote-proxy
test:
$(GO) test -race -coverprofile=coverage.out ./...
lint:
$(GO) vet ./...
gofmt -l . | grep -v '^$$' && exit 1 || true
clean:
rm -rf $(OUT_DIR) coverage.out
# --- Docker ---
docker-build:
docker build -f docker/Dockerfile.api-server -t visiona/api-server:$(VERSION) .
docker build -f docker/Dockerfile.remote-proxy -t visiona/remote-proxy:$(VERSION) .
docker-up:
docker compose -f docker/docker-compose.yml up --build
docker-down:
docker compose -f docker/docker-compose.yml down
# --- Dev ---
# 本機同時啟動兩個 binary非雛形交付物僅開發便利
# 交付物定義見 design-doc.md §2.4 Non-Goal。
run-dev:
@trap 'kill 0' EXIT; \
$(GO) run ./cmd/remote-proxy & \
$(GO) run ./cmd/api-server & \
wait
```
---
## 2. Dockerfile.api-server
```dockerfile
# --- build stage ---
FROM golang:1.26-alpine AS build
WORKDIR /src
COPY go.mod go.sum ./
RUN go mod download
COPY . .
RUN CGO_ENABLED=0 go build -ldflags="-s -w" -o /out/api-server ./cmd/api-server
# --- runtime stage ---
FROM gcr.io/distroless/static:nonroot
WORKDIR /app
COPY --from=build /out/api-server /app/api-server
USER nonroot:nonroot
EXPOSE 3001
ENTRYPOINT ["/app/api-server"]
```
## 3. Dockerfile.remote-proxy
```dockerfile
FROM golang:1.26-alpine AS build
WORKDIR /src
COPY go.mod go.sum ./
RUN go mod download
COPY . .
RUN CGO_ENABLED=0 go build -ldflags="-s -w" -o /out/remote-proxy ./cmd/remote-proxy
FROM gcr.io/distroless/static:nonroot
WORKDIR /app
COPY --from=build /out/remote-proxy /app/remote-proxy
USER nonroot:nonroot
EXPOSE 3800 3801
ENTRYPOINT ["/app/remote-proxy"]
```
---
## 4. docker/docker-compose.yml
```yaml
services:
api-server:
build:
context: ../
dockerfile: docker/Dockerfile.api-server
image: visiona/api-server:dev
ports: ["3001:3001"]
environment:
VISIONA_API_PORT: "3001"
VISIONA_SESSION_BACKEND: inmemory
VISIONA_STORAGE_BACKEND: localfs
VISIONA_STORAGE_LOCALFS_ROOT: /data/storage
VISIONA_STORAGE_LOCALFS_BASE_URL: http://localhost:3001/storage
VISIONA_STORAGE_SIGNING_SECRET: dev-secret-do-not-use-in-prod
VISIONA_AUTH_MODE: static
VISIONA_STATIC_USER_ID: demo-user
VISIONA_PAIRING_MODE: static
VISIONA_PAIRING_TOKEN: "${VISIONA_PAIRING_TOKEN}"
VISIONA_CONVERTER_MODE: stub
volumes:
- storage-data:/data
remote-proxy:
build:
context: ../
dockerfile: docker/Dockerfile.remote-proxy
image: visiona/remote-proxy:dev
ports:
- "3800:3800" # tunnel
- "3801:3801" # internal
environment:
VISIONA_TUNNEL_PORT: "3800"
VISIONA_PROXY_INTERNAL_PORT: "3801"
VISIONA_SESSION_BACKEND: inmemory
VISIONA_PAIRING_MODE: static
VISIONA_PAIRING_TOKEN: "${VISIONA_PAIRING_TOKEN}"
# 雛形設計ADR-006 / Q1
# - remote-proxy 是唯一持有 yamux.Session 的 processin-memory
# - api-server 無狀態,透過 internal HTTP 向 remote-proxy 查詢 session
# - 兩 binary 之間用 VISIONA_PROXY_INTERNAL_URL 連結(下方 api-server 已設 env
volumes:
storage-data:
```
另外 api-server service 需要新增 env指向 remote-proxy 的 internal URL
```yaml
api-server:
environment:
# 上方所有 env 保留,新增:
VISIONA_SESSION_BACKEND: proxy-client
VISIONA_PROXY_INTERNAL_URL: http://remote-proxy:3801
```
### 4.1 雛形推薦的開發方式
```bash
# .env
VISIONA_PAIRING_TOKEN="vAc_$(openssl rand -hex 16)" # 格式見 security.md §1.3
# 方式 1本機 Makefile 平行跑兩個 binary開發便利工具
make run-dev
# 方式 2Docker Compose更接近 Production 拓撲)
make docker-up
```
**結論**:雛形交付物是雙 binary + docker-compose兩者皆可用於 demo`make run-dev` 僅為本機開發便利工具(**非交付物**,見 design-doc.md §2.4 Non-Goal
---
## 5. 前端建置
```bash
cd visionA-frontend
pnpm install
pnpm dev # 本機開發 http://localhost:3000
pnpm build # 產出 .next/
pnpm start # 生產模式跑 Next.js server
```
### 5.1 Next.js Build 模式
| 模式 | 用途 | 如何設定 |
|------|------|---------|
| `next build` + `next start` | SSR / ISR 支援 | 適合 Phase 1需要 Next.js runtime |
| `output: 'export'` | 靜態 export放 CDN | 若所有頁面都可靜態化,最便宜 |
**雛形建議**`next start` on Node方便 API rewrites / middlewarePhase 1 視需求切換。
### 5.2 環境變數
```
# visionA-frontend/.env.example
NEXT_PUBLIC_API_BASE=http://localhost:3001
NEXT_PUBLIC_WS_BASE=ws://localhost:3001
# 雛形開發用:
NEXT_PUBLIC_DEV_PAIRING_TOKEN=<same as VISIONA_PAIRING_TOKEN>
```
---
## 6. 本機完整開發流程
```bash
# Terminal 1 — Backend
cd visionA-backend
export VISIONA_PAIRING_TOKEN=$(openssl rand -hex 32)
export VISIONA_STATIC_USER_ID=demo-user
make run-dev
# api at :3001, tunnel at :3800
# Terminal 2 — local agent (local-tool 現有 + 開雲端模式,或 POC 的 edge-ai-server)
cd /Users/jimchen/Innovedus/edge-ai-platform/edge-ai-platform
./dist/edge-ai-server --relay-url=ws://localhost:3800/tunnel/connect --relay-token=$VISIONA_PAIRING_TOKEN
# Terminal 3 — Frontend
cd visionA-frontend
cp .env.example .env.local
# 編輯 .env.local 填入 PAIRING_TOKEN
pnpm dev
# http://localhost:3000
```
---
## 7. Phase 1 部署草圖
### 7.1 AWS ECS Fargate + Application Load Balancer
```
┌──────────────────────────────────────────────────────────────┐
│ Route 53 — api.visiona.cloud / proxy.visiona.cloud │
└─────────────────────┬────────────────────┬───────────────────┘
│ │
▼ ▼
┌─────────────────┐ ┌───────────────────┐
│ ALB │ │ NLB │
│ (HTTPS/WSS) │ │ (TCP passthrough)│
│ api.* │ │ proxy.* │
└────────┬────────┘ └─────────┬─────────┘
│ │
▼ ▼
┌─────────────────────┐ ┌─────────────────────┐
│ ECS Service: │ │ ECS Service: │
│ api-server │ │ remote-proxy │
│ (Fargate, 2+ tasks)│ │ (Fargate, 2+ tasks) │
└──────┬──────────────┘ └──────────┬──────────┘
│ │
└──────┬───────────────┬──────┘
│ │
▼ ▼
┌──────────────┐ ┌─────────────────┐
│ ElastiCache │ │ RDS │
│ Redis │ │ PostgreSQL │
└──────────────┘ └─────────────────┘
▲ ▲
│ │
└────────────────┘
┌──────────────┐
│ S3 bucket │
└──────────────┘
```
### 7.2 Kubernetes 方案
- api-server`Deployment` + `HorizontalPodAutoscaler`
- remote-proxy`Deployment` + HPA按 tunnel 數 metric
- Redis / Postgresmanaged service 或 StatefulSet
- Ingressnginx-ingress 或 cloud LB controller
**Cloud-agnostic 原則**Helm chart 不綁特定雲storage / DB 依 env 注入連線資訊。
---
## 8. CI/CDPhase 1 規劃)
### 8.1 GitHub Actions
```yaml
# .github/workflows/ci.yml
name: CI
on: [push, pull_request]
jobs:
backend-test:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
- uses: actions/setup-go@v5
with: { go-version: '1.26' }
- run: cd visionA-backend && make test
- run: cd visionA-backend && make lint
frontend-test:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
- uses: pnpm/action-setup@v3
- uses: actions/setup-node@v4
with: { node-version: '20', cache: 'pnpm' }
- run: cd visionA-frontend && pnpm install --frozen-lockfile
- run: cd visionA-frontend && pnpm test
- run: cd visionA-frontend && pnpm build
```
### 8.2 Release PipelinePhase 1
1. PR merged to `main`
2. CI 跑 test + lint + build
3. 產出 Docker image → push to registryECR / GCR / GitHub Packages
4. Tag image 為 `main-<sha>`
5. 手動觸發 deploy或 GitOps 自動)→ ECS task definition 更新 / K8s rollout
---
## 9. 環境變數對照表(摘要)
| Env | 雛形 | Phase 1 |
|-----|------|--------|
| `VISIONA_AUTH_MODE` | `static` | `clerk` / `oidc` |
| `VISIONA_PAIRING_MODE` | `static` | `db` |
| `VISIONA_SESSION_BACKEND` | `inmemory` | `redis` |
| `VISIONA_STORAGE_BACKEND` | `localfs` | `s3` |
| `VISIONA_CONVERTER_MODE` | `stub` | `http` |
| `VISIONA_REDIS_URL` | — | `redis://...` |
| `VISIONA_DB_URL` | — | `postgres://...` |
| `VISIONA_S3_*` | — | 實際 credentials |
---
**雛形實作重點**
- `make run-dev`(單 binary 兩 listener
- 不需要 DockerDocker 檔案寫好以備 Phase 1
- 不需要 CI但 Makefile 有 `test` + `lint` 方便本機檢查
**Phase 1 必做**
- Docker image CI 產出
- K8s / ECS manifest
- Blue-green 或 rolling deploy
- Staging 環境