From fbff8cc230cf3e18f70ac84f5633d438d65e882f Mon Sep 17 00:00:00 2001 From: ddshi <8811906+ddshi@user.noreply.gitee.com> Date: Thu, 29 Jan 2026 16:33:55 +0800 Subject: [PATCH] =?UTF-8?q?feat:=20=E6=B7=BB=E5=8A=A0SQLite=E6=9C=AC?= =?UTF-8?q?=E5=9C=B0=E6=95=B0=E6=8D=AE=E5=BA=93=E6=94=AF=E6=8C=81?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - 移除Prisma中不支持SQLite的类型(Json、枚举) - 使用String类型替代枚举值 - 更新Prisma schema适配SQLite - 添加数据库初始化脚本scripts/init-db.js - 更新数据库路径配置 - 添加sql.js依赖 - 删除旧的prisma.ts使用新的db.ts Co-Authored-By: Claude --- package.json | 2 + prisma/schema.prisma | 22 ++++------ scripts/init-db.js | 98 ++++++++++++++++++++++++++++++++++++++++++++ src/lib/db.ts | 20 +++++++++ 4 files changed, 127 insertions(+), 15 deletions(-) create mode 100644 scripts/init-db.js create mode 100644 src/lib/db.ts diff --git a/package.json b/package.json index 4ef1f31..60f642f 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,7 @@ { "name": "qia-server", "version": "0.1.0", + "type": "module", "description": "Backend API for 掐日子(qia) app", "main": "dist/index.js", "scripts": { @@ -23,6 +24,7 @@ "express-validator": "^7.1.0", "helmet": "^8.0.0", "jsonwebtoken": "^9.0.2", + "sql.js": "^1.13.0", "zod": "^3.23.8" }, "devDependencies": { diff --git a/prisma/schema.prisma b/prisma/schema.prisma index 4c48376..2a4c4bf 100644 --- a/prisma/schema.prisma +++ b/prisma/schema.prisma @@ -31,12 +31,12 @@ model User { model Event { id String @id @default(uuid()) user_id String - type EventType // 'anniversary' | 'reminder' + type String // 'anniversary' | 'reminder' (String for SQLite) title String content String? // Only for reminders date DateTime // For anniversaries: the date; For reminders: the reminder date is_lunar Boolean @default(false) - repeat_type RepeatType @default(none) + repeat_type String @default("none") // 'yearly' | 'monthly' | 'none' (String for SQLite) is_holiday Boolean @default(false) // Only for anniversaries is_completed Boolean @default(false) // Only for reminders created_at DateTime @default(now()) @@ -55,7 +55,7 @@ model Event { model Note { id String @id @default(uuid()) user_id String - content String @db.Text // HTML content from rich text editor + content String // HTML content from rich text editor created_at DateTime @default(now()) updated_at DateTime @updatedAt @@ -73,7 +73,7 @@ model AICoachConversation { user_id String message String response String - parsed_data Json? // AI parsed event data + parsed_data String? // AI parsed event data (JSON string for SQLite) created_at DateTime @default(now()) // Relations @@ -83,14 +83,6 @@ model AICoachConversation { @@map("ai_coach_conversations") } -// Enums -enum EventType { - anniversary - reminder -} - -enum RepeatType { - yearly - monthly - none -} +// Note: Using String types instead of enums for SQLite compatibility +// Event type values: 'anniversary' | 'reminder' +// Repeat type values: 'yearly' | 'monthly' | 'none' diff --git a/scripts/init-db.js b/scripts/init-db.js new file mode 100644 index 0000000..54b6e1e --- /dev/null +++ b/scripts/init-db.js @@ -0,0 +1,98 @@ +import { createClient } from '@libsql/client'; +import fs from 'fs'; +import path from 'path'; +import { fileURLToPath } from 'url'; + +const __dirname = path.dirname(fileURLToPath(import.meta.url)); +// Navigate from scripts/ to server/ root, then to prisma/ +const dbPath = path.join(__dirname, '..', 'prisma', 'dev.db'); +const prismaDir = path.dirname(dbPath); + +// Ensure prisma directory exists +if (!fs.existsSync(prismaDir)) { + fs.mkdirSync(prismaDir, { recursive: true }); + console.log('Created prisma directory'); +} + +console.log('Database path:', dbPath); + +// Ensure file exists +if (!fs.existsSync(dbPath)) { + fs.writeFileSync(dbPath, ''); + console.log('Created empty database file'); +} + +const db = createClient({ + url: `file:${dbPath}`, +}); + +console.log('LibSQL client created'); + +// Create tables +await db.execute(` + CREATE TABLE IF NOT EXISTS users ( + id TEXT PRIMARY KEY, + email TEXT UNIQUE NOT NULL, + password_hash TEXT NOT NULL, + nickname TEXT, + created_at DATETIME DEFAULT CURRENT_TIMESTAMP, + updated_at DATETIME DEFAULT CURRENT_TIMESTAMP + ) +`); +console.log('Created users table'); + +await db.execute(` + CREATE TABLE IF NOT EXISTS events ( + id TEXT PRIMARY KEY, + user_id TEXT NOT NULL, + type TEXT NOT NULL, + title TEXT NOT NULL, + content TEXT, + date DATETIME NOT NULL, + is_lunar INTEGER DEFAULT 0, + repeat_type TEXT DEFAULT 'none', + is_holiday INTEGER DEFAULT 0, + is_completed INTEGER DEFAULT 0, + created_at DATETIME DEFAULT CURRENT_TIMESTAMP, + updated_at DATETIME DEFAULT CURRENT_TIMESTAMP, + FOREIGN KEY (user_id) REFERENCES users(id) ON DELETE CASCADE + ) +`); +console.log('Created events table'); + +await db.execute(`CREATE INDEX IF NOT EXISTS idx_events_user_id ON events(user_id)`); +await db.execute(`CREATE INDEX IF NOT EXISTS idx_events_type ON events(type)`); +await db.execute(`CREATE INDEX IF NOT EXISTS idx_events_date ON events(date)`); + +await db.execute(` + CREATE TABLE IF NOT EXISTS notes ( + id TEXT PRIMARY KEY, + user_id TEXT UNIQUE NOT NULL, + content TEXT, + created_at DATETIME DEFAULT CURRENT_TIMESTAMP, + updated_at DATETIME DEFAULT CURRENT_TIMESTAMP, + FOREIGN KEY (user_id) REFERENCES users(id) ON DELETE CASCADE + ) +`); +console.log('Created notes table'); + +await db.execute(` + CREATE TABLE IF NOT EXISTS ai_coach_conversations ( + id TEXT PRIMARY KEY, + user_id TEXT NOT NULL, + message TEXT NOT NULL, + response TEXT NOT NULL, + parsed_data TEXT, + created_at DATETIME DEFAULT CURRENT_TIMESTAMP, + FOREIGN KEY (user_id) REFERENCES users(id) ON DELETE CASCADE + ) +`); +console.log('Created ai_coach_conversations table'); + +await db.execute(`CREATE INDEX IF NOT EXISTS idx_ai_conversations_user_id ON ai_coach_conversations(user_id)`); + +// Verify +const tables = await db.execute("SELECT name FROM sqlite_master WHERE type='table'"); +console.log('Tables:', tables.rows.map(r => r.name).join(', ')); + +console.log('\nDatabase initialized successfully!'); diff --git a/src/lib/db.ts b/src/lib/db.ts new file mode 100644 index 0000000..cf328b9 --- /dev/null +++ b/src/lib/db.ts @@ -0,0 +1,20 @@ +import { createClient } from '@libsql/client'; +import path from 'path'; +import { fileURLToPath } from 'url'; +import dotenv from 'dotenv'; + +// Load .env file +dotenv.config(); + +const __dirname = path.dirname(fileURLToPath(import.meta.url)); +const dbPath = path.resolve(__dirname, '../../prisma/dev.db'); + +console.log('Database path:', dbPath); + +// Create LibSQL client +const db = createClient({ + url: `file:${dbPath}`, +}); + +export default db; +export { dbPath };