import { Low } from 'lowdb'; import { JSONFile } from 'lowdb/node'; import { mkdirSync } from 'fs'; import { dirname } from 'path'; const DB_PATH = process.env.DB_PATH || './data/budget.json'; const DEFAULT = { categories: [ { id: 1, name: 'Kivra', sort_order: 1, color: '#7C3AED' }, { id: 2, name: 'eFaktura', sort_order: 2, color: '#2563EB' }, { id: 3, name: 'Manuella', sort_order: 3, color: '#D97706' }, { id: 4, name: 'Autogiro', sort_order: 4, color: '#059669' }, ], months: [], income: [], bills: [], subscriptions: [], loans: [], payments: [], banking: { enable_banking: { enabled: false, status: 'not_connected', institution: '', last_sync_at: null, notes: '', application_id: '', private_key_pem: '', redirect_url: '', country: 'SE', psu_type: 'personal', auto_sync_enabled: false, sync_interval_minutes: 360, import_from_date: null, incremental_sync_days: 7, pending_state: null, last_callback_at: null, last_callback_url: '', last_callback_code_present: false, last_callback_state: '', last_callback_exchange_at: null, last_callback_exchange_error: '', session_id: null, session_expires_at: null, session_created_at: null, account_aliases: {}, transaction_categories: {}, accounts: [], transactions: [], last_sync_summary: null, last_error: null, }, }, auth: { settings: { enabled: false, issuer: '', discovery_url: '', client_id: '', client_secret: '', redirect_uri: '', scope: 'openid profile email groups', session_secret: '', allowed_groups: '', allowed_emails: '', allowed_domains: '', session_ttl_hours: 12, }, sessions: [], pending: [], }, ai: { ollama: { enabled: false, base_url: 'http://host.docker.internal:11434', model: '', vision_model: '', system_prompt: '', include_budget_context: true, include_banking_context: true, }, conversations: [], }, app_settings: { finance_profile: { salary_day_of_month: 25, buffer_days_target: 7, salary_account_uid: '', recurring_income_note: '', }, account_view: { primary_account_uid: '', include_savings_in_ai: false, hide_zero_balance_accounts: false, }, notifications: { ntfy_enabled: false, ntfy_base_url: 'https://ntfy.sh', ntfy_topic: '', ntfy_access_token: '', ntfy_title: 'Enkelbudget', ntfy_tags: 'money_with_wings,bank', ntfy_click_url: '', ntfy_priority: 3, notify_new_transactions: true, include_pending_transactions: false, minimum_transaction_amount: 0, last_error: '', last_sent_at: null, }, }, _seq: { categories: 4, months: 0, income: 0, bills: 0, subscriptions: 0, loans: 0, payments: 0 }, }; let _db = null; export async function getDb() { if (!_db) { mkdirSync(dirname(DB_PATH), { recursive: true }); const adapter = new JSONFile(DB_PATH); _db = new Low(adapter, DEFAULT); await _db.read(); _db.data ||= structuredClone(DEFAULT); _db.data.categories ||= structuredClone(DEFAULT.categories); _db.data.months ||= []; _db.data.income ||= []; _db.data.bills ||= []; _db.data.subscriptions ||= []; _db.data.loans ||= []; _db.data.payments ||= []; _db.data.banking ||= structuredClone(DEFAULT.banking); _db.data.banking.enable_banking ||= structuredClone(DEFAULT.banking.enable_banking); _db.data.auth ||= structuredClone(DEFAULT.auth); _db.data.auth.settings ||= structuredClone(DEFAULT.auth.settings); _db.data.auth.sessions ||= []; _db.data.auth.pending ||= []; _db.data.ai ||= structuredClone(DEFAULT.ai); _db.data.ai.ollama ||= structuredClone(DEFAULT.ai.ollama); _db.data.ai.conversations ||= []; _db.data.app_settings ||= structuredClone(DEFAULT.app_settings); _db.data.app_settings.finance_profile ||= structuredClone(DEFAULT.app_settings.finance_profile); _db.data.app_settings.account_view ||= structuredClone(DEFAULT.app_settings.account_view); _db.data.app_settings.notifications ||= structuredClone(DEFAULT.app_settings.notifications); _db.data._seq ||= {}; for (const [key, value] of Object.entries(DEFAULT._seq)) { _db.data._seq[key] ??= value; } } return _db; } export function nextId(db, table) { db.data._seq[table] = (db.data._seq[table] ?? 0) + 1; return db.data._seq[table]; }