import Database from 'better-sqlite3'; import { fileURLToPath } from 'url'; import { dirname, join } from 'path'; import { existsSync, mkdirSync } from 'fs'; const __filename = fileURLToPath(import.meta.url); const __dirname = dirname(__filename); /** * Initialize the SQLite database with all required tables * @param {string} dbPath - Path to the SQLite database file * @returns {Database} - The initialized database instance */ export function initDatabase(dbPath) { // Ensure data directory exists const dataDir = dirname(dbPath); if (!existsSync(dataDir)) { mkdirSync(dataDir, { recursive: true }); } const db = new Database(dbPath); // Enable WAL mode for better concurrent performance db.pragma('journal_mode = WAL'); // Create tables // API keys for agent authentication db.exec(` CREATE TABLE IF NOT EXISTS api_keys ( id INTEGER PRIMARY KEY AUTOINCREMENT, key TEXT UNIQUE NOT NULL, name TEXT NOT NULL, device_prefix TEXT NOT NULL, created_at DATETIME DEFAULT CURRENT_TIMESTAMP, last_used_at DATETIME ); `); // --- MIGRATION: Drop old tables if they exist --- // User requested deleting old sensor data but keeping keys. db.exec(` DROP TABLE IF EXISTS sensor_data; DROP TABLE IF EXISTS sensor_data_10m; DROP TABLE IF EXISTS sensor_data_1h; `); // --- NEW SCHEMA: Sensor Events with RLE support --- db.exec(` CREATE TABLE IF NOT EXISTS sensor_events ( id INTEGER PRIMARY KEY AUTOINCREMENT, timestamp DATETIME NOT NULL, until DATETIME, -- NULL if point, Time if duplicated range end device TEXT NOT NULL, channel TEXT NOT NULL, value REAL, -- Nullable data TEXT, -- Nullable (JSON) data_type TEXT NOT NULL -- 'number' or 'json' ); CREATE INDEX IF NOT EXISTS idx_sensor_events_search ON sensor_events(device, channel, timestamp); -- Phase 2: Authentication & Views CREATE TABLE IF NOT EXISTS users ( id INTEGER PRIMARY KEY AUTOINCREMENT, username TEXT UNIQUE NOT NULL, password_hash TEXT NOT NULL, role TEXT NOT NULL CHECK(role IN ('admin', 'normal')), created_at DATETIME DEFAULT CURRENT_TIMESTAMP ); CREATE TABLE IF NOT EXISTS views ( id INTEGER PRIMARY KEY AUTOINCREMENT, name TEXT UNIQUE NOT NULL, config TEXT NOT NULL, -- JSON string of view configuration created_by INTEGER, created_at DATETIME DEFAULT CURRENT_TIMESTAMP, FOREIGN KEY(created_by) REFERENCES users(id) ); `); console.log('[DB] Database initialized successfully'); return db; } export default { initDatabase };