codex-bot 8b650fdf78
PROMPT CI and deployment / verify-and-deploy (push) Successful in 8m18s
Build PROMPT media library with masonry UI and persistent PostgreSQL S3 storage
2026-10-22 22:18:11 +08:00

PROMPT

仓库名:Probably Rendering One More Pixel Tonight。其余命名使用 PROMPT / prompt。

自己的素材与创作工作台:Next.js Pages Router、React、TypeScript、Tailwind CSS、Node.js 24。中文界面包含瀑布流、日期贡献热力图、素材导入、多媒体详情、分类管理、独立素材架与线条图形工具。没有创建作品的按钮;图文和动态作品由代码实现。

启动

nvm use
npm ci
cp .env.example .env
# 修改 .env 中的三个密码 / 密钥,不使用示例值。
docker compose --env-file .env up -d
set -a; . ./.env; set +a
npm run storage:prepare
npm run dev
# 另一终端加载同一份 .env 后运行后台视频处理:
npm run worker

本机需要 Docker Compose 和 FFmpeg / ffprobe。开发中的 .env、数据库文件和对象不进入 Git;生产凭据由部署脚本随机生成。

页面与数据

  • /:所有非空素材包与已注册代码作品,按创建时间倒序;顶部为过去一年的贡献汇总。
  • /images、/videos、/audio:图片、视频、声音收藏。
  • /articles、/motion:代码编写的图文与动态作品。
  • 上述五类路径可以附加标签树路径,如 /images/style/white-line;父分类会包含后代分类的内容。
  • /p/<slug>:素材包的稳定详情地址,不随标签改变。多图切换、多视频及其关联音轨、多声音均可展示。
  • /library/import:导入同一类型的一组文件和说明,自动收为一份收藏。支持最多 16 个文件,单个 250 MB,总计 1 GB。
  • /library/assets:查看每份独立素材及被引用次数,复制 ID 以复用。
  • /library/tags:维护严格单父级分类树;同名 slug 只允许出现在不同分支。
  • /tools/doodle:手绘线条造型参考,可选色、复制和下载透明 SVG。不是 AI 图片编辑器。

素材库不插入演示或占位帖子。空分类显示简洁的空状态;空素材包不进入任何瀑布流。分页游标由时间与 UUID 组成,重复时间戳下仍稳定。

详细模型见 docs/data-model.md。PostgreSQL 保存引用、分类、元数据、文字和任务;RustFS 通过 S3 协议保存二进制对象。数据库和 S3 端口只监听宿主机 127.0.0.1,浏览器通过应用访问媒体,支持 Range 分段读取。

视频原件保持不变,后台生成无声 MP4、封面,以及存在声音时的 M4A 音轨。音轨记录在 asset_variants,始终属于原视频;不会生成独立声音资产。后台单任务处理,使用数据库锁领取任务,失败重试三次,支持超时任务恢复。FFmpeg 禁止输入网络协议。

编程加入素材包与作品

POST /api/posts 接收 kind、title、可选 slug、assets: [{id, role?}]、tags: [tagId]、text、coverAssetId 和 pageKey。role 为 content 或 caption。素材和说明文字都可以通过 ID 引用已有资产;text 是创建新文字资产的快捷方式。引用不会复制原文件。

POST /api/tags 接收 name、slug 和可选 parentId。

图文或动态作品需要先在 src/content/registry.tsx 注册 React 组件与类型,再通过 API 创建引用对应 pageKey 的素材包。封面引用普通图片资产。动态作品的 React 组件就是画面本身,不会被强制套入视频播放器。未注册的页面不会出现在瀑布流中,也不会执行数据库中的任意路径或脚本。

当前不内置作品模板或视频导出引擎;后续一起确定设计系统和制作方案。

贡献记录

contribution_events 保存细分的 kind、对象类型、对象 ID、数量、发生时间、元数据和可选幂等键。显示时按上海时区汇总所有类型。当前没有将上传、分类或保存动作自动算作贡献;触发规则留待确定。没有随机或伪造热力图数据。

CI/CD 与运维

.gitea/workflows/ci.yml 使用 runs-on: ces-control。PR 执行验证;main 上的 push 或手动运行,在类型检查、单元测试、生产构建、启动检查及真实 PostgreSQL/S3/视频集成测试通过后才部署。

集成测试启动带独立 project 名称和随机本机端口的 Docker 服务,使用临时随机凭据;结束后只删除该测试项目的容器和卷,不触碰生产卷。

生产约定:

项目 值
Web prompt.service,HTTP 18131
视频处理 prompt-worker.service
PostgreSQL Docker Compose prompt-postgres,回环端口 18132
S3 / RustFS Docker Compose prompt-objects,回环端口 18133,控制台关闭
发布 /opt/prompt/releases/<commit>,/opt/prompt/current 原子软链接
部署清单 /opt/prompt/infra/compose.yml
密钥 /etc/prompt.env,root:prompt / 0640,不进代码或日志
运行数据 /var/lib/prompt;数据库与 S3 使用 Docker named volumes
发布前数据库备份 /var/lib/prompt/backups,保留最近 10 份

服务由 systemd 管理,支持开机启动、故障重启和失败回退。迁移采用追加方式;发布前创建 PostgreSQL dump,回退应用版本不会破坏已写入数据。S3 对象卷不随发布清理;对象备份需要另外快照该卷,数据库 dump 不能代替对象备份。

curl -fsS http://127.0.0.1:18131/api/health # 当前应用版本
curl -fsS http://127.0.0.1:18131/api/ready  # 数据库和 S3 连接
sudo systemctl status prompt.service prompt-worker.service
sudo journalctl -u prompt-worker.service -n 100 --no-pager
sudo docker compose -p prompt --env-file /etc/prompt.env -f /opt/prompt/infra/compose.yml ps

首次部署可能安装 Docker Compose 和 FFmpeg;runner 需能访问宿主机 systemd、Docker,并具备 root 或免密 sudo。服务不配置公网域名或防火墙,按现有内网访问方式使用。

验证命令

npm run typecheck
npm test
npm run build
npm run test:smoke
# 在已配置的隔离 PostgreSQL / S3 环境:
npm run storage:prepare
PROMPT_INTEGRATION_TEST=1 npm run test:integration
# 或在 Docker 主机上一键创建并清理隔离环境:
bash scripts/integration.sh
S
Description
No description provided
Readme 80 MiB
Languages
TypeScript 86.8%
CSS 10.5%
PLpgSQL 2.4%
JavaScript 0.3%