Peter267/ech0-workers

Serverless port of Ech0 on Cloudflare Workers + D1 + R2. Built with vibe coding, code by AI.

★ 0Forks 0TypeScriptGitHub ↗Compare

README

Ech0 Workers

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、评论区、第三方登录、多用户等扩展能力。

一键部署

Deploy to Cloudflare

点击上方按钮即可自动 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 订阅源。

公开 API 契约

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 地理位置,可为空

API 一览

公开接口(无需鉴权)

方法 路径 说明
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 读取图片(公开)

管理接口(需 Bearer Token)

方法 路径 说明
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 方式简版:

1. 前置准备

npm install

2. 创建 Cloudflare 资源

# 创建 D1 数据库
npx wrangler d1 create ech0
# 将返回的 database_id 填入 wrangler.toml

# 创建 R2 存储桶
npx wrangler r2 bucket create ech0-files

打开 wrangler.toml,把 database_id 替换为上一步返回的值,并把 PUBLIC_URL 改为你的最终域名。

3. 初始化数据库

# 本地
npm run db:init:local
# 远程
npm run db:init

4. 设置密钥

# JWT 签名密钥(生产必填)
npx wrangler secret put JWT_SECRET

# 可选:公开读取 API 的访问密钥;留空则允许匿名读取
npx wrangler secret put READ_API_KEY

5. 部署

npm run deploy

6. 初始化管理员并开始使用

部署完成后,调用一次初始化接口创建管理员:

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 的设计移植,仅供学习与个人使用。

Contributors

Peter267

Issues