feat: 添加更好的 SQLite3 模块加载和错误处理,更新相关组件以支持项目路径

This commit is contained in:
catlog22
2026-02-06 23:07:56 +08:00
parent 5cdbb43b3b
commit 3d862e6ed8
15 changed files with 541 additions and 38 deletions

View File

@@ -2,11 +2,11 @@
* MCP Templates Database Module
* Stores MCP server configurations as reusable templates
*/
import Database from 'better-sqlite3';
import { existsSync, mkdirSync } from 'fs';
import { join, dirname } from 'path';
import { homedir } from 'os';
import { StoragePaths, ensureStorageDir } from '../../config/storage-paths.js';
import { createDatabase } from '../../utils/db-loader.js';
// Database path - uses centralized storage
const DB_DIR = StoragePaths.global.databases();
@@ -16,14 +16,15 @@ const DB_PATH = StoragePaths.global.mcpTemplates();
ensureStorageDir(DB_DIR);
// Initialize database connection
let db: Database.Database | null = null;
let db: any | null = null;
/**
* Get or create database connection
*/
function getDb(): Database.Database {
function getDb(): any {
if (!db) {
db = new Database(DB_PATH);
db = createDatabase(DB_PATH);
if (!db) return null;
initDatabase();
}
return db;
@@ -33,7 +34,7 @@ function getDb(): Database.Database {
* Initialize database schema
*/
function initDatabase() {
const db = getDb();
if (!db) return;
// Create templates table
db.exec(`
@@ -83,6 +84,7 @@ export interface McpTemplate {
export function saveTemplate(template: McpTemplate): { success: boolean; id?: number; error?: string } {
try {
const db = getDb();
if (!db) return { success: false, error: 'Database unavailable (native module issue)' };
const now = Date.now();
const stmt = db.prepare(`
@@ -125,6 +127,7 @@ export function saveTemplate(template: McpTemplate): { success: boolean; id?: nu
export function getAllTemplates(): McpTemplate[] {
try {
const db = getDb();
if (!db) return [];
const rows = db.prepare('SELECT * FROM mcp_templates ORDER BY name').all();
return rows.map((row: any) => ({
@@ -149,6 +152,7 @@ export function getAllTemplates(): McpTemplate[] {
export function getTemplateByName(name: string): McpTemplate | null {
try {
const db = getDb();
if (!db) return null;
const row = db.prepare('SELECT * FROM mcp_templates WHERE name = ?').get(name);
if (!row) return null;
@@ -175,6 +179,7 @@ export function getTemplateByName(name: string): McpTemplate | null {
export function getTemplatesByCategory(category: string): McpTemplate[] {
try {
const db = getDb();
if (!db) return [];
const rows = db.prepare('SELECT * FROM mcp_templates WHERE category = ? ORDER BY name').all(category);
return rows.map((row: any) => ({
@@ -199,6 +204,7 @@ export function getTemplatesByCategory(category: string): McpTemplate[] {
export function deleteTemplate(name: string): { success: boolean; error?: string } {
try {
const db = getDb();
if (!db) return { success: false, error: 'Database unavailable (native module issue)' };
const result = db.prepare('DELETE FROM mcp_templates WHERE name = ?').run(name);
return {
@@ -219,6 +225,7 @@ export function deleteTemplate(name: string): { success: boolean; error?: string
export function searchTemplates(keyword: string): McpTemplate[] {
try {
const db = getDb();
if (!db) return [];
const searchPattern = `%${keyword}%`;
const rows = db.prepare(`
SELECT * FROM mcp_templates
@@ -248,6 +255,7 @@ export function searchTemplates(keyword: string): McpTemplate[] {
export function getAllCategories(): string[] {
try {
const db = getDb();
if (!db) return [];
const rows = db.prepare('SELECT DISTINCT category FROM mcp_templates WHERE category IS NOT NULL ORDER BY category').all();
return rows.map((row: any) => row.category);
} catch (error: unknown) {

View File

@@ -1734,7 +1734,7 @@ async function toggleChineseResponse(enabled, target) {
}
try {
var response = await fetch('/api/language/chinese-response', {
var response = await csrfFetch('/api/language/chinese-response', {
method: 'POST',
headers: { 'Content-Type': 'application/json' },
body: JSON.stringify({ enabled: enabled, target: target })
@@ -1799,7 +1799,7 @@ async function toggleWindowsPlatform(enabled) {
windowsPlatformLoading = true;
try {
var response = await fetch('/api/language/windows-platform', {
var response = await csrfFetch('/api/language/windows-platform', {
method: 'POST',
headers: { 'Content-Type': 'application/json' },
body: JSON.stringify({ enabled: enabled })
@@ -1848,7 +1848,7 @@ async function toggleCodexCliEnhancement(enabled) {
codexCliEnhancementLoading = true;
try {
var response = await fetch('/api/language/codex-cli-enhancement', {
var response = await csrfFetch('/api/language/codex-cli-enhancement', {
method: 'POST',
headers: { 'Content-Type': 'application/json' },
body: JSON.stringify({ enabled: enabled, action: 'toggle' })
@@ -1888,7 +1888,7 @@ async function refreshCodexCliEnhancement() {
codexCliEnhancementLoading = true;
try {
var response = await fetch('/api/language/codex-cli-enhancement', {
var response = await csrfFetch('/api/language/codex-cli-enhancement', {
method: 'POST',
headers: { 'Content-Type': 'application/json' },
body: JSON.stringify({ action: 'refresh' })

View File

@@ -0,0 +1,54 @@
/**
* Database Loader - Centralized better-sqlite3 loading with native module error handling
* Catches NODE_MODULE_VERSION mismatch errors and provides actionable fix instructions
*/
let warningShown = false;
function showNativeModuleWarning(error: Error): void {
if (warningShown) return;
warningShown = true;
const isVersionMismatch = error.message?.includes('NODE_MODULE_VERSION') ||
(error as any).code === 'ERR_DLOPEN_FAILED';
if (isVersionMismatch) {
console.error(
'\n[CCW] better-sqlite3 native module version mismatch.\n' +
' The module was compiled for a different Node.js version.\n' +
' Fix: run one of the following commands:\n' +
' npm rebuild better-sqlite3\n' +
' npm install better-sqlite3 --build-from-source\n'
);
}
}
/**
* Load better-sqlite3 Database constructor with error handling.
* Returns the Database class or null if loading fails.
*/
export function loadDatabase(): typeof import('better-sqlite3') | null {
try {
// Use dynamic import via require for native module
const Database = require('better-sqlite3');
return Database;
} catch (error: any) {
showNativeModuleWarning(error);
return null;
}
}
/**
* Create a database instance with error handling.
* Returns the database instance or null if creation fails.
*/
export function createDatabase(dbPath: string, options?: any): any | null {
const Database = loadDatabase();
if (!Database) return null;
try {
return new Database(dbPath, options);
} catch (error: any) {
showNativeModuleWarning(error);
return null;
}
}