starraid/star-raid/server/db.js
JOJO 9edc366cf9 图鉴测试模式+巡飞弹新武器+存档云同步+机库合并+默认无尽
①图鉴隐藏测试模式:连点标题'军'字10次开关,纯内存标志位全部军械可见可演示,不动SAVE.codex(persist/submitRun读它,动了会污染后端),刷新失效
②24件主武器说明文案全部替换(用户提供梗文案);演示页介绍单行显示、去掉类别前缀、删'实战模拟·自动驾驶'提示行
③新主武器'巡飞弹'(loiter):慢攻速追踪导弹(制导引擎新增maxSpd上限5保持中速),命中留火场(zones新kind:fire纯粒子火舌,无圆形区域)+分裂4枚小炸弹快爆;新精灵loms;CODEX_KEYS同步63
④存档云同步:星币/机体/选中机体/永久强化/本地纪录上服务端(player_state表+POST /api/state+vState白名单清洗);login/me/register响应带state;客户端persist()钩子防抖600ms自动上传;服务端整体替换本地防串号,登出全清;老账号首登自动上传本地存档迁移
⑤机体选择/购买从出击准备并入机库强化(buildShips);出击默认无尽模式
服务端已重启生效;/api/state全链路在独立测试库curl验证通过(合法推送/回读/非法拦截清洗/401)
2026-08-06 12:15:17 +08:00

71 lines
2.4 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.

"use strict";
/* ================= SQLitenode:sqlite 内置驱动,零依赖) ================= */
const { DatabaseSync } = require("node:sqlite");
const path = require("node:path");
const fs = require("node:fs");
const { expandHome } = require("./env");
/* 数据目录不放源码树:默认 ~/.starraid可用 DATA_DIR 环境变量 / .env 覆盖 */
const DATA_DIR = expandHome(process.env.DATA_DIR || path.join(require("node:os").homedir(), ".starraid"));
fs.mkdirSync(DATA_DIR, { recursive: true });
const db = new DatabaseSync(path.join(DATA_DIR, "starraid.db"));
db.exec("PRAGMA journal_mode=WAL;");
db.exec("PRAGMA foreign_keys=ON;");
db.exec(`
CREATE TABLE IF NOT EXISTS users(
id INTEGER PRIMARY KEY AUTOINCREMENT,
username TEXT NOT NULL UNIQUE COLLATE NOCASE,
email TEXT NOT NULL UNIQUE COLLATE NOCASE,
pass_hash TEXT NOT NULL,
created_at INTEGER NOT NULL
);
CREATE TABLE IF NOT EXISTS sessions(
token TEXT PRIMARY KEY,
user_id INTEGER NOT NULL REFERENCES users(id) ON DELETE CASCADE,
created_at INTEGER NOT NULL,
expires_at INTEGER NOT NULL
);
CREATE INDEX IF NOT EXISTS idx_sessions_user ON sessions(user_id);
CREATE TABLE IF NOT EXISTS user_stats(
user_id INTEGER PRIMARY KEY REFERENCES users(id) ON DELETE CASCADE,
boss_kills INTEGER NOT NULL DEFAULT 0,
endless_high INTEGER NOT NULL DEFAULT 0,
runs INTEGER NOT NULL DEFAULT 0,
updated_at INTEGER NOT NULL DEFAULT 0
);
CREATE TABLE IF NOT EXISTS codex_unlocks(
user_id INTEGER NOT NULL REFERENCES users(id) ON DELETE CASCADE,
entry_key TEXT NOT NULL,
unlocked_at INTEGER NOT NULL,
PRIMARY KEY(user_id, entry_key)
);
CREATE TABLE IF NOT EXISTS runs(
id INTEGER PRIMARY KEY AUTOINCREMENT,
user_id INTEGER NOT NULL REFERENCES users(id) ON DELETE CASCADE,
mode TEXT NOT NULL,
diff INTEGER NOT NULL,
score INTEGER NOT NULL,
wave INTEGER NOT NULL,
stage INTEGER NOT NULL,
time_sec INTEGER NOT NULL,
boss_kills INTEGER NOT NULL,
win INTEGER NOT NULL,
created_at INTEGER NOT NULL
);
CREATE INDEX IF NOT EXISTS idx_runs_user ON runs(user_id);
CREATE TABLE IF NOT EXISTS player_state(
user_id INTEGER PRIMARY KEY REFERENCES users(id) ON DELETE CASCADE,
state TEXT NOT NULL,
updated_at INTEGER NOT NULL
);
`);
/* 定期清理过期 session */
setInterval(() => {
try { db.prepare("DELETE FROM sessions WHERE expires_at<?").run(Date.now()); } catch (e) { /* 忽略 */ }
}, 3600000).unref();
module.exports = db;