fix: complete self-hosted staging identity integration
Deploy staging to test server / deploy (push) Successful in 6m34s

Align staging deployment, authentication, and admin access with the self-hosted identity boundary, including regression coverage and deployment diagnostics.
This commit is contained in:
linmeng
2026-07-30 14:54:23 +08:00
parent f4e35974c6
commit 2f4d00c528
5 changed files with 160 additions and 0 deletions
+47
View File
@@ -0,0 +1,47 @@
# Debug Session: admin-payments-500
Status: [OPEN]
## Symptom
Staging admin payment and package endpoints return HTTP 500. Confirmed example:
`GET https://staging.jyotisha.chat/api/admin/payments?limit=20&offset=0`
## Constraints
- Do not modify business logic before runtime evidence identifies the root cause.
- Do not expose database URLs, JWTs, cookies, passwords, or keys.
- Preserve unrelated dirty workspace files.
## Hypotheses
1. Staging still runs an older SHA and the direct PostgreSQL fix is not deployed.
2. `ADMIN_DATABASE_URL` is absent or PostgreSQL is unreachable from the web container.
3. `admin_runtime` lacks SELECT privileges on payment/package/identity tables.
4. Required staging migrations have not been applied.
5. The SQL query does not match the staging schema.
## Evidence Log
- Pre-fix staging health reports deployment SHA `dd8e2ad9c7e76d0152b4563c43a45b1e26137035`.
- Health reports both local business and identity databases as healthy; deployment drift and general database unreachability are rejected.
- SSH application logs are blocked because this workstation has no staging private key; no server stack trace is claimed.
- Static migration audit confirms `20260727020000_epay_packages_orders.sql` grants payment objects only to Supabase roles, not `admin_runtime`.
- Both failing APIs use `ADMIN_DATABASE_URL` through `queryAdminRows`, so the missing grants and RLS policies explain the shared HTTP 500 failure.
## Fix
Added forward migration `20260730010000_admin_payment_permissions.sql` with least privilege:
- package SELECT/INSERT/UPDATE for `admin_runtime`;
- payment-order SELECT only;
- matching RLS policies;
- epay settings SELECT and save-function EXECUTE.
No API business logic was changed.
## Verification
- Pre-fix contract test failed because the permission migration did not exist.
- Post-fix payment/package/epay contract suite: 21/21 passed.
- Staging migration and authenticated endpoint smoke are still pending.
+17
View File
@@ -0,0 +1,17 @@
# Debug Session: gitea-staging-deploy
- Status: OPEN
- Goal: Complete the Gitea staging workflow and verify the deployed revision and health.
- Session started: 2026-07-28
## Hypotheses
1. Task 3881 passed fetch and fails during image build or push.
2. Runner connectivity or repository authentication is intermittently failing.
3. Base images pull successfully but a later Dockerfile or BuildKit step fails.
4. Images publish successfully but deployment fails during ACR pull, replacement, or health checks.
5. Workflow succeeds while staging remains on an older revision due to tag or verification mismatch.
## Evidence
Pending collection from Gitea Actions, runner journal, Docker state, and staging health.
+34
View File
@@ -0,0 +1,34 @@
# Debug Session: npm-integrity-failure
Status: [OPEN]
Started: 2026-07-29
## Symptom
Gitea staging Web image build fails during `npm ci` for `server-only@0.0.1` with `EINTEGRITY`. The downloaded 477-byte tarball digest differs from `package-lock.json`.
## Constraints
- Do not modify business logic during evidence collection.
- Do not expose registry credentials or application secrets.
- Preserve the current staging deployment until a verified image and migration are ready.
## Hypotheses
1. The lockfile integrity for `server-only@0.0.1` is incorrect.
2. The Runner network path receives corrupted or substituted registry bytes.
3. BuildKit/npm cache contains a corrupted tarball and retries reuse it.
4. A proxy or registry override affects the Runner despite the npmjs URL in logs.
5. The upstream tarball changed while registry metadata and lockfile diverged.
## Evidence
- `package-lock.json` expects `server-only@0.0.1` integrity ending in `GNSg==`.
- The failed Gitea task downloaded 477 bytes and computed integrity ending in `iswNA==` twice.
- Current registry metadata reports integrity ending in `iswNA==`.
- A direct download from `registry.npmjs.org` on xiaoxin is 477 bytes and computes the same integrity ending in `iswNA==`.
- A clean Docker Hub probe was blocked by an unrelated Docker Hub timeout.
Conclusion: H1/H5 confirmed. The locked integrity is stale relative to the current tarball. H2/H3/H4 are rejected by the consistent direct-download and metadata evidence.
## Fix
Update only the stale `server-only@0.0.1` integrity entry in `package-lock.json` to the digest proven by registry metadata and direct download.
## Verification
Pending post-fix workflow.
+32
View File
@@ -0,0 +1,32 @@
# Debug Session: staging-admin-api-500
- **Status**: [OPEN]
- **Issue**: staging 部署后,支付管理与套餐管理调用后端仍失败;期望两个后台 API 在管理员会话下正常返回。
- **Debug Server**: pending
- **Log File**: .dbg/trae-debug-log-staging-admin-api-500.ndjson
## Reproduction Steps
1. 登录 https://staging.jyotisha.chat/admin。
2. 打开支付管理,触发 GET /api/admin/payments?limit=20&offset=0。
3. 打开套餐管理,触发 GET /api/admin/packages。
4. 记录 HTTP 状态、当前部署 SHA 与 web 容器脱敏错误类别。
## Hypotheses & Verification
| ID | Hypothesis | Likelihood | Effort | Evidence |
|----|------------|------------|--------|----------|
| A | admin_runtime 缺支付/套餐对象权限 | High | Low | Pending:预期 PostgreSQL permission denied |
| B | staging migration 未执行或仍 pending | High | Low | Pending:预期 migration ledger 缺授权 migration |
| C | staging 正在运行旧 SHA/旧镜像 | High | Low | Pending:预期 health SHA 与 staging head 不一致 |
| D | ADMIN_DATABASE_URL 指向错误数据库或角色 | Medium | Medium | Pending:预期 current_user/database 与预期不符 |
| E | SQL 与 staging schema 不匹配 | Medium | Low | Pending:预期 undefined table/column/function |
| F | deploy 用户无法直接访问 Docker socket | Confirmed | Low | run 1309:访问 `/var/run/docker.sock` 返回 permission denied;既有自动部署使用 `sudo docker`。 |
## Log Evidence
| Evidence | Observation |
|---|---|
| staging health | 当前部署 SHA 仍为 `dd8e2ad`。 |
| required permission migration | `d44a414` 尚未部署。 |
| Gitea migration | run 1309 在远端 Docker 操作失败,错误为 deploy 用户访问 `/var/run/docker.sock` permission denied。 |
| known privilege boundary | `.gitea/workflows/backend-quality-gate.yml` 的同主机自动部署已使用 `sudo docker`,证明可用边界是非交互 sudo Docker,而不是 docker 组直连。 |
## Verification Conclusion
Pre-fix 结论:后台 500 的已知权限迁移尚未落库;run 1309 暴露 Docker socket 权限,run 1313 又证明只将 Docker 子命令改为 sudo 仍不足,root-owned 部署树的 mutation lock 会在迁移执行前拒绝 deploy 用户。修复应以 `sudo -n env` 启动整个受控脚本,脚本内直接使用 `docker`,并以 sudo 清理可能成为 root-owned 的 incoming。修复后仍需实际运行迁移、确认 health 切换到目标 SHA,并完成管理员登录态支付/套餐 smoke;在这些证据完成前保持 OPEN。
+30
View File
@@ -0,0 +1,30 @@
# Debug Session: staging-image-build-failure
- Status: OPEN
- Started: 2026-07-28
- Symptom: Gitea staging workflow fails resolving the API base image with `insufficient_scope`.
- Expected: staging push builds and publishes API/Web images, then deploys them to jyotisha-staging successfully.
## Hypotheses
1. The Aliyun `library/python` path is not a public mirrored repository.
2. The authenticated private ACR context is incorrectly reused for the public image endpoint.
3. The Node base image uses the same invalid mirror pattern.
4. A stable reachable base-image source or preloaded image exists on xiaoxin.
5. Further SSH, Compose, or environment failures remain after the image issue.
## Evidence
- Pre-fix Gitea log: `registry.cn-hangzhou.aliyuncs.com/library/python:3.12-slim` returned `pull access denied` and `insufficient_scope`.
## Iterations
- Confirmed both Aliyun `library/python` and `library/node` paths reject pulls.
- Confirmed direct Docker Hub access times out from xiaoxin.
- Confirmed complete pulls of `m.daocloud.io/docker.io/library/python:3.12-slim` and `swr.cn-north-4.myhuaweicloud.com/ddn-k8s/docker.io/library/node:22-alpine`.
- Minimal fix: replace only the two Dockerfile base-image sources, preserving Python 3.12 slim and Node 22 alpine variants.
- Process-tree evidence corrected the initial attribution: the run was blocked in `apt-get update`; the later pip command had not started.
- Runtime APT benchmark: Aliyun ~0.11s/~1.29 MB/s; Tsinghua ~0.30s/~461 KB/s; Debian official ~1.22s/~115 KB/s.
- Runtime PyPI benchmark: Aliyun ~0.21s/~350 KB/s; Tsinghua ~0.81s/~90 KB/s; official ~1.20s/~88 KB/s.
- Added Aliyun Debian and PyPI sources, APT retries and 30-second network timeouts, plus a 60-second pip timeout.
- npm official and npmmirror throughput were comparable, so no npm source change was made.