发布于 /编辑于 /晚叙 CMS v1.0.0 — 外贸企业官网与内容管理系统
zhangxianlin (@zhangxianlin)/https://blog.dsriji.com/projects/wan-xu-cms-v100-wai-mao-qi-ye-guan-wang-yu-nei-rong-guan-li-xi-tong
LOADING...
基于 Cloudflare Workers 的全栈 CMS:Hono + D1 + R2 + Assets,中英双语,前后台一体部署,零服务器运维。
基于 Cloudflare Workers 的全栈 CMS:Hono + D1 + R2 + Assets,中英双语,前后台一体部署,零服务器运维。
| 层级 | 技术 | 说明 |
|---|---|---|
| 运行时 | Cloudflare Workers | 无服务器边缘计算平台,全球就近响应 |
| Web 框架 | Hono | 轻量、快速、支持 JSX 服务端渲染 |
| 模板 / SSR | Hono JSX + 原生 HTML/CSS/JS | 服务端直接渲染完整页面,利于 SEO |
| 数据库 | Cloudflare D1 | SQLite 边缘数据库,存储站点内容、用户、询盘 |
| 对象存储 | Cloudflare R2 | 图片 / 媒体文件存储,兼容 S3 API |
| 静态资源 | Workers Assets | 前台 CSS/JS/Favicon + 后台 SPA 静态托管 |
| 身份认证 | Cookie + PBKDF2-SHA256 | 服务端会话,密码 10 万次迭代哈希 |
| 富文本 | Markdown / HTML 双模式编辑器 | 后台在线编辑,支持图片上传 |
| 部署工具 | Wrangler + GitHub Actions | 一条 自动类型检查、建桶、初始化数据库、部署 |
前台( 双语)
管理后台(,路径可自定义)
部署前必须先创建 D1 数据库 和 R2 存储桶。
方式 A:命令行
成功后会输出 ,复制备用。
方式 B:Cloudflare 控制台
方式 A:命令行
方式 B:Cloudflare 控制台
⚠️ R2 存储桶名称在 Cloudflare 全平台全局唯一。如果提示名字已被占用,请换一个唯一名字(例如 ),并同步修改环境变量 。
⚠️ 安全提示: 里只有占位符,不要把真实值填进去再提交。
使用占位符,部署时由 CI/CD 自动替换:
需要设置的环境变量:
配置位置:
Cloudflare 官方「Git 自动部署」。代码 push 到 GitHub, 分支自动部署。
首次部署会自动执行 建表 + 写入默认数据;后续部署只同步 ,不会清空数据。
本仓库已包含 。每次 到 ,GitHub Actions 自动执行:
需配置的 Secrets:
部署成功后 Cloudflare 会自动分配:
域名必须已接入 Cloudflare DNS。
绑定自定义域后,把环境变量 改为 并重新部署,SEO 相关的 canonical / sitemap / hreflang 会使用真实域名。
本地示例见 。
本项目基于 MIT License 开源,可自由使用、修改、分发,包括商业用途。
| 环境变量 | 值 | 说明 |
|---|
| 2.1 中复制的 D1 UUID | D1 数据库真实 ID | |
| 2.2 中创建的 R2 桶名 | 例如 | |
| 你的正式域名 | 例如 | |
| 后台访问入口路径(可选) | 默认 ;可改为 、 等 | |
| admin 初始密码(可选) | 默认 ;建议首次部署时改成强密码 | |
| 是否按地区自动跳转语言(可选) | 默认 ;不需要可填 | |
| 随机字符串(可选但强烈建议设置) | 开发者验证 Cookie 签名密钥,生产环境必须修改 | |
| 自定义答案(可选但强烈建议设置) | 修改开发者联系方式时的验证答案 |
| Secret 名称 | 必填 | 说明 |
|---|
| 是 | D1 数据库 UUID | |
| 是 | R2 存储桶名称 | |
| 是 | 正式域名 | |
| 是 | 权限:Workers 编辑、D1 编辑、R2 编辑、账号读取 | |
| 是 | Cloudflare Account ID | |
| 否 | 后台入口路径,默认 | |
| 否 | admin 初始密码,默认 | |
| 否 | 默认 |
| 变量 | 位置 | 说明 |
|---|
| GitHub Secrets / Cloudflare 环境变量 | 正式域名(SEO 用) | |
| GitHub Secrets / Cloudflare 环境变量 | D1 数据库 UUID | |
| GitHub Secrets / Cloudflare 环境变量 | R2 存储桶名称 | |
| secret / | 询盘邮件 Webhook(如 Resend) | |
| secret / | 邮件服务 Bearer 令牌 | |
| secret / | 发件人(缺省 no-reply@站点域名) |
git push/en/zh//zh/en/admin//admin/adminAdmin@12345database_idwanxu_cmsxxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxxdatabase_idwanxu-cms-mediawanxu-cms-media-你的用户名R2_BUCKET_NAMEwrangler.tomlwrangler.tomlmainwanxu-cmsmainDATABASE_IDR2_BUCKET_NAMESITE_URLnode scripts/replace-wrangler-placeholders.mjs && npm run buildnode scripts/init-remote-db.mjs && npm run deployhttps://wanxu-cms.<你的子域>.workers.devschema.sqlseed.sqlschema.sql.github/workflows/deploy.ymlgit pushmainnpm ciwrangler.tomlnpm run buildwrangler deploywanxu-cmswww.yoursite.comSITE_URLSITE_URLhttps://www.yoursite.com/admin/admin / Admin@12345.dev.vars.example/media/*Content-Disposition: attachmentX-Content-Type-Options: nosniffX-Frame-Options: DENYX-Content-Type-Options: nosniffReferrer-Policysuper_admin/api/siteadmin / Admin@12345DEV_CHALLENGE_SECRETDEV_CHALLENGE_ANSWERDATABASE_IDR2_BUCKET_NAMEwanxu-cms-mediaSITE_URLhttps://www.yoursite.comADMIN_PATH/admin/manage/consoleADMIN_PASSWORDAdmin@12345GEO_REDIRECTtruefalseDEV_CHALLENGE_SECRETDEV_CHALLENGE_ANSWERDATABASE_IDR2_BUCKET_NAMESITE_URLCLOUDFLARE_API_TOKENCLOUDFLARE_ACCOUNT_IDADMIN_PATH/adminADMIN_PASSWORDAdmin@12345GEO_REDIRECTtrueSITE_URLDATABASE_IDR2_BUCKET_NAMEEMAIL_API_URL.dev.varsEMAIL_API_TOKEN.dev.varsEMAIL_FROM.dev.varshttps://wanxu-cms.<你的子域>.workers.devsrc/ Worker 源码(路由 / 页面 JSX / repo / 中间件)
public/ 前台静态资源 + admin/ 后台 SPA
db/ D1 schema.sql + seed.sql
docs/ DEPLOY.md 部署教程
scripts/ 部署辅助脚本(占位符替换、自动初始化远程 D1 等)
.github/ GitHub Actions 工作流npm install
npm run db:local # 初始化本地 D1(schema + 种子数据)
npm run dev # http://localhost:8787cd 晚叙CMS1.1
git init
git add .
git commit -m "init(wanxu-cms): v1.0.0 ready for cloudflare deploy"
# 方式 A:使用 GitHub CLI
git branch -M main
gh repo create wanxu-cms --public --source=. --remote=origin --push
# 方式 B:在 GitHub 网页新建空仓库后手动关联
# git remote add origin https://github.com/<你的用户名>/wanxu-cms.git
# git branch -M main
# git push -u origin mainnpx wrangler login
npx wrangler d1 create wanxu_cmsnpx wrangler r2 bucket create wanxu-cms-media[[d1_databases]]
database_id = "__DATABASE_ID__"
[[r2_buckets]]
bucket_name = "__R2_BUCKET_NAME__"
[vars]
SITE_URL = "__SITE_URL__"