claude-code-cchui/src/drizzle/schema.ts
gaoziman 799fb3ab58 feat(数据库): 扩展消息表支持用户上传的图片和文档
- 新增 uploadedImages 字段存储用户上传的图片(Base64编码数组)
- 新增 uploadedDocuments 字段存储用户上传的文档数据
- 定义 UploadedDocumentData 接口,包含文档名称、大小、类型和内容
2025-12-20 12:12:33 +08:00

279 lines
10 KiB
TypeScript
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

import {
pgTable,
serial,
varchar,
text,
boolean,
integer,
timestamp,
jsonb,
} from 'drizzle-orm/pg-core';
import { relations } from 'drizzle-orm';
// ============================================
// 用户表
// ============================================
export const users = pgTable('users', {
id: serial('id').primaryKey(),
// 用户唯一标识
userId: varchar('user_id', { length: 64 }).notNull().unique(),
// 基本信息
email: varchar('email', { length: 255 }).notNull().unique(),
password: varchar('password', { length: 255 }).notNull(),
nickname: varchar('nickname', { length: 64 }).notNull(),
avatar: varchar('avatar', { length: 512 }),
// 套餐和状态
plan: varchar('plan', { length: 20 }).default('free'), // free, pro, enterprise
status: varchar('status', { length: 20 }).default('active'), // active, inactive, banned
emailVerified: boolean('email_verified').default(false),
// 登录信息
lastLoginAt: timestamp('last_login_at', { withTimezone: true }),
// 时间戳
createdAt: timestamp('created_at', { withTimezone: true }).defaultNow(),
updatedAt: timestamp('updated_at', { withTimezone: true }).defaultNow(),
});
// ============================================
// 验证码表
// ============================================
export const verificationCodes = pgTable('verification_codes', {
id: serial('id').primaryKey(),
// 邮箱
email: varchar('email', { length: 255 }).notNull(),
// 验证码
code: varchar('code', { length: 6 }).notNull(),
// 类型: register(注册), login(登录), reset(重置密码)
type: varchar('type', { length: 20 }).notNull(),
// 过期时间
expiresAt: timestamp('expires_at', { withTimezone: true }).notNull(),
// 是否已使用
used: boolean('used').default(false),
// 创建时间
createdAt: timestamp('created_at', { withTimezone: true }).defaultNow(),
});
// ============================================
// 用户设置表
// ============================================
export const userSettings = pgTable('user_settings', {
id: serial('id').primaryKey(),
// 关联用户
userId: varchar('user_id', { length: 64 }).unique(),
// CCH 配置
cchUrl: varchar('cch_url', { length: 512 }).notNull().default('http://localhost:13500'),
cchApiKey: varchar('cch_api_key', { length: 512 }),
cchApiKeyConfigured: boolean('cch_api_key_configured').default(false),
// 默认设置
defaultModel: varchar('default_model', { length: 64 }).default('claude-sonnet-4-20250514'),
defaultTools: jsonb('default_tools').$type<string[]>().default(['web_search', 'code_execution', 'web_fetch']),
// AI 行为设置
systemPrompt: text('system_prompt'), // 系统提示词
temperature: varchar('temperature', { length: 10 }).default('0.7'), // 温度参数 (0-1)
// 偏好设置
theme: varchar('theme', { length: 20 }).default('light'),
language: varchar('language', { length: 10 }).default('zh-CN'),
fontSize: integer('font_size').default(15), // 全局字体大小 (12-20)
enableThinking: boolean('enable_thinking').default(false),
saveChatHistory: boolean('save_chat_history').default(true),
// 时间戳
createdAt: timestamp('created_at', { withTimezone: true }).defaultNow(),
updatedAt: timestamp('updated_at', { withTimezone: true }).defaultNow(),
});
// ============================================
// 对话表
// ============================================
export const conversations = pgTable('conversations', {
id: serial('id').primaryKey(),
// 对话唯一标识
conversationId: varchar('conversation_id', { length: 64 }).notNull().unique(),
// 关联用户
userId: varchar('user_id', { length: 64 }),
// 对话信息
title: varchar('title', { length: 255 }).notNull().default('新对话'),
summary: text('summary'),
// 模型和工具配置
model: varchar('model', { length: 64 }).notNull(),
tools: jsonb('tools').$type<string[]>().default([]),
enableThinking: boolean('enable_thinking').default(false),
// AI 行为设置(对话级别,可覆盖全局设置)
systemPrompt: text('system_prompt'), // 对话专属系统提示词
temperature: varchar('temperature', { length: 10 }), // 对话专属温度参数
// 统计信息
messageCount: integer('message_count').default(0),
totalTokens: integer('total_tokens').default(0),
// 状态
isArchived: boolean('is_archived').default(false),
isPinned: boolean('is_pinned').default(false),
// 时间戳
createdAt: timestamp('created_at', { withTimezone: true }).defaultNow(),
updatedAt: timestamp('updated_at', { withTimezone: true }).defaultNow(),
lastMessageAt: timestamp('last_message_at', { withTimezone: true }).defaultNow(),
});
// ============================================
// 消息表
// ============================================
export const messages = pgTable('messages', {
id: serial('id').primaryKey(),
// 消息唯一标识
messageId: varchar('message_id', { length: 64 }).notNull().unique(),
// 关联对话
conversationId: varchar('conversation_id', { length: 64 }).notNull(),
// 消息内容
role: varchar('role', { length: 20 }).notNull(), // user, assistant, system
content: text('content').notNull(),
// 思考内容
thinkingContent: text('thinking_content'),
thinkingCollapsed: boolean('thinking_collapsed').default(true),
// 工具调用记录
toolCalls: jsonb('tool_calls').$type<ToolCall[]>(),
toolResults: jsonb('tool_results').$type<ToolResult[]>(),
// 代码执行产生的图片Base64 编码数组)
images: jsonb('images').$type<string[]>(),
// 用户上传的图片Base64 编码数组)
uploadedImages: jsonb('uploaded_images').$type<string[]>(),
// 用户上传的文档
uploadedDocuments: jsonb('uploaded_documents').$type<UploadedDocumentData[]>(),
// Token 统计
inputTokens: integer('input_tokens').default(0),
outputTokens: integer('output_tokens').default(0),
// 状态
status: varchar('status', { length: 20 }).default('completed'), // pending, streaming, completed, error
errorMessage: text('error_message'),
// 用户反馈
feedback: varchar('feedback', { length: 10 }), // thumbs_up, thumbs_down
// 时间戳
createdAt: timestamp('created_at', { withTimezone: true }).defaultNow(),
updatedAt: timestamp('updated_at', { withTimezone: true }).defaultNow(),
});
// ============================================
// 工具表
// ============================================
export const tools = pgTable('tools', {
id: serial('id').primaryKey(),
// 工具唯一标识
toolId: varchar('tool_id', { length: 64 }).notNull().unique(),
// 工具信息
name: varchar('name', { length: 64 }).notNull(),
displayName: varchar('display_name', { length: 128 }).notNull(),
description: text('description'),
icon: varchar('icon', { length: 64 }),
// Claude Tool Schema
inputSchema: jsonb('input_schema').notNull(),
// 配置
isEnabled: boolean('is_enabled').default(true),
isDefault: boolean('is_default').default(false),
sortOrder: integer('sort_order').default(0),
// 时间戳
createdAt: timestamp('created_at', { withTimezone: true }).defaultNow(),
updatedAt: timestamp('updated_at', { withTimezone: true }).defaultNow(),
});
// ============================================
// 模型表
// ============================================
export const models = pgTable('models', {
id: serial('id').primaryKey(),
// 模型唯一标识
modelId: varchar('model_id', { length: 128 }).notNull().unique(),
// 模型信息
name: varchar('name', { length: 64 }).notNull(),
displayName: varchar('display_name', { length: 128 }).notNull(),
description: text('description'),
// 模型类型claude | codex
modelType: varchar('model_type', { length: 20 }).notNull().default('claude'),
// 模型特性
supportsTools: boolean('supports_tools').default(true),
supportsThinking: boolean('supports_thinking').default(true),
supportsVision: boolean('supports_vision').default(false),
maxTokens: integer('max_tokens').default(8192),
contextWindow: integer('context_window').default(200000),
// 配置
isEnabled: boolean('is_enabled').default(true),
isDefault: boolean('is_default').default(false),
sortOrder: integer('sort_order').default(0),
// 时间戳
createdAt: timestamp('created_at', { withTimezone: true }).defaultNow(),
updatedAt: timestamp('updated_at', { withTimezone: true }).defaultNow(),
});
// ============================================
// 关系定义
// ============================================
export const usersRelations = relations(users, ({ many, one }) => ({
conversations: many(conversations),
settings: one(userSettings, {
fields: [users.userId],
references: [userSettings.userId],
}),
}));
export const userSettingsRelations = relations(userSettings, ({ one }) => ({
user: one(users, {
fields: [userSettings.userId],
references: [users.userId],
}),
}));
export const conversationsRelations = relations(conversations, ({ many, one }) => ({
messages: many(messages),
user: one(users, {
fields: [conversations.userId],
references: [users.userId],
}),
}));
export const messagesRelations = relations(messages, ({ one }) => ({
conversation: one(conversations, {
fields: [messages.conversationId],
references: [conversations.conversationId],
}),
}));
// ============================================
// 类型定义
// ============================================
export interface ToolCall {
id: string;
name: string;
input: Record<string, unknown>;
}
export interface ToolResult {
toolUseId: string;
content: string;
isError?: boolean;
}
// 上传的文档数据(用于持久化存储)
export interface UploadedDocumentData {
name: string;
size: number;
type: string;
content: string;
}
// 导出类型
export type User = typeof users.$inferSelect;
export type NewUser = typeof users.$inferInsert;
export type VerificationCode = typeof verificationCodes.$inferSelect;
export type NewVerificationCode = typeof verificationCodes.$inferInsert;
export type UserSettings = typeof userSettings.$inferSelect;
export type NewUserSettings = typeof userSettings.$inferInsert;
export type Conversation = typeof conversations.$inferSelect;
export type NewConversation = typeof conversations.$inferInsert;
export type Message = typeof messages.$inferSelect;
export type NewMessage = typeof messages.$inferInsert;
export type Tool = typeof tools.$inferSelect;
export type NewTool = typeof tools.$inferInsert;
export type Model = typeof models.$inferSelect;
export type NewModel = typeof models.$inferInsert;