From 1cc5c54471a68239fe4156dec65512f4bd7eb2aa Mon Sep 17 00:00:00 2001 From: openclaw Date: Wed, 22 Jul 2026 00:05:43 +0800 Subject: [PATCH] Add one-command dev and production compose --- .dockerignore | 11 ++++++ .env.example | 15 +++---- README.md | 2 +- apps/web/Dockerfile | 23 +++++++++++ apps/web/nginx.conf | 20 ++++++++++ compose.frp-prod.yml | 34 ---------------- compose.pro.yaml | 83 +++++++++++++++++++++++++++++++++++++++ docs/内网穿透调试.md | 8 ++-- package.json | 5 ++- services/api/Dockerfile | 16 ++++++++ services/api/package.json | 1 + 11 files changed, 167 insertions(+), 51 deletions(-) create mode 100644 .dockerignore create mode 100644 apps/web/Dockerfile create mode 100644 apps/web/nginx.conf delete mode 100644 compose.frp-prod.yml create mode 100644 compose.pro.yaml create mode 100644 services/api/Dockerfile diff --git a/.dockerignore b/.dockerignore new file mode 100644 index 0000000..53ca06d --- /dev/null +++ b/.dockerignore @@ -0,0 +1,11 @@ +.git +.env +.env.* +node_modules +dist +apps/*/dist +services/*/dist +packages/*/dist +legacy-cent-app +codex-tmux-scroll +*.log diff --git a/.env.example b/.env.example index 249d033..2776c8f 100644 --- a/.env.example +++ b/.env.example @@ -2,7 +2,7 @@ DOMAIN=cents.homemade.net.cn # App install identity. Use "dev" for cents-dev and "pro" for cents-pro. -APP_VARIANT=dev +APP_VARIANT=pro # Optional overrides for the PWA install name/id. # VITE_APP_NAME=Cents Dev # VITE_APP_SHORT_NAME=Cents Dev @@ -12,15 +12,10 @@ APP_VARIANT=dev FRPC_SERVER_HOST=119.28.12.24 FRPC_SERVER_PORT=7000 -# Production target. For host-debug mode, keep host.docker.internal. -APP_HOST=host.docker.internal -APP_PORT=6064 -APP_PROTOCOL=http - # Use false only after the tunnel is known to work. TEST_MODE=false -# API and PostgreSQL. The development database is provided by compose.dev.yaml. -DATABASE_URL=postgres://cents:cents-dev-only@127.0.0.1:55432/cents -PUBLIC_ORIGIN=https://dev.cents.homemade.net.cn -COOKIE_SECURE=true +# PostgreSQL. Use a strong secret in real production. +POSTGRES_DB=cents +POSTGRES_USER=cents +POSTGRES_PASSWORD=change-me diff --git a/README.md b/README.md index d8a3a3e..1e16fc9 100644 --- a/README.md +++ b/README.md @@ -22,7 +22,7 @@ npm install npm run dev ``` -前端默认运行在 `http://localhost:5173/`。 +`npm run dev` 会同时启动 API 和前端。前端运行在 `http://localhost:6064/`,并与 `compose.dev.yaml` 的 frp 调试入口保持一致。 常用检查: diff --git a/apps/web/Dockerfile b/apps/web/Dockerfile new file mode 100644 index 0000000..7d7dc7e --- /dev/null +++ b/apps/web/Dockerfile @@ -0,0 +1,23 @@ +FROM node:22-alpine AS build + +WORKDIR /app + +COPY package.json package-lock.json tsconfig.base.json ./ +COPY apps/web/package.json apps/web/package.json +COPY packages/domain/package.json packages/domain/package.json +COPY services/api/package.json services/api/package.json +RUN npm ci + +COPY packages/domain packages/domain +COPY apps/web apps/web + +ARG APP_VARIANT=pro +ENV APP_VARIANT=$APP_VARIANT +RUN npm run build --workspace @cents/web + +FROM nginx:1.27-alpine + +COPY apps/web/nginx.conf /etc/nginx/conf.d/default.conf +COPY --from=build /app/apps/web/dist /usr/share/nginx/html + +EXPOSE 80 diff --git a/apps/web/nginx.conf b/apps/web/nginx.conf new file mode 100644 index 0000000..f896173 --- /dev/null +++ b/apps/web/nginx.conf @@ -0,0 +1,20 @@ +server { + listen 80; + server_name _; + + root /usr/share/nginx/html; + index index.html; + + location /api/ { + proxy_pass http://api:3000; + proxy_http_version 1.1; + proxy_set_header Host $host; + proxy_set_header X-Real-IP $remote_addr; + proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for; + proxy_set_header X-Forwarded-Proto $scheme; + } + + location / { + try_files $uri $uri/ /index.html; + } +} diff --git a/compose.frp-prod.yml b/compose.frp-prod.yml deleted file mode 100644 index 3328749..0000000 --- a/compose.frp-prod.yml +++ /dev/null @@ -1,34 +0,0 @@ -services: - frpc: - image: homemade/auto-cert/proxy:latest - restart: unless-stopped - environment: - DOMAIN: ${DOMAIN} - APP_HOST: ${APP_HOST} - APP_PORT: ${APP_PORT} - APP_PROTOCOL: ${APP_PROTOCOL} - FRPC_SERVER_HOST: ${FRPC_SERVER_HOST} - FRPC_SERVER_PORT: ${FRPC_SERVER_PORT} - extra_hosts: - - "host.docker.internal:host-gateway" - volumes: - - frp_cert_volume:/etc/letsencrypt - networks: - - frp_prod - - cert: - image: homemade/auto-cert/cert:latest - restart: "no" - command: --domain ${DOMAIN} --test-mode ${TEST_MODE} - volumes: - - frp_cert_volume:/etc/letsencrypt - networks: - - frp_prod - -volumes: - frp_cert_volume: - -networks: - frp_prod: - driver: bridge - diff --git a/compose.pro.yaml b/compose.pro.yaml new file mode 100644 index 0000000..413be35 --- /dev/null +++ b/compose.pro.yaml @@ -0,0 +1,83 @@ +services: + db: + image: postgres:17-alpine + restart: unless-stopped + environment: + POSTGRES_DB: ${POSTGRES_DB:-cents} + POSTGRES_USER: ${POSTGRES_USER:-cents} + POSTGRES_PASSWORD: ${POSTGRES_PASSWORD} + volumes: + - cents_prod_db:/var/lib/postgresql/data + healthcheck: + test: ["CMD-SHELL", "pg_isready -U ${POSTGRES_USER:-cents} -d ${POSTGRES_DB:-cents}"] + interval: 3s + timeout: 3s + retries: 20 + networks: + - app + + api: + build: + context: . + dockerfile: services/api/Dockerfile + restart: unless-stopped + environment: + DATABASE_URL: postgres://${POSTGRES_USER:-cents}:${POSTGRES_PASSWORD}@db:5432/${POSTGRES_DB:-cents} + PUBLIC_ORIGIN: https://${DOMAIN} + COOKIE_SECURE: "true" + PORT: "3000" + depends_on: + db: + condition: service_healthy + networks: + - app + + web: + build: + context: . + dockerfile: apps/web/Dockerfile + args: + APP_VARIANT: ${APP_VARIANT:-pro} + restart: unless-stopped + depends_on: + - api + networks: + - app + + frpc: + image: homemade/auto-cert/proxy:latest + restart: unless-stopped + environment: + DOMAIN: ${DOMAIN} + APP_HOST: web + APP_PORT: "80" + APP_PROTOCOL: http + FRPC_SERVER_HOST: ${FRPC_SERVER_HOST} + FRPC_SERVER_PORT: ${FRPC_SERVER_PORT} + volumes: + - frp_cert_volume:/etc/letsencrypt + depends_on: + - web + - cert + networks: + - app + - frp_prod + + cert: + image: homemade/auto-cert/cert:latest + restart: "no" + command: --domain ${DOMAIN} --test-mode ${TEST_MODE:-false} + volumes: + - frp_cert_volume:/etc/letsencrypt + networks: + - frp_prod + +volumes: + cents_prod_db: + frp_cert_volume: + +networks: + app: + driver: bridge + frp_prod: + driver: bridge diff --git a/docs/内网穿透调试.md b/docs/内网穿透调试.md index 7070c73..a8c5415 100644 --- a/docs/内网穿透调试.md +++ b/docs/内网穿透调试.md @@ -37,10 +37,10 @@ ## 启动 -先启动前端: +启动 API 和前端: ```bash -npm run dev --workspace @cents/web -- --port 6064 +npm run dev ``` 再启动内网穿透: @@ -74,7 +74,7 @@ curl -vkI https://$DOMAIN ```bash cp .env.example .env -docker compose -f compose.frp-prod.yml up -d +docker compose -f compose.pro.yaml up -d --build ``` 不要提交真实 `.env`。 @@ -83,5 +83,5 @@ docker compose -f compose.frp-prod.yml up -d - 证书申请失败:检查域名 DNS、VPS 80 端口、frps vhost HTTP 配置。 - HTTPS 访问失败:检查 frps 443 端口和 frpc 日志。 -- 后端连不上:确认本机 Vite 服务正在监听 `6064`。 +- 后端连不上:确认 `npm run dev` 已启动 API 和监听 `6064` 的 Vite 服务。 - frps 有 token:需要同步配置 frps 和 frpc,不要把真实 token 提交到仓库。 diff --git a/package.json b/package.json index 51147d6..eece7a6 100644 --- a/package.json +++ b/package.json @@ -9,7 +9,9 @@ "packages/*" ], "scripts": { - "dev": "npm run dev --workspace @cents/web", + "dev": "npm run dev:api & npm run dev:web:tunnel & wait", + "dev:web": "npm run dev --workspace @cents/web", + "dev:web:tunnel": "npm run dev --workspace @cents/web -- --port 6064", "dev:api": "npm run dev --workspace @cents/api", "build": "npm run build --workspaces --if-present", "typecheck": "npm run typecheck --workspaces --if-present", @@ -19,4 +21,3 @@ "node": ">=22" } } - diff --git a/services/api/Dockerfile b/services/api/Dockerfile new file mode 100644 index 0000000..e604335 --- /dev/null +++ b/services/api/Dockerfile @@ -0,0 +1,16 @@ +FROM node:22-alpine + +WORKDIR /app + +COPY package.json package-lock.json tsconfig.base.json ./ +COPY apps/web/package.json apps/web/package.json +COPY packages/domain/package.json packages/domain/package.json +COPY services/api/package.json services/api/package.json +RUN npm ci + +COPY packages/domain packages/domain +COPY services/api services/api +RUN npm run typecheck --workspace @cents/api + +EXPOSE 3000 +CMD ["npm", "run", "start", "--workspace", "@cents/api"] diff --git a/services/api/package.json b/services/api/package.json index 7c74352..1741c2f 100644 --- a/services/api/package.json +++ b/services/api/package.json @@ -5,6 +5,7 @@ "type": "module", "scripts": { "dev": "tsx watch src/server.ts", + "start": "tsx src/server.ts", "bootstrap": "tsx src/bootstrap.ts", "password:reset": "tsx src/reset-password.ts", "build": "tsc --noEmit",