starraid/star-raid/server/db.js
JOJO 9d048a2aa9 无尽模式断点续玩:进度云端存档,中途退出可续玩,新开需确认结算旧档
- 服务端:run_saves 表(每账号1栏位)+ GET/POST/DELETE /api/run_save,vRunSave 白名单校验
- 波次检查点:startWave 拍快照上传;中途放弃任务不结算(星币/分数留快照);死亡删档
- 出击页卡片显示未完成对局并一键续玩(该波从头重打,bossN 不偏移)
- 新开无尽(不分难度)弹窗确认→旧档按当前进度结算(星币入账+上报排行榜)再开新局
- NET._rsQ 串行队列防删建竞态;顺带修复 SHIP_KEYS 漏 asura 致修罗云同步丢失
2026-08-06 12:50:37 +08:00

76 lines
2.5 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
);
CREATE TABLE IF NOT EXISTS run_saves(
user_id INTEGER PRIMARY KEY REFERENCES users(id) ON DELETE CASCADE,
payload 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;