feat: secure frontend and provider ingress
This commit is contained in:
@@ -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
|
||||
|
||||
@@ -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。
|
||||
|
||||
@@ -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` 中修改这些值,并重新启动栈以刷新派生环境文件。
|
||||
|
||||
@@ -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` 返回 ok,frontend `/health` 返回 ok,database 端口监听,`/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` 返回 ok,frontend 公网 `/health` 返回 ok,provider ingress 公网 `/health` 返回 ok,database 在容器内 `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
@@ -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
|
||||
|
||||
|
||||
@@ -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 API,frontend 再通过 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 的外部接入地址只作为连接拓扑信息展示,不作为浏览器数据源。
|
||||
|
||||
@@ -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
|
||||
|
||||
|
||||
@@ -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
|
||||
|
||||
Reference in New Issue
Block a user