IMPORTANT:本网站完全使用 vibe coding 构建,代码由 AI 撰写,介意者慎用。
This project was built entirely via vibe coding; all code was written by an AI assistant. If that concerns you, please proceed with caution.
Ech0 Workers 是开源项目 Ech0 的 Serverless 移植版,运行于 Cloudflare 生态(Workers + D1 + R2)。它保留了 Ech0 的核心功能:发布说说(短动态)、单管理员账号系统、图片上传,以及供第三方读取的公开 API。
为保持精简,本移植版不包含 AI、评论区、第三方登录、多用户等扩展能力。
点击上方按钮即可自动 fork 本仓库并部署到你的 Cloudflare 账号(自动创建 D1 数据库与 R2 存储桶)。详细步骤(含 GUI 控制台部署、CLI 部署、配置说明、FAQ)见 docs/DEPLOYMENT.md。
- 发布说说:管理员通过 API 发布 Markdown 内容,自动渲染为 HTML。
- 单管理员账号:通过初始化引导创建唯一管理员,无第三方登录。
- 图片上传:图片存入 R2 对象存储,通过 API 读取。
- 置顶与位置:支持说说置顶、附带地理位置。
- 公开读取 API:第三方接口返回符合约定 JSON 结构的说说列表。
- 访客瀑布流:未登录时首页以瀑布流呈现,图片懒加载 + 滚动无限加载。
- 搜索与标签:支持
?q=内容搜索、?tag=标签筛选(含快捷键⌘K/Ctrl+K)。 - RSS 订阅:
GET /api/rss输出 RSS 2.0 订阅源。
GET /api/statuses 返回如下 JSON 数组(私密说说自动排除,置顶项优先):
[
{
"id": "dynamic-id",
"published": 1721059200000,
"html": "<p>动态内容 HTML</p>",
"images": [
{ "alt": "图片描述", "src": "/path/to/image.jpg" }
],
"searchText": "纯文本搜索内容",
"pinned": false,
"location": "广西"
}
]字段说明:
| 字段 | 类型 | 说明 |
|---|---|---|
id |
string | 说说唯一 ID(UUID) |
published |
number | 发布时间(毫秒时间戳) |
html |
string | 由 Markdown 渲染出的 HTML |
images |
array | 关联图片,每项含 alt 与 src |
searchText |
string | 从内容提取的纯文本,便于搜索 |
pinned |
boolean | 是否置顶 |
location |
string|null | 地理位置,可为空 |
| 方法 | 路径 | 说明 |
|---|---|---|
GET |
/api/init/status |
查询是否已初始化管理员 |
POST |
/api/init |
创建管理员(仅首次可用) |
POST |
/api/login |
登录,返回访问令牌 |
POST |
/api/auth/refresh |
刷新访问令牌 |
POST |
/api/auth/logout |
登出 |
GET |
/api/statuses |
获取说说列表(公开,支持 page/pageSize/q/tag) |
GET |
/api/statuses/:id |
获取单条说说(公开) |
GET |
/api/statuses/stats |
站点统计(说说/标签/图片数、首末条时间) |
GET |
/api/rss |
RSS 2.0 订阅源 |
GET |
/api/tags |
标签列表(公开) |
GET |
/api/files/:id |
读取图片(公开) |
| 方法 | 路径 | 说明 |
|---|---|---|
POST |
/api/posts |
发布说说 |
PUT |
/api/posts/:id |
更新说说 |
PATCH |
/api/posts/:id/pin |
切换置顶 |
DELETE |
/api/posts/:id |
删除说说 |
GET |
/api/posts |
管理员说说列表(含私密) |
POST |
/api/files/upload |
上传图片 |
GET |
/api/files |
文件列表 |
DELETE |
/api/files/:id |
删除文件 |
- 运行时:Cloudflare Workers(Hono 框架)
- 数据库:Cloudflare D1(SQLite)
- 对象存储:Cloudflare R2
- 鉴权:JWT(HS256,WebCrypto)+ HttpOnly 刷新 Cookie
- 密码:PBKDF2-SHA256(WebCrypto 原生,无外部依赖)
- Markdown:marked
推荐直接使用一键部署按钮或控制台 GUI 部署,完整步骤见 docs/DEPLOYMENT.md。以下为 CLI 方式简版:
- 安装 Node.js(18+)。
- 注册 Cloudflare 账号。
- 安装依赖:
npm install# 创建 D1 数据库
npx wrangler d1 create ech0
# 将返回的 database_id 填入 wrangler.toml
# 创建 R2 存储桶
npx wrangler r2 bucket create ech0-files打开 wrangler.toml,把 database_id 替换为上一步返回的值,并把 PUBLIC_URL 改为你的最终域名。
# 本地
npm run db:init:local
# 远程
npm run db:init# JWT 签名密钥(生产必填)
npx wrangler secret put JWT_SECRET
# 可选:公开读取 API 的访问密钥;留空则允许匿名读取
npx wrangler secret put READ_API_KEYnpm run deploy部署完成后,调用一次初始化接口创建管理员:
curl -X POST https://<你的worker域名>/api/init \
-H 'Content-Type: application/json' \
-d '{"username":"admin","password":"your-password"}'登录获取令牌:
curl -X POST https://<你的worker域名>/api/login \
-H 'Content-Type: application/json' \
-d '{"username":"admin","password":"your-password"}'随后即可用返回的 access_token 发布说说、上传图片。第三方前端可直接请求 GET /api/statuses 获取说说列表。
npm run db:init:local # 首次初始化本地数据库
npm run dev # 启动本地 Miniflare(http://localhost:8787)- 所有写入接口均需有效的 JWT 访问令牌,匿名请求会被拒绝。
- 密码使用 PBKDF2-SHA256(10 万次迭代)加盐存储,不存明文;登录接口带 IP 级限流(10 次失败锁 15 分钟)。
- 登录失败对未知用户名做等时校验,防止时序枚举用户名。
- 公开读取 API 返回的 HTML 会先剔除原始 HTML 标签,再由 Markdown 渲染,并过滤
javascript:等危险链接,降低 XSS 风险。 - 全站 CSP 安全头(HTML 页面启用)、
nosniff/DENY等响应头;CORS 仅对公开读取接口开放。 - 私密说说不会出现在公开 API 的返回结果中。
- 刷新令牌存放于 HttpOnly、Secure、SameSite=Lax 的 Cookie 中。
- JWT 签名密钥未配置时自动生成并持久化到 D1(不再使用硬编码 fallback)。
- 扩展元信息抓取接口内置 SSRF 防护(拦截内网/保留 IP、限制重定向与超时)。
- 可选开启
READ_API_KEY,为公开读取接口增加一层鉴权。 - 数据库自动迁移(幂等),新部署无需手动执行 schema。
本项目参考 Ech0 的设计移植,仅供学习与个人使用。