从 0 到 1 打造我的全栈内容系统
大家好,我是 namelesserlx。
CodeLess's Blog 正式上线了!开源地址:https://github.com/namelesserlx/codeless-blog
这是一个完全由我一人从 0 到 1 设计、开发、部署的现代化全栈博客系统。它不仅仅是一个“能发文章”的网站,更是我对开发者写作工具和个人内容系统的一次重新思考。
我做这个博客,不是为了简单替代某个现成博客平台,也不是为了做一个只有页面展示的作品集。更重要的是,我希望把目前自己掌握的前端、后端、数据库、缓存、搜索、SEO、性能优化、部署、可观测性以及 AI 接入能力,放到一个真实、完整、长期运行的项目里做一次系统化输出。
换句话说,CodeLess Blog 对我来说有三层意义:
内容入口:承载我的技术文章、项目记录、学习笔记和生活碎片;
工程作品:集中展示我目前掌握的全栈开发能力;
AI 底座:为后续 AI 内容编辑、内容续写、内容美化和知识库问答打基础。
这篇文章不会写成教程,也不会把每个接口和每个字段都展开。我会重点介绍 CodeLess's Blog 已经具备的核心功能、关键设计和后续 AI 方向。
为什么我要重新造一个博客系统?
市面上成熟的博客和内容平台已经很多了。WordPress、Ghost、Hexo、Hugo、Notion、语雀、掘金、微信公众号,都能完成内容发布。如果目标只是“把文章放到网上”,自研一套博客系统并不是最省事的选择。但我想解决的不是“发布一篇文章”这么简单。
我真正需要的是一个长期属于自己的内容系统:
内容可以在后台完成创作、预览、发布和维护;
文章、代码片段、照片、标签、评论可以形成统一内容资产;
读者可以通过搜索、标签和互动重新发现历史内容;
系统可以沉淀浏览量、点赞、评论、阅读时长等反馈;
后续可以接入 AI,让写作、整理和问答变得更自然;
代码、数据、部署和运行状态都掌握在自己手里。
普通博客更像一个内容展示页,而 CodeLess's Blog 更像一套围绕个人创作构建的内容系统。CodeLess 的核心理念是「Code Less,Write More」 —— 技术永远服务于内容,而不是反过来拖后腿。于是,我决定用 2026 年最现代的技术栈,从零构建一个真正为开发者量身定制的博客系统。
核心功能亮点概览
CodeLess's Blog 不是简单的“文章列表 + 文章详情”,而是围绕内容生产、内容展示、内容互动、内容分析和未来 AI 能力设计的一整套系统。
模块 | 核心能力 | 价值 |
|---|---|---|
博客前台 | 首页、文章列表、文章详情、标签、搜索、代码片段、照片墙、评论、点赞、阅读统计、PWA、SEO、性能优化 | 面向读者,负责内容消费和内容发现 |
文章系统 | 富文本编辑、草稿、预览、发布、封面、摘要、标签、导入导出、搜索同步 | 让文章从“文件”变成可管理的内容资产 |
片段系统 | 短内容、图片、视频、封面、互动 | 承载不适合写成长文的技术记录、想法和日常记录 |
相册系统 | 照片、地点、时间线、分类和标签 | 让博客不只承载文字,也承载生活与视觉内容 |
评论互动 | 文章评论、片段评论、回复、点赞、状态管理 | 让内容和读者产生反馈关系 |
搜索系统 | 基于 MeiliSearch 的全文检索、标签检索和搜索结果展示 | 让历史内容可以被重新发现 |
SEO 与性能 | SSR、元信息、站点地图、结构化内容、图片优化、代码分割、缓存与加载优化 | 让文章更容易被搜索引擎收录,也让读者更快进入阅读状态 |
指标系统 | 浏览量、点赞、阅读时长、文章报表、热门内容 | 让内容表现可被观察和分析 |
管理后台 | 文章、片段、评论、标签、照片、用户、基础配置、数据看板 | 让内容生产和系统治理从代码流程中独立出来 |
工程部署 | Monorepo、Docker Compose、Jenkins、环境变量分层、健康检查、可观测性 | 让项目具备上线运行和长期维护能力 |
AI 能力 | 摘要生成基础,后续扩展内容续写、内容美化、改写、标题建议、知识库问答 | 让博客逐步从内容展示走向智能内容系统 |
一句话总结:CodeLess = 极致写作体验 + 生产级后台 + 现代全栈架构 + 开发者友好。
整体技术选型及架构
技术选型
CodeLess Blog 采用Monorepo 架构(Turborepo + pnpm Workspace架构,将博客前台、管理后台、服务端和共享基础包放在同一个仓库里管理。
Blog/
├── apps/
│ ├── blog/ # 博客前台
│ └── admin/
│ ├── client/ # 管理后台前端
│ └── server/ # 管理后台服务端
├── packages/
│ ├── db/ # Prisma schema 与数据库客户端
│ ├── shared/ # 跨端共享类型与工具
│ └── config/ # ESLint / Prettier 等工程配置
├── docs/ # 项目文档
├── deploy/ # 部署与观测配置
└── docker-compose.ymlMonorepo 对这个项目的意义,不是把所有东西放到一起那么简单。它真正解决的是跨端一致性问题。
共享包 | 职责 |
| 统一维护 Prisma schema、数据库客户端和 seed 数据 |
| 放跨端共享类型、响应结构、业务枚举和稳定工具 |
| 统一 ESLint、Prettier 等工程规则 |
这样做之后,数据库模型变化、共享类型变化、工程规范变化,都可以在一个仓库里统一演进。
层级 | 技术选型 | 主要职责 |
博客前台 | Next16、React19、Tailwind CSS4 | 公开内容展示、SEO、性能优化、文章阅读、搜索、评论和互动 |
管理后台 | Vite8、React19、Ant Design6、Zustand | 内容管理、数据看板和系统配置 |
服务端 | Koa3、TypeScript | 后台 API、业务规则、搜索同步和指标任务 |
数据层 | Prisma、MySQL / MariaDB | 数据模型、迁移、类型化数据库访问 |
缓存与指标 | Redis | Session、高频统计、点赞状态、阅读时长聚合 |
搜索 | MeiliSearch | 全文检索、搜索索引、内容发现 |
文件存储 | COS 对象存储 | 图片、媒体和文章资源管理 |
编辑器 | Tiptap、 | 内容编辑、只读渲染、代码块增强、拖拽和文件上传 |
工程化 | pnpm workspace、Turbo、ESLint、Prettier、Vitest | 多包管理、构建编排、代码规范和基础测试 |
部署与观测 | Docker Compose、Jenkins、OpenTelemetry、Prometheus、Grafana、Tempo | 容器编排、凭证注入、上线流程和运行状态观测 |
我并不想把这篇文章写成“技术栈报菜名”。技术选型背后的关键不是用了多少工具,而是每一层是否解决了一个明确的问题:
Next.js 负责公开页面、SEO、性能优化和读者体验。
React Admin 负责后台管理效率。
Koa 负责服务端业务边界。
Prisma 负责数据库模型和类型安全。
Redis 负责高频状态和异步指标。
MeiliSearch 负责全文搜索和内容发现。
Tiptap Editor SDK 负责内容编辑与渲染。
Docker 与 Jenkins 负责上线流程。
OpenTelemetry+Prometheus+Grafana负责上线后的可观测性。
这些技术组合在一起,最终服务的是一个目标:让 CodeLess Blog 成为一个可以长期运行、长期写作、长期演进的个人内容系统。
系统分层架构
.png)
这套架构不是为了把系统拆得更复杂,而是为了让每一层只处理自己最擅长的问题。
层级 | 职责 | 设计重点 |
用户访问层 | 承接读者和管理员的访问入口 | 区分公开阅读场景和后台管理场景,保证 PC、移动端和后台入口都能进入对应应用 |
应用层 | 承载具体用户体验 | Blog 前台负责阅读、搜索、SEO、性能和互动;Admin 后台负责文章、片段、评论、照片、数据看板等内容管理;Editor SDK 同时服务编辑和只读渲染 |
网关层 | 统一请求入口和资源分发 | 通过 Nginx 处理静态资源、反向代理、HTTPS 和服务转发,让外部访问不直接暴露内部服务结构 |
服务层 | 承载业务规则和系统能力 | Koa API Server 负责内容服务、互动服务、搜索同步、文件上传、AI 辅助、异常处理、日志和 Worker 任务,避免复杂逻辑散落在前端 |
数据与中间件层 | 为不同类型的数据选择合适的存储和处理方式 | MySQL / MariaDB 负责结构化持久化,Redis 负责高频状态和指标缓冲,MeiliSearch 负责全文搜索,COS 负责图片和媒体资源 |
基础设施层 | 保障系统可部署、可运行、可观测 | Docker Compose 负责编排,Jenkins 负责凭证注入和部署流程,Prometheus、Grafana、Tempo 和 OpenTelemetry 提供基础观测能力 |
这样的分层带来的好处是:每一层的变化都尽量被限制在自己的边界内。比如,前台视觉和 SEO 策略可以独立优化,不影响后台内容管理;后台编辑器能力升级,不需要重写文章展示页;搜索索引策略调整,不会影响文章的主存储;指标统计从 Redis 到 MySQL 的同步方式变化,也不会阻塞读者正常阅读。
对一个会长期迭代的个人博客来说,这种边界比单纯“功能能跑”更重要。它让 CodeLess Blog 不是一次性页面项目,而是一个可以持续扩展的内容系统。不过,架构图只能说明系统被拆成了哪些层,真正决定体验的,是这些层如何一起完成一篇内容从创作、发布、展示、搜索到反馈的全过程。换句话说,CodeLess's Blog 的价值不只在于“分层清楚”,更在于这些层最终能组成一条可持续运转的内容闭环。
从创作发布到阅读反馈
这条内容闭环贯穿后台、前台、搜索、指标和未来的 AI 能力。它不是某一个页面,也不是某一个后台功能,而是一套完整的内容流转过程:
写作 → 编辑 → 草稿 → 预览 → 发布 → 展示 → 搜索 → 互动 → 数据反馈 → 再创作1. 内容生产:从编辑器到发布
我不希望写作流程始终绑定在本地 Markdown 文件和 Git 提交流程里。对开发者来说,这种方式当然可行,但它不适合长期、频繁、系统化地管理内容。
在 CodeLess‘s Blog 里,文章从编辑到发布更接近一个完整后台系统:
flowchart LR
A[进入后台] --> B[编辑文章]
B --> C[保存草稿]
C --> D[预览内容]
D --> E[发布文章]
E --> F[写入数据库]
E --> G[同步搜索索引]
F --> H[前台展示]
G --> I[搜索可见]草稿用于承接中间状态,预览用于发布前检查,发布则代表内容正式进入前台和搜索系统。发布文章时,系统不仅会写入数据库,还会同步搜索索引,让文章进入前台后也能被站内搜索重新发现。
2. Tiptap Editor SDK:写作体验的核心基础
编辑器是 CodeLess Blog 中非常重要的能力。它不是普通组件,而是内容生产入口。文章内容最终如何存储、如何渲染、如何扩展 AI 能力,很大程度上都取决于编辑器的设计。
所以我封装了自己的 Tiptap Editor SDK:@namelesserlx/editor。
它在系统里承担两个角色:
后台用于文章编辑;
前台用于文章只读渲染。
import { Editor } from '@namelesserlx/editor/react/editor';
import { ReadonlyRenderer } from '@namelesserlx/editor/readonly';
import '@namelesserlx/editor/style.css';它不是 CodeLess Blog 里的临时组件,而是一个围绕 Tiptap 封装的 React 富文本编辑器 SDK:内置 UI、以 JSON 内容为核心、支持只读渲染,并提供 HTML / Markdown 的导入导出能力。



