diff --git a/server.js b/server.js index 747b030..a238a54 100644 --- a/server.js +++ b/server.js @@ -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 }); });