feat(blog-cms): add database-backed blog CMS (Post/Author/Tag)#19
Open
yumingHe0705 wants to merge 1 commit into
Open
feat(blog-cms): add database-backed blog CMS (Post/Author/Tag)#19yumingHe0705 wants to merge 1 commit into
yumingHe0705 wants to merge 1 commit into
Conversation
Adds an admin-only blog CMS to the Wasp app: - Data model: Post, Author, Tag (+ PostStatus enum) with a many-to-many Post<->Tag relation and an optional Author<->User link. - Operations: admin CRUD, public read (published only), tag/author creation, with zod input validation and admin-only authorization. - Slug handling: auto-slugify from title, app-level uniqueness with a numeric suffix, and a DB unique constraint as the final guard (P2002 -> 409). - Publish lifecycle modeled as an explicit state machine (DRAFT/PUBLISHED/ ARCHIVED); publishedAt is stamped on first publish; archived posts must be restored to draft before going live again. - Admin UI: posts list (filter/search/paginate, inline status transitions, delete) and a create/edit editor (inline author/tag creation, SEO fields), plus a "Blog Posts" sidebar entry. - Dev seed (seedBlogContent) and unit tests for slug + status-machine logic. Co-authored-by: Cursor <cursoragent@cursor.com>
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
博客 CMS(Post / Author / Tag)—— PR 说明
概述
为 Open SaaS 应用新增了一个仅管理员可用、基于数据库的博客 CMS。内容编辑可以创建/编辑/删除文章、用标签归类、指定作者,并让每篇文章在一套显式的发布生命周期中流转。同时提供只读的公开查询(仅返回已发布文章),便于后续接入公开博客页或 SEO 流水线。
模板自带的博客是基于 Astro 的静态 Markdown,因此真正的 CMS 更适合放在 Wasp 应用里——数据库、认证、后台管理都在这一侧。
改动内容
schema.prisma):新增Post、Author、Tag三个模型与PostStatus枚举。Post⇄Tag为多对多;Post→Author为多对一;Author→User为可选的一对一关联。Post还带有 SEO 字段(metaTitle、metaDescription、canonicalUrl)以及@@index([status, publishedAt])索引。src/blog-cms/operations.ts):getAdminPosts(按状态筛选 + 搜索 + 分页)、getAdminPostById、createPost、updatePost、changePostStatus、deletePost、createAuthor、createTag、getAllAuthors。getPublishedPosts(可按标签过滤)、getPublishedPostBySlug、getAllTags。src/blog-cms/slug.ts):slugify(处理重音字符)、isValidSlug、generateUniquePostSlug(数字后缀解决冲突)。src/blog-cms/postStatus.ts):集中、可校验的状态迁移规则。AdminPostsPage.tsx、AdminPostEditorPage.tsx)以及侧边栏 “Blog Posts” 入口。src/blog-cms/seeds.ts)与 单元测试(slug.test.ts、postStatus.test.ts)。关键设计取舍
Author是内容作者档案(name、slug、bio、avatar),可选关联到登录User。这样把“编辑身份”与“认证账号”分开(支持外部投稿作者、改名、多账号等场景),代价是多一张表和一次 join。对 CMS 而言,这份灵活性值得。ALLOWED_TRANSITIONS),非法跳转被统一拒绝,也非常易于单测。我刻意禁止ARCHIVED → PUBLISHED:归档文章必须先恢复为草稿并重新审阅后才能再次上线。publishedAt仅在首次发布时写入并保持稳定(再次发布不会改写历史时间)。my-post、my-post-2…);数据库@unique约束作为并发下的最终兜底,并把 Prisma 的P2002转换为清晰的409 Conflict。仅当标题或 slug 真正变化时才重算 slug,避免已发布 URL 被悄悄改变。authRequired拦截;每个写操作/管理端操作在服务端再次校验context.user.isAdmin(辅助函数抛401/403)。公开查询永不泄露草稿——只筛选status = PUBLISHED。ensureArgsSchemaOrThrowHttpError,非法输入快速失败并返回400。@prisma/client,因此把状态值镜像到一个客户端安全的constants.ts,服务端仍使用真正的 Prisma 枚举。tags使用 Prisma 的set(整体替换),与编辑器 UX 一致(表单每次都提交完整的标签选择)。安全性考虑
PUBLISHED文章。404(PrismaP2025),而不是 500。测试方式
wasp test client run)——共 16 个,全部通过:slug.test.ts:slugify(大小写/标点/重音/空输入)、isValidSlug、generateUniquePostSlug(无冲突、冲突后缀、excludePostId)。postStatus.test.ts:所有允许/禁止的迁移 + 同状态幂等。-2→ 编辑 → 筛选/搜索/分页 → 删除;并验证非管理员访问/admin/posts会被重定向。wasp db seed seedBlogContent)会创建 1 位作者、3 个标签、4 篇涵盖所有状态的文章,便于快速人工查看。本地评审步骤
提示:注册前把你的邮箱加入
.env.server的ADMIN_EMAILS,即可获得管理员权限。说明 / 局限与后续规划
migrations/目录,因此以schema.prisma作为唯一事实来源,运行wasp db migrate-dev即可在本地生成迁移。Post上的 SEO 字段特意预留,便于后续「SEO 发布流水线」消费(sitemap / canonical / metadata)。Post ⇄ Tag目前使用 Prisma 隐式多对多;若后续需要排序或额外元数据,可切换为显式 join 表。