feat: centralizar resolución de DB y reexport en web

Co-authored-by: aider (openrouter/openai/gpt-5) <aider@aider.chat>
main
brobert 1 month ago
parent 6196dbadc9
commit 2669d4287c

@ -1,4 +1,3 @@
import { join, resolve } from 'path';
// Carga compatible del entorno: en SvelteKit usa $env/dynamic/private;
// en tests/ejecución fuera de SvelteKit cae a process.env.
@ -10,21 +9,7 @@ try {
env = process.env as any;
}
/**
* Resuelve la ruta absoluta al archivo de la base de datos SQLite compartida.
* Prioridad:
* 1) DB_PATH (ruta completa al archivo)
* 2) DATA_DIR + filename (en prod por defecto /app/data; en dev por defecto ./tmp)
*/
export function resolveDbAbsolutePath(filename: string = 'tasks.db'): string {
const dbPathEnv = (env.DB_PATH || '').trim();
if (dbPathEnv) {
return resolve(dbPathEnv);
}
const isProdEnv = String(env.NODE_ENV || 'development').trim().toLowerCase() === 'production';
const dataDir = env.DATA_DIR ? String(env.DATA_DIR) : (isProdEnv ? '/app/data' : 'tmp');
return resolve(join(dataDir, filename));
}
export { resolveDbAbsolutePath } from '../../../../../src/env/db-path';
export const WEB_BASE_URL = (env.WEB_BASE_URL || '').trim();
export const COOKIE_SECRET = (env.COOKIE_SECRET || '').trim();

@ -1,5 +1,11 @@
import { sveltekit } from '@sveltejs/kit/vite';
import { defineConfig } from 'vite';
import { fileURLToPath } from 'node:url';
import { dirname, resolve as resolvePath } from 'node:path';
const __filename = fileURLToPath(import.meta.url);
const __dirname = dirname(__filename);
const repoRoot = resolvePath(__dirname, '..', '..');
export default defineConfig(({ mode }) => {
const isDev = mode === 'development';
@ -19,7 +25,7 @@ export default defineConfig(({ mode }) => {
// Evitar prebundling de drivers nativos
exclude: ['bun:sqlite', 'better-sqlite3']
},
// Permitir host remoto en desarrollo
server: isDev ? { allowedHosts: ['server.brobert.net'] } : undefined
// Permitir host remoto en desarrollo y permitir importar fuera del root (reexport desde core)
server: isDev ? { allowedHosts: ['server.brobert.net'], fs: { allow: [repoRoot] } } : undefined
};
});

@ -1,9 +1,10 @@
import { Database } from 'bun:sqlite';
import { normalizeWhatsAppId } from './utils/whatsapp';
import { mkdirSync } from 'fs';
import { join, resolve, dirname } from 'path';
import { dirname } from 'path';
import { Migrator } from './db/migrator';
import { migrations } from './db/migrations';
import { resolveDbAbsolutePath } from './env/db-path';
function applyDefaultPragmas(instance: Database): void {
try {
@ -19,38 +20,19 @@ function applyDefaultPragmas(instance: Database): void {
}
}
// Function to get a database instance. Defaults to 'data/tasks.db'
// Function to get a database instance. Defaults to 'tmp/tasks.db' in dev and '/app/data/tasks.db' in prod (overridable via DB_PATH/DATA_DIR)
export function getDb(filename: string = 'tasks.db'): Database {
// Prioridad 1: DB_PATH (ruta completa al archivo). Si está definida, se usa tal cual.
const dbPathEnv = process?.env?.DB_PATH ? String(process.env.DB_PATH).trim() : '';
if (dbPathEnv) {
const absolutePath = resolve(dbPathEnv);
// Crear directorio padre si no existe
try {
mkdirSync(dirname(absolutePath), { recursive: true });
} catch (err) {
const code = (err && typeof err === 'object') ? (err as { code?: string }).code : undefined;
if (code !== 'EEXIST') throw err; // Solo ignorar "ya existe"
}
const instance = new Database(absolutePath);
applyDefaultPragmas(instance);
return instance;
}
// Prioridad 2: DATA_DIR + filename (comportamiento actual)
// Determine base directory for the database (env DATA_DIR or default './data'), resolve to absolute
const dataDir = process?.env?.DATA_DIR ? String(process.env.DATA_DIR) : 'data';
const dirPath = resolve(dataDir);
const absolutePath = resolveDbAbsolutePath(filename);
// Try to create data directory if it doesn't exist (ignore if already exists)
// Asegurar directorio padre
try {
mkdirSync(dirPath, { recursive: true });
mkdirSync(dirname(absolutePath), { recursive: true });
} catch (err) {
const code = (err && typeof err === 'object') ? (err as { code?: string }).code : undefined;
if (code !== 'EEXIST') throw err; // Only ignore "already exists" errors
if (code !== 'EEXIST') throw err;
}
const instance = new Database(join(dirPath, filename));
const instance = new Database(absolutePath);
applyDefaultPragmas(instance);
return instance;
}

18
src/env/db-path.ts vendored

@ -0,0 +1,18 @@
import { join, resolve } from 'path';
/**
* Resuelve la ruta absoluta al archivo de la base de datos SQLite compartida.
* Prioridad:
* 1) DB_PATH (ruta completa al archivo)
* 2) DATA_DIR + filename
* - En producción (NODE_ENV=production) por defecto '/app/data'
* - En no-producción por defecto './tmp'
*/
export function resolveDbAbsolutePath(filename: string = 'tasks.db'): string {
const dbPathEnv = String(process.env.DB_PATH || '').trim();
if (dbPathEnv) return resolve(dbPathEnv);
const isProdEnv = String(process.env.NODE_ENV || 'development').trim().toLowerCase() === 'production';
const dataDir = process.env.DATA_DIR ? String(process.env.DATA_DIR) : (isProdEnv ? '/app/data' : 'tmp');
return resolve(join(dataDir, filename));
}

@ -18,6 +18,7 @@
"src/tasks/**/*.ts",
"src/tasks/**/*.d.ts",
"src/services/**/*.ts",
"src/env/**/*.ts",
"src/types/**/*.d.ts",
"proxy.ts"
],

Loading…
Cancel
Save