我还封装了三个tiptap自定义扩展:
extension-code-block-pro:更适合技术文章的代码块,代码块不是简单把代码包起来就结束了。这个扩展主要解决的是代码内容的阅读和展示体验:macOS 风格头部、亮色 / 暗色 / 自动主题、行号、代码折叠、基于lowlight的语法高亮、语言切换、一键复制、Mermaid 图表渲染,以及键盘友好和语义化标记。它也支持只读模式:在文章详情页里,读者不能编辑代码,但依然可以复制、全屏、折叠 / 展开、切换行号,以及查看 Mermaid 的代码和图表预览。
NPM:https://www.npmjs.com/package/@tiptap-codeless/extension-code-block-pro
GitHub:https://github.com/namelesserlx/tiptap-codeless/tree/main/packages/extension-code-block-pro

@tiptap-codeless/extension-drag-handle:让内容块更容易组织,是一个块级拖拽与插入菜单扩展。长文章写作时,经常需要调整段落顺序、移动内容块、重新组织结构。如果只能复制粘贴,体验会很割裂。这个扩展提供了 hover 后出现的拖拽手柄、空块插入按钮、/触发的插入菜单、自定义菜单项,以及lockDragHandle、unlockDragHandle、hideDragHandle等命令,方便和其他弹层、菜单进行集成。它的意义是让编辑体验更接近现代文档工具:内容不是一整块不可移动的文本,而是可以按块组织、调整和插入。
NPM:https://www.npmjs.com/package/@tiptap-codeless/extension-drag-handle
GitHub:https://github.com/namelesserlx/tiptap-codeless/tree/main/packages/extension-drag-handle


