From 0e0d9990274ad64c9fca0236838a4bae8e5a93a0 Mon Sep 17 00:00:00 2001 From: Michael Date: Sat, 21 Feb 2026 13:18:38 +0100 Subject: [PATCH] feat: implement dynamic database connection reloading --- src/db/index.ts | 80 +++++++++++++++++++++++--------- src/features/settings/actions.ts | 5 ++ 2 files changed, 62 insertions(+), 23 deletions(-) diff --git a/src/db/index.ts b/src/db/index.ts index 9c4b4b7..fc8ce66 100644 --- a/src/db/index.ts +++ b/src/db/index.ts @@ -1,43 +1,77 @@ -import { drizzle as nodeDrizzle } from "drizzle-orm/node-postgres"; +import { drizzle, NodePgDatabase } from "drizzle-orm/node-postgres"; import { Pool } from "pg"; import * as schema from "./schema"; import fs from "fs"; import path from "path"; -let connectionString = process.env.DATABASE_URL; +let pool: Pool | null = null; +let currentDb: NodePgDatabase | null = null; function maskUrl(url: string | undefined) { if (!url) return "undefined"; return url.replace(/:([^@]+)@/, ":****@"); } -console.log("[DB] Initial DATABASE_URL (env):", maskUrl(connectionString)); +/** + * Initializes or returns the current database instance. + */ +export function getDb() { + if (currentDb) return currentDb; -// On tente de lire la config sauvegardée via l'UI -try { - const CONFIG_PATH = path.join(process.cwd(), "data", ".db-config.json"); - if (fs.existsSync(CONFIG_PATH)) { - console.log("[DB] Found config file at:", CONFIG_PATH); - const config = JSON.parse(fs.readFileSync(CONFIG_PATH, "utf-8")); - if (config.url) { - connectionString = config.url; - console.log("[DB] Using saved database URL:", maskUrl(connectionString)); + let connectionString = process.env.DATABASE_URL; + + console.log("[DB] Initializing connection..."); + console.log("[DB] Env DATABASE_URL:", maskUrl(process.env.DATABASE_URL)); + + try { + const CONFIG_PATH = path.join(process.cwd(), "data", ".db-config.json"); + if (fs.existsSync(CONFIG_PATH)) { + console.log("[DB] Found config file at:", CONFIG_PATH); + const config = JSON.parse(fs.readFileSync(CONFIG_PATH, "utf-8")); + if (config.url) { + connectionString = config.url; + console.log("[DB] Using saved database URL:", maskUrl(connectionString)); + } } else { - console.warn("[DB] Config file found but no 'url' key present."); + console.log("[DB] No saved config file found. Using environment variable."); } - } else { - console.log("[DB] No saved config file found at:", CONFIG_PATH, "(Falling back to env)"); + } catch (err) { + console.error("[DB] Error reading config file:", err); } -} catch (err) { - console.error("[DB] Error reading database config file:", err); + + if (!connectionString) { + console.error("[DB] CRITICAL: No database connection string available!"); + } + + pool = new Pool({ + connectionString, + }); + + currentDb = drizzle(pool, { schema }); + return currentDb; } -if (!connectionString) { - console.error("[DB] CRITICAL: No database connection string available!"); +/** + * Resets the current database connection. + * Called when settings are updated via the UI. + */ +export function refreshDb() { + console.log("[DB] Refreshing database connection..."); + if (pool) { + pool.end().catch(err => console.error("[DB] Error closing pool:", err)); + pool = null; + } + currentDb = null; } -const pool = new Pool({ - connectionString, +/** + * Proxy for the 'db' instance. + * Allows existing imports (import { db } from "@/db") to work + * while the underlying instance can be hot-swapped. + */ +export const db = new Proxy({} as NodePgDatabase, { + get(target, prop, receiver) { + const d = getDb(); + return Reflect.get(d, prop, receiver); + } }); - -export const db = nodeDrizzle(pool, { schema }); diff --git a/src/features/settings/actions.ts b/src/features/settings/actions.ts index f8f08e0..1449af1 100644 --- a/src/features/settings/actions.ts +++ b/src/features/settings/actions.ts @@ -41,6 +41,11 @@ export async function saveDatabaseSettings(url: string) { await fs.writeFile(CONFIG_FILE, JSON.stringify(config, null, 2)); console.log("Database configuration saved to:", CONFIG_FILE); + + // Refresh the shared DB instance immediately + const { refreshDb } = await import("@/db"); + refreshDb(); + return { success: true }; } catch (error: unknown) { const errorMessage = error instanceof Error ? error.message : String(error);