-
Notifications
You must be signed in to change notification settings - Fork 56
Storage Adapters
one-ea edited this page Apr 25, 2026
·
2 revisions
Monolith 通过双适配器抽象,让数据库和对象存储都可以零代码改动整套迁移。
server/src/storage/interfaces.ts 定义两个核心接口:
export interface IDatabase {
// Posts
getPostBySlug(slug: string): Promise<Post | null>;
listPosts(opts?: ListOptions): Promise<Post[]>;
createPost(input: NewPost): Promise<Post>;
updatePost(id: number, patch: Partial<Post>): Promise<Post>;
deletePost(id: number): Promise<void>;
// ... 还有 categories / series / comments / reactions / versions / media
}
export interface IObjectStorage {
put(key: string, body: ArrayBuffer | ReadableStream, opts?: PutOptions): Promise<void>;
get(key: string): Promise<ReadableStream | null>;
delete(key: string): Promise<void>;
list(prefix?: string): Promise<string[]>;
}server/src/storage/factory.ts 根据环境变量返回具体实现:
export async function createDatabase(env: Env): Promise<IDatabase> {
switch (env.DB_PROVIDER ?? 'd1') {
case 'turso': return new TursoDatabase(env);
case 'postgres': return new PostgresDatabase(env);
default: return new D1Database(env.DB);
}
}
export function createObjectStorage(env: Env): IObjectStorage {
switch (env.STORAGE_PROVIDER ?? 'r2') {
case 's3': return new S3Storage(env);
default: return new R2Storage(env.MEDIA);
}
}业务路由只依赖接口:
const db = await createDatabase(c.env);
const post = await db.getPostBySlug(slug);| 维度 | D1 (默认) | Turso (LibSQL) | PostgreSQL |
|---|---|---|---|
| 运行环境 | Cloudflare 内置 | 任何边缘 | 自建/RDS/Supabase |
| 协议 | Workers Binding | HTTP / libsql | postgres 协议 |
| 驱动 | drizzle-orm/d1 |
@libsql/client |
pg (Hyperdrive 推荐) |
| schema 文件 | schema.ts |
schema.ts |
schema-pg.ts |
| 免费额度 | 5GB · 5M 读/天 | 9GB · 1B 行读/月 | 取决于供应商 |
| 延迟 | <5ms (同区域) | <30ms (边缘节点) | 取决于网络 |
| 强一致性 | 单 region | 主备异步 | 取决于配置 |
| 适合场景 | Cloudflare 全家桶 | 多区域低延迟 | 已有 PG 基础设施 |
详见 部署指南 - 切换数据库后端。
| 维度 | R2 (默认) | S3 兼容 |
|---|---|---|
| 服务 | Cloudflare R2 | AWS S3 / B2 / OSS / COS / MinIO |
| 协议 | Workers Binding | aws-sdk-v4 签名 |
| 出流量费用 | 0 元 | 按 GB 计费 |
| CDN 集成 | 同 Cloudflare 网络免费 | 需额外配置 |
| 签名 URL | 支持 | 支持 |
| 适合场景 | 公开博客图片 | 私有文件 / 多云策略 |
cd server
npx wrangler secret put STORAGE_PROVIDER # s3
npx wrangler secret put S3_ENDPOINT # https://s3.amazonaws.com
npx wrangler secret put S3_REGION # us-east-1
npx wrangler secret put S3_BUCKET # monolith-uploads
npx wrangler secret put S3_ACCESS_KEY_ID
npx wrangler secret put S3_SECRET_ACCESS_KEY| 组合 | 适合场景 | 月成本预估 |
|---|---|---|
| D1 + R2 ⭐ | 个人博客 / 中小流量 | ~$0 (免费额度) |
| Turso + R2 | 多区域低延迟 + 高读 | ~$5 |
| PostgreSQL + S3 | 已有团队基础设施 | 取决于自建 |
| D1 + S3 | 私有云对象存储 | ~$0 + S3 存储 |
-
创建实现文件
server/src/storage/db/mongo.ts # 数据库 server/src/storage/object/azure-blob.ts # 对象存储 -
实现接口
export class MongoDatabase implements IDatabase { constructor(env: Env) { /* 初始化连接 */ } async getPostBySlug(slug: string) { /* ... */ } // 必须实现所有 IDatabase 方法 }
-
注册到 factory
case 'mongo': return new MongoDatabase(env);
-
更新 schema (如果是数据库)
- SQL 数据库: 复用
schema.ts或schema-pg.ts - NoSQL: 自定义类型映射
- SQL 数据库: 复用
-
加迁移命令
"db:push:mongo": "node scripts/mongo-push.mjs"
⚠️ 铁律: schema 修改必须同步到所有数据库适配器,否则 Turso/PG 会崩。
# 1. 修改 schema.ts (D1/Turso) 或 schema-pg.ts (PG)
vim server/src/db/schema.ts
# 2. 生成迁移 SQL
cd server
npm run db:generate
# 3. 人工审核 server/migrations/000X_*.sql
# - 是否有 DEFAULT 值(避免存量记录崩溃)
# - 外键级联模式是否明确
# 4. 同步 schema-pg.ts(手动 mirror 修改)
# 5. 同步三个适配器实现
# - server/src/storage/db/d1.ts
# - server/src/storage/db/turso.ts
# - server/src/storage/db/postgres.ts
# 6. 应用迁移
npx wrangler d1 migrations apply monolith-db --local
npx wrangler d1 migrations apply monolith-db --remotedrizzle-sync-check skill 在编辑 schema 时会自动触发提醒。
并发写入冲突。检查是否有未关闭的事务,或加重试逻辑。
边缘环境每次冷启动都会建连。改用 Hyperdrive 或 PgBouncer 做连接池。
确认 Pages Functions 中 client/functions/cdn/[[path]].ts 存在并指向正确的 Workers 域。
D1 不支持 IF NOT EXISTS,手动编辑迁移 SQL 跳过已存在列,或直接回滚后重做。
Monolith · 边缘原生全栈博客 · MIT License · 文档以 main 分支 代码为准