fix: update

This commit is contained in:
Daniel
2026-03-03 17:27:55 +08:00
parent 29c921f498
commit 1764a44eb3
22 changed files with 818 additions and 30 deletions

Binary file not shown.

View File

@@ -7,6 +7,8 @@ const fs = require('fs')
const dbPath = process.env.DB_PATH || path.join(__dirname, 'data.db')
let _db = null
/** sql.js 构造函数initDb 时注入,供 reloadFromFile 使用 */
let _sqlJs = null
function getDb() {
if (!_db) throw new Error('DB not initialized. Call initDb() first.')
@@ -239,6 +241,7 @@ function runMigrations(db) {
async function initDb() {
const initSqlJs = require('sql.js')
const SQL = await initSqlJs()
_sqlJs = SQL
let data = new Uint8Array(0)
if (fs.existsSync(dbPath)) {
data = new Uint8Array(fs.readFileSync(dbPath))
@@ -261,6 +264,30 @@ async function initDb() {
return _db
}
/**
* 从磁盘重新加载 DB爬虫写入同一文件后调用使 Node 内存中的库与文件一致)
*/
function reloadFromFile() {
if (!_sqlJs || !_db) throw new Error('DB not initialized. Call initDb() first.')
let data = new Uint8Array(0)
if (fs.existsSync(dbPath)) {
data = new Uint8Array(fs.readFileSync(dbPath))
}
const nativeDb = new _sqlJs.Database(data)
function persist() {
try {
const buf = nativeDb.export()
fs.writeFileSync(dbPath, Buffer.from(buf))
} catch (e) {
console.error('[db] persist error:', e.message)
}
}
nativeDb.run('PRAGMA journal_mode = WAL')
const wrapped = wrapDatabase(nativeDb, persist)
runMigrations(wrapped)
_db = wrapped
}
const proxy = {
prepare(sql) {
return getDb().prepare(sql)
@@ -276,3 +303,4 @@ const proxy = {
module.exports = proxy
module.exports.initDb = initDb
module.exports.getDb = getDb
module.exports.reloadFromFile = reloadFromFile

View File

@@ -14,6 +14,9 @@ const openApiSpec = require('./openapi')
const app = express()
const PORT = process.env.API_PORT || 3001
// 爬虫通知用的共享密钥API_CRAWLER_TOKEN仅在服务端与爬虫进程间传递
const CRAWLER_TOKEN = process.env.API_CRAWLER_TOKEN || ''
app.set('trust proxy', 1)
app.use(cors())
app.use(express.json())
@@ -23,7 +26,14 @@ app.use('/api-docs', swaggerUi.serve, swaggerUi.setup(openApiSpec))
app.use('/api', routes)
app.get('/api/health', (_, res) => res.json({ ok: true }))
app.post('/api/crawler/notify', (_, res) => {
app.post('/api/crawler/notify', (req, res) => {
// 若配置了 API_CRAWLER_TOKEN则要求爬虫携带 X-Crawler-Token 头
if (CRAWLER_TOKEN) {
const token = req.headers['x-crawler-token']
if (typeof token !== 'string' || token !== CRAWLER_TOKEN) {
return res.status(401).json({ error: 'unauthorized' })
}
}
notifyCrawlerUpdate()
res.json({ ok: true })
})
@@ -59,13 +69,18 @@ function broadcastSituation() {
app.set('broadcastSituation', broadcastSituation)
setInterval(broadcastSituation, 3000)
// 供爬虫调用:更新 situation.updated_at 并立即广播
// 供爬虫调用:先从磁盘重载 DB纳入爬虫写入更新 updated_at 并立即广播
function notifyCrawlerUpdate() {
try {
const db = require('./db')
db.reloadFromFile()
db.prepare("INSERT OR REPLACE INTO situation (id, data, updated_at) VALUES (1, '{}', ?)").run(new Date().toISOString())
broadcastSituation()
} catch (_) {}
const n = db.prepare('SELECT COUNT(*) as c FROM situation_update').get().c
console.log('[crawler/notify] DB 已重载并广播situation_update 条数:', n)
} catch (e) {
console.error('[crawler/notify]', e?.message || e)
}
}
db.initDb().then(() => {

View File

@@ -5,8 +5,22 @@ const db = require('./db')
const router = express.Router()
// 数据库 Dashboard返回各表原始数据
router.get('/db/dashboard', (req, res) => {
// 简单鉴权:通过环境变量配置的 API_ADMIN_KEY 保护敏感接口(不返回真实密钥)
const ADMIN_API_KEY = process.env.API_ADMIN_KEY || ''
function requireAdmin(req, res, next) {
if (!ADMIN_API_KEY) {
return res.status(500).json({ error: 'admin key not configured' })
}
const token = req.headers['x-api-key']
if (typeof token !== 'string' || token !== ADMIN_API_KEY) {
return res.status(401).json({ error: 'unauthorized' })
}
return next()
}
// 数据库 Dashboard返回各表原始数据需 admin 鉴权)
router.get('/db/dashboard', requireAdmin, (req, res) => {
try {
const tables = [
'feedback',
@@ -58,8 +72,14 @@ router.get('/db/dashboard', (req, res) => {
}
})
// 资讯内容(独立表,供后续消费)
// 资讯内容(独立表,供后续消费,可选 admin key若配置了 ADMIN_API_KEY 则也要求鉴权
router.get('/news', (req, res) => {
if (ADMIN_API_KEY) {
const token = req.headers['x-api-key']
if (typeof token !== 'string' || token !== ADMIN_API_KEY) {
return res.status(401).json({ error: 'unauthorized' })
}
}
try {
const limit = Math.min(parseInt(req.query.limit, 10) || 50, 200)
const rows = db.prepare('SELECT id, title, summary, url, source, published_at, category, severity, created_at FROM news_content ORDER BY published_at DESC LIMIT ?').all(limit)