@tiptap-codeless/extension-file-upload:让图片和文件进入写作流,是一个开箱即用的文件上传扩展。写技术文章时,图片、截图、附件和媒体资源经常会穿插在正文中。如果上传能力做得不好,写作过程会频繁被打断。这个扩展支持拖拽上传、粘贴上传、图片预览与拖拽缩放、图片对齐、视频预览、非图片文件卡片展示、上传占位状态、文件类型和大小限制,并提供 memory、Base64、local、custom 等多种存储模式。其中 custom 模式比较关键:编辑器侧负责占位、预览和内容插入,业务侧只需要处理真正的上传逻辑并返回最终资源信息。这让它可以适配自己的后端接口、对象存储或云存储服务。
NPM:https://www.npmjs.com/package/@tiptap-codeless/extension-file-upload
GitHub:https://github.com/namelesserlx/tiptap-codeless/tree/main/packages/extension-file-upload


3. 内容消费:展示、发现、SEO 与性能
CodeLess's Blog 前台不是后台数据的简单展示层,而是整个内容系统的公开入口。目前前台主要包含首页、文章列表、文章详情、标签页、搜索、代码片段、照片墙和关于页。
文章页尤其重要。技术文章依赖稳定的排版、清晰的目录、舒服的代码块和低干扰的阅读环境。对博客前台来说,视觉效果只是表层,真正重要的是读者能不能顺畅地读完内容。
随着文章越来越多,首页时间线一定不够用。所以系统接入了 MeiliSearch,让文章标题、摘要、正文和标签都可以进入搜索索引。
flowchart LR
A[文章发布 / 更新 / 删除] --> B[数据库变更]
A --> C[搜索索引同步]
D[读者输入关键词] --> E[MeiliSearch 检索]
E --> F[搜索结果]
F --> G[进入文章 / 标签 / 片段]搜索的意义不是“页面上有一个搜索框”,而是让历史内容在需要的时候被重新发现。
在 SEO 和性能上,我没有只做一句 title 和 description,而是尽量把 Next.js App Router 能承载的基础能力都用起来。这里简单列几个实际实现点,不展开成教程。
App Router 动态 Metadata
文章详情页通过 generateMetadata 动态生成每篇文章的 SEO 信息。不存在的文章返回不可索引的 metadata,正常文章则根据文章标题、摘要、作者、标签和发布时间生成页面元信息。
文章级 Open Graph / Twitter Card
每篇文章会根据内容生成 title、description、canonical、robots、keywords、authors、Open Graph 和 Twitter Card。预览状态下会显式禁止索引,正式发布后才允许搜索引擎抓取。
结构化数据 JSON-LD
文章页还会输出 Article 类型的 JSON-LD,让搜索引擎更清楚地理解文章标题、摘要、发布时间、更新时间、作者和封面图。
sitemap 与 robots
站点通过 App Router 的 sitemap.ts 和 robots.ts 自动生成 sitemap 和 robots。静态页面和已发布文章都会进入 sitemap,接口路径则在 robots 中禁止抓取。
全站基础 Metadata 与 PWA
根布局里配置了站点级 metadata、manifest、icons、Apple Web App、viewport 和 themeColor。
性能方面,我主要围绕“让读者尽快进入阅读状态”来做:使用 App Router 的服务端渲染能力减少首屏等待;文章页优先保证正文、目录和代码块的稳定展示;静态资源、图片和非首屏交互尽量按需加载;本地字体避免外部字体请求;PWA 和 manifest 提供更接近应用的访问体验。
4. 互动反馈:让内容不只是单向输出
一个博客如果只有作者输出,没有读者反馈,很容易变成单向归档。所以 CodeLess's Blog 不只关注文章展示,也关注评论、点赞、阅读时长、热门内容和后台数据看板。
类型 | 代表能力 | 作用 |
显性互动 | 评论、回复、点赞 | 让读者表达观点和反馈 |
隐性反馈 | 浏览量、阅读时长、热门内容 | 帮助作者判断内容是否真正被阅读 |
评论系统的价值在于承载交流;指标系统的价值在于帮助我理解内容表现。
但这类数据有一个特点:频率高,不一定要求每次都强一致。因此系统采用 Redis 承接高频行为,再通过 Worker 同步到 MySQL。
flowchart LR
A[读者访问 / 点赞 / 阅读] --> B[Blog API]
B --> C[Redis 实时记录]
C --> D[Worker 聚合同步]
D --> E[MySQL 持久化]
E --> F[文章报表 / 数据看板]这样做的核心不是“用了 Redis”,而是区分不同数据的一致性要求:文章正文和发布状态必须可靠,直接持久化到 MySQL;阅读量和阅读时长属于高频数据,可以先进入 Redis,再异步聚合;搜索索引追求最终一致;报表数据则面向分析和趋势观察。
5. 多种方式部署
项目支持多种部署策略,根据场景选择:
方式一:Docker Compose 全量一键部署
最适合新环境或全量上线。执行 scripts/docker-up.sh 脚本:
# 先基于 .env.example 配置好 .env.staging 或 .env.production
export APP_ENV=staging
sh scripts/docker-up.sh脚本按依赖顺序分四步执行:
Step 1/4: 启动基础设施
mysql + redis + meilisearch ← 并行启动,等待健康检查通过
Step 2/4: 执行初始化 Job
admin-server-migrate ← 数据库迁移 (Prisma Migrate Deploy)
admin-server-seed ← 数据播种 (首次初始化管理员账号)
admin-search-init ← 搜索索引创建 (MeiliSearch 配置)
Step 3/4: 构建并启动业务服务
admin-server ← Koa API 服务 (权限校验 + 业务路由)
admin-metrics-worker ← 指标刷入 Worker (Redis → MySQL)
admin-client ← Nginx 托管静态资源 + 反向代理
blog ← Next.js SSR 服务 (standalone 模式)
Step 4/4: 输出访问地址
→ http://localhost:3000 (博客前台)
→ http://localhost:8080 (管理后台)
→ http://localhost:8000 (API 接口)全部服务通过 depends_on + healthcheck 确保严格的启动顺序,不会出现"数据库还没就绪就启动应用"的经典问题。
方式二:三端独立部署
当需要单独更新某个模块时,可以只部署一端:
模块 | 部署方式 | 说明 |
|---|---|---|
Blog 前台 | Docker 或 | Next.js standalone 构建,产出自包含产物 |
Admin 服务端 | PM2 进程管理 | 生产环境启用 cluster 模式,多实例负载均衡 |
Admin 前端 | Nginx直接部署静态资源 | Vite 构建产出纯静态文件 |
Admin Server 的 PM2 配置(ecosystem.config.js):
module.exports = {
apps: [
{ name: 'blog-admin-server-prod',
script: './dist/app.js',
instances: 2, // 启动 2 个实例
exec_mode: 'cluster', // Cluster 模式
max_memory_restart: '500M', // 超过 500MB 自动重启
},
{ name: 'blog-admin-metrics-worker-prod',
script: './dist/scripts/flush-metrics.js',
// 独立进程运行,不阻塞主服务
},
],
};PM2 提供了进程守护、自动重启、日志管理、零停机重载等能力。指标 Worker 与主服务进程级隔离——即使 Worker 因处理大量指标而崩溃,API 服务不受影响。
Docker 部署中的指标 Worker
admin-metrics-worker 在 Docker 中作为一个独立容器运行,与 admin server 解耦。它的工作模式:
Blog 前台 → Redis (实时写入)
│
▼ (每 60s 轮询)
admin-metrics-worker
├─ flushPostReadTime() ← Hash 增量 → PostReadTime upsert
├─ flushPostUv() ← Set 成员 → PostViewDaily 批量插入
└─ flushPostLikes() ← Set 双向同步 → PostLike 增量/删除
│
▼
MySQL (持久化)无论 Worker 是否运行,前台写入 Redis 的操作都不会受影响。Worker 本身的健康状态也只影响指标数据的实时性,不影响博客核心阅读体验。
功能展示
前面更多是在讲 CodeLess's Blog 的设计思路和核心链路。以下是我的测试数据的界面展示
1. 博客首页:内容入口与个人品牌展示



