Files
hometown/server/db.js
2026-03-07 19:37:38 +08:00

160 lines
4.5 KiB
JavaScript
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
/**
* SQLite统计、留言、后端配置弹幕开关与位置
*/
const Database = require('better-sqlite3');
const path = require('path');
const DB_PATH = path.join(__dirname, 'data', 'pano.db');
function getDb() {
const db = new Database(DB_PATH);
db.pragma('journal_mode = WAL');
return db;
}
function initDb() {
const fs = require('fs');
const dir = path.join(__dirname, 'data');
if (!fs.existsSync(dir)) fs.mkdirSync(dir, { recursive: true });
const db = getDb();
db.exec(`
CREATE TABLE IF NOT EXISTS stats (
id INTEGER PRIMARY KEY CHECK (id = 1),
view_count INTEGER NOT NULL DEFAULT 0,
like_count INTEGER NOT NULL DEFAULT 0,
share_count INTEGER NOT NULL DEFAULT 0,
watching_now INTEGER NOT NULL DEFAULT 0
);
INSERT OR IGNORE INTO stats (id, view_count, like_count, share_count, watching_now) VALUES (1, 0, 0, 0, 0);
CREATE TABLE IF NOT EXISTS viewers (
viewer_id TEXT PRIMARY KEY,
updated_at INTEGER NOT NULL
);
CREATE TABLE IF NOT EXISTS comments (
id INTEGER PRIMARY KEY AUTOINCREMENT,
content TEXT NOT NULL,
nickname TEXT,
created_at INTEGER NOT NULL
);
CREATE TABLE IF NOT EXISTS settings (
key TEXT PRIMARY KEY,
value TEXT NOT NULL
);
INSERT OR IGNORE INTO settings (key, value) VALUES ('danmaku_enabled', '0');
INSERT OR IGNORE INTO settings (key, value) VALUES ('danmaku_position', 'top');
`);
db.close();
}
function getConfig() {
const db = getDb();
const rows = db.prepare('SELECT key, value FROM settings').all();
db.close();
const map = {};
rows.forEach((r) => { map[r.key] = r.value; });
return {
danmakuEnabled: map.danmaku_enabled === '1',
danmakuPosition: (map.danmaku_position || 'top').toLowerCase(),
};
}
function getStats() {
const db = getDb();
const row = db.prepare('SELECT view_count, like_count, share_count, watching_now FROM stats WHERE id = 1').get();
const commentRow = db.prepare('SELECT COUNT(*) as n FROM comments').get();
db.close();
return {
viewCount: row.view_count,
commentCount: commentRow.n,
likeCount: row.like_count,
shareCount: row.share_count,
watchingNow: row.watching_now,
};
}
function incView() {
const db = getDb();
db.prepare('UPDATE stats SET view_count = view_count + 1 WHERE id = 1').run();
const out = getStats();
db.close();
return out;
}
function incLike() {
const db = getDb();
db.prepare('UPDATE stats SET like_count = like_count + 1 WHERE id = 1').run();
const row = db.prepare('SELECT like_count FROM stats WHERE id = 1').get();
db.close();
return { likeCount: row.like_count };
}
function incShare() {
const db = getDb();
db.prepare('UPDATE stats SET share_count = share_count + 1 WHERE id = 1').run();
const row = db.prepare('SELECT share_count FROM stats WHERE id = 1').get();
db.close();
return { shareCount: row.share_count };
}
function joinViewer(viewerId) {
const db = getDb();
const now = Date.now();
db.prepare('INSERT OR REPLACE INTO viewers (viewer_id, updated_at) VALUES (?, ?)').run(viewerId, now);
db.prepare('DELETE FROM viewers WHERE updated_at < ?').run(now - 120000);
const row = db.prepare('SELECT COUNT(*) as n FROM viewers').get();
db.prepare('UPDATE stats SET watching_now = ? WHERE id = 1').run(row.n);
db.close();
return row.n;
}
function leaveViewer(viewerId) {
const db = getDb();
db.prepare('DELETE FROM viewers WHERE viewer_id = ?').run(viewerId);
const row = db.prepare('SELECT COUNT(*) as n FROM viewers').get();
db.prepare('UPDATE stats SET watching_now = ? WHERE id = 1').run(row.n);
db.close();
return row.n;
}
function getComments(limit = 100) {
const db = getDb();
const rows = db.prepare(
'SELECT id, content, nickname, created_at FROM comments ORDER BY id DESC LIMIT ?'
).all(limit);
db.close();
return rows.reverse().map((r) => ({
id: r.id,
content: r.content,
nickname: r.nickname || '游客',
createdAt: r.created_at,
}));
}
function addComment(content, nickname) {
const db = getDb();
const now = Date.now();
const r = db.prepare('INSERT INTO comments (content, nickname, created_at) VALUES (?, ?, ?)').run(
String(content).trim().slice(0, 200) || '(空)',
nickname ? String(nickname).trim().slice(0, 32) : null,
now
);
db.close();
return { id: r.lastInsertRowid, content: content.trim().slice(0, 200), nickname: nickname || '游客', createdAt: now };
}
module.exports = {
initDb,
getConfig,
getStats,
incView,
incLike,
incShare,
joinViewer,
leaveViewer,
getComments,
addComment,
};