Overview
- What
- 为 Games、Reviews、Tools 与 Media 提供内容管理、公开展示和长期维护的一体化系统。
- Why
- 把内容模型、结构化数据、媒体存储和自托管部署收敛在一套可以持续演进的工程中。
- Boundary
- OpenList 保持独立;Kita 只保存并跳转公开 URL,不共享数据库或部署生命周期。
Architecture
Request · navigation · rendering
Application: Routes · server rendering · UI / Content: Admin · collections · access
Structured data: Content · relations · migrations / Object storage: Payload Media · game covers
Separate application · URL-level connection only
Server rendering, Admin and content access stay in one runtime instead of creating a second backend service.
Trade-off: Next.js and Payload upgrades must be verified together.
Read rationale →The same Node process can read content without an internal HTTP hop, while mappers keep Payload documents out of UI components.
Trade-off: Local API access must remain explicit; public getters use overrideAccess false and published filters.
Read rationale →Relations and metadata need a database, while uploaded media must outlive a replaceable application container.
Trade-off: A complete recovery requires PostgreSQL metadata and R2 objects to be restored together.
Read rationale →Resource browsing belongs in OpenList without sharing Kita's database, secrets, volume or deployment lifecycle.
Trade-off: OpenList still needs its own monitoring, backup and upgrade plan.
Read rationale →Node, pnpm and PostgreSQL stay reproducible while the Windows host only supplies Git, Docker and VS Code.
Trade-off: DIND, volume ownership and Windows filesystem performance require explicit safeguards.
Read rationale →The deployable application, database and backup process have visible runtime relationships without adding an orchestrator.
Trade-off: A successful build does not prove that migrations and the production runner can start.
Read rationale →Run Kita locally
Windows 只负责保存源码和运行 Docker Desktop;Node、pnpm、PostgreSQL 与项目命令都留在可重建的 Dev Container 边界内。
pnpm devRebuild sequence
- Open the workspace
Dev Containers: Reopen in Container等待 postCreate 完成依赖安装,并确认工作区用户是 node。 - Create local configuration
.env.example → .env填写本地数据库和 Payload 值;默认使用 local media,不复制生产 R2 secret。 - Start the application
pnpm dev用户守卫先检查工作区,再启动并等待 PostgreSQL healthy,最后运行 Next.js。 - Run quality gates
pnpm check && pnpm testformat、lint、typecheck 与测试是不同证据,不能互相替代。 - Verify the production build
pnpm build先停止开发服务器,避免两个进程同时写入 .next。
Verification
whoami · node --version · pnpm --versiondocker compose -f compose.yaml -f compose.dev.yaml pshttp://localhost:3000 · /adminProject structure & configuration
目录按框架入口、业务功能与运行边界分层;环境变量表只公开键名、安全默认值和用途,不保存任何真实 secret。
Key files and surfaces
| Parameter | Source / scope | Requirement | Effect |
|---|---|---|---|
NEXT_PUBLIC_SITE_URL | Source: .env.example → .env / Coolify Scope: Public build | Requirement · RequiredDefault · http://localhost:3000Apply · Rebuild | 允许进入浏览器 bundle 的站点 URL;不能承载任何 secret。 |
PAYLOAD_SECRET | Source: .env.example → .env / Coolify Scope: Server runtime | Requirement · RequiredValue · SecretApply · Restart | Payload 的服务端 secret,至少 32 字符;生产值必须稳定。 |
ENABLE_DEV_SEED | Source: .env.example → .env Scope: Development only | Requirement · OptionalDefault · falseApply · Restart | 只有非 production 且显式为 true 时才允许开发 seed。 |
POSTGRES_DB | Source: .env.example → .env / Coolify Scope: Database initialization | Requirement · RequiredDefault · kitaApply · First initialization | PostgreSQL 首次初始化的数据库名,必须与 DATABASE_URI 对应。 |
POSTGRES_USER | Source: .env.example → .env / Coolify Scope: Database initialization | Requirement · RequiredDefault · postgresApply · First initialization | PostgreSQL 首次初始化用户,必须与 DATABASE_URI 对应。 |
POSTGRES_PASSWORD | Source: .env.example → .env / Coolify Scope: Database initialization | Requirement · RequiredValue · SecretApply · Database rotation | 只在空数据目录首次初始化时设置数据库密码;修改变量不会自动轮换已有用户。 |
DATABASE_URI | Source: .env.example → .env / Coolify Scope: Server runtime | Requirement · RequiredValue · SecretApply · Restart | 应用连接 PostgreSQL;本地 host 为 localhost,生产 Compose host 为 postgres。 |
MEDIA_STORAGE_MODE | Source: .env.example → .env / Coolify Scope: Media runtime | Requirement · RequiredDefault · localApply · Restart | local 写入开发目录;生产选择 r2 后会强制检查完整 R2 配置。 |
MEDIA_R2_BUCKET | Source: .env.example → .env / Coolify Scope: Media runtime | Apply · Restart | MEDIA_STORAGE_MODE=r2 时指定 Payload Media 的对象 bucket。 |
MEDIA_R2_ENDPOINT | Source: .env.example → .env / Coolify Scope: Media runtime | Apply · Restart | R2 的 S3-compatible API endpoint,不是公开图片 URL。 |
MEDIA_R2_PUBLIC_URL | Source: .env.example → .env / Coolify Scope: Public media | Apply · Rebuild & restart | 浏览器和 Next Image 使用的公开 custom domain。 |
MEDIA_R2_ACCESS_KEY_ID / MEDIA_R2_SECRET_ACCESS_KEY | Source: .env.example → .env / Coolify Scope: Server runtime | Value · SecretApply · Restart | 仅在 r2 模式下提供给服务端 storage adapter;不得进入 NEXT_PUBLIC、文档或日志。 |
Startup & runtime troubleshooting
Port 5432 is already in useCheck
Cause: 另一套本地 PostgreSQL 或另一个项目占用了开发 override 要映射的端口。
- 先确认端口占用者和当前 Docker context,不要启动多套数据库再猜测连接目标。
- 检查 compose.dev.yaml 是否仍只暴露开发所需的 PostgreSQL 端口。
- 停止重复服务,或有意识地修改开发 override 与 DATABASE_URI。
- 重新运行 pnpm dev,并等待 PostgreSQL healthcheck 通过。
PostgreSQL is running, but Payload cannot connectCheck
Cause: 容器只有 running 但尚未 healthy,或者 DATABASE_URI 的 host、用户、密码、数据库名与运行位置不一致。
- 在 Dev Container 中查看 Compose 状态与 PostgreSQL logs。
- 本地 Next.js 使用 localhost;生产 Compose 内部服务使用 postgres。
- 使用 --wait 等待 healthcheck,而不是仅判断容器进程存在。
- 让 POSTGRES_DB、POSTGRES_USER、POSTGRES_PASSWORD 与 DATABASE_URI 保持同一组值。
docker compose -f compose.yaml -f compose.dev.yaml psPOSTGRES_PASSWORD changed, but authentication still failsCaution
Cause: PostgreSQL 官方镜像只在空 data volume 首次初始化时应用 POSTGRES_PASSWORD;修改环境变量不会改动已有数据库用户。
- 先确认 volume 中的数据是否需要保留,并核对当前数据库用户。
- 在数据库内部正确轮换用户密码。
- 同步更新 DATABASE_URI 与运行时 secret,再重启应用。
- 只有确认开发数据可丢弃时,才考虑重建 volume。
Build succeeds, but production returns 503Resolved
Cause: Payload migration 在精简 runner 中导入了未被 selective COPY 带入的源码,entrypoint 在启动 Next Server 前退出。
- 先看 Coolify runtime log,不要只重复查看成功的 build log。
- 区分 builder 文件系统、runner 文件系统与 Payload CLI 的运行时 import 路径。
- 移除不必要的跨层 import,或把 migration 实际需要的源码复制进 runner。
- 用接近生产 runner 的最小文件集合验证 Payload config 和 migration 能加载。
- 重新构建镜像并确认 container 通过 migration 后开始监听端口。
pnpm buildEvolution
- CreatedProject history begins
The first recorded Kita commit establishes the repository and development base.
- IncidentGames enters Payload; production returns 503
The runtime dependency boundary becomes visible through a real failure.
- WorkflowPull requests and quality checks guard main
Deployment and permission to enter the production branch become separate steps.
- MediaGames covers move to Payload Media and R2
Changing content no longer requires publishing application source.
- Milestonev1 complete; content and stability maintenance begins
The first functional and visual version is complete. Content curation and known maintenance issues remain ongoing.
Project Activity
| Date | Type | Event |
|---|---|---|
| 05 SEP | Review | Kita v1 wrap-up and current maintenance stage recorded |
| 05 AUG | Review | Kita architecture and boundary review documented |
| 04 AUG | Recovery | Restore drill procedure and current evidence recorded |
| 04 AUG | Incident | Production 503 incident reviewed |
| 03 AUG | Decision | Technology boundaries and deliberate omissions documented |
| 22 JUL | Media | Games media moved beyond source-controlled assets |
Engineering Notes
| Date | Type | Title |
|---|---|---|
| 2026.09.05 | Review | Kita 的第一版,终于可以收尾了 |
| 2026.08.05 | Reference | Kita 项目整体架构、数据流与边界审查 |
| 2026.08.04 | Case | 从 Payload Media 到 Cloudflare R2 的完整配置 |
| 2026.08.04 | Incident | 为什么 Build 通过了,生产容器仍然 503 |
| 2026.08.03 | Decision | Kita 用了哪些技术,以及我刻意没有使用什么 |
| 2026.08.15 | Case | 把 Codex CLI 接进 Kita:Dev Container 实践与踩坑 |