2. 文章列表&详情:稳定、舒适的阅读体验




3. 代码片段:承载更轻量的技术记录

4. 标签云:整理内容标签
此处特别说明:该页面复制grtsinry43大佬的的旧版博客的标签页设计

5. 照片墙:让博客不只有技术
.png)

6. 管理后台:内容生产和系统管理中心


.png)


未来规划
CodeLess's Blog 后续最重要的方向,不是继续堆页面,而是让它逐步具备 AI 内容编辑和个人知识库能力。
1. AI 内容编辑
我希望 AI 能直接进入编辑器工作流,而不是停留在一个独立的输入框里。
计划中的能力包括:
内容续写:根据当前段落继续补充后续内容;
内容扩写:把简短观点扩展成更完整的解释;
内容美化:优化表达、节奏、标题和段落结构;
内容改写:将口语化表达改成更正式的技术文章风格;
摘要生成:自动生成适合列表页和 SEO 的摘要。
这里的关键不是“调用一个 AI 接口”,而是让 AI 和富文本编辑器结合。比如选中一段内容后,可以直接选择“续写”“润色”“压缩”“改成更技术化表达”;对整篇文章,可以自动生成摘要、标签和标题建议。这样博客后台就会从“内容管理工具”进一步变成“内容创作工具”。
2. AI 知识库问答
第二个方向,是基于我的文章内容构建知识库问答。随着文章越来越多,博客会沉淀大量个人知识:技术实践、项目经验、踩坑记录、架构思考、源码分析、学习笔记。
我希望未来读者不仅能搜索文章,还能直接提问:
“这个博客系统用了哪些技术栈?”
“有没有写过关于 React 性能优化的内容?”
“这套系统的搜索是怎么设计的?”
“作者关于全栈架构有什么实践经验?”
系统可以基于我已经发布的文章进行检索和回答,并给出相关引用文章。
这会让博客从“文章列表”进化成“可对话的个人知识库”。
flowchart LR
A[已发布文章] --> B[内容解析]
B --> C[向量化 / 索引]
C --> D[知识库]
E[读者提问] --> F[检索相关内容]
F --> G[AI 生成回答]
G --> H[返回答案与相关文章]这是我后续最期待的方向:让写过的内容不只是被阅读,还能被理解、关联和再次使用。
结语
但对我来说,这个博客是一个非常重要的起点:我终于有了一个完全属于自己的内容入口,也有了一个可以持续输出技术、验证想法、接入 AI 能力的长期项目。
我会继续在这里写技术文章、记录项目经验、沉淀学习笔记,也会继续迭代这个系统本身。
未来,我希望 CodeLess Blog 不只是一个博客,而是逐渐成为:
我的技术内容主页;
我的全栈工程作品;
我的个人知识库;
我的 AI 内容编辑实验场;
一个可以持续演进的开发者写作系统。


评论
还没有评论
来发表第一条评论吧!