/** * 后端 API 服务(可单独部署)。仅提供 /api/* 接口,不提供静态资源。 * 弹幕开关与位置由 GET /api/config 返回(默认不显示弹幕)。 */ const express = require('express'); const db = require('./db.js'); db.initDb(); const app = express(); app.use(express.json()); const corsOrigin = process.env.CORS_ORIGIN || '*'; app.use((req, res, next) => { res.setHeader('Access-Control-Allow-Origin', corsOrigin); res.setHeader('Access-Control-Allow-Methods', 'GET, POST, OPTIONS'); res.setHeader('Access-Control-Allow-Headers', 'Content-Type'); if (req.method === 'OPTIONS') return res.sendStatus(204); next(); }); app.get('/config', (req, res) => { try { res.json(db.getConfig()); } catch (e) { res.status(500).json({ error: String(e.message) }); } }); app.get('/stats', (req, res) => { try { res.json(db.getStats()); } catch (e) { res.status(500).json({ error: String(e.message) }); } }); app.post('/view', (req, res) => { try { res.json(db.incView()); } catch (e) { res.status(500).json({ error: String(e.message) }); } }); app.post('/like', (req, res) => { try { res.json(db.incLike()); } catch (e) { res.status(500).json({ error: String(e.message) }); } }); app.post('/share', (req, res) => { try { res.json(db.incShare()); } catch (e) { res.status(500).json({ error: String(e.message) }); } }); app.post('/join', (req, res) => { try { const viewerId = req.body && req.body.viewerId ? String(req.body.viewerId) : null; if (!viewerId) return res.status(400).json({ error: 'viewerId required' }); const watchingNow = db.joinViewer(viewerId); res.json({ watchingNow }); } catch (e) { res.status(500).json({ error: String(e.message) }); } }); app.post('/leave', (req, res) => { try { const viewerId = req.body && req.body.viewerId ? String(req.body.viewerId) : null; if (!viewerId) return res.status(400).json({ error: 'viewerId required' }); const watchingNow = db.leaveViewer(viewerId); res.json({ watchingNow }); } catch (e) { res.status(500).json({ error: String(e.message) }); } }); app.get('/comments', (req, res) => { try { const limit = Math.min(parseInt(req.query.limit, 10) || 100, 200); res.json(db.getComments(limit)); } catch (e) { res.status(500).json({ error: String(e.message) }); } }); app.post('/comments', (req, res) => { try { const content = req.body && req.body.content != null ? String(req.body.content) : ''; const nickname = req.body && req.body.nickname != null ? String(req.body.nickname) : ''; if (!content.trim()) return res.status(400).json({ error: 'content required' }); const comment = db.addComment(content.trim(), nickname.trim() || null); res.json(comment); } catch (e) { res.status(500).json({ error: String(e.message) }); } }); module.exports = app;