feat(db): 添加 Drizzle ORM 数据库配置
- 添加 drizzle.config.ts 配置文件 - 添加数据库 schema 定义(会话、消息、设置等表) - 添加数据库连接配置 - 添加数据库迁移文件 - 添加种子数据脚本
This commit is contained in:
parent
0d43d78317
commit
77ef569d34
15
drizzle.config.ts
Normal file
15
drizzle.config.ts
Normal file
@ -0,0 +1,15 @@
|
||||
import { defineConfig } from 'drizzle-kit';
|
||||
|
||||
export default defineConfig({
|
||||
schema: './src/drizzle/schema.ts',
|
||||
out: './src/drizzle/migrations',
|
||||
dialect: 'postgresql',
|
||||
dbCredentials: {
|
||||
host: process.env.DB_HOST || 'localhost',
|
||||
port: parseInt(process.env.DB_PORT || '35433'),
|
||||
user: process.env.DB_USER || 'postgres',
|
||||
password: process.env.DB_PASSWORD || 'postgres',
|
||||
database: process.env.DB_NAME || 'cchcode_ui',
|
||||
ssl: false,
|
||||
},
|
||||
});
|
||||
35
src/drizzle/db.ts
Normal file
35
src/drizzle/db.ts
Normal file
@ -0,0 +1,35 @@
|
||||
import { drizzle } from 'drizzle-orm/node-postgres';
|
||||
import { Pool } from 'pg';
|
||||
import * as schema from './schema';
|
||||
|
||||
// 创建连接池
|
||||
const pool = new Pool({
|
||||
host: process.env.DB_HOST || 'localhost',
|
||||
port: parseInt(process.env.DB_PORT || '35433'),
|
||||
user: process.env.DB_USER || 'postgres',
|
||||
password: process.env.DB_PASSWORD || 'postgres',
|
||||
database: process.env.DB_NAME || 'cchcode_ui',
|
||||
max: 10, // 最大连接数
|
||||
idleTimeoutMillis: 30000, // 空闲超时
|
||||
connectionTimeoutMillis: 5000, // 连接超时
|
||||
ssl: false, // 禁用 SSL
|
||||
});
|
||||
|
||||
// 创建 Drizzle 实例
|
||||
export const db = drizzle(pool, { schema });
|
||||
|
||||
// 导出 pool 以便需要时直接使用
|
||||
export { pool };
|
||||
|
||||
// 健康检查函数
|
||||
export async function checkDatabaseConnection(): Promise<boolean> {
|
||||
try {
|
||||
const client = await pool.connect();
|
||||
await client.query('SELECT 1');
|
||||
client.release();
|
||||
return true;
|
||||
} catch (error) {
|
||||
console.error('Database connection check failed:', error);
|
||||
return false;
|
||||
}
|
||||
}
|
||||
87
src/drizzle/migrations/0000_white_warlock.sql
Normal file
87
src/drizzle/migrations/0000_white_warlock.sql
Normal file
@ -0,0 +1,87 @@
|
||||
CREATE TABLE "conversations" (
|
||||
"id" serial PRIMARY KEY NOT NULL,
|
||||
"conversation_id" varchar(64) NOT NULL,
|
||||
"title" varchar(255) DEFAULT '新对话' NOT NULL,
|
||||
"summary" text,
|
||||
"model" varchar(64) NOT NULL,
|
||||
"tools" jsonb DEFAULT '[]'::jsonb,
|
||||
"enable_thinking" boolean DEFAULT false,
|
||||
"message_count" integer DEFAULT 0,
|
||||
"total_tokens" integer DEFAULT 0,
|
||||
"is_archived" boolean DEFAULT false,
|
||||
"is_pinned" boolean DEFAULT false,
|
||||
"created_at" timestamp with time zone DEFAULT now(),
|
||||
"updated_at" timestamp with time zone DEFAULT now(),
|
||||
"last_message_at" timestamp with time zone DEFAULT now(),
|
||||
CONSTRAINT "conversations_conversation_id_unique" UNIQUE("conversation_id")
|
||||
);
|
||||
--> statement-breakpoint
|
||||
CREATE TABLE "messages" (
|
||||
"id" serial PRIMARY KEY NOT NULL,
|
||||
"message_id" varchar(64) NOT NULL,
|
||||
"conversation_id" varchar(64) NOT NULL,
|
||||
"role" varchar(20) NOT NULL,
|
||||
"content" text NOT NULL,
|
||||
"thinking_content" text,
|
||||
"thinking_collapsed" boolean DEFAULT true,
|
||||
"tool_calls" jsonb,
|
||||
"tool_results" jsonb,
|
||||
"input_tokens" integer DEFAULT 0,
|
||||
"output_tokens" integer DEFAULT 0,
|
||||
"status" varchar(20) DEFAULT 'completed',
|
||||
"error_message" text,
|
||||
"feedback" varchar(10),
|
||||
"created_at" timestamp with time zone DEFAULT now(),
|
||||
"updated_at" timestamp with time zone DEFAULT now(),
|
||||
CONSTRAINT "messages_message_id_unique" UNIQUE("message_id")
|
||||
);
|
||||
--> statement-breakpoint
|
||||
CREATE TABLE "models" (
|
||||
"id" serial PRIMARY KEY NOT NULL,
|
||||
"model_id" varchar(128) NOT NULL,
|
||||
"name" varchar(64) NOT NULL,
|
||||
"display_name" varchar(128) NOT NULL,
|
||||
"description" text,
|
||||
"supports_tools" boolean DEFAULT true,
|
||||
"supports_thinking" boolean DEFAULT true,
|
||||
"supports_vision" boolean DEFAULT false,
|
||||
"max_tokens" integer DEFAULT 8192,
|
||||
"context_window" integer DEFAULT 200000,
|
||||
"is_enabled" boolean DEFAULT true,
|
||||
"is_default" boolean DEFAULT false,
|
||||
"sort_order" integer DEFAULT 0,
|
||||
"created_at" timestamp with time zone DEFAULT now(),
|
||||
"updated_at" timestamp with time zone DEFAULT now(),
|
||||
CONSTRAINT "models_model_id_unique" UNIQUE("model_id")
|
||||
);
|
||||
--> statement-breakpoint
|
||||
CREATE TABLE "tools" (
|
||||
"id" serial PRIMARY KEY NOT NULL,
|
||||
"tool_id" varchar(64) NOT NULL,
|
||||
"name" varchar(64) NOT NULL,
|
||||
"display_name" varchar(128) NOT NULL,
|
||||
"description" text,
|
||||
"icon" varchar(64),
|
||||
"input_schema" jsonb NOT NULL,
|
||||
"is_enabled" boolean DEFAULT true,
|
||||
"is_default" boolean DEFAULT false,
|
||||
"sort_order" integer DEFAULT 0,
|
||||
"created_at" timestamp with time zone DEFAULT now(),
|
||||
"updated_at" timestamp with time zone DEFAULT now(),
|
||||
CONSTRAINT "tools_tool_id_unique" UNIQUE("tool_id")
|
||||
);
|
||||
--> statement-breakpoint
|
||||
CREATE TABLE "user_settings" (
|
||||
"id" serial PRIMARY KEY NOT NULL,
|
||||
"cch_url" varchar(512) DEFAULT 'http://localhost:13500' NOT NULL,
|
||||
"cch_api_key" varchar(512),
|
||||
"cch_api_key_configured" boolean DEFAULT false,
|
||||
"default_model" varchar(64) DEFAULT 'claude-sonnet-4-20250514',
|
||||
"default_tools" jsonb DEFAULT '["web_search","code_execution","web_fetch"]'::jsonb,
|
||||
"theme" varchar(20) DEFAULT 'light',
|
||||
"language" varchar(10) DEFAULT 'zh-CN',
|
||||
"enable_thinking" boolean DEFAULT false,
|
||||
"save_chat_history" boolean DEFAULT true,
|
||||
"created_at" timestamp with time zone DEFAULT now(),
|
||||
"updated_at" timestamp with time zone DEFAULT now()
|
||||
);
|
||||
4
src/drizzle/migrations/0001_daffy_paladin.sql
Normal file
4
src/drizzle/migrations/0001_daffy_paladin.sql
Normal file
@ -0,0 +1,4 @@
|
||||
ALTER TABLE "conversations" ADD COLUMN "system_prompt" text;--> statement-breakpoint
|
||||
ALTER TABLE "conversations" ADD COLUMN "temperature" varchar(10);--> statement-breakpoint
|
||||
ALTER TABLE "user_settings" ADD COLUMN "system_prompt" text;--> statement-breakpoint
|
||||
ALTER TABLE "user_settings" ADD COLUMN "temperature" varchar(10) DEFAULT '0.7';
|
||||
571
src/drizzle/migrations/meta/0000_snapshot.json
Normal file
571
src/drizzle/migrations/meta/0000_snapshot.json
Normal file
@ -0,0 +1,571 @@
|
||||
{
|
||||
"id": "03e6937f-d493-44f6-8109-704693f38906",
|
||||
"prevId": "00000000-0000-0000-0000-000000000000",
|
||||
"version": "7",
|
||||
"dialect": "postgresql",
|
||||
"tables": {
|
||||
"public.conversations": {
|
||||
"name": "conversations",
|
||||
"schema": "",
|
||||
"columns": {
|
||||
"id": {
|
||||
"name": "id",
|
||||
"type": "serial",
|
||||
"primaryKey": true,
|
||||
"notNull": true
|
||||
},
|
||||
"conversation_id": {
|
||||
"name": "conversation_id",
|
||||
"type": "varchar(64)",
|
||||
"primaryKey": false,
|
||||
"notNull": true
|
||||
},
|
||||
"title": {
|
||||
"name": "title",
|
||||
"type": "varchar(255)",
|
||||
"primaryKey": false,
|
||||
"notNull": true,
|
||||
"default": "'新对话'"
|
||||
},
|
||||
"summary": {
|
||||
"name": "summary",
|
||||
"type": "text",
|
||||
"primaryKey": false,
|
||||
"notNull": false
|
||||
},
|
||||
"model": {
|
||||
"name": "model",
|
||||
"type": "varchar(64)",
|
||||
"primaryKey": false,
|
||||
"notNull": true
|
||||
},
|
||||
"tools": {
|
||||
"name": "tools",
|
||||
"type": "jsonb",
|
||||
"primaryKey": false,
|
||||
"notNull": false,
|
||||
"default": "'[]'::jsonb"
|
||||
},
|
||||
"enable_thinking": {
|
||||
"name": "enable_thinking",
|
||||
"type": "boolean",
|
||||
"primaryKey": false,
|
||||
"notNull": false,
|
||||
"default": false
|
||||
},
|
||||
"message_count": {
|
||||
"name": "message_count",
|
||||
"type": "integer",
|
||||
"primaryKey": false,
|
||||
"notNull": false,
|
||||
"default": 0
|
||||
},
|
||||
"total_tokens": {
|
||||
"name": "total_tokens",
|
||||
"type": "integer",
|
||||
"primaryKey": false,
|
||||
"notNull": false,
|
||||
"default": 0
|
||||
},
|
||||
"is_archived": {
|
||||
"name": "is_archived",
|
||||
"type": "boolean",
|
||||
"primaryKey": false,
|
||||
"notNull": false,
|
||||
"default": false
|
||||
},
|
||||
"is_pinned": {
|
||||
"name": "is_pinned",
|
||||
"type": "boolean",
|
||||
"primaryKey": false,
|
||||
"notNull": false,
|
||||
"default": false
|
||||
},
|
||||
"created_at": {
|
||||
"name": "created_at",
|
||||
"type": "timestamp with time zone",
|
||||
"primaryKey": false,
|
||||
"notNull": false,
|
||||
"default": "now()"
|
||||
},
|
||||
"updated_at": {
|
||||
"name": "updated_at",
|
||||
"type": "timestamp with time zone",
|
||||
"primaryKey": false,
|
||||
"notNull": false,
|
||||
"default": "now()"
|
||||
},
|
||||
"last_message_at": {
|
||||
"name": "last_message_at",
|
||||
"type": "timestamp with time zone",
|
||||
"primaryKey": false,
|
||||
"notNull": false,
|
||||
"default": "now()"
|
||||
}
|
||||
},
|
||||
"indexes": {},
|
||||
"foreignKeys": {},
|
||||
"compositePrimaryKeys": {},
|
||||
"uniqueConstraints": {
|
||||
"conversations_conversation_id_unique": {
|
||||
"name": "conversations_conversation_id_unique",
|
||||
"nullsNotDistinct": false,
|
||||
"columns": [
|
||||
"conversation_id"
|
||||
]
|
||||
}
|
||||
},
|
||||
"policies": {},
|
||||
"checkConstraints": {},
|
||||
"isRLSEnabled": false
|
||||
},
|
||||
"public.messages": {
|
||||
"name": "messages",
|
||||
"schema": "",
|
||||
"columns": {
|
||||
"id": {
|
||||
"name": "id",
|
||||
"type": "serial",
|
||||
"primaryKey": true,
|
||||
"notNull": true
|
||||
},
|
||||
"message_id": {
|
||||
"name": "message_id",
|
||||
"type": "varchar(64)",
|
||||
"primaryKey": false,
|
||||
"notNull": true
|
||||
},
|
||||
"conversation_id": {
|
||||
"name": "conversation_id",
|
||||
"type": "varchar(64)",
|
||||
"primaryKey": false,
|
||||
"notNull": true
|
||||
},
|
||||
"role": {
|
||||
"name": "role",
|
||||
"type": "varchar(20)",
|
||||
"primaryKey": false,
|
||||
"notNull": true
|
||||
},
|
||||
"content": {
|
||||
"name": "content",
|
||||
"type": "text",
|
||||
"primaryKey": false,
|
||||
"notNull": true
|
||||
},
|
||||
"thinking_content": {
|
||||
"name": "thinking_content",
|
||||
"type": "text",
|
||||
"primaryKey": false,
|
||||
"notNull": false
|
||||
},
|
||||
"thinking_collapsed": {
|
||||
"name": "thinking_collapsed",
|
||||
"type": "boolean",
|
||||
"primaryKey": false,
|
||||
"notNull": false,
|
||||
"default": true
|
||||
},
|
||||
"tool_calls": {
|
||||
"name": "tool_calls",
|
||||
"type": "jsonb",
|
||||
"primaryKey": false,
|
||||
"notNull": false
|
||||
},
|
||||
"tool_results": {
|
||||
"name": "tool_results",
|
||||
"type": "jsonb",
|
||||
"primaryKey": false,
|
||||
"notNull": false
|
||||
},
|
||||
"input_tokens": {
|
||||
"name": "input_tokens",
|
||||
"type": "integer",
|
||||
"primaryKey": false,
|
||||
"notNull": false,
|
||||
"default": 0
|
||||
},
|
||||
"output_tokens": {
|
||||
"name": "output_tokens",
|
||||
"type": "integer",
|
||||
"primaryKey": false,
|
||||
"notNull": false,
|
||||
"default": 0
|
||||
},
|
||||
"status": {
|
||||
"name": "status",
|
||||
"type": "varchar(20)",
|
||||
"primaryKey": false,
|
||||
"notNull": false,
|
||||
"default": "'completed'"
|
||||
},
|
||||
"error_message": {
|
||||
"name": "error_message",
|
||||
"type": "text",
|
||||
"primaryKey": false,
|
||||
"notNull": false
|
||||
},
|
||||
"feedback": {
|
||||
"name": "feedback",
|
||||
"type": "varchar(10)",
|
||||
"primaryKey": false,
|
||||
"notNull": false
|
||||
},
|
||||
"created_at": {
|
||||
"name": "created_at",
|
||||
"type": "timestamp with time zone",
|
||||
"primaryKey": false,
|
||||
"notNull": false,
|
||||
"default": "now()"
|
||||
},
|
||||
"updated_at": {
|
||||
"name": "updated_at",
|
||||
"type": "timestamp with time zone",
|
||||
"primaryKey": false,
|
||||
"notNull": false,
|
||||
"default": "now()"
|
||||
}
|
||||
},
|
||||
"indexes": {},
|
||||
"foreignKeys": {},
|
||||
"compositePrimaryKeys": {},
|
||||
"uniqueConstraints": {
|
||||
"messages_message_id_unique": {
|
||||
"name": "messages_message_id_unique",
|
||||
"nullsNotDistinct": false,
|
||||
"columns": [
|
||||
"message_id"
|
||||
]
|
||||
}
|
||||
},
|
||||
"policies": {},
|
||||
"checkConstraints": {},
|
||||
"isRLSEnabled": false
|
||||
},
|
||||
"public.models": {
|
||||
"name": "models",
|
||||
"schema": "",
|
||||
"columns": {
|
||||
"id": {
|
||||
"name": "id",
|
||||
"type": "serial",
|
||||
"primaryKey": true,
|
||||
"notNull": true
|
||||
},
|
||||
"model_id": {
|
||||
"name": "model_id",
|
||||
"type": "varchar(128)",
|
||||
"primaryKey": false,
|
||||
"notNull": true
|
||||
},
|
||||
"name": {
|
||||
"name": "name",
|
||||
"type": "varchar(64)",
|
||||
"primaryKey": false,
|
||||
"notNull": true
|
||||
},
|
||||
"display_name": {
|
||||
"name": "display_name",
|
||||
"type": "varchar(128)",
|
||||
"primaryKey": false,
|
||||
"notNull": true
|
||||
},
|
||||
"description": {
|
||||
"name": "description",
|
||||
"type": "text",
|
||||
"primaryKey": false,
|
||||
"notNull": false
|
||||
},
|
||||
"supports_tools": {
|
||||
"name": "supports_tools",
|
||||
"type": "boolean",
|
||||
"primaryKey": false,
|
||||
"notNull": false,
|
||||
"default": true
|
||||
},
|
||||
"supports_thinking": {
|
||||
"name": "supports_thinking",
|
||||
"type": "boolean",
|
||||
"primaryKey": false,
|
||||
"notNull": false,
|
||||
"default": true
|
||||
},
|
||||
"supports_vision": {
|
||||
"name": "supports_vision",
|
||||
"type": "boolean",
|
||||
"primaryKey": false,
|
||||
"notNull": false,
|
||||
"default": false
|
||||
},
|
||||
"max_tokens": {
|
||||
"name": "max_tokens",
|
||||
"type": "integer",
|
||||
"primaryKey": false,
|
||||
"notNull": false,
|
||||
"default": 8192
|
||||
},
|
||||
"context_window": {
|
||||
"name": "context_window",
|
||||
"type": "integer",
|
||||
"primaryKey": false,
|
||||
"notNull": false,
|
||||
"default": 200000
|
||||
},
|
||||
"is_enabled": {
|
||||
"name": "is_enabled",
|
||||
"type": "boolean",
|
||||
"primaryKey": false,
|
||||
"notNull": false,
|
||||
"default": true
|
||||
},
|
||||
"is_default": {
|
||||
"name": "is_default",
|
||||
"type": "boolean",
|
||||
"primaryKey": false,
|
||||
"notNull": false,
|
||||
"default": false
|
||||
},
|
||||
"sort_order": {
|
||||
"name": "sort_order",
|
||||
"type": "integer",
|
||||
"primaryKey": false,
|
||||
"notNull": false,
|
||||
"default": 0
|
||||
},
|
||||
"created_at": {
|
||||
"name": "created_at",
|
||||
"type": "timestamp with time zone",
|
||||
"primaryKey": false,
|
||||
"notNull": false,
|
||||
"default": "now()"
|
||||
},
|
||||
"updated_at": {
|
||||
"name": "updated_at",
|
||||
"type": "timestamp with time zone",
|
||||
"primaryKey": false,
|
||||
"notNull": false,
|
||||
"default": "now()"
|
||||
}
|
||||
},
|
||||
"indexes": {},
|
||||
"foreignKeys": {},
|
||||
"compositePrimaryKeys": {},
|
||||
"uniqueConstraints": {
|
||||
"models_model_id_unique": {
|
||||
"name": "models_model_id_unique",
|
||||
"nullsNotDistinct": false,
|
||||
"columns": [
|
||||
"model_id"
|
||||
]
|
||||
}
|
||||
},
|
||||
"policies": {},
|
||||
"checkConstraints": {},
|
||||
"isRLSEnabled": false
|
||||
},
|
||||
"public.tools": {
|
||||
"name": "tools",
|
||||
"schema": "",
|
||||
"columns": {
|
||||
"id": {
|
||||
"name": "id",
|
||||
"type": "serial",
|
||||
"primaryKey": true,
|
||||
"notNull": true
|
||||
},
|
||||
"tool_id": {
|
||||
"name": "tool_id",
|
||||
"type": "varchar(64)",
|
||||
"primaryKey": false,
|
||||
"notNull": true
|
||||
},
|
||||
"name": {
|
||||
"name": "name",
|
||||
"type": "varchar(64)",
|
||||
"primaryKey": false,
|
||||
"notNull": true
|
||||
},
|
||||
"display_name": {
|
||||
"name": "display_name",
|
||||
"type": "varchar(128)",
|
||||
"primaryKey": false,
|
||||
"notNull": true
|
||||
},
|
||||
"description": {
|
||||
"name": "description",
|
||||
"type": "text",
|
||||
"primaryKey": false,
|
||||
"notNull": false
|
||||
},
|
||||
"icon": {
|
||||
"name": "icon",
|
||||
"type": "varchar(64)",
|
||||
"primaryKey": false,
|
||||
"notNull": false
|
||||
},
|
||||
"input_schema": {
|
||||
"name": "input_schema",
|
||||
"type": "jsonb",
|
||||
"primaryKey": false,
|
||||
"notNull": true
|
||||
},
|
||||
"is_enabled": {
|
||||
"name": "is_enabled",
|
||||
"type": "boolean",
|
||||
"primaryKey": false,
|
||||
"notNull": false,
|
||||
"default": true
|
||||
},
|
||||
"is_default": {
|
||||
"name": "is_default",
|
||||
"type": "boolean",
|
||||
"primaryKey": false,
|
||||
"notNull": false,
|
||||
"default": false
|
||||
},
|
||||
"sort_order": {
|
||||
"name": "sort_order",
|
||||
"type": "integer",
|
||||
"primaryKey": false,
|
||||
"notNull": false,
|
||||
"default": 0
|
||||
},
|
||||
"created_at": {
|
||||
"name": "created_at",
|
||||
"type": "timestamp with time zone",
|
||||
"primaryKey": false,
|
||||
"notNull": false,
|
||||
"default": "now()"
|
||||
},
|
||||
"updated_at": {
|
||||
"name": "updated_at",
|
||||
"type": "timestamp with time zone",
|
||||
"primaryKey": false,
|
||||
"notNull": false,
|
||||
"default": "now()"
|
||||
}
|
||||
},
|
||||
"indexes": {},
|
||||
"foreignKeys": {},
|
||||
"compositePrimaryKeys": {},
|
||||
"uniqueConstraints": {
|
||||
"tools_tool_id_unique": {
|
||||
"name": "tools_tool_id_unique",
|
||||
"nullsNotDistinct": false,
|
||||
"columns": [
|
||||
"tool_id"
|
||||
]
|
||||
}
|
||||
},
|
||||
"policies": {},
|
||||
"checkConstraints": {},
|
||||
"isRLSEnabled": false
|
||||
},
|
||||
"public.user_settings": {
|
||||
"name": "user_settings",
|
||||
"schema": "",
|
||||
"columns": {
|
||||
"id": {
|
||||
"name": "id",
|
||||
"type": "serial",
|
||||
"primaryKey": true,
|
||||
"notNull": true
|
||||
},
|
||||
"cch_url": {
|
||||
"name": "cch_url",
|
||||
"type": "varchar(512)",
|
||||
"primaryKey": false,
|
||||
"notNull": true,
|
||||
"default": "'http://localhost:13500'"
|
||||
},
|
||||
"cch_api_key": {
|
||||
"name": "cch_api_key",
|
||||
"type": "varchar(512)",
|
||||
"primaryKey": false,
|
||||
"notNull": false
|
||||
},
|
||||
"cch_api_key_configured": {
|
||||
"name": "cch_api_key_configured",
|
||||
"type": "boolean",
|
||||
"primaryKey": false,
|
||||
"notNull": false,
|
||||
"default": false
|
||||
},
|
||||
"default_model": {
|
||||
"name": "default_model",
|
||||
"type": "varchar(64)",
|
||||
"primaryKey": false,
|
||||
"notNull": false,
|
||||
"default": "'claude-sonnet-4-20250514'"
|
||||
},
|
||||
"default_tools": {
|
||||
"name": "default_tools",
|
||||
"type": "jsonb",
|
||||
"primaryKey": false,
|
||||
"notNull": false,
|
||||
"default": "'[\"web_search\",\"code_execution\",\"web_fetch\"]'::jsonb"
|
||||
},
|
||||
"theme": {
|
||||
"name": "theme",
|
||||
"type": "varchar(20)",
|
||||
"primaryKey": false,
|
||||
"notNull": false,
|
||||
"default": "'light'"
|
||||
},
|
||||
"language": {
|
||||
"name": "language",
|
||||
"type": "varchar(10)",
|
||||
"primaryKey": false,
|
||||
"notNull": false,
|
||||
"default": "'zh-CN'"
|
||||
},
|
||||
"enable_thinking": {
|
||||
"name": "enable_thinking",
|
||||
"type": "boolean",
|
||||
"primaryKey": false,
|
||||
"notNull": false,
|
||||
"default": false
|
||||
},
|
||||
"save_chat_history": {
|
||||
"name": "save_chat_history",
|
||||
"type": "boolean",
|
||||
"primaryKey": false,
|
||||
"notNull": false,
|
||||
"default": true
|
||||
},
|
||||
"created_at": {
|
||||
"name": "created_at",
|
||||
"type": "timestamp with time zone",
|
||||
"primaryKey": false,
|
||||
"notNull": false,
|
||||
"default": "now()"
|
||||
},
|
||||
"updated_at": {
|
||||
"name": "updated_at",
|
||||
"type": "timestamp with time zone",
|
||||
"primaryKey": false,
|
||||
"notNull": false,
|
||||
"default": "now()"
|
||||
}
|
||||
},
|
||||
"indexes": {},
|
||||
"foreignKeys": {},
|
||||
"compositePrimaryKeys": {},
|
||||
"uniqueConstraints": {},
|
||||
"policies": {},
|
||||
"checkConstraints": {},
|
||||
"isRLSEnabled": false
|
||||
}
|
||||
},
|
||||
"enums": {},
|
||||
"schemas": {},
|
||||
"sequences": {},
|
||||
"roles": {},
|
||||
"policies": {},
|
||||
"views": {},
|
||||
"_meta": {
|
||||
"columns": {},
|
||||
"schemas": {},
|
||||
"tables": {}
|
||||
}
|
||||
}
|
||||
596
src/drizzle/migrations/meta/0001_snapshot.json
Normal file
596
src/drizzle/migrations/meta/0001_snapshot.json
Normal file
@ -0,0 +1,596 @@
|
||||
{
|
||||
"id": "dc7ae80d-12dc-4e13-9c7b-101b9f79b198",
|
||||
"prevId": "03e6937f-d493-44f6-8109-704693f38906",
|
||||
"version": "7",
|
||||
"dialect": "postgresql",
|
||||
"tables": {
|
||||
"public.conversations": {
|
||||
"name": "conversations",
|
||||
"schema": "",
|
||||
"columns": {
|
||||
"id": {
|
||||
"name": "id",
|
||||
"type": "serial",
|
||||
"primaryKey": true,
|
||||
"notNull": true
|
||||
},
|
||||
"conversation_id": {
|
||||
"name": "conversation_id",
|
||||
"type": "varchar(64)",
|
||||
"primaryKey": false,
|
||||
"notNull": true
|
||||
},
|
||||
"title": {
|
||||
"name": "title",
|
||||
"type": "varchar(255)",
|
||||
"primaryKey": false,
|
||||
"notNull": true,
|
||||
"default": "'新对话'"
|
||||
},
|
||||
"summary": {
|
||||
"name": "summary",
|
||||
"type": "text",
|
||||
"primaryKey": false,
|
||||
"notNull": false
|
||||
},
|
||||
"model": {
|
||||
"name": "model",
|
||||
"type": "varchar(64)",
|
||||
"primaryKey": false,
|
||||
"notNull": true
|
||||
},
|
||||
"tools": {
|
||||
"name": "tools",
|
||||
"type": "jsonb",
|
||||
"primaryKey": false,
|
||||
"notNull": false,
|
||||
"default": "'[]'::jsonb"
|
||||
},
|
||||
"enable_thinking": {
|
||||
"name": "enable_thinking",
|
||||
"type": "boolean",
|
||||
"primaryKey": false,
|
||||
"notNull": false,
|
||||
"default": false
|
||||
},
|
||||
"system_prompt": {
|
||||
"name": "system_prompt",
|
||||
"type": "text",
|
||||
"primaryKey": false,
|
||||
"notNull": false
|
||||
},
|
||||
"temperature": {
|
||||
"name": "temperature",
|
||||
"type": "varchar(10)",
|
||||
"primaryKey": false,
|
||||
"notNull": false
|
||||
},
|
||||
"message_count": {
|
||||
"name": "message_count",
|
||||
"type": "integer",
|
||||
"primaryKey": false,
|
||||
"notNull": false,
|
||||
"default": 0
|
||||
},
|
||||
"total_tokens": {
|
||||
"name": "total_tokens",
|
||||
"type": "integer",
|
||||
"primaryKey": false,
|
||||
"notNull": false,
|
||||
"default": 0
|
||||
},
|
||||
"is_archived": {
|
||||
"name": "is_archived",
|
||||
"type": "boolean",
|
||||
"primaryKey": false,
|
||||
"notNull": false,
|
||||
"default": false
|
||||
},
|
||||
"is_pinned": {
|
||||
"name": "is_pinned",
|
||||
"type": "boolean",
|
||||
"primaryKey": false,
|
||||
"notNull": false,
|
||||
"default": false
|
||||
},
|
||||
"created_at": {
|
||||
"name": "created_at",
|
||||
"type": "timestamp with time zone",
|
||||
"primaryKey": false,
|
||||
"notNull": false,
|
||||
"default": "now()"
|
||||
},
|
||||
"updated_at": {
|
||||
"name": "updated_at",
|
||||
"type": "timestamp with time zone",
|
||||
"primaryKey": false,
|
||||
"notNull": false,
|
||||
"default": "now()"
|
||||
},
|
||||
"last_message_at": {
|
||||
"name": "last_message_at",
|
||||
"type": "timestamp with time zone",
|
||||
"primaryKey": false,
|
||||
"notNull": false,
|
||||
"default": "now()"
|
||||
}
|
||||
},
|
||||
"indexes": {},
|
||||
"foreignKeys": {},
|
||||
"compositePrimaryKeys": {},
|
||||
"uniqueConstraints": {
|
||||
"conversations_conversation_id_unique": {
|
||||
"name": "conversations_conversation_id_unique",
|
||||
"nullsNotDistinct": false,
|
||||
"columns": [
|
||||
"conversation_id"
|
||||
]
|
||||
}
|
||||
},
|
||||
"policies": {},
|
||||
"checkConstraints": {},
|
||||
"isRLSEnabled": false
|
||||
},
|
||||
"public.messages": {
|
||||
"name": "messages",
|
||||
"schema": "",
|
||||
"columns": {
|
||||
"id": {
|
||||
"name": "id",
|
||||
"type": "serial",
|
||||
"primaryKey": true,
|
||||
"notNull": true
|
||||
},
|
||||
"message_id": {
|
||||
"name": "message_id",
|
||||
"type": "varchar(64)",
|
||||
"primaryKey": false,
|
||||
"notNull": true
|
||||
},
|
||||
"conversation_id": {
|
||||
"name": "conversation_id",
|
||||
"type": "varchar(64)",
|
||||
"primaryKey": false,
|
||||
"notNull": true
|
||||
},
|
||||
"role": {
|
||||
"name": "role",
|
||||
"type": "varchar(20)",
|
||||
"primaryKey": false,
|
||||
"notNull": true
|
||||
},
|
||||
"content": {
|
||||
"name": "content",
|
||||
"type": "text",
|
||||
"primaryKey": false,
|
||||
"notNull": true
|
||||
},
|
||||
"thinking_content": {
|
||||
"name": "thinking_content",
|
||||
"type": "text",
|
||||
"primaryKey": false,
|
||||
"notNull": false
|
||||
},
|
||||
"thinking_collapsed": {
|
||||
"name": "thinking_collapsed",
|
||||
"type": "boolean",
|
||||
"primaryKey": false,
|
||||
"notNull": false,
|
||||
"default": true
|
||||
},
|
||||
"tool_calls": {
|
||||
"name": "tool_calls",
|
||||
"type": "jsonb",
|
||||
"primaryKey": false,
|
||||
"notNull": false
|
||||
},
|
||||
"tool_results": {
|
||||
"name": "tool_results",
|
||||
"type": "jsonb",
|
||||
"primaryKey": false,
|
||||
"notNull": false
|
||||
},
|
||||
"input_tokens": {
|
||||
"name": "input_tokens",
|
||||
"type": "integer",
|
||||
"primaryKey": false,
|
||||
"notNull": false,
|
||||
"default": 0
|
||||
},
|
||||
"output_tokens": {
|
||||
"name": "output_tokens",
|
||||
"type": "integer",
|
||||
"primaryKey": false,
|
||||
"notNull": false,
|
||||
"default": 0
|
||||
},
|
||||
"status": {
|
||||
"name": "status",
|
||||
"type": "varchar(20)",
|
||||
"primaryKey": false,
|
||||
"notNull": false,
|
||||
"default": "'completed'"
|
||||
},
|
||||
"error_message": {
|
||||
"name": "error_message",
|
||||
"type": "text",
|
||||
"primaryKey": false,
|
||||
"notNull": false
|
||||
},
|
||||
"feedback": {
|
||||
"name": "feedback",
|
||||
"type": "varchar(10)",
|
||||
"primaryKey": false,
|
||||
"notNull": false
|
||||
},
|
||||
"created_at": {
|
||||
"name": "created_at",
|
||||
"type": "timestamp with time zone",
|
||||
"primaryKey": false,
|
||||
"notNull": false,
|
||||
"default": "now()"
|
||||
},
|
||||
"updated_at": {
|
||||
"name": "updated_at",
|
||||
"type": "timestamp with time zone",
|
||||
"primaryKey": false,
|
||||
"notNull": false,
|
||||
"default": "now()"
|
||||
}
|
||||
},
|
||||
"indexes": {},
|
||||
"foreignKeys": {},
|
||||
"compositePrimaryKeys": {},
|
||||
"uniqueConstraints": {
|
||||
"messages_message_id_unique": {
|
||||
"name": "messages_message_id_unique",
|
||||
"nullsNotDistinct": false,
|
||||
"columns": [
|
||||
"message_id"
|
||||
]
|
||||
}
|
||||
},
|
||||
"policies": {},
|
||||
"checkConstraints": {},
|
||||
"isRLSEnabled": false
|
||||
},
|
||||
"public.models": {
|
||||
"name": "models",
|
||||
"schema": "",
|
||||
"columns": {
|
||||
"id": {
|
||||
"name": "id",
|
||||
"type": "serial",
|
||||
"primaryKey": true,
|
||||
"notNull": true
|
||||
},
|
||||
"model_id": {
|
||||
"name": "model_id",
|
||||
"type": "varchar(128)",
|
||||
"primaryKey": false,
|
||||
"notNull": true
|
||||
},
|
||||
"name": {
|
||||
"name": "name",
|
||||
"type": "varchar(64)",
|
||||
"primaryKey": false,
|
||||
"notNull": true
|
||||
},
|
||||
"display_name": {
|
||||
"name": "display_name",
|
||||
"type": "varchar(128)",
|
||||
"primaryKey": false,
|
||||
"notNull": true
|
||||
},
|
||||
"description": {
|
||||
"name": "description",
|
||||
"type": "text",
|
||||
"primaryKey": false,
|
||||
"notNull": false
|
||||
},
|
||||
"supports_tools": {
|
||||
"name": "supports_tools",
|
||||
"type": "boolean",
|
||||
"primaryKey": false,
|
||||
"notNull": false,
|
||||
"default": true
|
||||
},
|
||||
"supports_thinking": {
|
||||
"name": "supports_thinking",
|
||||
"type": "boolean",
|
||||
"primaryKey": false,
|
||||
"notNull": false,
|
||||
"default": true
|
||||
},
|
||||
"supports_vision": {
|
||||
"name": "supports_vision",
|
||||
"type": "boolean",
|
||||
"primaryKey": false,
|
||||
"notNull": false,
|
||||
"default": false
|
||||
},
|
||||
"max_tokens": {
|
||||
"name": "max_tokens",
|
||||
"type": "integer",
|
||||
"primaryKey": false,
|
||||
"notNull": false,
|
||||
"default": 8192
|
||||
},
|
||||
"context_window": {
|
||||
"name": "context_window",
|
||||
"type": "integer",
|
||||
"primaryKey": false,
|
||||
"notNull": false,
|
||||
"default": 200000
|
||||
},
|
||||
"is_enabled": {
|
||||
"name": "is_enabled",
|
||||
"type": "boolean",
|
||||
"primaryKey": false,
|
||||
"notNull": false,
|
||||
"default": true
|
||||
},
|
||||
"is_default": {
|
||||
"name": "is_default",
|
||||
"type": "boolean",
|
||||
"primaryKey": false,
|
||||
"notNull": false,
|
||||
"default": false
|
||||
},
|
||||
"sort_order": {
|
||||
"name": "sort_order",
|
||||
"type": "integer",
|
||||
"primaryKey": false,
|
||||
"notNull": false,
|
||||
"default": 0
|
||||
},
|
||||
"created_at": {
|
||||
"name": "created_at",
|
||||
"type": "timestamp with time zone",
|
||||
"primaryKey": false,
|
||||
"notNull": false,
|
||||
"default": "now()"
|
||||
},
|
||||
"updated_at": {
|
||||
"name": "updated_at",
|
||||
"type": "timestamp with time zone",
|
||||
"primaryKey": false,
|
||||
"notNull": false,
|
||||
"default": "now()"
|
||||
}
|
||||
},
|
||||
"indexes": {},
|
||||
"foreignKeys": {},
|
||||
"compositePrimaryKeys": {},
|
||||
"uniqueConstraints": {
|
||||
"models_model_id_unique": {
|
||||
"name": "models_model_id_unique",
|
||||
"nullsNotDistinct": false,
|
||||
"columns": [
|
||||
"model_id"
|
||||
]
|
||||
}
|
||||
},
|
||||
"policies": {},
|
||||
"checkConstraints": {},
|
||||
"isRLSEnabled": false
|
||||
},
|
||||
"public.tools": {
|
||||
"name": "tools",
|
||||
"schema": "",
|
||||
"columns": {
|
||||
"id": {
|
||||
"name": "id",
|
||||
"type": "serial",
|
||||
"primaryKey": true,
|
||||
"notNull": true
|
||||
},
|
||||
"tool_id": {
|
||||
"name": "tool_id",
|
||||
"type": "varchar(64)",
|
||||
"primaryKey": false,
|
||||
"notNull": true
|
||||
},
|
||||
"name": {
|
||||
"name": "name",
|
||||
"type": "varchar(64)",
|
||||
"primaryKey": false,
|
||||
"notNull": true
|
||||
},
|
||||
"display_name": {
|
||||
"name": "display_name",
|
||||
"type": "varchar(128)",
|
||||
"primaryKey": false,
|
||||
"notNull": true
|
||||
},
|
||||
"description": {
|
||||
"name": "description",
|
||||
"type": "text",
|
||||
"primaryKey": false,
|
||||
"notNull": false
|
||||
},
|
||||
"icon": {
|
||||
"name": "icon",
|
||||
"type": "varchar(64)",
|
||||
"primaryKey": false,
|
||||
"notNull": false
|
||||
},
|
||||
"input_schema": {
|
||||
"name": "input_schema",
|
||||
"type": "jsonb",
|
||||
"primaryKey": false,
|
||||
"notNull": true
|
||||
},
|
||||
"is_enabled": {
|
||||
"name": "is_enabled",
|
||||
"type": "boolean",
|
||||
"primaryKey": false,
|
||||
"notNull": false,
|
||||
"default": true
|
||||
},
|
||||
"is_default": {
|
||||
"name": "is_default",
|
||||
"type": "boolean",
|
||||
"primaryKey": false,
|
||||
"notNull": false,
|
||||
"default": false
|
||||
},
|
||||
"sort_order": {
|
||||
"name": "sort_order",
|
||||
"type": "integer",
|
||||
"primaryKey": false,
|
||||
"notNull": false,
|
||||
"default": 0
|
||||
},
|
||||
"created_at": {
|
||||
"name": "created_at",
|
||||
"type": "timestamp with time zone",
|
||||
"primaryKey": false,
|
||||
"notNull": false,
|
||||
"default": "now()"
|
||||
},
|
||||
"updated_at": {
|
||||
"name": "updated_at",
|
||||
"type": "timestamp with time zone",
|
||||
"primaryKey": false,
|
||||
"notNull": false,
|
||||
"default": "now()"
|
||||
}
|
||||
},
|
||||
"indexes": {},
|
||||
"foreignKeys": {},
|
||||
"compositePrimaryKeys": {},
|
||||
"uniqueConstraints": {
|
||||
"tools_tool_id_unique": {
|
||||
"name": "tools_tool_id_unique",
|
||||
"nullsNotDistinct": false,
|
||||
"columns": [
|
||||
"tool_id"
|
||||
]
|
||||
}
|
||||
},
|
||||
"policies": {},
|
||||
"checkConstraints": {},
|
||||
"isRLSEnabled": false
|
||||
},
|
||||
"public.user_settings": {
|
||||
"name": "user_settings",
|
||||
"schema": "",
|
||||
"columns": {
|
||||
"id": {
|
||||
"name": "id",
|
||||
"type": "serial",
|
||||
"primaryKey": true,
|
||||
"notNull": true
|
||||
},
|
||||
"cch_url": {
|
||||
"name": "cch_url",
|
||||
"type": "varchar(512)",
|
||||
"primaryKey": false,
|
||||
"notNull": true,
|
||||
"default": "'http://localhost:13500'"
|
||||
},
|
||||
"cch_api_key": {
|
||||
"name": "cch_api_key",
|
||||
"type": "varchar(512)",
|
||||
"primaryKey": false,
|
||||
"notNull": false
|
||||
},
|
||||
"cch_api_key_configured": {
|
||||
"name": "cch_api_key_configured",
|
||||
"type": "boolean",
|
||||
"primaryKey": false,
|
||||
"notNull": false,
|
||||
"default": false
|
||||
},
|
||||
"default_model": {
|
||||
"name": "default_model",
|
||||
"type": "varchar(64)",
|
||||
"primaryKey": false,
|
||||
"notNull": false,
|
||||
"default": "'claude-sonnet-4-20250514'"
|
||||
},
|
||||
"default_tools": {
|
||||
"name": "default_tools",
|
||||
"type": "jsonb",
|
||||
"primaryKey": false,
|
||||
"notNull": false,
|
||||
"default": "'[\"web_search\",\"code_execution\",\"web_fetch\"]'::jsonb"
|
||||
},
|
||||
"system_prompt": {
|
||||
"name": "system_prompt",
|
||||
"type": "text",
|
||||
"primaryKey": false,
|
||||
"notNull": false
|
||||
},
|
||||
"temperature": {
|
||||
"name": "temperature",
|
||||
"type": "varchar(10)",
|
||||
"primaryKey": false,
|
||||
"notNull": false,
|
||||
"default": "'0.7'"
|
||||
},
|
||||
"theme": {
|
||||
"name": "theme",
|
||||
"type": "varchar(20)",
|
||||
"primaryKey": false,
|
||||
"notNull": false,
|
||||
"default": "'light'"
|
||||
},
|
||||
"language": {
|
||||
"name": "language",
|
||||
"type": "varchar(10)",
|
||||
"primaryKey": false,
|
||||
"notNull": false,
|
||||
"default": "'zh-CN'"
|
||||
},
|
||||
"enable_thinking": {
|
||||
"name": "enable_thinking",
|
||||
"type": "boolean",
|
||||
"primaryKey": false,
|
||||
"notNull": false,
|
||||
"default": false
|
||||
},
|
||||
"save_chat_history": {
|
||||
"name": "save_chat_history",
|
||||
"type": "boolean",
|
||||
"primaryKey": false,
|
||||
"notNull": false,
|
||||
"default": true
|
||||
},
|
||||
"created_at": {
|
||||
"name": "created_at",
|
||||
"type": "timestamp with time zone",
|
||||
"primaryKey": false,
|
||||
"notNull": false,
|
||||
"default": "now()"
|
||||
},
|
||||
"updated_at": {
|
||||
"name": "updated_at",
|
||||
"type": "timestamp with time zone",
|
||||
"primaryKey": false,
|
||||
"notNull": false,
|
||||
"default": "now()"
|
||||
}
|
||||
},
|
||||
"indexes": {},
|
||||
"foreignKeys": {},
|
||||
"compositePrimaryKeys": {},
|
||||
"uniqueConstraints": {},
|
||||
"policies": {},
|
||||
"checkConstraints": {},
|
||||
"isRLSEnabled": false
|
||||
}
|
||||
},
|
||||
"enums": {},
|
||||
"schemas": {},
|
||||
"sequences": {},
|
||||
"roles": {},
|
||||
"policies": {},
|
||||
"views": {},
|
||||
"_meta": {
|
||||
"columns": {},
|
||||
"schemas": {},
|
||||
"tables": {}
|
||||
}
|
||||
}
|
||||
20
src/drizzle/migrations/meta/_journal.json
Normal file
20
src/drizzle/migrations/meta/_journal.json
Normal file
@ -0,0 +1,20 @@
|
||||
{
|
||||
"version": "7",
|
||||
"dialect": "postgresql",
|
||||
"entries": [
|
||||
{
|
||||
"idx": 0,
|
||||
"version": "7",
|
||||
"when": 1765985557199,
|
||||
"tag": "0000_white_warlock",
|
||||
"breakpoints": true
|
||||
},
|
||||
{
|
||||
"idx": 1,
|
||||
"version": "7",
|
||||
"when": 1765991206886,
|
||||
"tag": "0001_daffy_paladin",
|
||||
"breakpoints": true
|
||||
}
|
||||
]
|
||||
}
|
||||
190
src/drizzle/schema.ts
Normal file
190
src/drizzle/schema.ts
Normal file
@ -0,0 +1,190 @@
|
||||
import {
|
||||
pgTable,
|
||||
serial,
|
||||
varchar,
|
||||
text,
|
||||
boolean,
|
||||
integer,
|
||||
timestamp,
|
||||
jsonb,
|
||||
} from 'drizzle-orm/pg-core';
|
||||
import { relations } from 'drizzle-orm';
|
||||
|
||||
// ============================================
|
||||
// 用户设置表
|
||||
// ============================================
|
||||
export const userSettings = pgTable('user_settings', {
|
||||
id: serial('id').primaryKey(),
|
||||
// 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'),
|
||||
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(),
|
||||
// 对话信息
|
||||
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[]>(),
|
||||
// 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'),
|
||||
// 模型特性
|
||||
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 conversationsRelations = relations(conversations, ({ many }) => ({
|
||||
messages: many(messages),
|
||||
}));
|
||||
|
||||
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 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;
|
||||
182
src/drizzle/seed.ts
Normal file
182
src/drizzle/seed.ts
Normal file
@ -0,0 +1,182 @@
|
||||
import { db } from './db';
|
||||
import { userSettings, tools, models } from './schema';
|
||||
import { eq } from 'drizzle-orm';
|
||||
|
||||
// 初始化用户设置
|
||||
async function seedUserSettings() {
|
||||
const existing = await db.query.userSettings.findFirst({
|
||||
where: eq(userSettings.id, 1),
|
||||
});
|
||||
|
||||
if (!existing) {
|
||||
await db.insert(userSettings).values({
|
||||
id: 1,
|
||||
cchUrl: 'http://localhost:13500',
|
||||
cchApiKeyConfigured: false,
|
||||
defaultModel: 'claude-sonnet-4-5-20250929',
|
||||
defaultTools: ['web_search', 'code_execution', 'web_fetch'],
|
||||
theme: 'light',
|
||||
language: 'zh-CN',
|
||||
enableThinking: false,
|
||||
saveChatHistory: true,
|
||||
});
|
||||
console.log('✓ User settings initialized');
|
||||
} else {
|
||||
console.log('→ User settings already exist');
|
||||
}
|
||||
}
|
||||
|
||||
// 初始化工具
|
||||
async function seedTools() {
|
||||
const toolsData = [
|
||||
{
|
||||
toolId: 'web_search',
|
||||
name: 'web_search',
|
||||
displayName: 'Web Search',
|
||||
description: '搜索互联网获取最新信息',
|
||||
icon: 'Search',
|
||||
inputSchema: {
|
||||
type: 'object',
|
||||
properties: {
|
||||
query: { type: 'string', description: '搜索查询' },
|
||||
},
|
||||
required: ['query'],
|
||||
},
|
||||
isEnabled: true,
|
||||
isDefault: true,
|
||||
sortOrder: 1,
|
||||
},
|
||||
{
|
||||
toolId: 'code_execution',
|
||||
name: 'code_execution',
|
||||
displayName: 'Code Execution',
|
||||
description: '执行代码片段并返回结果',
|
||||
icon: 'Terminal',
|
||||
inputSchema: {
|
||||
type: 'object',
|
||||
properties: {
|
||||
code: { type: 'string', description: '要执行的代码' },
|
||||
language: { type: 'string', description: '编程语言' },
|
||||
},
|
||||
required: ['code', 'language'],
|
||||
},
|
||||
isEnabled: true,
|
||||
isDefault: true,
|
||||
sortOrder: 2,
|
||||
},
|
||||
{
|
||||
toolId: 'web_fetch',
|
||||
name: 'web_fetch',
|
||||
displayName: 'Web Fetch',
|
||||
description: '获取网页内容',
|
||||
icon: 'Globe',
|
||||
inputSchema: {
|
||||
type: 'object',
|
||||
properties: {
|
||||
url: { type: 'string', description: '要获取的URL' },
|
||||
},
|
||||
required: ['url'],
|
||||
},
|
||||
isEnabled: true,
|
||||
isDefault: true,
|
||||
sortOrder: 3,
|
||||
},
|
||||
];
|
||||
|
||||
for (const tool of toolsData) {
|
||||
const existing = await db.query.tools.findFirst({
|
||||
where: eq(tools.toolId, tool.toolId),
|
||||
});
|
||||
|
||||
if (!existing) {
|
||||
await db.insert(tools).values(tool);
|
||||
console.log(`✓ Tool "${tool.displayName}" initialized`);
|
||||
} else {
|
||||
console.log(`→ Tool "${tool.displayName}" already exists`);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// 初始化模型
|
||||
async function seedModels() {
|
||||
const modelsData = [
|
||||
{
|
||||
modelId: 'claude-sonnet-4-5-20250929',
|
||||
name: 'claude-sonnet-4-5-20250929',
|
||||
displayName: 'Default (recommended)',
|
||||
description: '使用默认模型(Sonnet 4.5),$3/$15 per Mtok',
|
||||
supportsTools: true,
|
||||
supportsThinking: true,
|
||||
supportsVision: true,
|
||||
maxTokens: 8192,
|
||||
contextWindow: 200000,
|
||||
isEnabled: true,
|
||||
isDefault: true,
|
||||
sortOrder: 1,
|
||||
},
|
||||
{
|
||||
modelId: 'claude-opus-4-5-20251101',
|
||||
name: 'claude-opus-4-5-20251101',
|
||||
displayName: 'Opus',
|
||||
description: 'Opus 4.5 - 最强大的复杂工作,$5/$25 per Mtok',
|
||||
supportsTools: true,
|
||||
supportsThinking: true,
|
||||
supportsVision: true,
|
||||
maxTokens: 8192,
|
||||
contextWindow: 200000,
|
||||
isEnabled: true,
|
||||
isDefault: false,
|
||||
sortOrder: 2,
|
||||
},
|
||||
{
|
||||
modelId: 'claude-haiku-4-5-20251001',
|
||||
name: 'claude-haiku-4-5-20251001',
|
||||
displayName: 'Haiku',
|
||||
description: 'Haiku 4.5 - 快速回答,$1/$5 per Mtok',
|
||||
supportsTools: true,
|
||||
supportsThinking: false,
|
||||
supportsVision: false,
|
||||
maxTokens: 8192,
|
||||
contextWindow: 200000,
|
||||
isEnabled: true,
|
||||
isDefault: false,
|
||||
sortOrder: 3,
|
||||
},
|
||||
];
|
||||
|
||||
for (const model of modelsData) {
|
||||
const existing = await db.query.models.findFirst({
|
||||
where: eq(models.modelId, model.modelId),
|
||||
});
|
||||
|
||||
if (!existing) {
|
||||
await db.insert(models).values(model);
|
||||
console.log(`✓ Model "${model.displayName}" initialized`);
|
||||
} else {
|
||||
console.log(`→ Model "${model.displayName}" already exists`);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// 主函数
|
||||
export async function seed() {
|
||||
console.log('\n🌱 Starting database seed...\n');
|
||||
|
||||
try {
|
||||
await seedUserSettings();
|
||||
await seedTools();
|
||||
await seedModels();
|
||||
|
||||
console.log('\n✅ Database seed completed!\n');
|
||||
} catch (error) {
|
||||
console.error('\n❌ Database seed failed:', error);
|
||||
throw error;
|
||||
}
|
||||
}
|
||||
|
||||
// 如果直接运行此文件
|
||||
if (require.main === module) {
|
||||
seed()
|
||||
.then(() => process.exit(0))
|
||||
.catch(() => process.exit(1));
|
||||
}
|
||||
Loading…
Reference in New Issue
Block a user