Kita 项目预览

Overview

What
为 Games、Reviews、Tools 与 Media 提供内容管理、公开展示和长期维护的一体化系统。
Why
把内容模型、结构化数据、媒体存储和自托管部署收敛在一套可以持续演进的工程中。
Boundary
OpenList 保持独立;Kita 只保存并跳转公开 URL,不共享数据库或部署生命周期。
Type
Content system
Period
2025 — Present
Maintainer
Kral
Application
Next.js 16 · TypeScript
Content
Payload CMS 3 · Local API
Data
PostgreSQL · Payload Media
Runtime
Docker Compose · Coolify
Media
Cloudflare R2
System boundaries

Architecture

01
Client
Browser → Kita application boundary

Request · navigation · rendering

02
Kita application boundary
Next.js · Payload Local API

Application: Routes · server rendering · UI / Content: Admin · collections · access

03
Persistent dependencies
PostgreSQL · Cloudflare R2

Structured data: Content · relations · migrations / Object storage: Payload Media · game covers

04
Runtime / deployment
Docker Compose · Coolify
05
Independent relation
OpenList Archive

Separate application · URL-level connection only

Technical decisions
Application boundary
Next.js and Payload share one deployable application

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 →
Server data access
Payload Local API through server getters and mappers

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 →
Persistence boundary
PostgreSQL for structured data; Cloudflare R2 for media objects

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 →
Archive boundary
OpenList remains an independent application connected by public URL

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 →
Development environment
Dev Container with Docker-in-Docker and targeted Linux volumes

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 →
Deployment boundary
Docker Compose describes services; Coolify builds and runs production

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 →

Windows 只负责保存源码和运行 Docker Desktop;Node、pnpm、PostgreSQL 与项目命令都留在可重建的 Dev Container 边界内。

Development server
pnpm dev
Host requirements
Git · Docker Desktop · VS Code · Dev Containers
Toolchain
Node 22 · pnpm 10.28.2 · remoteUser node
Development services
Dev Container → DIND → PostgreSQL 16
Environment contract
.env.example → .env

Rebuild sequence

  1. Open the workspaceDev Containers: Reopen in Container等待 postCreate 完成依赖安装,并确认工作区用户是 node。
  2. Create local configuration.env.example → .env填写本地数据库和 Payload 值;默认使用 local media,不复制生产 R2 secret。
  3. Start the applicationpnpm dev用户守卫先检查工作区,再启动并等待 PostgreSQL healthy,最后运行 Next.js。
  4. Run quality gatespnpm check && pnpm testformat、lint、typecheck 与测试是不同证据,不能互相替代。
  5. Verify the production buildpnpm build先停止开发服务器,避免两个进程同时写入 .next。

Verification

Runtime identity
预期用户为 node,Node 为 22.x,pnpm 与 packageManager 声明一致。
whoami · node --version · pnpm --version
PostgreSQL health
postgres 必须为 healthy;running 不等于已经可以接受连接。
docker compose -f compose.yaml -f compose.dev.yaml ps
Application surfaces
同时检查公开页面、Payload Admin 和一个真实依赖数据库的页面。
http://localhost:3000 · /admin
Repository map & environment contract

Project structure & configuration

目录按框架入口、业务功能与运行边界分层;环境变量表只公开键名、安全默认值和用途,不保存任何真实 secret。

Key files and surfaces

Commands & dependency baseline
package.json · pnpm-lock.yaml
Routes & composition
src/app
Feature modules
src/features
Server-only reads
src/server
CMS schema & access
src/payload
Validated runtime config
src/config
Database evolution
src/migrations
Development & production runtime
.devcontainer/devcontainer.json · compose.yaml · compose.dev.yaml · Dockerfile
ParameterSource / scopeRequirementEffect
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 · RestartPayload 的服务端 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 initializationPostgreSQL 首次初始化的数据库名,必须与 DATABASE_URI 对应。
POSTGRES_USER
Source: .env.example → .env / Coolify
Scope: Database initialization
Requirement · RequiredDefault · postgresApply · First initializationPostgreSQL 首次初始化用户,必须与 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 · Restartlocal 写入开发目录;生产选择 r2 后会强制检查完整 R2 配置。
MEDIA_R2_BUCKET
Source: .env.example → .env / Coolify
Scope: Media runtime
Apply · RestartMEDIA_STORAGE_MODE=r2 时指定 Payload Media 的对象 bucket。
MEDIA_R2_ENDPOINT
Source: .env.example → .env / Coolify
Scope: Media runtime
Apply · RestartR2 的 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、文档或日志。
Operations

Startup & runtime troubleshooting

Port 5432 is already in useCheck

Cause: 另一套本地 PostgreSQL 或另一个项目占用了开发 override 要映射的端口。

Check
  • 先确认端口占用者和当前 Docker context,不要启动多套数据库再猜测连接目标。
  • 检查 compose.dev.yaml 是否仍只暴露开发所需的 PostgreSQL 端口。
Resolution
  1. 停止重复服务,或有意识地修改开发 override 与 DATABASE_URI。
  2. 重新运行 pnpm dev,并等待 PostgreSQL healthcheck 通过。
PostgreSQL is running, but Payload cannot connectCheck

Cause: 容器只有 running 但尚未 healthy,或者 DATABASE_URI 的 host、用户、密码、数据库名与运行位置不一致。

Check
  • 在 Dev Container 中查看 Compose 状态与 PostgreSQL logs。
  • 本地 Next.js 使用 localhost;生产 Compose 内部服务使用 postgres。
Resolution
  1. 使用 --wait 等待 healthcheck,而不是仅判断容器进程存在。
  2. 让 POSTGRES_DB、POSTGRES_USER、POSTGRES_PASSWORD 与 DATABASE_URI 保持同一组值。
docker compose -f compose.yaml -f compose.dev.yaml ps
POSTGRES_PASSWORD changed, but authentication still failsCaution

Cause: PostgreSQL 官方镜像只在空 data volume 首次初始化时应用 POSTGRES_PASSWORD;修改环境变量不会改动已有数据库用户。

Check
  • 先确认 volume 中的数据是否需要保留,并核对当前数据库用户。
Resolution
  1. 在数据库内部正确轮换用户密码。
  2. 同步更新 DATABASE_URI 与运行时 secret,再重启应用。
  3. 只有确认开发数据可丢弃时,才考虑重建 volume。
Build succeeds, but production returns 503Resolved

Cause: Payload migration 在精简 runner 中导入了未被 selective COPY 带入的源码,entrypoint 在启动 Next Server 前退出。

Check
  • 先看 Coolify runtime log,不要只重复查看成功的 build log。
  • 区分 builder 文件系统、runner 文件系统与 Payload CLI 的运行时 import 路径。
Resolution
  1. 移除不必要的跨层 import,或把 migration 实际需要的源码复制进 runner。
  2. 用接近生产 runner 的最小文件集合验证 Payload config 和 migration 能加载。
  3. 重新构建镜像并确认 container 通过 migration 后开始监听端口。
pnpm build
Project history

Evolution

2026.06 — Current
  • Created
    Project history begins

    The first recorded Kita commit establishes the repository and development base.

  • Incident
    Games enters Payload; production returns 503

    The runtime dependency boundary becomes visible through a real failure.

  • Workflow
    Pull requests and quality checks guard main

    Deployment and permission to enter the production branch become separate steps.

  • Media
    Games covers move to Payload Media and R2

    Changing content no longer requires publishing application source.

  • Milestone
    v1 complete; content and stability maintenance begins

    The first functional and visual version is complete. Content curation and known maintenance issues remain ongoing.