This commit is contained in:
Daniel
2026-03-04 00:39:01 +08:00
parent 95e2fe1c41
commit 3264b3252a
8 changed files with 152 additions and 17 deletions

View File

@@ -236,6 +236,15 @@ function runMigrations(db) {
INSERT OR IGNORE INTO share_count (id, total) VALUES (1, 0);
`)
} catch (_) {}
try {
exec(`
CREATE TABLE IF NOT EXISTS like_count (
id INTEGER PRIMARY KEY CHECK (id = 1),
total INTEGER NOT NULL DEFAULT 0
);
INSERT OR IGNORE INTO like_count (id, total) VALUES (1, 0);
`)
} catch (_) {}
try {
exec(`
CREATE TABLE IF NOT EXISTS display_stats (

View File

@@ -32,15 +32,18 @@ app.post('/api/crawler/notify', (req, res) => {
res.json({ ok: true })
})
// 生产环境:提供前端静态文件
// 生产环境:提供前端静态文件(含修订页 /edit依赖 SPA fallback
const distPath = path.join(__dirname, '..', 'dist')
if (fs.existsSync(distPath)) {
app.use(express.static(distPath))
// 非 API/WS 的请求一律返回 index.html由前端路由处理 /、/edit、/db 等
app.get('*', (req, res, next) => {
if (!req.path.startsWith('/api') && req.path !== '/ws') {
res.sendFile(path.join(distPath, 'index.html'))
} else next()
})
} else {
console.warn('[server] dist 目录不存在,前端页面(含 /edit 修订页)不可用。请在项目根目录执行 npm run build 后再启动。')
}
const server = http.createServer(app)

View File

@@ -163,6 +163,18 @@ router.post('/share', (req, res) => {
}
})
router.post('/like', (req, res) => {
try {
db.prepare(
'INSERT INTO like_count (id, total) VALUES (1, 1) ON CONFLICT(id) DO UPDATE SET total = total + 1'
).run()
res.json(getStats())
} catch (err) {
console.error(err)
res.status(500).json({ viewers: 0, cumulative: 0, feedbackCount: 0, shareCount: 0, likeCount: 0 })
}
})
router.get('/stats', (req, res) => {
try {
res.json(getStats())
@@ -215,6 +227,10 @@ router.get('/edit/raw', (req, res) => {
"SELECT COUNT(*) as n FROM visits WHERE last_seen > datetime('now', '-2 minutes')"
).get()?.n ?? 0
const realFeedback = db.prepare('SELECT COUNT(*) as n FROM feedback').get()?.n ?? 0
let realLikeCount = 0
try {
realLikeCount = db.prepare('SELECT total FROM like_count WHERE id = 1').get()?.total ?? 0
} catch (_) {}
res.json({
combatLosses: { us: lossesUs || null, iran: lossesIr || null },
keyLocations: { us: locUs || [], iran: locIr || [] },
@@ -224,7 +240,7 @@ router.get('/edit/raw', (req, res) => {
viewers: displayStats?.viewers ?? liveViewers,
cumulative: displayStats?.cumulative ?? realCumulative,
shareCount: displayStats?.share_count ?? realShare,
likeCount: displayStats?.like_count ?? 0,
likeCount: displayStats?.like_count ?? realLikeCount,
feedbackCount: displayStats?.feedback_count ?? realFeedback,
},
})

View File

@@ -13,11 +13,15 @@ function getStats() {
const cumulativeRow = db.prepare('SELECT total FROM visitor_count WHERE id = 1').get()
const feedbackRow = db.prepare('SELECT COUNT(*) as n FROM feedback').get()
const shareRow = db.prepare('SELECT total FROM share_count WHERE id = 1').get()
let realLikeCount = 0
try {
realLikeCount = toNum(db.prepare('SELECT total FROM like_count WHERE id = 1').get()?.total)
} catch (_) {}
let viewers = toNum(viewersRow?.n)
let cumulative = toNum(cumulativeRow?.total)
let feedbackCount = toNum(feedbackRow?.n)
let shareCount = toNum(shareRow?.total)
let likeCount = 0
let likeCount = realLikeCount
let display = null
try {
display = db.prepare('SELECT viewers, cumulative, share_count, like_count, feedback_count FROM display_stats WHERE id = 1').get()
@@ -27,6 +31,7 @@ function getStats() {
if (display.cumulative != null) cumulative = toNum(display.cumulative)
if (display.share_count != null) shareCount = toNum(display.share_count)
if (display.like_count != null) likeCount = toNum(display.like_count)
else likeCount = realLikeCount
if (display.feedback_count != null) feedbackCount = toNum(display.feedback_count)
}
return { viewers, cumulative, feedbackCount, shareCount, likeCount }