release(main): 同步 dev 最新候选改动
Storage Persistence Guard / storage-persistence-audit (push) Has been cancelled
Client Quality Gates / Shared client and Web (push) Has been cancelled
Client Quality Gates / macOS Desktop (push) Has been cancelled
Client Quality Gates / Shared client and Web (pull_request) Has been cancelled
Client Quality Gates / macOS Desktop (pull_request) Has been cancelled
Storage Persistence Guard / storage-persistence-audit (pull_request) Has been cancelled
Storage Persistence Guard / storage-persistence-audit (push) Has been cancelled
Client Quality Gates / Shared client and Web (push) Has been cancelled
Client Quality Gates / macOS Desktop (push) Has been cancelled
Client Quality Gates / Shared client and Web (pull_request) Has been cancelled
Client Quality Gates / macOS Desktop (pull_request) Has been cancelled
Storage Persistence Guard / storage-persistence-audit (pull_request) Has been cancelled
This commit is contained in:
@@ -147,12 +147,12 @@ bash scripts/install.sh release --base-url https://ctms.example.com
|
||||
|
||||
```text
|
||||
1. 检查 docker、docker compose、openssl、curl。
|
||||
2. 检查 .env;已存在则保留不改,不存在则新建。
|
||||
3. 新建 main/release 的 .env 时自动生成 JWT 和 RSA 私钥。
|
||||
2. 检查 .env;保留已有身份与登录密钥,补齐并默认启用标准 ONLYOFFICE 配置,不存在则新建。
|
||||
3. 所有环境自动生成独立的 ONLYOFFICE JWT 与稳定实例标识;新建 main/release 的 .env 时同时生成登录 JWT 和 RSA 私钥。
|
||||
4. main/release 先构建并执行 backend-init,确保数据库迁移使用当前代码中的 Alembic revision。
|
||||
5. 启动容器;默认执行 docker compose up -d --build,--skip-build 时执行 docker compose up -d。
|
||||
5. 默认构建并启动数据库、后端、前端/Nginx 和 ONLYOFFICE;ONLYOFFICE 不再使用可选 Compose Profile。
|
||||
6. 执行 docker compose run --rm backend python -m alembic upgrade head。
|
||||
7. 检查容器状态、后端环境、/health、/api/v1/auth/login-key。
|
||||
7. 检查全部容器状态、后端环境、/health、/api/v1/auth/login-key 和 /onlyoffice/healthcheck。
|
||||
```
|
||||
|
||||
更新进入“执行部署更新”后会持续显示 Docker 镜像拉取与构建进度:交互终端使用滚动实时输出窗口,CI、远程面板或管道环境直接流式输出构建日志,并在两种模式下显示累计耗时。
|
||||
@@ -164,6 +164,7 @@ git checkout
|
||||
git pull
|
||||
docker compose down -v
|
||||
删除 pg_data
|
||||
删除 backend/app/uploads
|
||||
删除数据库 volume
|
||||
自动安装系统依赖
|
||||
```
|
||||
@@ -194,6 +195,12 @@ ENV=development
|
||||
JWT_SECRET_KEY=dev-secret
|
||||
LOGIN_RSA_KEY_ID=default
|
||||
LOGIN_RSA_PRIVATE_KEY=
|
||||
ONLYOFFICE_ENABLED=true
|
||||
ONLYOFFICE_JWT_SECRET=<替换为独立的随机密钥,至少 32 个字符>
|
||||
ONLYOFFICE_INTERNAL_URL=http://onlyoffice
|
||||
ONLYOFFICE_STORAGE_BASE_URL=http://backend:8000
|
||||
ONLYOFFICE_INSTANCE_ID=ctms-dev-<替换为随机实例标识>
|
||||
ONLYOFFICE_CONFIG_TTL_SECONDS=300
|
||||
EOF
|
||||
```
|
||||
|
||||
@@ -315,6 +322,12 @@ ENV=production
|
||||
JWT_SECRET_KEY=<替换为 JWT 密钥>
|
||||
LOGIN_RSA_KEY_ID=main-YYYYMMDD
|
||||
LOGIN_RSA_PRIVATE_KEY=<替换为 RSA 私钥单行文本>
|
||||
ONLYOFFICE_ENABLED=true
|
||||
ONLYOFFICE_JWT_SECRET=<替换为不同于 JWT_SECRET_KEY 的独立随机密钥>
|
||||
ONLYOFFICE_INTERNAL_URL=http://onlyoffice
|
||||
ONLYOFFICE_STORAGE_BASE_URL=http://backend:8000
|
||||
ONLYOFFICE_INSTANCE_ID=ctms-main-<替换为随机实例标识>
|
||||
ONLYOFFICE_CONFIG_TTL_SECONDS=300
|
||||
EOF
|
||||
```
|
||||
|
||||
@@ -494,6 +507,12 @@ ENV=production
|
||||
JWT_SECRET_KEY=<替换为生产 JWT 密钥>
|
||||
LOGIN_RSA_KEY_ID=release-YYYYMMDD
|
||||
LOGIN_RSA_PRIVATE_KEY=<替换为生产 RSA 私钥单行文本>
|
||||
ONLYOFFICE_ENABLED=true
|
||||
ONLYOFFICE_JWT_SECRET=<替换为不同于 JWT_SECRET_KEY 的独立生产随机密钥>
|
||||
ONLYOFFICE_INTERNAL_URL=http://onlyoffice
|
||||
ONLYOFFICE_STORAGE_BASE_URL=http://backend:8000
|
||||
ONLYOFFICE_INSTANCE_ID=ctms-release-<替换为随机实例标识>
|
||||
ONLYOFFICE_CONFIG_TTL_SECONDS=300
|
||||
EOF
|
||||
```
|
||||
|
||||
|
||||
@@ -106,8 +106,8 @@ npm run desktop:build:app
|
||||
npm run desktop:build:app
|
||||
```
|
||||
|
||||
正式桌面发布构建仍必须设置 updater 签名私钥后执行
|
||||
`npm run desktop:build -- --bundles app`。
|
||||
正式桌面发布构建仍必须设置 updater 签名私钥和 Apple 签名/公证变量后,先执行
|
||||
`npm run desktop:release-readiness:check`,再执行 `npm run desktop:build:macos-release -- --ci`。
|
||||
|
||||
后端改动应补充执行受影响模块的后端测试、迁移检查和接口回归。
|
||||
|
||||
|
||||
@@ -30,12 +30,19 @@ The runtime contract currently provides:
|
||||
- file picker/save/open adapters
|
||||
- desktop system notification adapters
|
||||
- desktop updater adapters
|
||||
- native desktop application menu and shortcut synchronization
|
||||
- desktop window lifecycle and system appearance synchronization
|
||||
|
||||
Business modules must use this public runtime entry point. They must not inspect
|
||||
Tauri globals or import Tauri packages directly. Native files, notifications,
|
||||
secure session storage, and automatic updates must remain behind
|
||||
`frontend/src/runtime/` and explicit capability flags.
|
||||
|
||||
Desktop menu accelerators and window appearance are synchronized through narrow
|
||||
Tauri commands owned by `frontend/src/runtime/`. macOS close/reopen behavior is
|
||||
handled by the Tauri event loop so a genuinely closed main window can be rebuilt from the Dock;
|
||||
these concerns must not move into Vue business modules.
|
||||
|
||||
## Version Change
|
||||
|
||||
Update every client manifest with one command:
|
||||
@@ -58,6 +65,10 @@ Manual edits that leave these files inconsistent fail CI.
|
||||
|
||||
## Stabilization Gates
|
||||
|
||||
Client builds require Node.js 22.13.0 or newer. The development frontend container,
|
||||
Web CI, macOS release candidates, Windows internal validation, and the production
|
||||
Web image use the same Node 22.13 baseline.
|
||||
|
||||
Client release candidates must pass the shared Web checks and the Desktop
|
||||
release/security gate before promotion:
|
||||
|
||||
@@ -122,8 +133,8 @@ The release pipeline must:
|
||||
2. build macOS Universal desktop artifacts;
|
||||
3. sign and notarize the macOS app;
|
||||
4. produce updater artifacts and `.sig` files with the updater private key;
|
||||
5. generate `latest.json` and a checksum manifest;
|
||||
6. verify the feed with `npm run desktop:update-feed:check -- --feed <latest.json> --artifacts-dir <artifact-dir>`;
|
||||
5. generate `latest.json` and a checksum manifest with `npm run desktop:update-feed:create`;
|
||||
6. verify the feed with `npm run desktop:update-feed:check -- --feed <release-dir>/latest.json --artifacts-dir <release-dir>`;
|
||||
7. upload immutable artifacts first;
|
||||
8. atomically replace `latest.json` last.
|
||||
|
||||
@@ -131,6 +142,13 @@ For Universal macOS artifacts, `latest.json` must provide both
|
||||
`darwin-aarch64` and `darwin-x86_64` entries pointing at the same Universal
|
||||
update package.
|
||||
|
||||
The signed release candidate workflow lives at
|
||||
`.github/workflows/desktop-release-candidate.yml`. It must be run from a
|
||||
matching `vX.Y.Z` tag and produces a verified release directory as a GitHub
|
||||
artifact. That artifact is still only a release candidate; the release owner
|
||||
must upload immutable files to the production download origin and replace
|
||||
`latest.json` atomically after validation.
|
||||
|
||||
## Windows Build Readiness
|
||||
|
||||
Second-phase Windows work is limited to CI compatibility validation. A
|
||||
@@ -147,6 +165,16 @@ Windows validation must cover:
|
||||
- notification and updater compilation;
|
||||
- future code-signing requirements.
|
||||
|
||||
The manual internal Windows validation workflow lives at
|
||||
`.github/workflows/desktop-windows-internal.yml`. It is `workflow_dispatch`
|
||||
only, runs on `windows-latest`, injects `VITE_BUILD_CHANNEL` and
|
||||
`VITE_BUILD_COMMIT` from the selected branch context, executes the shared client
|
||||
and Desktop safety gates, builds an unsigned NSIS installer with updater
|
||||
artifacts disabled, and uploads the `.exe` plus `SHA256SUMS.txt` as a GitHub
|
||||
Actions artifact. This workflow is for internal compatibility verification
|
||||
only; it must not generate `latest.json`, update feeds, signed release
|
||||
directories, or formal Windows release artifacts.
|
||||
|
||||
## Required Checks
|
||||
|
||||
```bash
|
||||
@@ -167,8 +195,10 @@ export TAURI_SIGNING_PRIVATE_KEY="$UPDATER_PRIVATE_KEY"
|
||||
export TAURI_SIGNING_PRIVATE_KEY_PASSWORD="$UPDATER_PRIVATE_KEY_PASSWORD"
|
||||
export REQUIRE_DESKTOP_SIGNING=true
|
||||
npm run release:env:check
|
||||
npm run desktop:build -- --bundles app
|
||||
npm run desktop:update-feed:check -- --feed src-tauri/target/release/bundle/latest.json --artifacts-dir src-tauri/target/release/bundle
|
||||
npm run desktop:release-readiness:check
|
||||
npm run desktop:build:macos-release -- --ci
|
||||
npm run desktop:update-feed:create -- --artifact <CTMS.app.tar.gz> --base-url <versioned-https-artifact-prefix> --output-dir <release-dir>
|
||||
npm run desktop:update-feed:check -- --feed <release-dir>/latest.json --artifacts-dir <release-dir>
|
||||
```
|
||||
|
||||
The Desktop build must run on macOS for the current first-phase target. A signed
|
||||
|
||||
@@ -0,0 +1,47 @@
|
||||
# ONLYOFFICE 标准组件运行说明
|
||||
|
||||
ONLYOFFICE Document Server 是 CTMS 的标准部署组件,用于附件和文档版本预览,以及共享库在线协作编辑。PDF、图片、另存为和桌面端“打开”仍使用各自原有链路。
|
||||
|
||||
## 默认安装
|
||||
|
||||
执行标准安装即可同时安装 CTMS 与 ONLYOFFICE:
|
||||
|
||||
```bash
|
||||
bash scripts/install.sh dev
|
||||
```
|
||||
|
||||
安装脚本会自动完成以下工作:
|
||||
|
||||
- 首次安装时生成独立的 256-bit 随机 `ONLYOFFICE_JWT_SECRET`,写入根目录 `.env`,且不在终端回显。
|
||||
- 自动生成稳定、按部署环境隔离的实例标识并写入 `.env`。
|
||||
- 将 `.env` 权限设置为 `0600`;该文件已被 Git 忽略。
|
||||
- 默认构建并启动 backend、Document Server 与 Nginx,不再使用可选 Compose Profile。
|
||||
- 将 Document Server 纳入容器状态、后端配置和 HTTP 健康检查。
|
||||
- 后续启动默认复用密钥,防止后端和 Document Server 因签名密钥漂移而无法通信。
|
||||
|
||||
开发环境需要单独重建、验证或主动轮换密钥时,可使用兼容维护脚本:
|
||||
|
||||
```bash
|
||||
bash scripts/onlyoffice-dev-up.sh
|
||||
bash scripts/onlyoffice-dev-up.sh --rotate-secret
|
||||
```
|
||||
|
||||
轮换会强制重建相关容器,已签发但尚未使用的短时预览配置会立即失效。生产环境不使用该自动生成流程,仍必须由部署密钥管理系统显式提供密钥。
|
||||
|
||||
可用 `ONLYOFFICE_IMAGE` 覆盖默认的 `onlyoffice/documentserver:9.4.0.1`。默认派生镜像只增加 Noto Sans CJK/Noto Serif CJK,不包含微软字体。
|
||||
|
||||
## 配置边界
|
||||
|
||||
- Compose 安装中的 `ONLYOFFICE_ENABLED` 默认 `true`;安装或更新旧环境时也会自动启用。
|
||||
- `ONLYOFFICE_INTERNAL_URL` 默认 `http://onlyoffice`,只供后端健康探测。
|
||||
- `ONLYOFFICE_STORAGE_BASE_URL` 默认 `http://backend:8000`,只允许后端生成固定的内部文件地址。
|
||||
- `ONLYOFFICE_CONFIG_TTL_SECONDS` 默认 300 秒,允许范围为 60–900 秒。
|
||||
- 内部内容接口不经过 Nginx 公网入口,仅接受 `AuthorizationJwt`,且 JWT 必须绑定到请求的精确 URL。
|
||||
- 配置响应禁止缓存;JWT、内部文件 URL 和磁盘路径不得进入日志、审计详情或桌面缓存。
|
||||
- `onlyoffice_data`、`onlyoffice_lib` 和 `onlyoffice_logs` 不是 CTMS 业务权威数据,不纳入业务备份或恢复来源。
|
||||
|
||||
## 生产部署要求
|
||||
|
||||
标准生产安装同样包含 ONLYOFFICE,并自动生成独立随机密钥和稳定、唯一的 `ONLYOFFICE_INSTANCE_ID`。正式上线前仍必须确认商业许可或 Community 版本授权边界、批准镜像、容量与备份监控策略,并完成 Web、macOS 和 Windows 内测端的真实 DOCX/XLSX/PPTX/WPS/ET/DPS 预览验证。
|
||||
|
||||
故障回退可临时关闭 `ONLYOFFICE_ENABLED`;下一次标准安装或更新会按标准组件策略重新启用。回退不得影响附件下载、另存为、打开、PDF 或图片预览。Community 与商业版本的授权边界以 [ONLYOFFICE 官方许可说明](https://helpcenter.onlyoffice.com/docs/faq/docs-community.aspx) 为准。
|
||||
@@ -11,6 +11,7 @@
|
||||
|
||||
## 1.1 容器构建校验
|
||||
- [ ] `docker compose config` 渲染成功
|
||||
- [ ] 后端 `/code/app/uploads` 已持久化挂载到宿主机 `backend/app/uploads`
|
||||
- [ ] `docker compose build backend frontend` 成功
|
||||
- [ ] `docker compose run --rm --build backend-init` 成功
|
||||
|
||||
|
||||
@@ -0,0 +1,94 @@
|
||||
# 系统监测简易运维指南
|
||||
|
||||
## 定位与访问边界
|
||||
|
||||
系统监测用于单实例或小规模 CTMS 部署的日常巡检、访问追踪和初步故障定位,不替代集中式指标、日志和告警平台。监测页面及 `/api/v1/permission-monitoring/*` 仅允许系统管理员访问,项目 PM 不具备系统级监测权限。
|
||||
|
||||
## 健康检查
|
||||
|
||||
- `GET /health`:进程存活探针,不访问数据库,适合判断 HTTP 服务是否仍在响应。
|
||||
- `GET /readyz`:服务就绪探针,会执行数据库查询;数据库不可用时返回 `503`。Nginx、安装脚本和后端容器健康检查均使用或暴露此探针。
|
||||
- `GET /api/v1/permission-monitoring/health`:管理员健康详情,包含权限检查质量、缓存、进程内告警、数据库延迟、权限/安全日志写入队列、留存任务和监测表数据量。
|
||||
|
||||
部署后的最小检查:
|
||||
|
||||
```bash
|
||||
curl -fsS http://127.0.0.1:8888/health
|
||||
curl -fsS http://127.0.0.1:8888/readyz
|
||||
docker compose ps
|
||||
```
|
||||
|
||||
## 数据留存
|
||||
|
||||
后台任务在应用启动后立即清理一次,之后按固定间隔执行。默认策略:
|
||||
|
||||
- 权限访问日志和安全访问日志:90 天。
|
||||
- 权限小时指标:400 天。
|
||||
- 账号登录活动:180 天。
|
||||
- 清理周期:86400 秒(每天)。
|
||||
|
||||
可通过后端环境变量调整:
|
||||
|
||||
```text
|
||||
MONITORING_ACCESS_LOG_RETENTION_DAYS=90
|
||||
MONITORING_METRIC_RETENTION_DAYS=400
|
||||
MONITORING_RETENTION_INTERVAL_SECONDS=86400
|
||||
MONITORING_IP_GEO_FALLBACK_ENABLED=true
|
||||
MONITORING_IP_GEO_FALLBACK_TIMEOUT_SECONDS=2.5
|
||||
MONITORING_IP_GEO_FALLBACK_CACHE_SECONDS=604800
|
||||
MONITORING_IP_GEO_FALLBACK_MAX_LOOKUPS=10
|
||||
USER_LOGIN_ACTIVITY_RETENTION_DAYS=180
|
||||
USER_SESSION_ONLINE_SECONDS=300
|
||||
```
|
||||
|
||||
访问日志留存范围为 7–3650 天,指标留存范围为 30–3650 天,清理周期范围为 60–604800 秒。修改后需重启后端。正式环境部署新版本前必须先执行 Alembic migration。
|
||||
|
||||
账号管理页的“在线”状态由服务端会话心跳计算:最近 `USER_SESSION_ONLINE_SECONDS` 秒内成功心跳且未退出的会话视为在线。登录记录保存客户端类型、版本、服务端观测到的登录 IP、登录/最近活动/退出时间;IP 属地由本地离线数据库按需解析,不发送到外部服务。登录记录接口仅限系统管理员访问并禁止 HTTP 缓存,不保存 Token 或密码,并与登录活动一起按 `USER_LOGIN_ACTIVITY_RETENTION_DAYS` 清理。
|
||||
|
||||
登录 IP 只接受可信反向代理提供的转发地址。Docker Compose 默认信任容器常用的 `172.16.0.0/12` 和 `192.168.0.0/16` 内部网段;其他部署必须通过 `TRUSTED_PROXY_CIDRS` 明确配置实际代理网段,不得直接信任任意来源的 `X-Forwarded-For`。
|
||||
|
||||
## 来源地图服务器位置
|
||||
|
||||
来源地图中的服务器标记不使用固定城市或前端坐标。后端启动时按以下顺序确定部署服务器的公网地址,并缓存定位结果:
|
||||
|
||||
1. `MONITORING_SERVER_PUBLIC_IP` 明确指定的公网 IP,适用于禁止主动访问公网探测服务的生产网络。
|
||||
2. `FRONTEND_PUBLIC_URL` 主机名解析出的公网 IP。
|
||||
3. `MONITORING_PUBLIC_IP_DISCOVERY_URLS` 配置的公网出口 IP 查询服务。
|
||||
|
||||
默认公网查询服务为 `https://api64.ipify.org,https://icanhazip.com`,单次超时 2.5 秒。解析成功后通过本地 ip2region 数据库确定属地并转换为地图坐标;本地库没有坐标时复用下述 IP2Location.io 受控兜底。两条链路均失败时 API 返回 `server_location: null`,地图不会使用任意默认城市代替。默认打开全球视图,以便服务器与访问来源分属不同国家时仍能完整显示飞线。
|
||||
|
||||
## 访问来源坐标兜底
|
||||
|
||||
访问来源始终先使用本地 ip2region 和内置行政区质心解析。只有公网 IP 已识别、但本地链路无法得到地图坐标时,后端才调用 IPAddress.my 使用的 IP2Location.io 官方 JSON API `https://api.ip2location.io/` 补充经纬度;私网、回环、链路本地和无效地址不会发送给第三方。
|
||||
|
||||
兜底默认启用,单次请求最多查询 10 个尚未缓存的公网 IP,最多并发 5 个请求,超时 2.5 秒。成功结果缓存 7 天,失败结果缓存 1 小时;第三方不可用时继续返回本地解析结果,不影响访问来源接口。可设置 `MONITORING_IP_GEO_FALLBACK_ENABLED=false` 完全禁用外部查询。无密钥模式受服务方每日额度限制;如需配置 API Key,使用 `MONITORING_IP_GEO_FALLBACK_API_KEY`,后端通过 `Authorization: Bearer` 发送,禁止把密钥写入 URL 或日志。
|
||||
|
||||
受限网络建议显式配置:
|
||||
|
||||
```text
|
||||
MONITORING_SERVER_PUBLIC_IP=<部署服务器公网IP>
|
||||
MONITORING_PUBLIC_IP_DISCOVERY_URLS=
|
||||
```
|
||||
|
||||
公网 IP 仅用于服务端定位,不写入来源分析 API 响应或浏览器界面。
|
||||
|
||||
## 日志完整性与隐私
|
||||
|
||||
- 每个请求由服务端生成 UUID 请求标识,并通过响应头 `X-Request-ID` 返回;权限日志和安全日志以该标识消除同一请求的重复统计。
|
||||
- 写入队列保持非阻塞;队列满或数据库批次写入最终失败时不阻塞业务请求,但会累计丢弃量、失败批次、队列占用和最近错误时间,并在管理员健康页显示降级。
|
||||
- 请求正文不采集。请求头仅保留运维白名单字段,认证、Cookie、密钥类字段统一脱敏;查询参数中的令牌、账号、受试者/患者、姓名、邮箱、电话、证件和地址类值会替换为 `[redacted]`。
|
||||
- IP、User-Agent 和请求路径仍属于运维审计数据,应按管理员最小授权和上述留存策略管理,不应复制到公开工单或通知正文。
|
||||
|
||||
## 常见异常处理
|
||||
|
||||
| 现象 | 首要检查 | 建议处理 |
|
||||
| --- | --- | --- |
|
||||
| `/health` 正常、`/readyz` 返回 503 | 数据库容器、连接串、迁移状态 | 检查 `docker compose ps`、数据库日志和 `alembic current` |
|
||||
| 日志写入器降级 | 队列占用、丢弃量、失败批次、最近错误 | 检查数据库连接和容量;恢复后确认队列回落并重启以清零进程累计计数 |
|
||||
| 留存任务异常 | 最近成功/错误时间 | 检查数据库权限、表结构和后端日志,确认 migration 已升级 |
|
||||
| 页面显示“可能是上次成功结果” | 对应 API 或网络请求 | 使用刷新按钮重试,并结合 `/readyz` 与浏览器网络面板定位 |
|
||||
| 安全事件突然增多 | 分类、来源 IP、路径和状态码 | 优先核对敏感路径探测、5xx 和无效令牌,不要仅按 4xx 总量或地理位置判断 |
|
||||
|
||||
## 当前边界与后续升级
|
||||
|
||||
权限缓存指标、告警列表和写入器累计计数仍为单进程内存状态,重启后会重置;当前也不负责短信、邮件、Slack 等外部通知。需要多实例部署、跨重启趋势、值班通知或长期容量分析时,应接入 Prometheus/OpenTelemetry、集中日志和 Alertmanager 类告警链路,并保留本模块作为管理员快速诊断入口。
|
||||
Reference in New Issue
Block a user