feat: 添加SQLite本地数据库支持
- 移除Prisma中不支持SQLite的类型(Json、枚举) - 使用String类型替代枚举值 - 更新Prisma schema适配SQLite - 添加数据库初始化脚本scripts/init-db.js - 更新数据库路径配置 - 添加sql.js依赖 - 删除旧的prisma.ts使用新的db.ts Co-Authored-By: Claude <noreply@anthropic.com>
This commit is contained in:
parent
e35bd77e06
commit
fbff8cc230
@ -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": {
|
||||
|
||||
@ -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'
|
||||
|
||||
98
scripts/init-db.js
Normal file
98
scripts/init-db.js
Normal file
@ -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!');
|
||||
20
src/lib/db.ts
Normal file
20
src/lib/db.ts
Normal file
@ -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 };
|
||||
Loading…
x
Reference in New Issue
Block a user