Skip to content

feat: add blog CMS and AI animation renderer#18

Open
wangschang wants to merge 1 commit into
floatboatai:mainfrom
wangschang:codex/blog-cms-ai-animator
Open

feat: add blog CMS and AI animation renderer#18
wangschang wants to merge 1 commit into
floatboatai:mainfrom
wangschang:codex/blog-cms-ai-animator

Conversation

@wangschang

Copy link
Copy Markdown

PR:博客 CMS 后台与 AI HTML 动画生成/视频渲染

建议 PR 标题:feat: add admin blog CMS and AI HTML animation-to-video pipeline

本文可直接复制到 Pull Request 描述中。当前只准备文档和代码变更,不创建 PR、不提交、不推送分支,待人工验收后再执行。

1. 变更摘要

本 PR 在现有 Wasp + React + Prisma 的 SaaS 模板上完成两项独立功能:

功能 面向用户 结果
博客 CMS 后台 管理员 管理作者、标签和文章;草稿/发布状态、Markdown 编辑、公开博客
AI Animator 登录用户 提示词生成安全 HTML 动画,在线预览并异步导出 MP4/WebM

两项功能均延续既有 ShadCN、Tailwind、后台侧栏、Toast/Dialog 与 Wasp
operations 的工程约定;真正的数据权限在服务端校验,前端限制仅用于体验。

2. 功能一:博客 CMS 后台

2.1 用户能力与路由

路由 权限 功能
/blog 公开 已发布文章列表、分页、作者/标签/发布时间/封面/摘要
/blog/:slug 公开 安全渲染 Markdown 的文章详情;不可见文章返回 404
/admin/blog 管理员 文章列表、状态/作者/标签筛选、发布/撤回、删除确认
/admin/blog/new 管理员 新建草稿
/admin/blog/:id/edit 管理员 编辑文章、实时 Markdown 预览、slug 校验
/admin/blog/authors 管理员 作者 CRUD
/admin/blog/tags 管理员 标签 CRUD

2.2 数据关系

