Files
knowledge-governance/compose.yaml
T
姚章浩 4cb5110845
build / build (push) Successful in 1m1s
fix(version-history): 页面链接改走公网 origin(抓取仍走内网)+ tag 补链接
现象(主人反馈):点「查看提交」打开是空白页。原因:链接用的是**抓取用的内网地址**
(`http://192.168.50.223:3000/...`),内网外的浏览器到不了;而公网地址
`https://gitea.zheromiku.top/...` 实测 200。另外 tag 证据里**根本没有 url**,
页面渲染出空 `href`,点下去像"没反应"。

修复:

- 新增 `VERSION_HISTORY_LINK_ORIGIN`(页面链接 origin,公网;留空 = 与抓取 origin 相同):
  抓取仍匿名走内网白名单地址,**给人点的链接**用公网 origin 拼 —— 两者本就不该共用一个地址;
  校验必须是干净的 http(s) 绝对地址。
- tag 证据补 `url`(`<link origin>/<repo>/src/tag/<name>`),并在前端**没有链接就不渲染**该按钮。
- 测试:客户端(LinkOrigin 生效 / 未配时退回 / 非法拒绝)、规则(tag 链接写入证据 / 无基址不编链接)、
  前端(有链接才渲染「查看 tag」)。

验证:make verify 15/15;真实链路重同步后库里 commit_url = `https://gitea.zheromiku.top/...`(200)、
release_tags 带 url。
2026-10-14 11:06:13 +08:00

