2026-07-01 15:53:07 +00:00
|
|
|
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',
|
2026-07-01 16:56:49 +00:00
|
|
|
display_duration_seconds INTEGER NOT NULL DEFAULT 10,
|
2026-07-01 15:53:07 +00:00
|
|
|
created_at INTEGER NOT NULL
|
|
|
|
|
);
|
2026-07-01 16:49:51 +00:00
|
|
|
|
|
|
|
|
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)
|
|
|
|
|
);
|
2026-07-01 15:53:07 +00:00
|
|
|
`);
|
|
|
|
|
|
2026-07-01 16:56:49 +00:00
|
|
|
// 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');
|
|
|
|
|
}
|
|
|
|
|
|
2026-07-01 15:53:07 +00:00
|
|
|
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 });
|
|
|
|
|
}
|
|
|
|
|
|
2026-07-01 16:49:51 +00:00
|
|
|
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());
|
|
|
|
|
}
|
|
|
|
|
|
2026-07-01 16:56:49 +00:00
|
|
|
function updateDisplayDuration(streamerId, seconds) {
|
|
|
|
|
db.prepare('UPDATE streamers SET display_duration_seconds = ? WHERE id = ?').run(seconds, streamerId);
|
|
|
|
|
}
|
|
|
|
|
|
2026-07-01 15:53:07 +00:00
|
|
|
module.exports = {
|
|
|
|
|
findStreamerByOverlayToken,
|
|
|
|
|
findStreamerBySenderToken,
|
|
|
|
|
getOrCreateStreamerByGoogle,
|
2026-07-01 16:49:51 +00:00
|
|
|
getViewerName,
|
|
|
|
|
setViewerName,
|
2026-07-01 16:56:49 +00:00
|
|
|
updateDisplayDuration,
|
2026-07-01 15:53:07 +00:00
|
|
|
};
|