feat: 重构服务支持双模式并接入 MongoDB 数据库

- 新增 MongoDB 存储层实现英雄、预设和版本数据的持久化
- 新增 push-meta 和 migrate-to-mongo 脚本支持数据同步迁移
- 支持开发版和线上设计版双模式运行
- 新增版本闸门机制,线上设计版落后时进入只读模式
- 优化前端 API 配置适配不同部署环境
- 新增线上设计版静态资源托管和路由回退
This commit is contained in:
panFD
2026-10-03 12:02:47 +08:00
parent 1bb275607a
commit 9c9cc99e64
14 changed files with 656 additions and 48 deletions

View File

@@ -0,0 +1,35 @@
/**
* 一次性迁移:把本地 data/heroes.json、data/templates.json 导入线上 MongoDB
* 用法:配置 MONGO_URL 后执行 node scripts/migrate-to-mongo.js
* 已在库中的数据会按 uuid 幂等覆盖,可重复执行。
*/
import fs from 'node:fs'
import path from 'node:path'
import url from 'node:url'
import { saveHeroes, saveTemplates, getHeroes } from '../db.js'
const __dirname = path.dirname(url.fileURLToPath(import.meta.url))
const DATA_DIR = path.join(__dirname, '..', 'data')
const readJson = (file, fallback) => {
const p = path.join(DATA_DIR, file)
return fs.existsSync(p) ? JSON.parse(fs.readFileSync(p, 'utf8')) : fallback
}
const heroes = readJson('heroes.json', { heroes: [] }).heroes || []
const tpl = readJson('templates.json', { triggerPresets: [], fieldPresets: [] })
if (heroes.length) {
await saveHeroes(heroes)
console.log(`[migrate] heroes 导入 ${heroes.length} 条`)
} else {
console.log('[migrate] 无本地 heroes.json 或为空,跳过英雄导入')
}
if ((tpl.triggerPresets?.length || 0) + (tpl.fieldPresets?.length || 0) > 0) {
await saveTemplates(tpl.triggerPresets || [], tpl.fieldPresets || [])
console.log(`[migrate] templates 导入 trigger=${tpl.triggerPresets?.length || 0} field=${tpl.fieldPresets?.length || 0}`)
} else {
console.log('[migrate] 无本地 templates.json 或为空,跳过预设导入')
}
console.log(`[migrate] 库中现有英雄 ${(await getHeroes()).length} 条`)
process.exit(0)

View File

@@ -0,0 +1,29 @@
/**
* 推送 meta 快照 + 结构版本到线上(项目开发版使用)
* 用法:先启动本地 dev 服务,再执行 npm run push-meta
* 产物:server/data/meta.json —— 需随线上设计版一起部署(git 提交或拷贝到 VPS)
*/
import fs from 'node:fs'
import path from 'node:path'
import url from 'node:url'
import { bumpSchemaVersion } from '../db.js'
const __dirname = path.dirname(url.fileURLToPath(import.meta.url))
const SNAPSHOT_PATH = path.join(__dirname, '..', 'data', 'meta.json')
const LOCAL_API = process.env.LOCAL_API || 'http://localhost:3001'
const SCHEMA_VERSION = Number(process.env.SCHEMA_VERSION || 1)
const res = await fetch(`${LOCAL_API}/api/meta-snapshot`)
if (!res.ok) {
const body = await res.text().catch(() => '')
throw new Error(`拉取 meta 快照失败(请确认本地 dev 服务已启动): ${res.status} ${body}`)
}
const snapshot = await res.json()
fs.mkdirSync(path.dirname(SNAPSHOT_PATH), { recursive: true })
fs.writeFileSync(SNAPSHOT_PATH, JSON.stringify(snapshot), 'utf8')
const dbVersion = await bumpSchemaVersion(SCHEMA_VERSION)
console.log(`[push-meta] meta.json 已生成(${Buffer.byteLength(JSON.stringify(snapshot))} bytes)`)
console.log(`[push-meta] 数据库结构版本: v${dbVersion}(本服务 v${SCHEMA_VERSION})`)
console.log('[push-meta] 请将 meta.json 与最新代码一起部署到线上设计版')
process.exit(0)