feat: secure frontend and provider ingress

This commit is contained in:
Codex
2026-05-04 11:40:56 +00:00
parent caa80ee5e7
commit 8726611b6f
25 changed files with 1491 additions and 450 deletions
+2 -1
View File
@@ -23,8 +23,9 @@
- Main Server Components
- UniDesk Stateless Services
- Run all business microservices as Docker containers
- Includes API gateway, task scheduler, project management, and other stateless modules
- Includes frontend gateway, task scheduler, project management, provider ingress, and other stateless modules
- Instances can scale horizontally; failure recovery requires no state synchronization
- Only the frontend gateway and provider ingress are public; core REST APIs and PostgreSQL remain on the Docker internal network
- PostgreSQL Database
- Deployed as a Docker container with a 10 GB named volume
- Stores all task metadata, node heartbeats, resource labels, and business state
+4 -4
View File
@@ -9,11 +9,11 @@ UniDesk 的统一 CLI 入口是根目录 `scripts/cli.ts`,运行方式固定
- `check` 执行配置校验、文件存在性检查、`scripts/` TypeScript 检查、`src/components/` TypeScript 检查和 Docker Compose 配置检查。
- `server start` 创建异步 job,在后台执行 Docker 构建和启动;命令本身只负责返回 job id、日志路径和启动命令。
- `server stop` 创建异步 job,在后台停止固定 Compose project 中的全部 UniDesk 服务。
- `server status` 查询固定端口、Compose 容器、core/frontend 健康检查和访问 URL。
- `server status` 查询公开端口、内部端口、Compose 容器、core/frontend/provider/database 健康检查和访问 URL。
- `server logs` 返回 `logs/` 文件日志和 Docker 容器日志的尾部,默认限制输出大小,避免日志爆炸。
- `job list``job status` 查询 `.state/jobs/` 文件系统状态,是异步命令的可观测入口。
- `debug health``debug dispatch` 走真实 HTTP、WebSocket、数据库和 provider 流程,只用于开发调试,不写入 `TEST.md` 的正式验收步骤。
- `e2e run` 使用 publicHost 派生的公开 URL 验证 core API、PostgreSQL、provider self-connection 和 Playwright 前端页面,是交付前的自动化 E2E 门禁。
- `debug health``debug dispatch` 走真实内部 core、WebSocket、数据库和 provider 流程,只用于开发调试,不写入 `TEST.md` 的正式验收步骤。
- `e2e run` 使用 publicHost 派生的公开 frontend/provider ingress URL,并通过 Docker 内网验证 core API、PostgreSQL、provider self-connection 和 Playwright 前端页面,是交付前的自动化 E2E 门禁。
## Async Job State
@@ -25,4 +25,4 @@ UniDesk 的统一 CLI 入口是根目录 `scripts/cli.ts`,运行方式固定
## Debug Contract
`debug` 子命令必须复用真实模块与真实端点,禁止维护平行实现。`debug dispatch`调用 core 的 `/api/dispatch`core 再通过 WebSocket 将任务下发给 provider gateway,因此它可以验证核心调度闭环。
`debug` 子命令必须复用真实模块与真实端点,禁止维护平行实现。`debug dispatch`在 backend-core 容器内调用内部 `/api/dispatch`core 再通过 WebSocket 将任务下发给 provider gateway,因此它可以验证核心调度闭环,同时不需要公开 core REST API
+8 -4
View File
@@ -6,14 +6,18 @@
TypeScript 运行时固定为 Bun。根目录 CLI、backend-core、frontend 和 provider-gateway 都直接运行 `.ts` 入口;Docker 镜像使用 `oven/bun` 基础镜像,本机命令使用 `bun scripts/cli.ts`
## Fixed Ports
## Network Ports
`config.json`固定三个对外端口:backend-core、frontend、database`network.publicHost` 必须是浏览器和外部客户端可访问的主 server 地址;公网 E2E 不允许把它保留为 `127.0.0.1``server start` 会在启动前检查这些端口,避免因端口冲突产生多个版本混乱的服务实例
`config.json`保留 core、frontend、database 和 providerIngress 的端口字段,但只有 frontend 与 providerIngress 允许映射到宿主机公网端口。core 和 database 的 `port` 字段用于测试公网阻断和历史兼容,实际服务只使用 Docker 内网 `containerPort`
## Auth
`auth.username``auth.password` 是 frontend 登录凭据,默认值分别为 `admin``Liang6516.``auth.sessionSecret` 用于签名 frontend HttpOnly Cookie`auth.sessionTtlSeconds` 控制登录会话有效期;修改后必须重新启动 Docker 栈以刷新派生环境变量。
## Compose Env Generation
Docker Compose 本身不读取 JSON,因此 CLI 会从 `config.json` 生成 `.state/docker-compose.env`。该文件是派生状态,不应手写;如需改端口、token、provider 标签或主机名,应修改 `config.json` 后重新运行 CLI。
Docker Compose 本身不读取 JSON,因此 CLI 会从 `config.json` 生成 `.state/docker-compose.env`。该文件是派生状态,不应手写;如需改端口、token、provider 标签、登录凭据或主机名,应修改 `config.json` 后重新运行 CLI。CLI 会在保留当前日志前缀的同时刷新新增配置键,避免旧 env 文件遗漏字段。
## Secrets
当前配置面向主 server 开发部署,包含开发用数据库密码provider token。公网暴露前必须在 `config.json` 中修改这些值,并重新启动栈以刷新派生环境文件。
当前配置面向主 server 开发部署,包含开发用数据库密码provider token 和默认登录密码。公网长期运行前必须在 `config.json` 中修改这些值,并重新启动栈以刷新派生环境文件。
+10 -6
View File
@@ -5,18 +5,22 @@
## Services
- `database` 使用 `postgres:16-alpine`,数据保存到 named volume `unidesk_pgdata_10gb`,初始化 SQL 位于 `src/components/database/init/`
- `backend-core` 是无状态核心服务,提供 REST API、provider WebSocket、任务调度入口和数据库访问层。
- `frontend`独立 Web 容器,通过浏览器访问 core 的公开 API URL
- `provider-gateway` 是当前主 server 的本机计算节点代理,通过 WebSocket 主动连到 backend-core,并挂载 `/var/run/docker.sock` 作为自动任务执行主路径。
- `backend-core` 是无状态核心服务,提供 Docker 内网 REST API、provider ingress WebSocket、任务调度入口和数据库访问层。
- `frontend`唯一公开 Web 控制台,提供登录、React 静态资产和到 backend-core 的同源代理
- `provider-gateway` 是当前主 server 的本机计算节点代理,通过 WebSocket 主动连到 provider ingress,并挂载 `/var/run/docker.sock` 作为自动任务执行主路径。
## Public Exposure Boundary
Docker Compose 只能向公网暴露两个接口:frontend host port 和 provider ingress host port。backend-core REST API 和 PostgreSQL database 必须只在 Docker 内部网络中可达,不允许映射到宿主机公网端口;浏览器访问 core API 必须通过 frontend 的同源代理完成。
## Start And Stop
`bun scripts/cli.ts server start``bun scripts/cli.ts server stop` 都是异步 job。启动 job 会先清理固定 Compose project 的旧容器,再重新构建并启动,避免主 server 上残留旧容器或旧镜像配置。启动后用 `job status latest` 观察后台命令,用 `server status` 验证端口、容器和健康检查
`bun scripts/cli.ts server start``bun scripts/cli.ts server stop` 都是异步 job。启动 job 会先清理固定 Compose project 的旧容器,再重新构建并启动,避免主 server 上残留旧容器或旧镜像配置。启动和停止流程禁止删除 Docker named volume
## Health Criteria
服务跑通的最低标准是:backend-core `/health` 返回 okfrontend `/health` 返回 okdatabase 端口监听`/api/nodes` 中出现 `main-server` provider 且状态为 `online``debug dispatch main-server docker.ps` 能完成真实任务下发。交付前还必须运行 `bun scripts/cli.ts e2e run`,并以 `docs/reference/e2e.md` 的门禁作为最终判定。
服务跑通的最低标准是:backend-core 内网 `/health` 返回 okfrontend 公网 `/health` 返回 okprovider ingress 公网 `/health` 返回 okdatabase 在容器内 `pg_isready` 可用`/api/nodes` 中出现 `main-server` provider 且状态为 `online`。交付前还必须运行 `bun scripts/cli.ts e2e run`,并以 `docs/reference/e2e.md` 的门禁作为最终判定。
## Database Volume
架构要求数据库使用 10 GB named volume;当前实现将 volume 命名为 `unidesk_pgdata_10gb` 以固定生命周期。Docker named volume 默认不强制容量上限;如需硬配额,应在主机存储层或 Docker volume driver 层配置。CLI server 控制只能使用不删除 volume 的 `down` / `up` 流程,禁止使用 `down -v` 或删除 `unidesk_pgdata_10gb`
架构要求数据库使用 10 GB named volume;当前实现将 volume 命名为 `unidesk_pgdata_10gb` 以固定生命周期。Docker named volume 默认不强制容量上限;如需硬配额,应在主机存储层或 Docker volume driver 层配置。CLI server 控制只能使用不删除 volume 的 `down` / `up` 流程,禁止使用 `down -v``docker volume rm` 或删除 `unidesk_pgdata_10gb`
+14 -9
View File
@@ -1,25 +1,30 @@
# UniDesk E2E Reference
UniDesk delivery is not complete until the public frontend, public core API, PostgreSQL database, and local provider-gateway self-connection pass one end-to-end check. The canonical automated command is `bun scripts/cli.ts e2e run`.
UniDesk delivery is not complete until the public frontend, public provider ingress, internal core API, PostgreSQL database, local provider-gateway self-connection, and frontend Playwright flow pass one end-to-end check. The canonical automated command is `bun scripts/cli.ts e2e run`.
## Required Preconditions
- `config.json` `network.publicHost` must be the externally reachable host name or IP of the main server, not `127.0.0.1`, when validating browser access from outside the server.
- `bunx playwright install chromium` and `bunx playwright install-deps chromium` must have been run on hosts that execute browser E2E tests.
- The Docker stack must be running through `bun scripts/cli.ts server start`, and `bun scripts/cli.ts server status` must report healthy core, frontend, database, and provider-gateway containers.
- The Docker stack must be running through `bun scripts/cli.ts server start`, and `bun scripts/cli.ts server status` must report healthy frontend, provider ingress, internal core, database, and provider-gateway containers.
## Automated E2E Scope
`bun scripts/cli.ts e2e run` validates the following through the public URLs derived from `config.json`:
`bun scripts/cli.ts e2e run` validates the following URLs and internal checks derived from `config.json`:
- Core API: `GET /api/overview` reports `dbReady: true` and at least one online node.
- Provider self-connection: `GET /api/nodes` contains `main-server` with `status: online`.
- Database: the command writes an `unidesk_e2e_markers` row through `docker exec unidesk-database psql`, confirms provider state is stored in PostgreSQL, and probes the public PostgreSQL port with `pg_isready`.
- Frontend: Playwright opens the public frontend URL, waits for `核心在线`, asserts that `main-server` and `Main Server Provider` are visible, checks the metrics panel, and captures a screenshot under `.state/e2e/`.
- Public exposure: Docker port summary must show only frontend and provider ingress host mappings; public core and public database probes must fail.
- Core API: `docker exec unidesk-backend-core` calls internal `GET /api/overview`, which must report `dbReady: true` and at least one online node.
- Provider self-connection: internal `GET /api/nodes` must contain `main-server` with `status: online`; public provider ingress `/health` must return ok.
- Database: the command writes an `unidesk_e2e_markers` row through `docker exec unidesk-database psql` and confirms provider state is stored in PostgreSQL.
- Frontend: Playwright opens the public frontend URL, logs in with the configured account, waits for `核心在线`, asserts that `main-server` and `Main Server Provider` are visible, confirms no raw JSON is visible before clicking `查看原始JSON`, then clicks the explicit raw JSON button and verifies the raw payload appears.
## Public Frontend Rule
## Frontend JSON Rule
The frontend must not inject `127.0.0.1` as the browser-facing core API URL for public deployments. If a loopback URL is accidentally injected and the page itself is opened from a non-loopback host, `public/app.js` rewrites the API host to `window.location.hostname` as a safety net; however the correct fix is still to set `network.publicHost` correctly in `config.json` and restart the stack.
The frontend must render JSON data into React controls by default. Raw JSON is allowed only after an explicit `查看原始JSON` user action, and E2E must fail if the initial page exposes raw JSON text or a raw JSON block.
## Public Boundary Rule
The public frontend URL and provider ingress URL are the only public network interfaces. backend-core REST API and PostgreSQL database are Docker-internal only; E2E must prove the historical public core/database ports are not reachable.
## Database Persistence Rule
+11 -3
View File
@@ -1,10 +1,18 @@
# UniDesk Frontend Reference
UniDesk 前端是工业控制台,不追求展示型大屏效果。设计目标是高信息密度、低装饰、低字号、低间距,并让调度、节点、事件和配置入口在单屏内快速切换。
UniDesk 前端是 React 组件化工业控制台,不追求展示型大屏效果。设计目标是高信息密度、低装饰、低字号、低间距,并让调度、节点、事件和配置入口在单屏内快速切换。
## Layout
左侧边栏切换主模块:运行总览、资源节点、任务调度、系统配置。顶部标签切换子模块:Overview、Live Nodes、Event Log、Dispatch。桌面端采用双列内容网格,移动端将左侧栏压缩为横向模块条
左侧边栏切换主模块:运行总览、资源节点、任务调度、系统配置。顶部标签切换当前主模块内的子功能;例如资源节点下的节点清单、资源标签、心跳状态只属于资源节点,和运行总览、任务调度、系统配置没有重复或共享语义
## Component Data Rendering
前端必须把 backend-core 返回的 JSON 渲染为合适的控件:状态徽标、指标卡、表格列、标签 chip、字段摘要、任务结果卡、日志行和表单控件。默认页面禁止暴露裸 JSON、`pre` JSON 或整段 `JSON.stringify` 文本;只有用户明确点击 `查看原始JSON` 按钮后,才允许在弹窗或高级编辑区展示原始 JSON。
## Login
frontend 提供账号密码登录,默认账号为 `admin`,默认密码为 `Liang6516.`。登录会话使用 frontend 容器签发的 HttpOnly Cookie;浏览器后续只访问同源 frontend APIfrontend 再通过 Docker 内网代理 backend-core。
## Visual Language
@@ -12,4 +20,4 @@ UniDesk 前端是工业化控制台,不追求展示型大屏效果。设计目
## Data Flow
frontend 容器服务静态资产和轻量 HTML 注入;浏览器根据 `CORE_PUBLIC_URL` 调用 backend-core 的 REST API。调度表单调用 `/api/dispatch`,事件表和节点表通过轮询刷新
浏览器不直接访问 backend-core。frontend 容器服务 React 静态资产、登录接口、会话接口和同源 API 代理;代理目标是 Docker 内网 `http://backend-core:8080`。provider-gateway 的外部接入地址只作为连接拓扑信息展示,不作为浏览器数据源
+6 -2
View File
@@ -1,10 +1,14 @@
# Provider Gateway Reference
Provider Gateway 是计算节点侧容器。它只主动连出到 backend-core 的 WebSocket,不要求计算节点有公网 IP,适合 NAT、内网和防火墙后的机器。
Provider Gateway 是计算节点侧容器。它只主动连出到主 server 暴露的 provider ingress WebSocket,不要求计算节点有公网 IP,适合 NAT、内网和防火墙后的机器。
## Main Server Self Provider
当前主 server 也运行一个 provider-gateway`providerId` 固定来自 `config.json``providerGateway.id`。这让单机环境也能验证完整的分布式调度闭环:frontend 发起任务,core 写数据库并通过 WebSocket 下发,provider gateway 执行后回传状态。
当前主 server 也运行一个 provider-gateway`providerId` 固定来自 `config.json``providerGateway.id`。这让单机环境也能验证完整的分布式调度闭环:frontend 发起任务,core 写数据库并通过 provider ingress WebSocket 下发,provider gateway 执行后回传状态。
## Provider Ingress
provider ingress 是唯一允许公网暴露的 provider 连接接口,当前由 backend-core 容器的独立端口提供 `/ws/provider``/health`。backend-core REST API 仍只在 Docker 内网开放,外部计算节点只应连接 provider ingress。
## Docker Socket Path
+4 -4
View File
@@ -17,7 +17,7 @@
- debug.ts (Real-flow debug helpers)
- command.ts (Bounded command execution helpers)
- output.ts (JSON output helpers)
- e2e.ts (Public API, database, provider, and Playwright frontend E2E checks)
- e2e.ts (Public frontend/provider ingress, internal core/database, and Playwright frontend E2E checks)
- logs/ (Generated service logs; ignored by git)
- .state/ (Generated job state and compose env; ignored by git)
- docs/
@@ -46,13 +46,13 @@
- package.json
- tsconfig.json
- Dockerfile
- src/index.ts (REST API, WebSocket provider server, scheduler, database access)
- src/index.ts (Internal REST API, public provider ingress WebSocket, scheduler, database access)
- frontend/ (Frontend web application container)
- package.json
- tsconfig.json
- Dockerfile
- src/index.ts (Bun static server and runtime config injection)
- public/ (HTML/CSS/JS assets for the compact industrial console)
- src/index.ts (Bun static server, login/session handling, and same-origin internal API proxy)
- public/ (React HTML/CSS/JS assets for the compact industrial console)
- provider-gateway/ (Compute node Provider Gateway container)
- package.json
- tsconfig.json