Initial commit

This commit is contained in:
michaelswanson
2026-06-25 19:58:40 +00:00
commit 860d5f55cc
47 changed files with 19216 additions and 0 deletions
Executable
+156
View File
@@ -0,0 +1,156 @@
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];
}