From 1b9518a5d7637e3bb8c807a97e66883963c54a20 Mon Sep 17 00:00:00 2001 From: null Date: Thu, 28 May 2026 23:28:53 -0500 Subject: [PATCH] fix: migration dedup and legacy reconcile gaps - Removed double log line in runMigrations (migration name printed twice) - Added v0.54 (user_settings) and v0.55 (user_login_history device metadata) to reconcileLegacyMigrations - Both are idempotent, no data was ever lost, but legacy upgrades were re-running them unnecessarily --- db/database.js | 46 ++++++++++++++++++++++++++++++++++++++++++++-- package.json | 2 +- 2 files changed, 45 insertions(+), 3 deletions(-) diff --git a/db/database.js b/db/database.js index a077fdc..e5b1927 100644 --- a/db/database.js +++ b/db/database.js @@ -877,6 +877,50 @@ function reconcileLegacyMigrations() { console.log('[migration] user_login_history table created'); } }, + { + version: 'v0.54', + description: 'user_settings: per-user display and billing preferences', + check: function() { + return !!db.prepare("SELECT name FROM sqlite_master WHERE type='table' AND name='user_settings'").get(); + }, + run: function() { + db.exec(` + CREATE TABLE IF NOT EXISTS user_settings ( + user_id INTEGER NOT NULL REFERENCES users(id) ON DELETE CASCADE, + key TEXT NOT NULL, + value TEXT NOT NULL, + updated_at TEXT DEFAULT (datetime('now')), + PRIMARY KEY (user_id, key) + ) + `); + const userSettingKeys = ['currency', 'date_format', 'grace_period_days', 'notify_days_before']; + const users = db.prepare('SELECT id FROM users').all(); + const getCurrent = db.prepare('SELECT value FROM settings WHERE key = ?'); + const insert = db.prepare('INSERT OR IGNORE INTO user_settings (user_id, key, value) VALUES (?, ?, ?)'); + for (const user of users) { + for (const key of userSettingKeys) { + const row = getCurrent.get(key); + if (row) insert.run(user.id, key, row.value); + } + } + console.log('[migration] user_settings table ensured'); + } + }, + { + version: 'v0.55', + description: 'user_login_history: parsed device metadata and fingerprint', + check: function() { + const cols = db.prepare('PRAGMA table_info(user_login_history)').all().map(c => c.name); + return ['browser', 'os', 'device_type', 'device_fingerprint'].every(c => cols.includes(c)); + }, + run: function() { + const cols = db.prepare('PRAGMA table_info(user_login_history)').all().map(c => c.name); + for (const col of ['browser', 'os', 'device_type', 'device_fingerprint']) { + if (!cols.includes(col)) db.exec(`ALTER TABLE user_login_history ADD COLUMN ${col} TEXT`); + } + console.log('[migration] user_login_history device metadata columns ensured'); + } + }, { version: 'v0.56', description: 'bills/categories: soft-delete columns', @@ -1895,8 +1939,6 @@ function runMigrations() { if (!hasMigrationBeenApplied(migration.version)) { // Validate dependencies before applying const depCheck = validateMigrationDependencies(migration, appliedVersions); - console.log(`[migration] Applying ${migration.version}: ${migration.description}`); - if (!depCheck.valid) { console.error(`[migration-error] ${migration.version} depends on [${depCheck.missing.join(', ')}] which have not been applied. Skipping.`); continue; diff --git a/package.json b/package.json index 404241b..89d0750 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "bill-tracker", - "version": "0.30.1", + "version": "0.30.2", "description": "Monthly bill tracking system", "main": "server.js", "scripts": {