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",
|
"name": "qia-server",
|
||||||
"version": "0.1.0",
|
"version": "0.1.0",
|
||||||
|
"type": "module",
|
||||||
"description": "Backend API for 掐日子(qia) app",
|
"description": "Backend API for 掐日子(qia) app",
|
||||||
"main": "dist/index.js",
|
"main": "dist/index.js",
|
||||||
"scripts": {
|
"scripts": {
|
||||||
@ -23,6 +24,7 @@
|
|||||||
"express-validator": "^7.1.0",
|
"express-validator": "^7.1.0",
|
||||||
"helmet": "^8.0.0",
|
"helmet": "^8.0.0",
|
||||||
"jsonwebtoken": "^9.0.2",
|
"jsonwebtoken": "^9.0.2",
|
||||||
|
"sql.js": "^1.13.0",
|
||||||
"zod": "^3.23.8"
|
"zod": "^3.23.8"
|
||||||
},
|
},
|
||||||
"devDependencies": {
|
"devDependencies": {
|
||||||
|
|||||||
@ -31,12 +31,12 @@ model User {
|
|||||||
model Event {
|
model Event {
|
||||||
id String @id @default(uuid())
|
id String @id @default(uuid())
|
||||||
user_id String
|
user_id String
|
||||||
type EventType // 'anniversary' | 'reminder'
|
type String // 'anniversary' | 'reminder' (String for SQLite)
|
||||||
title String
|
title String
|
||||||
content String? // Only for reminders
|
content String? // Only for reminders
|
||||||
date DateTime // For anniversaries: the date; For reminders: the reminder date
|
date DateTime // For anniversaries: the date; For reminders: the reminder date
|
||||||
is_lunar Boolean @default(false)
|
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_holiday Boolean @default(false) // Only for anniversaries
|
||||||
is_completed Boolean @default(false) // Only for reminders
|
is_completed Boolean @default(false) // Only for reminders
|
||||||
created_at DateTime @default(now())
|
created_at DateTime @default(now())
|
||||||
@ -55,7 +55,7 @@ model Event {
|
|||||||
model Note {
|
model Note {
|
||||||
id String @id @default(uuid())
|
id String @id @default(uuid())
|
||||||
user_id String
|
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())
|
created_at DateTime @default(now())
|
||||||
updated_at DateTime @updatedAt
|
updated_at DateTime @updatedAt
|
||||||
|
|
||||||
@ -73,7 +73,7 @@ model AICoachConversation {
|
|||||||
user_id String
|
user_id String
|
||||||
message String
|
message String
|
||||||
response 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())
|
created_at DateTime @default(now())
|
||||||
|
|
||||||
// Relations
|
// Relations
|
||||||
@ -83,14 +83,6 @@ model AICoachConversation {
|
|||||||
@@map("ai_coach_conversations")
|
@@map("ai_coach_conversations")
|
||||||
}
|
}
|
||||||
|
|
||||||
// Enums
|
// Note: Using String types instead of enums for SQLite compatibility
|
||||||
enum EventType {
|
// Event type values: 'anniversary' | 'reminder'
|
||||||
anniversary
|
// Repeat type values: 'yearly' | 'monthly' | 'none'
|
||||||
reminder
|
|
||||||
}
|
|
||||||
|
|
||||||
enum RepeatType {
|
|
||||||
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