feat: 按历史痕迹恢复 nginx 同域入口
This commit is contained in:
@@ -4,14 +4,14 @@
|
||||
- 生产入口:`docker-compose.yaml`
|
||||
- 初始化方式:`docker compose run --rm backend-init`
|
||||
- 启动方式:`docker compose up -d --build`
|
||||
- 运行拓扑:`frontend`、`backend`、`db`
|
||||
- 对外入口:仓库外部统一反向代理;建议将 `/` 转发到 `frontend`,将 `/api` 与 `/health` 转发到 `backend`
|
||||
- 运行拓扑:`nginx`、`backend`、`db`
|
||||
- 对外入口:`nginx` 提供前端静态资源,并同域反代后端 API
|
||||
- 数据库 schema 来源:Alembic migration,不再依赖 `database/init.sql`
|
||||
- 默认无任何 demo 数据;生产初始化只确保固定管理员 `admin@huapont.cn / admin123` 存在
|
||||
- 验证方式:
|
||||
- `docker compose config`
|
||||
- `curl -i http://127.0.0.1:4173/`
|
||||
- `curl -i http://127.0.0.1:8000/health`
|
||||
- `curl -i http://127.0.0.1/`
|
||||
- `curl -i http://127.0.0.1/health`
|
||||
|
||||
## 账号与注册
|
||||
- 初始化管理员:`admin@huapont.cn / admin123`(通过生产初始化命令显式创建)
|
||||
@@ -28,11 +28,9 @@
|
||||
- 普通成员(无项目角色):仅浏览
|
||||
|
||||
## 访问方式
|
||||
- 前端:`http://localhost:4173`
|
||||
- 后端 API:`http://localhost:8000/api/v1/*`
|
||||
- 生产环境建议通过外部反向代理提供同域访问,前端代码默认请求同域 `/api/v1/*`
|
||||
- 如果本地直接访问 `http://localhost:4173`,可在 `frontend/.env` 中设置 `VITE_API_BASE_URL=http://localhost:8000`,让前端直连后端 API
|
||||
- `docker compose` 构建前端容器时,默认不注入 `VITE_API_BASE_URL`,生产会走同域 `/api/v1/*`;仅在本地直连后端时才显式设置 `VITE_API_BASE_URL=http://localhost:8000`
|
||||
- 前端:`http://localhost`
|
||||
- 后端 API:同域 `/api/v1/*`
|
||||
- `nginx` 负责托管前端静态资源,并将 `/api` 与 `/health` 转发到 `backend`
|
||||
|
||||
## 仓库治理文档
|
||||
- 分支治理规范:`docs/branch-governance.md`
|
||||
|
||||
+12
-7
@@ -50,16 +50,21 @@ services:
|
||||
networks:
|
||||
- ctms_net
|
||||
|
||||
frontend:
|
||||
nginx:
|
||||
build:
|
||||
context: ./frontend
|
||||
dockerfile: Dockerfile
|
||||
args:
|
||||
VITE_API_BASE_URL: ${VITE_API_BASE_URL:-}
|
||||
container_name: ctms_frontend
|
||||
context: .
|
||||
dockerfile: nginx/Dockerfile
|
||||
container_name: ctms_nginx
|
||||
restart: always
|
||||
ports:
|
||||
- "4173:4173"
|
||||
- "80:80"
|
||||
- "443:443"
|
||||
- "8888:8888"
|
||||
volumes:
|
||||
- ./nginx/certs:/etc/nginx/certs:ro
|
||||
- ./frontend/public/favicon.ico:/usr/share/nginx/html/favicon.ico:ro
|
||||
depends_on:
|
||||
- backend
|
||||
networks:
|
||||
- ctms_net
|
||||
|
||||
|
||||
@@ -103,8 +103,8 @@ Expected: PASS
|
||||
|
||||
**Step 3: Check HTTP endpoints**
|
||||
|
||||
Run: `curl -i http://127.0.0.1:4173/`
|
||||
Run: `curl -i http://127.0.0.1/`
|
||||
Expected: `200 OK` and HTML payload
|
||||
|
||||
Run: `curl -i http://127.0.0.1:8000/health`
|
||||
Run: `curl -i http://127.0.0.1/health`
|
||||
Expected: successful backend health response
|
||||
|
||||
@@ -0,0 +1,19 @@
|
||||
# Restore In-Repo Nginx Design
|
||||
|
||||
**Goal:** Restore the repository-managed Nginx entrypoint on `dev` by writing back the earlier removal diff, so frontend static hosting and backend API proxying return to a single same-origin entry.
|
||||
|
||||
**Decisions**
|
||||
- Restore `nginx/nginx.conf` and `nginx/Dockerfile` from the pre-removal topology.
|
||||
- Replace the `frontend` runtime service in `docker-compose.yaml` with an `nginx` service that serves the built SPA and proxies `/api` and `/health` to `backend`.
|
||||
- Remove the Caddy-specific runtime file because the repository will no longer use a separate frontend web server.
|
||||
- Keep later non-nginx fixes that remain valid, especially the frontend API base URL fallback logic for optional local direct access.
|
||||
|
||||
**Architecture**
|
||||
- `db` remains unchanged.
|
||||
- `backend` remains unchanged and is reachable internally on `backend:8000`.
|
||||
- `nginx` becomes the public entrypoint again, serving the frontend bundle and reverse proxying API traffic to `backend`.
|
||||
- The browser returns to same-origin access through the Nginx entrypoint.
|
||||
|
||||
**Operational Notes**
|
||||
- This is an in-place restoration based on the previous removal footprint, not a fresh redesign.
|
||||
- The current Tencent Cloud/private registry removal remains intact and is not being reintroduced.
|
||||
@@ -1,5 +0,0 @@
|
||||
:4173
|
||||
|
||||
root * /srv
|
||||
try_files {path} /index.html
|
||||
file_server
|
||||
+3
-7
@@ -2,18 +2,14 @@ FROM node:20-alpine AS build
|
||||
|
||||
WORKDIR /app
|
||||
|
||||
ARG VITE_API_BASE_URL
|
||||
ENV VITE_API_BASE_URL=${VITE_API_BASE_URL}
|
||||
|
||||
COPY package*.json ./
|
||||
RUN npm ci
|
||||
|
||||
COPY . .
|
||||
RUN npm run build
|
||||
|
||||
FROM caddy:2-alpine
|
||||
FROM alpine:3.21
|
||||
|
||||
COPY Caddyfile /etc/caddy/Caddyfile
|
||||
COPY --from=build /app/dist /srv
|
||||
WORKDIR /opt/frontend
|
||||
|
||||
EXPOSE 4173
|
||||
COPY --from=build /app/dist ./dist
|
||||
|
||||
@@ -0,0 +1,14 @@
|
||||
FROM node:20-alpine AS frontend-build
|
||||
|
||||
WORKDIR /app
|
||||
|
||||
COPY frontend/package*.json ./
|
||||
RUN npm ci
|
||||
|
||||
COPY frontend/ ./
|
||||
RUN npm run build
|
||||
|
||||
FROM nginx:1.27-alpine
|
||||
|
||||
COPY nginx/nginx.conf /etc/nginx/nginx.conf
|
||||
COPY --from=frontend-build /app/dist /usr/share/nginx/html
|
||||
@@ -0,0 +1,65 @@
|
||||
worker_processes 1;
|
||||
|
||||
error_log /var/log/nginx/error.log warn;
|
||||
pid /var/run/nginx.pid;
|
||||
|
||||
events {
|
||||
worker_connections 1024;
|
||||
}
|
||||
|
||||
http {
|
||||
include /etc/nginx/mime.types;
|
||||
default_type application/octet-stream;
|
||||
resolver 127.0.0.11 valid=10s ipv6=off;
|
||||
|
||||
log_format main '$remote_addr - $remote_user [$time_local] "$request" '
|
||||
'$status $body_bytes_sent "$http_referer" '
|
||||
'"$http_user_agent" "$http_x_forwarded_for"';
|
||||
|
||||
access_log /var/log/nginx/access.log main;
|
||||
|
||||
sendfile on;
|
||||
keepalive_timeout 65;
|
||||
root /usr/share/nginx/html;
|
||||
index index.html;
|
||||
|
||||
upstream backend {
|
||||
zone backend 64k;
|
||||
server backend:8000 resolve;
|
||||
}
|
||||
|
||||
server {
|
||||
listen 80;
|
||||
server_name _;
|
||||
client_max_body_size 20m;
|
||||
|
||||
location = /favicon.ico {
|
||||
root /usr/share/nginx/html;
|
||||
try_files /favicon.ico =204;
|
||||
access_log off;
|
||||
log_not_found off;
|
||||
expires 1d;
|
||||
add_header Cache-Control "public, max-age=86400" always;
|
||||
}
|
||||
|
||||
location /api/ {
|
||||
proxy_pass http://backend;
|
||||
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 /health {
|
||||
proxy_pass http://backend;
|
||||
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;
|
||||
}
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user