erDiagram
  AUTHOR ||--o{ POST : writes
  POST }o--o{ TAG : categorised_by

  AUTHOR {
    uuid id PK
    string name
    string slug UK
    string bio
    string avatarUrl
  }
  TAG {
    uuid id PK
    string name
    string slug UK
  }
  POST {
    uuid id PK
    string title
    string slug UK
    string excerpt
    string content
    string coverImageUrl
    PostStatus status
    datetime publishedAt
    uuid authorId FK
  }
Loading

2.3 业务规则

  • 文章状态只有 DRAFTPUBLISHED;发布时写入 publishedAt=now(),撤回时清空发布时间。
  • 公开查询同时要求 status=PUBLISHEDpublishedAt <= now(),因此草稿、撤回文章和未来发布时间均不会泄露。
  • PostAuthorTag 的 slug 各自唯一,格式为 3–120 位小写 ASCII、数字与单连字符:^[a-z0-9]+(?:-[a-z0-9]+)*$
  • 表单可根据拉丁标题/名称生成 slug;纯非拉丁文本要求管理员手动输入合法 slug。
  • 作者被文章引用时禁止删除并返回 409;删除标签会自动解除文章关联;删除文章要求 UI 二次确认。
  • 正文使用 react-markdown + remark-gfm,不启用原始 HTML,降低 XSS 风险。

2.4 权限与错误码

flowchart LR
  A[请求 CMS operation] --> B{已登录?}
  B -- 否 --> U[401]
  B -- 是 --> C{isAdmin?}
  C -- 否 --> F[403]
  C -- 是 --> D[校验输入、关系与 slug]
  D --> E[读写 Prisma 数据]

  P[公开 Blog 请求] --> Q{已发布且发布时间已到?}
  Q -- 否 --> N[404]
  Q -- 是 --> R[返回公开文章]
Loading
场景 返回
未登录访问 CMS operation 401
普通用户访问 CMS operation 403
slug 重复、删除被引用作者 409
输入无效、Author/Tag 不存在 400
公开侧请求草稿、撤回或不存在 slug 404

这符合 Open SaaS 的约定:客户端权限判断仅改善体验,服务端 context.user 与管理员检查才是实际访问控制。Open SaaS 授权指南

2.5 管理员操作手册

  1. 使用被 ADMIN_EMAILS 配置识别的账号登录。
  2. 打开 Admin → Blog CMS → Authors,创建作者:名称、slug、简介、头像 URL(可选)。
  3. 打开 Tags,创建一个或多个标签。
  4. 打开 Posts → New post,填写标题、摘要、Markdown 正文、作者和标签;检查自动生成的 slug,必要时手动调整。
  5. 保存后默认是草稿;在列表或编辑页点击 Publish
  6. 打开 /blog/<slug> 核对公开效果;需要下线时点击 Unpublish
  7. 删除前确认影响:被文章使用的作者需先重新分配文章或删除文章,标签可直接删除。

3. 功能二:AI HTML 动画生成与视频渲染

3.1 页面与用户流程

路由 权限 功能
/animations 登录用户 自己的动画历史、生成/渲染状态、重试、删除
/animations/new 登录用户 标题、提示词与 1–10 秒时长
/animations/:id 登录用户 沙箱预览、播放/暂停/继续/重播、渲染进度、下载
sequenceDiagram
  actor U as 登录用户
  participant W as Wasp Web/App
  participant J as PgBoss 生成任务
  participant M as AI Provider
  participant R as 独立 Renderer
  participant S as S3/本地存储

  U->>W: 提交标题、提示词、时长、idempotencyKey
  W->>W: 校验输入、所有权与积分预占
  W-->>U: 快速返回 GENERATING 记录
  W->>J: 投递生成任务
  J->>M: 系统提示词 + 用户提示词
  M-->>J: JSON { title, html }
  J->>J: AST/脚本/资源安全校验
  J->>W: PREVIEW_READY + 创建 QUEUED render
  W-->>U: iframe 沙箱预览与播放控制
  R->>R: 原子领取任务、租约心跳
  R->>R: Playwright 按 30fps 逐帧截图
  R->>R: FFmpeg 编码 MP4 + WebM + poster
  R->>S: 上传文件
  R->>W: SUCCEEDED + 文件 key
  U->>W: 请求下载 URL
  W-->>U: 私有 S3 签名 URL(15 分钟)
Loading

3.2 动画效果展示(已验证实例)

本地验收样例使用提示词:

深蓝色科技品牌开场:发光粒子汇聚成透明玻璃球体,缓慢旋转并产生青蓝色光晕;随后粒子展开为 “AI CREATES MOTION”,最后出现 “Powered by Qwen”。全片在 10 秒内完成。

效果要点:深蓝渐变背景、粒子/玻璃球聚合、青蓝色光晕、清晰高对比标题与署名文案。在线预览支持 Pause / Continue / Replay;已验证导出为 1080p、30fps、10 秒 H.264 MP4 与 VP9 WebM。

本地演示入口(运行本项目后可访问):

上述链接为本机验收产物,未提交二进制视频到仓库。PR 合并后建议在 GitHub PR 中上传短录屏/GIF,或部署 Preview 环境后替换为可公开访问的演示链接。

3.3 生成内容与 AI Provider

  • AnimationModelProvider 抽象隔离业务层与模型 SDK;已实现 OpenAI 与阿里云百炼(Model Studio)适配器。
  • 阿里云路径使用 OpenAI 兼容 Chat Completions 接口,qwen-plus 以 JSON 输出 HTML 动画结果;API Key 可由专属工作空间 CSV 安全读取,绝不发送到浏览器或记录到日志。
  • 系统提示词有版本号,规定 1920×1080、1–10 秒、有限动画次数、可读文字和高性能 CSS transform/opacity 优先策略。
  • 生成允许 HTML、内联 CSS、内联 JavaScript、SVG、Canvas、Web Animations API 与内置 Anime.js 3.2.2。
  • 生成禁止 CDN/外链、fetch、WebSocket、表单、跳转、iframe、动态导入、存储 API、媒体标签,以及 CSS @import / url(...)
  • 如果首次输出被安全校验拒绝,后台仅带安全错误摘要自动重生一次;第二次仍不合规则失败并退回预占积分。

3.4 状态机与可靠性

stateDiagram-v2
  [*] --> GENERATING: 创建记录并预占积分
  GENERATING --> PREVIEW_READY: 生成和 HTML 校验成功
  GENERATING --> GENERATION_FAILED: Provider/校验失败,退回积分
  PREVIEW_READY --> QUEUED: 创建渲染任务
  QUEUED --> RENDERING: Worker 原子领取 + 租约
  RENDERING --> SUCCEEDED: 上传 MP4/WebM/poster 成功
  RENDERING --> FAILED: 可识别异常、退避重试
  FAILED --> QUEUED: 用户手动重试(不重复扣积分)
  SUCCEEDED --> DELETING: 用户删除
  DELETING --> [*]
Loading
  • (userId, idempotencyKey) 唯一约束避免重复生成与重复扣费。
  • 非订阅用户成功消耗 1 credit;生成失败只退回一次;订阅用户免扣。
  • PgBoss 网络生成任务的失败策略为最多 2 次重试、退避、5 分钟过期;陈旧预占由定时任务补偿。
  • 独立 Worker 通过 FOR UPDATE SKIP LOCKED 与租约抢占任务;Worker 崩溃、浏览器截图、FFmpeg 或上传失败均可识别并重试,默认最大 3 次。

3.5 安全边界

flowchart TB
  G[模型生成 HTML:不可信] --> V[服务端 AST + 脚本规则校验]
  V --> I[iframe sandbox=allow-scripts]
  I --> C[CSP:仅内联内容与 data/blob 图片]
  C --> P[严格 postMessage 控制通道]
  V --> X[拒绝网络、跳转、存储、外链资源]
  R[Renderer 容器] --> N[Playwright 拦截全部外部网络]
  R --> L[非 root / 资源限制 / 临时文件]
Loading
  • 预览 iframe 不启用 allow-same-origin,父子页面只接受限定的 PLAYPAUSERESUMESEEKRESTARTGET_STATE 消息。
  • 所有动画查询、重试、重命名、删除、下载都在服务端按 userId 做所有权校验;匿名 401,跨用户记录返回 404
  • 生产必须使用私有 S3 和 15 分钟签名下载 URL。项目提供的 local 文件存储仅供 localhost 开发/验收:静态文件 URL 不可用于生产访问控制。

3.6 渲染与编码规范

  1. Renderer 使用固定 Playwright Chromium 打开规范化 HTML,拦截所有网络请求。
  2. 固定画布 1920×1080、30fps,动画最大 10 秒,即最多 300 帧。
  3. 运行时暂停真实时钟,并按 frame / 30 × 1000ms 精确 seek。
  4. Playwright 输出逐帧 PNG;FFmpeg 生成首帧 poster、MP4 与 WebM。
  5. 上传成功后写入存储 key,状态更新为 SUCCEEDED,前端启用下载按钮。
产物 编码 色彩格式 额外参数
MP4 H.264 / libx264 yuv420p CRF 18、30fps、faststart
WebM VP9 / libvpx-vp9 yuv420p CRF 30、30fps
Poster PNG 第 0 帧

商业发布前需要根据实际发布地区、FFmpeg 构建方式与 H.264 分发方式完成专利和许可证审查。

3.7 操作手册

生成与下载

  1. 登录后打开 /animations/new
  2. 输入至少 10 个字符的提示词,可选标题,选择 1–10 秒时长。
  3. 提交后页面先显示 Generating;任务完成后显示沙箱预览。
  4. Pause / Continue / Replay 检查时间轴效果。
  5. 等待视频状态从 Queued/Rendering 变为 Ready、进度 100%。
  6. 点击 Download MP4 + WebM,服务端只为当前用户的成功任务返回下载地址。
  7. 若生成失败可重试生成;若视频失败可重试渲染,渲染重试不会再次扣积分。

本地启动

# 终端 1:应用与数据库
cd template/app
wasp start db
wasp db migrate-dev
wasp start

# 终端 2:独立渲染 Worker(读取同一 DATABASE_URL)
cd template/animation-renderer
set -a
source ../app/.wasp/out/server/.env
source ../app/.env.server
set +a
npm start

本机无 S3 凭据时,在 template/app/.env.server 配置:

ANIMATION_STORAGE_DRIVER=local
ANIMATION_LOCAL_STORAGE_DIR=/absolute/path/to/template/app/public/generated-animations
ANIMATION_LOCAL_STORAGE_PUBLIC_URL=http://localhost:3000/generated-animations

生产环境改用 ANIMATION_STORAGE_DRIVER=s3,并设置 AWS 区域、Bucket、IAM Access Key/Secret;S3 凭据只留在服务端。Open SaaS 推荐采用 S3 签名 URL 保持凭据不暴露给客户端。文件上传指南

4. 主要实现位置

范畴 文件/目录
Prisma 数据模型与迁移 template/app/schema.prismatemplate/app/migrations/
博客路由与 operations template/app/src/blog/
后台导航与公开 Blog 导航 template/app/src/admin/layout/Sidebar.tsxtemplate/app/src/client/components/NavBar/constants.ts
动画路由、operations、任务 template/app/src/animation/
模型 Provider 与阿里云配置 template/app/src/animation/provider.tsaliyunCredentials.tsgenerationJob.ts
逐帧渲染 Worker template/animation-renderer/
存储与下载 template/app/src/file-upload/s3Utils.tstemplate/animation-renderer/src/storage.ts
专项工程文档 template/app/docs/blog-cms.mdtemplate/app/docs/ai-html-animation.md
E2E template/e2e-tests/tests/blogCmsTests.spec.tsanimationTests.spec.ts

5. 验收与测试

已执行

  • Application Vitest:7 个测试文件,34 个测试通过
  • TypeScript:通过 npx tsc --noEmit
  • Renderer TypeScript:通过 npm run typecheck
  • ESLint:通过 npm run lint
  • Prettier:通过 npm run prettier:check
  • git diff --check:通过。
  • 真实阿里云 qwen-plus 请求:成功生成预览。
  • 真实渲染:Worker 已完成 MP4 与 WebM;ffprobe 验证为 1920×1080、30fps、10.0 秒,MP4 为 H.264,WebM 为 VP9。

PR Reviewer 建议验收

  • 管理员创建 Author、Tag、草稿 Post,草稿不出现在 /blog
  • 发布后文章出现在 /blog 并可通过 slug 打开;撤回后详情 404。
  • 重复 slug、无效 URL、纯中文标题未手填 slug、删除被引用作者均显示正确错误。
  • 普通用户无法进入 CMS 或通过 operation 修改 CMS 内容。
  • 登录用户生成动画,确认预览控制与重播效果。
  • 等待渲染状态完成后下载 MP4/WebM,并用播放器验证画面与时长。
  • 验证跨用户无法获取、重试、删除其他人的动画和下载链接。
  • 在 CI 环境运行现有 Playwright 回归;Open SaaS E2E 目录与 CI 约定见测试指南

6. 部署前检查与后续建议

  • 配置正式 ADMIN_EMAILS、阿里云/模型 API Key、私有 S3 Bucket 与最小权限 IAM。
  • 关闭 ANIMATION_STORAGE_DRIVER=local;本地静态下载不能用于生产。
  • Renderer 以非 root、无公网出口、CPU/内存/进程/临时磁盘限制的容器运行;生产至少两个副本。
  • 为渲染队列长度、租约超时、失败率、任务耗时、S3 上传失败设置告警。
  • 在部署 Preview 中补充真实录屏或 GIF 到 PR,便于 Reviewer 直观检查动画。
  • 后续可扩展:定时发布、审核流、修订历史、用户绑定作者、富文本编辑器、定时渲染、套餐分辨率、音频和素材上传。

7. 推荐 PR 描述结尾

验证完成后,请确认:
- 数据库 migration 已在目标环境执行;
- 生产 S3、模型密钥和 Worker 环境变量已由部署平台注入;
- 本 PR 不包含任何 API Key、工作空间 CSV 或生成的二进制视频;
- E2E/CI 在目标分支上复跑通过。

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant