'use strict'; const Database = require('better-sqlite3'); const path = require('path'); let _db; function getDb() { if (!_db) { _db = new Database(path.join(__dirname, '..', 'data', 'app.db')); _db.pragma('journal_mode = WAL'); _db.pragma('foreign_keys = ON'); } return _db; } function initDb() { const db = getDb(); db.exec(` CREATE TABLE IF NOT EXISTS users ( id INTEGER PRIMARY KEY AUTOINCREMENT, username TEXT UNIQUE NOT NULL COLLATE NOCASE, password_hash TEXT NOT NULL, name TEXT NOT NULL, role TEXT NOT NULL CHECK(role IN ('superduperadmin','admin','tech','client')), active INTEGER NOT NULL DEFAULT 1, created_at TEXT NOT NULL DEFAULT (datetime('now')), updated_at TEXT NOT NULL DEFAULT (datetime('now')) ); CREATE TABLE IF NOT EXISTS sessions ( sid TEXT PRIMARY KEY NOT NULL, sess TEXT NOT NULL, expired INTEGER NOT NULL ); `); // Migrations const cols = db.prepare('PRAGMA table_info(users)').all().map(c => c.name); if (!cols.includes('company')) { db.exec("ALTER TABLE users ADD COLUMN company TEXT NOT NULL DEFAULT ''"); } if (!cols.includes('syncro_customer_id')) { db.exec('ALTER TABLE users ADD COLUMN syncro_customer_id INTEGER DEFAULT NULL'); } // Label queue migrations const lqCols = db.prepare('PRAGMA table_info(label_queue)').all().map(c => c.name); if (lqCols.length && !lqCols.includes('custom_line')) { db.exec("ALTER TABLE label_queue ADD COLUMN custom_line TEXT DEFAULT NULL"); } // Label Center queue — cross-device label batching db.exec(` CREATE TABLE IF NOT EXISTS label_queue ( id INTEGER PRIMARY KEY AUTOINCREMENT, user_id INTEGER NOT NULL REFERENCES users(id) ON DELETE CASCADE, asset_id INTEGER NOT NULL, asset_name TEXT NOT NULL, asset_serial TEXT, customer_name TEXT NOT NULL, customer_phone TEXT, custom_line TEXT, sheet_position INTEGER DEFAULT NULL, added_at TEXT NOT NULL DEFAULT (datetime('now')), UNIQUE(user_id, asset_id), UNIQUE(user_id, sheet_position) ); CREATE INDEX IF NOT EXISTS idx_label_queue_user ON label_queue(user_id); `); } // ── User queries ────────────────────────────────────────────────────────────── function getUserByUsername(username) { return getDb() .prepare('SELECT * FROM users WHERE username = ? AND active = 1') .get(username); } function getUserById(id) { return getDb() .prepare('SELECT id, username, name, company, syncro_customer_id, role, active, created_at FROM users WHERE id = ?') .get(id); } function getAllUsers() { return getDb() .prepare('SELECT id, username, name, company, syncro_customer_id, role, active, created_at FROM users ORDER BY company COLLATE NOCASE, username COLLATE NOCASE') .all(); } function createUser(username, passwordHash, name, role, company = '', syncroCustomerId = null) { return getDb() .prepare('INSERT INTO users (username, password_hash, name, role, company, syncro_customer_id) VALUES (?, ?, ?, ?, ?, ?)') .run(username, passwordHash, name, role, company, syncroCustomerId); } function updateUser(id, fields) { const allowed = ['name', 'company', 'syncro_customer_id', 'role', 'active', 'password_hash']; const filtered = Object.fromEntries( Object.entries(fields).filter(([k]) => allowed.includes(k)) ); if (!Object.keys(filtered).length) return; const sets = Object.keys(filtered).map(k => `${k} = ?`).join(', '); return getDb() .prepare(`UPDATE users SET ${sets}, updated_at = datetime('now') WHERE id = ?`) .run(...Object.values(filtered), id); } module.exports = { initDb, getDb, getUserByUsername, getUserById, getAllUsers, createUser, updateUser };