会话持久化到DB+有效期延至7天: 部署重启不再全员登出(之前会话在内存,每次发布都把用户踢下线)

This commit is contained in:
mzl92
2026-08-28 13:59:26 +08:00
parent 9307b4aacb
commit f955869052
+23 -7
View File
@@ -94,12 +94,15 @@ let rdb = new Database(SNAP_PATH, { readonly: true });
// 注意: makeSnapshot 用 rename 覆盖快照文件后, 已打开的连接仍指向旧 inode(读到陈旧数据),
// 必须在每次快照后重开连接(rdb 为 let 就是为此)。
// ---------- 会话(内存态,重启需重登) ----------
const sessions = new Map(); // sid -> { uid, role, expires }
const SESSION_HOURS = 12;
// ---------- 会话(持久化到users.db, 部署重启不再全员登出) ----------
const sessions = new Map(); // sid -> { uid, role, expires } (内存缓存,源在DB)
const SESSION_HOURS = 168; // 7天
udb.exec(`CREATE TABLE IF NOT EXISTS sessions (sid TEXT PRIMARY KEY, uid INTEGER NOT NULL, expires INTEGER NOT NULL)`);
function newSession(uid, role) {
const sid = crypto.randomBytes(24).toString('hex');
sessions.set(sid, { uid, role, expires: Date.now() + SESSION_HOURS * 3600e3 });
const expires = Date.now() + SESSION_HOURS * 3600e3;
sessions.set(sid, { uid, role, expires });
try { udb.prepare('INSERT OR REPLACE INTO sessions (sid, uid, expires) VALUES (?,?,?)').run(sid, uid, expires); } catch (_) {}
return sid;
}
function signSid(sid) { return sid + '.' + crypto.createHmac('sha256', SECRET).update(sid).digest('hex').slice(0, 16); }
@@ -113,8 +116,18 @@ function parseSid(cookie) {
function authUser(req) {
const sid = parseSid(req.headers.cookie);
if (!sid) return null;
const s = sessions.get(sid);
if (!s || s.expires < Date.now()) { sessions.delete(sid); return null; }
let s = sessions.get(sid);
if (!s) { // 容器重启后从DB恢复
const row = udb.prepare('SELECT uid, expires FROM sessions WHERE sid = ?').get(sid);
if (row) {
if (row.expires < Date.now()) { udb.prepare('DELETE FROM sessions WHERE sid = ?').run(sid); return null; }
const u = udb.prepare('SELECT id, role FROM users WHERE id = ? AND disabled = 0').get(row.uid);
if (!u) return null;
s = { uid: u.id, role: u.role, expires: row.expires };
sessions.set(sid, s);
}
}
if (!s || s.expires < Date.now()) { sessions.delete(sid); try { udb.prepare('DELETE FROM sessions WHERE sid = ?').run(sid); } catch (_) {} return null; }
return udb.prepare('SELECT id, username, role FROM users WHERE id = ? AND disabled = 0').get(s.uid) || null;
}
@@ -151,7 +164,10 @@ app.post('/api/logout', (req, res) => {
const sid = parseSid(req.headers.cookie);
const me = authUser(req);
if (me) logAccess(req, me.username, 'logout');
if (sid) sessions.delete(sid);
if (sid) {
sessions.delete(sid);
try { udb.prepare('DELETE FROM sessions WHERE sid = ?').run(sid); } catch (_) {}
}
res.setHeader('Set-Cookie', 'session=; HttpOnly; Path=/; Max-Age=0');
res.json({ ok: true });
});