242 lines
9.9 KiB
YAML
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
name: knowledge-governance-p1
services:
postgres:
image: postgres:17-alpine
environment:
POSTGRES_DB: governance
POSTGRES_USER: governance
POSTGRES_PASSWORD_FILE: /run/secrets/postgres_password
secrets:
- postgres_password
volumes:
- governance-pgdata:/var/lib/postgresql/data
healthcheck:
test: ["CMD-SHELL", "pg_isready -U governance -d governance"]
interval: 5s
timeout: 3s
retries: 20
restart: unless-stopped
api:
build:
context: .
dockerfile: Dockerfile
command: ["/app/api"]
user: "${APP_UID:-1000}:${APP_GID:-1000}"
environment:
<<: &app-environment
DATABASE_URL: postgres://governance@postgres:5432/governance?sslmode=disable
PGPASSFILE: /run/secrets/pgpass
APP_ADDR: :8080
WORKER_POLL_INTERVAL: 1s
WORKER_LEASE_DURATION: 30s
WORKER_RETRY_DELAY: 5s
TEST_WEKNORA_BASE_URL: "${TEST_WEKNORA_BASE_URL:-}"
# 验收通过后是否自动批准:off | internal(默认,仅对内;对外仍需人工)| all。
# 只影响"审核"这一步;生产发布的双闸、人工授权与发布前快照门禁不受影响。
TEST_AUTO_REVIEW_POLICY: "${TEST_AUTO_REVIEW_POLICY:-internal}"
DINGTALK_LOGIN_SIDECAR_URL: "http://dingtalk-login:8091"
MINERU_BASE_URL: "${MINERU_BASE_URL:-}"
TEST_INTERNAL_WEKNORA_KB_ID: "${TEST_INTERNAL_WEKNORA_KB_ID:-1b52c140-2e18-4676-afc5-4c7d8d190a8c}"
TEST_EXTERNAL_WEKNORA_KB_ID: "${TEST_EXTERNAL_WEKNORA_KB_ID:-fd7db6aa-763a-46f1-84d2-ecc7c952a5ea}"
# 「版本记录」页:**匿名只读**远端仓库的提交与 tag(不使用任何凭据)。
# 留空 = 未接线(页面降级、同步入口不可用);来源只允许下面批准的 origin 与固定仓库。
VERSION_HISTORY_GITEA_BASE_URL: "${VERSION_HISTORY_GITEA_BASE_URL:-}"
VERSION_HISTORY_REPO: "${VERSION_HISTORY_REPO:-Weknora/knowledge-governance}"
VERSION_HISTORY_ALLOWED_ORIGINS: "${VERSION_HISTORY_ALLOWED_ORIGINS:-}"
# 页面链接用的 origin(给内网外的浏览器点开);留空 = 与抓取 origin 相同。
VERSION_HISTORY_LINK_ORIGIN: "${VERSION_HISTORY_LINK_ORIGIN:-}"
VERSION_HISTORY_HTTP_TIMEOUT: "${VERSION_HISTORY_HTTP_TIMEOUT:-15s}"
VERSION_HISTORY_MAX_PAGES: "${VERSION_HISTORY_MAX_PAGES:-200}"
# P10 readiness probe reads the routing KB with the same scoped key.
TEST_WEKNORA_API_KEY_FILE: /run/secrets/test_weknora_api_key
TEST_WEKNORA_ALLOW_DOCKER_HTTP: "${TEST_WEKNORA_ALLOW_DOCKER_HTTP:-false}"
WORKER_LIVENESS_STALE_AFTER: "${WORKER_LIVENESS_STALE_AFTER:-2m}"
PRODUCTION_RELEASE_REQUESTS_ENABLED: ${PRODUCTION_RELEASE_REQUESTS_ENABLED:-false}
PRODUCTION_BACKUP_RESTORE_VERIFIED: ${PRODUCTION_BACKUP_RESTORE_VERIFIED:-false}
# Read-only probe of the production WeKnora endpoint; publishing keeps
# reading its target from RELEASE_CONFIG_FILE.
PRODUCTION_WEKNORA_BASE_URL: "${PRODUCTION_WEKNORA_BASE_URL:-}"
PRODUCTION_WEKNORA_API_KEY_FILE: "${PRODUCTION_WEKNORA_API_KEY_FILE:-}"
PRODUCTION_WEKNORA_ALLOW_DOCKER_HTTP: "${PRODUCTION_WEKNORA_ALLOW_DOCKER_HTTP:-false}"
# 数据源发现(系统设置 → 数据源配置):api 用 dws 只读列举钉钉知识库与钉盘空间。
DWS_BIN: /usr/local/bin/dws
DWS_CONFIG_DIR: /data/dws
DWS_HOME: /data/home
DWS_DISABLE_KEYCHAIN: "1"
secrets:
- pgpass
- test_weknora_api_key
volumes:
# 与 worker 共用同一份 dws 状态:refresh token 是轮换制,绝不能在两处各留一份。
- /home/zheromiku/.dws:/data/dws
# dws 的文件型 keychain 必须有 $HOME/.local/share/dws-cli,故挂到 DWS_HOME 下。
- /home/zheromiku/.local/share/dws-cli:/data/home/.local/share/dws-cli
depends_on:
postgres:
condition: service_healthy
healthcheck:
# P10: liveness only — a third-party outage must never restart the API.
test: ["CMD", "wget", "-qO-", "http://127.0.0.1:8080/livez"]
interval: 5s
timeout: 3s
retries: 20
read_only: true
tmpfs:
- /tmp
networks:
- default
- weknora-local
restart: unless-stopped
worker:
build:
context: .
dockerfile: Dockerfile
command: ["/app/worker"]
user: "${APP_UID:-1000}:${APP_GID:-1000}"
environment:
<<: *app-environment
WORKER_ID: worker-1
PP_SCAN_ENABLED: "${PP_SCAN_ENABLED:-false}"
PP_SCAN_ALLOWED_ROOT: "${PP_SCAN_CONTAINER_DATA_ROOT:-/home/zheromiku/dobot-portal-sync/data}"
PP_SCAN_LEDGER_PATH: "${PP_SCAN_CONTAINER_DATA_ROOT:-/home/zheromiku/dobot-portal-sync/data}/public-weknora-sync/state.jsonl"
PP_SCAN_MANIFEST_PATH: "${PP_SCAN_CONTAINER_DATA_ROOT:-/home/zheromiku/dobot-portal-sync/data}/public-documents/manifest.json"
TEST_WEKNORA_ENABLED: "${TEST_WEKNORA_ENABLED:-false}"
TEST_WEKNORA_ALLOW_DOCKER_HTTP: "${TEST_WEKNORA_ALLOW_DOCKER_HTTP:-false}"
TEST_WEKNORA_KB_ID: "${TEST_WEKNORA_KB_ID:-}"
TEST_WEKNORA_API_KEY_FILE: /run/secrets/test_weknora_api_key
GOVERNANCE_SPOOL_ROOT: /var/lib/governance/spool
INGESTION_SPOOL_ROOT: /var/lib/governance/ingestion
TEST_WEKNORA_HTTP_TIMEOUT: "${TEST_WEKNORA_HTTP_TIMEOUT:-60s}"
TEST_WEKNORA_POLL_INITIAL: "${TEST_WEKNORA_POLL_INITIAL:-10s}"
TEST_WEKNORA_POLL_MAX: "${TEST_WEKNORA_POLL_MAX:-60s}"
TEST_WEKNORA_STALLED_AFTER: "${TEST_WEKNORA_STALLED_AFTER:-30m}"
TEST_WEKNORA_MAX_UPLOAD_BYTES: "${TEST_WEKNORA_MAX_UPLOAD_BYTES:-52428800}"
TEST_ACCEPTANCE_POLICY_VERSION: "${TEST_ACCEPTANCE_POLICY_VERSION:-structural-v1}"
# DingTalk 取件走 dws(默认优先 dws,可重试失败回落老路径):dws|auto|legacy
ARTIFACT_DINGTALK_FETCHER: "${ARTIFACT_DINGTALK_FETCHER:-auto}"
DWS_BIN: /usr/local/bin/dws
DWS_CONFIG_DIR: /data/dws
# dws 的文件型 keychain 在 $HOME/.local/share/dws-cli(XDG_DATA_HOME 不被识别),
# 仅给 dws 子进程换 HOME,不动整个 worker 的 HOME。
DWS_HOME: /data/home
DWS_DISABLE_KEYCHAIN: "1"
SSL_CERT_FILE: /etc/ssl/certs/ca-certificates.crt
secrets:
- pgpass
- test_weknora_api_key
volumes:
- ./spool:/var/lib/governance/spool:ro
- ./spool/ingestion:/var/lib/governance/ingestion
- "${PP_SCAN_HOST_DATA_ROOT:-/home/zheromiku/dobot-portal-sync/data}:${PP_SCAN_CONTAINER_DATA_ROOT:-/home/zheromiku/dobot-portal-sync/data}:ro"
- /home/zheromiku/dingtalk_kb_download:/sources/dingtalk:ro
# dws 凭据:与宿主共享同一份 state(refresh token 是轮换制,不能在两处各留一份)
- /home/zheromiku/.dws:/data/dws
# dws 的文件型 keychain:必须有 $HOME/.local/share/dws-cli,故挂到 DWS_HOME 下
- /home/zheromiku/.local/share/dws-cli:/data/home/.local/share/dws-cli
- /home/zheromiku/tmp/alist:/sources/alist:ro
- /home/zheromiku/dobot-portal-sync:/sources/pp:ro
- ./secrets/dingtalk_capture.json:/run/source-secrets/dingtalk_capture.json:ro
- ./secrets/alist_connections.xlsx:/run/source-secrets/alist_connections.xlsx:ro
- ./secrets/alist_api_token:/run/source-secrets/alist_api_token:ro
- ./secrets/pp_credentials.json:/run/source-secrets/pp_credentials.json:ro
networks:
- default
- weknora-local
depends_on:
postgres:
condition: service_healthy
read_only: true
tmpfs:
- /tmp
restart: unless-stopped
release-worker:
build:
context: .
dockerfile: Dockerfile
command: ["/app/release-worker"]
user: "${APP_UID:-1000}:${APP_GID:-1000}"
environment:
<<: *app-environment
WORKER_ID: release-worker-1
RELEASE_WORKER_ENABLED: "false"
RELEASE_CONFIG_FILE: ""
secrets:
- pgpass
depends_on:
postgres:
condition: service_healthy
read_only: true
tmpfs:
- /tmp
restart: unless-stopped
dingtalk-login:
# Chromium profile for DingTalk scan sessions. The bind-mounted secrets
# directory is rw ONLY here; the worker keeps reading the capture read-only.
build:
context: ./services/dingtalk-login
image: knowledge-governance-dingtalk-login:local
user: "${APP_UID:-1000}:${APP_GID:-1000}"
environment:
CAPTURE_FILE: /run/source-secrets/dingtalk_capture.json
DINGTALK_STATE_FILE: /sources/dingtalk/data/dingtalk-internal/inventory-state.json
VERIFY_SPACE: "${DINGTALK_VERIFY_SPACE:-9JOGO6332vlBnm4Q}"
QR_TTL_MS: "300000"
volumes:
- ./secrets:/run/source-secrets
- /home/zheromiku/dingtalk_kb_download:/sources/dingtalk
- dingtalk-chromium-profile:/profile
security_opt:
- no-new-privileges:true
tmpfs:
- /tmp
healthcheck:
test: ["CMD", "node", "-e", "require('http').get('http://127.0.0.1:8091/healthz',r=>process.exit(r.statusCode===200?0:1)).on('error',()=>process.exit(1))"]
interval: 10s
timeout: 5s
retries: 30
start_period: 15s
restart: unless-stopped
web:
build:
context: .
dockerfile: Dockerfile.web
ports:
- "8088:8080"
depends_on:
api:
condition: service_healthy
read_only: true
volumes:
# 上一版构建的静态资源(只读,由部署脚本落盘):CDN/浏览器可能还持有旧 index.html,
# 让它的旧哈希引用不至于 404 —— 否则页面会空白或"只有文字没样式"。
- ./deploy/legacy-assets:/usr/share/nginx/legacy:ro
tmpfs:
- /tmp
- /var/cache/nginx
- /var/run
restart: unless-stopped
secrets:
postgres_password:
file: ./secrets/postgres_password
pgpass:
file: ./secrets/pgpass
test_weknora_api_key:
file: ./secrets/test_weknora_api_key
volumes:
governance-pgdata:
dingtalk-chromium-profile:
networks:
weknora-local:
external: true
name: weknora071full-network