Files
suanming/server/database/index.cjs
patdelphi df9e1ca81c fix: 修复Koyeb Volume挂载路径问题
- 根据Koyeb实际挂载信息(/workspace/data)调整数据库路径配置
- 添加Koyeb环境检测逻辑,优先使用/workspace/data路径
- 更新.koyeb/koyeb.yaml中的mount_path和DB_PATH配置
- 增强日志输出,显示Koyeb环境状态和工作目录
- 解决数据库持久化路径不匹配导致的数据丢失问题
- 确保Volume正确挂载到/workspace/data目录
2025-08-23 00:03:03 +08:00

157 lines
4.1 KiB
JavaScript
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.
const Database = require('better-sqlite3');
const path = require('path');
const fs = require('fs');
class DatabaseManager {
constructor() {
this.db = null;
// 检测Koyeb环境并使用正确的挂载路径
const isKoyeb = process.env.KOYEB_APP_NAME || process.env.KOYEB_SERVICE_NAME || fs.existsSync('/workspace/data');
if (isKoyeb) {
// Koyeb环境Volume挂载到/workspace/data
this.dbPath = '/workspace/data/numerology.db';
} else if (process.env.NODE_ENV === 'production') {
// 其他生产环境:使用/app/data
this.dbPath = '/app/data/numerology.db';
} else {
// 开发环境:使用本地路径
this.dbPath = path.join(__dirname, '../../numerology.db');
}
this.schemaPath = path.join(__dirname, 'schema.sql');
// 输出数据库配置信息
console.log(`🗄️ 数据库路径: ${this.dbPath}`);
console.log(`🌍 运行环境: ${process.env.NODE_ENV || 'development'}`);
console.log(`📊 数据库文件: ${path.basename(this.dbPath)}`);
console.log(`🏢 Koyeb环境: ${isKoyeb ? 'Yes' : 'No'}`);
console.log(`📁 工作目录: ${process.cwd()}`);
}
// 初始化数据库连接
init() {
try {
// 确保数据库目录存在
const dbDir = path.dirname(this.dbPath);
if (!fs.existsSync(dbDir)) {
fs.mkdirSync(dbDir, { recursive: true });
console.log(`📁 创建数据库目录: ${dbDir}`);
} else {
console.log(`📁 数据库目录已存在: ${dbDir}`);
}
// 创建或连接到SQLite数据库
this.db = new Database(this.dbPath);
// 启用外键约束
this.db.pragma('foreign_keys = ON');
// 设置WAL模式以提高并发性能
this.db.pragma('journal_mode = WAL');
// 初始化数据库结构
this.initializeSchema();
console.log('数据库初始化成功');
return this.db;
} catch (error) {
console.error('数据库初始化失败:', error);
throw error;
}
}
// 初始化数据库结构
initializeSchema() {
try {
const schema = fs.readFileSync(this.schemaPath, 'utf8');
// 直接执行整个schema文件
this.db.exec(schema);
console.log('数据库结构初始化完成');
} catch (error) {
console.error('数据库结构初始化失败:', error);
throw error;
}
}
// 获取数据库实例
getDatabase() {
if (!this.db) {
this.init();
}
return this.db;
}
// 关闭数据库连接
close() {
if (this.db) {
this.db.close();
this.db = null;
console.log('数据库连接已关闭');
}
}
// 执行事务
transaction(callback) {
const db = this.getDatabase();
const transaction = db.transaction(callback);
return transaction;
}
// 备份数据库
backup(backupPath) {
try {
const db = this.getDatabase();
db.backup(backupPath);
console.log(`数据库备份成功: ${backupPath}`);
} catch (error) {
console.error('数据库备份失败:', error);
throw error;
}
}
// 清理过期会话
cleanupExpiredSessions() {
try {
const db = this.getDatabase();
const stmt = db.prepare('DELETE FROM user_sessions WHERE expires_at < ?');
const result = stmt.run(new Date().toISOString());
console.log(`清理了 ${result.changes} 个过期会话`);
return result.changes;
} catch (error) {
console.error('清理过期会话失败:', error);
throw error;
}
}
}
// 创建单例实例
const dbManager = new DatabaseManager();
// 导出数据库管理器和便捷方法
module.exports = {
dbManager,
getDB: () => dbManager.getDatabase(),
closeDB: () => dbManager.close(),
transaction: (callback) => dbManager.transaction(callback),
backup: (path) => dbManager.backup(path),
cleanupSessions: () => dbManager.cleanupExpiredSessions()
};
// 进程退出时自动关闭数据库
process.on('exit', () => {
dbManager.close();
});
process.on('SIGINT', () => {
dbManager.close();
process.exit(0);
});
process.on('SIGTERM', () => {
dbManager.close();
process.exit(0);
});