import { Injectable } from '@nestjs/common'; import Database from 'better-sqlite3'; import fs from 'fs'; import path from 'path'; @Injectable() export class ConfigService { private dataDir: string; private unifiedDbPath: string; constructor() { // Find project root by traversing up from current directory until we find the root package.json let projectRoot = process.cwd(); while (projectRoot !== path.dirname(projectRoot)) { if (fs.existsSync(path.join(projectRoot, 'package.json'))) { try { const pkg = JSON.parse( fs.readFileSync(path.join(projectRoot, 'package.json'), 'utf-8'), ); if (pkg.name === 'watch-finished-turbo') { break; } } catch (e) { // ignore } } projectRoot = path.dirname(projectRoot); } this.dataDir = path.resolve(projectRoot, 'data'); this.unifiedDbPath = path.resolve(this.dataDir, 'database.db'); console.log('ConfigService: Database path:', this.unifiedDbPath); console.log('ConfigService: Project root:', projectRoot); console.log('ConfigService: Current working directory:', process.cwd()); // Ensure database and tables exist const db = new Database(this.unifiedDbPath); db.exec(` CREATE TABLE IF NOT EXISTS settings ( key TEXT PRIMARY KEY, value TEXT ); `); db.close(); } getSettings(key?: string, defaultValue?: any): any { try { const db = new Database(this.unifiedDbPath, { readonly: true }); if (key) { const row = db .prepare('SELECT value FROM settings WHERE key = ?') .get(key) as { value?: string } | undefined; db.close(); return row && row.value !== undefined ? JSON.parse(row.value) : defaultValue; } else { const rows = db .prepare('SELECT key, value FROM settings') .all() as Array<{ key: string; value: string }>; db.close(); const settings: Record = {}; for (const row of rows) { settings[row.key] = JSON.parse(row.value); } return settings; } } catch (e) { if (key) return defaultValue; return {}; } } getConfigFile(name: string): any { // For datasources: name = kids.json, pr0n.json, tvshows.json const base = name.replace(/\.json$/, ''); try { const db = new Database(this.unifiedDbPath, { readonly: true }); const row = db .prepare('SELECT data FROM datasets WHERE name = ?') .get(base) as { data?: string } | undefined; db.close(); return row && row.data !== undefined ? JSON.parse(row.data) : null; } catch (e) { return null; } } listConfigs(): string[] { // Return all dataset names as .json (regardless of enabled) try { const db = new Database(this.unifiedDbPath, { readonly: true }); const rows = db.prepare('SELECT name FROM datasets').all() as Array<{ name: string; }>; db.close(); // Debug: log found rows if (rows.length === 0) { console.warn('No configs found in datasets table'); } return rows.map((row) => row.name + '.json'); } catch (e) { console.error('Error in listConfigs:', e); return []; } } setSettings(settings: Record): boolean { try { const db = new Database(this.unifiedDbPath); const insert = db.prepare( 'INSERT OR REPLACE INTO settings (key, value) VALUES (?, ?)', ); for (const [key, value] of Object.entries(settings)) { // If value is already a JSON string, save it as-is // Otherwise, stringify it let valueToSave: string; if (typeof value === 'string') { // Check if it's already valid JSON try { JSON.parse(value); valueToSave = value; } catch { // Not valid JSON, stringify it valueToSave = JSON.stringify(value); } } else { // It's an object, stringify it valueToSave = JSON.stringify(value); } insert.run(key, valueToSave); } db.close(); return true; } catch (e) { console.error('Error setting settings:', e); return false; } } deleteSetting(key: string): boolean { try { const db = new Database(this.unifiedDbPath); const result = db.prepare('DELETE FROM settings WHERE key = ?').run(key); db.close(); return result.changes > 0; } catch (e) { console.error('Error deleting setting:', e); return false; } } }