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

11 KiB
Raw Permalink Blame History

Build & Deploy

建置、本機開發、Docker 打包、部署的實務細節。


1. MakefilevisionA-backend

.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

# --- 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

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

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

api-server:
  environment:
    # 上方所有 env 保留,新增:
    VISIONA_SESSION_BACKEND: proxy-client
    VISIONA_PROXY_INTERNAL_URL: http://remote-proxy:3801

4.1 雛形推薦的開發方式

# .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兩者皆可用於 demomake run-dev 僅為本機開發便利工具(非交付物,見 design-doc.md §2.4 Non-Goal


5. 前端建置

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. 本機完整開發流程

# 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-serverDeployment + HorizontalPodAutoscaler
  • remote-proxyDeployment + 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

# .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 環境