import sqlite3 from 'sqlite3'; import { open, Database } from 'sqlite'; let db: Database | null = null; export async function getDb() { if (db) return db; // Enable verbose mode for debugging sqlite3.verbose(); const dbPath = process.env.DB_PATH || './dashboard.db'; db = await open({ filename: dbPath, driver: sqlite3.Database }); await db.exec(` CREATE TABLE IF NOT EXISTS uptime_logs ( id INTEGER PRIMARY KEY AUTOINCREMENT, service_name TEXT NOT NULL, url TEXT NOT NULL, status TEXT NOT NULL, latency INTEGER, timestamp DATETIME DEFAULT CURRENT_TIMESTAMP ); CREATE TABLE IF NOT EXISTS visitors ( id INTEGER PRIMARY KEY AUTOINCREMENT, ip_hash TEXT, city TEXT, country TEXT, lat REAL, lon REAL, timestamp DATETIME DEFAULT CURRENT_TIMESTAMP ); CREATE TABLE IF NOT EXISTS monitored_services ( id INTEGER PRIMARY KEY AUTOINCREMENT, name TEXT NOT NULL UNIQUE, url TEXT NOT NULL, created_at DATETIME DEFAULT CURRENT_TIMESTAMP ); CREATE INDEX IF NOT EXISTS idx_uptime_logs_service_timestamp ON uptime_logs(service_name, timestamp); CREATE TABLE IF NOT EXISTS rss_feeds ( id INTEGER PRIMARY KEY AUTOINCREMENT, name TEXT NOT NULL, url TEXT NOT NULL UNIQUE, last_fetched DATETIME, created_at DATETIME DEFAULT CURRENT_TIMESTAMP ); CREATE TABLE IF NOT EXISTS rss_items ( id INTEGER PRIMARY KEY AUTOINCREMENT, feed_id INTEGER NOT NULL, title TEXT NOT NULL, link TEXT NOT NULL UNIQUE, pub_date DATETIME, creator TEXT, snippet TEXT, read INTEGER DEFAULT 0, bookmarked INTEGER DEFAULT 0, created_at DATETIME DEFAULT CURRENT_TIMESTAMP ); CREATE INDEX IF NOT EXISTS idx_rss_items_feed ON rss_items(feed_id); CREATE INDEX IF NOT EXISTS idx_rss_items_pubdate ON rss_items(pub_date DESC); `); // Seed default HN feed if rss_feeds is empty const feedCount = await db.get('SELECT COUNT(*) as cnt FROM rss_feeds'); if (feedCount?.cnt === 0) { await db.run( 'INSERT INTO rss_feeds (name, url) VALUES (?, ?)', 'Hacker News', 'https://news.ycombinator.com/rss' ); } return db; }