簡介
xClaw 將資料庫分為兩個系統:用於結構化配置資料的 PostgreSQL 和用於靈活 AI/聊天資料的 MongoDB。本文指導資料庫層的架構設計和實作。
1. 帶有 Drizzle ORM 的 PostgreSQL 模式
1.1 為什麼會下毛毛雨?
| 特點 | 細雨 | 棱鏡 | 類型ORM |
|---|---|---|---|
| 類型安全性 | 編譯時 | 運行時代碼產生 | 裝飾 |
| 捆綁尺寸 | 〜50KB | 〜2MB | 〜1MB |
| 原始 SQL | 本地 | 有限公司 | 查詢產生器 |
| 移民 | SQL 優先 | 汽車 | 汽車 |
| 效能 | 零開銷 | 代理開銷 | 反射開銷 |
1.2 模式定義
// packages/db/src/schema/tenants.ts
import { pgTable, uuid, varchar, timestamp, jsonb } from 'drizzle-orm/pg-core';
export const tenants = pgTable('tenants', {
id: uuid('id').primaryKey().defaultRandom(),
name: varchar('name', { length: 255 }).notNull(),
slug: varchar('slug', { length: 255 }).notNull().unique(),
settings: jsonb('settings').default({}),
createdAt: timestamp('created_at').defaultNow().notNull(),
updatedAt: timestamp('updated_at').defaultNow().notNull(),
});
export const users = pgTable('users', {
id: uuid('id').primaryKey().defaultRandom(),
email: varchar('email', { length: 255 }).notNull().unique(),
passwordHash: varchar('password_hash', { length: 255 }).notNull(),
name: varchar('name', { length: 255 }).notNull(),
tenantId: uuid('tenant_id').references(() => tenants.id).notNull(),
createdAt: timestamp('created_at').defaultNow().notNull(),
});
export const roles = pgTable('roles', {
id: uuid('id').primaryKey().defaultRandom(),
name: varchar('name', { length: 100 }).notNull(),
tenantId: uuid('tenant_id').references(() => tenants.id).notNull(),
permissions: jsonb('permissions').default([]),
});
export const userRoles = pgTable('user_roles', {
userId: uuid('user_id').references(() => users.id).notNull(),
roleId: uuid('role_id').references(() => roles.id).notNull(),
});
1.3 遷移
# Generate migration từ schema changes
npm run db:generate
# Run migrations
npm run db:migrate
# Visual database browser
npm run db:studio
2. 用於 AI 資料的 MongoDB
2.1 連線設定
// packages/db/src/mongo.ts
import { MongoClient, Db } from 'mongodb';
let client: MongoClient;
let db: Db;
export async function connectMongo(url: string): Promise<Db> {
client = new MongoClient(url);
await client.connect();
db = client.db();
// Create TTL indexes
await db.collection('audit_logs').createIndex(
{ createdAt: 1 },
{ expireAfterSeconds: 90 * 24 * 60 * 60 } // 90 days
);
await db.collection('system_logs').createIndex(
{ timestamp: 1 },
{ expireAfterSeconds: 30 * 24 * 60 * 60 } // 30 days
);
return db;
}
export function getMongo(): Db {
if (!db) throw new Error('MongoDB not connected');
return db;
}
2.2 集合結構
// Sessions collection
interface Session {
_id: string;
tenantId: string;
userId: string;
title: string;
model: string;
domainId?: string;
createdAt: Date;
updatedAt: Date;
}
// Messages collection
interface Message {
_id: string;
sessionId: string;
role: 'user' | 'assistant' | 'system' | 'tool';
content: string;
toolCalls?: ToolCall[];
toolCallId?: string;
images?: string[];
usage?: { promptTokens: number; completionTokens: number };
timestamp: Date;
}
// Memory entries
interface MemoryEntry {
_id: string;
sessionId: string;
type: 'conversation' | 'entity' | 'summary';
content: string;
embedding?: number[];
createdAt: Date;
}
3. 種子數據
// packages/db/src/seed.ts
export async function seedDatabase(pgDb: DrizzleDB, mongoDB: Db) {
// Create default tenant
const [tenant] = await pgDb.insert(tenants).values({
name: 'Default',
slug: 'default',
}).returning();
// Create admin user (password: password123)
const passwordHash = await hash('password123', 12);
const [admin] = await pgDb.insert(users).values({
email: '[email protected]',
passwordHash,
name: 'Admin',
tenantId: tenant.id,
}).returning();
// Create system roles with permissions
const systemRoles = [
{ name: 'owner', permissions: ALL_PERMISSIONS }, // 60 perms
{ name: 'admin', permissions: ADMIN_PERMISSIONS }, // 52 perms
{ name: 'member', permissions: MEMBER_PERMISSIONS }, // 14 perms
{ name: 'viewer', permissions: VIEWER_PERMISSIONS }, // 8 perms
];
for (const role of systemRoles) {
await pgDb.insert(roles).values({
...role,
tenantId: tenant.id,
});
}
}
4. 總結
- PostgreSQL + Drizzle ORM 用於結構化設定資料 — 類型安全性、遷移
- MongoDB 用於 AI/聊天資料 — 靈活的模式、TTL 索引
- Redis 用於快取 — 會話、速率限制
- 用於初始設定的種子資料 — 管理員使用者、系統角色
下一篇文章: 使用 Hono 建立 API 閘道 — 路由、中介軟體、JWT 驗證。