const fs = require('fs'); const path = require('path'); const crypto = require('crypto'); const { DatabaseSync } = require('node:sqlite'); const DB_PATH = process.env.DB_PATH || path.join(__dirname, 'data', 'app.db'); fs.mkdirSync(path.dirname(DB_PATH), { recursive: true }); const db = new DatabaseSync(DB_PATH); db.exec(` CREATE TABLE IF NOT EXISTS streamers ( id INTEGER PRIMARY KEY AUTOINCREMENT, google_id TEXT UNIQUE NOT NULL, email TEXT NOT NULL, name TEXT NOT NULL, picture TEXT, overlay_token TEXT UNIQUE NOT NULL, sender_token TEXT UNIQUE NOT NULL, tts_voice TEXT NOT NULL DEFAULT 'ru-RU-DmitryNeural', display_duration_seconds INTEGER NOT NULL DEFAULT 10, created_at INTEGER NOT NULL ); CREATE TABLE IF NOT EXISTS viewer_names ( streamer_id INTEGER NOT NULL, google_id TEXT NOT NULL, display_name TEXT NOT NULL, updated_at INTEGER NOT NULL, PRIMARY KEY (streamer_id, google_id) ); `); // Idempotent migration for databases created before display_duration_seconds existed. const existingColumns = db.prepare('PRAGMA table_info(streamers)').all().map((c) => c.name); if (!existingColumns.includes('display_duration_seconds')) { db.exec('ALTER TABLE streamers ADD COLUMN display_duration_seconds INTEGER NOT NULL DEFAULT 10'); } function generateToken() { return crypto.randomBytes(16).toString('hex'); } function findStreamerByGoogleId(googleId) { return db.prepare('SELECT * FROM streamers WHERE google_id = ?').get(googleId); } function findStreamerByOverlayToken(token) { return db.prepare('SELECT * FROM streamers WHERE overlay_token = ?').get(token); } function findStreamerBySenderToken(token) { return db.prepare('SELECT * FROM streamers WHERE sender_token = ?').get(token); } function createStreamer({ googleId, email, name, picture }) { const overlayToken = generateToken(); const senderToken = generateToken(); db.prepare( `INSERT INTO streamers (google_id, email, name, picture, overlay_token, sender_token, created_at) VALUES (?, ?, ?, ?, ?, ?, ?)` ).run(googleId, email, name, picture || null, overlayToken, senderToken, Date.now()); return findStreamerByGoogleId(googleId); } function getOrCreateStreamerByGoogle({ googleId, email, name, picture }) { return findStreamerByGoogleId(googleId) || createStreamer({ googleId, email, name, picture }); } function getViewerName(streamerId, googleId) { const row = db .prepare('SELECT display_name FROM viewer_names WHERE streamer_id = ? AND google_id = ?') .get(streamerId, googleId); return row ? row.display_name : null; } function setViewerName(streamerId, googleId, displayName) { db.prepare( `INSERT INTO viewer_names (streamer_id, google_id, display_name, updated_at) VALUES (?, ?, ?, ?) ON CONFLICT(streamer_id, google_id) DO UPDATE SET display_name = excluded.display_name, updated_at = excluded.updated_at` ).run(streamerId, googleId, displayName, Date.now()); } function updateDisplayDuration(streamerId, seconds) { db.prepare('UPDATE streamers SET display_duration_seconds = ? WHERE id = ?').run(seconds, streamerId); } module.exports = { findStreamerByOverlayToken, findStreamerBySenderToken, getOrCreateStreamerByGoogle, getViewerName, setViewerName, updateDisplayDuration, };