会话持久化到DB+有效期延至7天: 部署重启不再全员登出(之前会话在内存,每次发布都把用户踢下线)
This commit is contained in:
@@ -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 });
|
||||
});
|
||||
|
||||
Reference in New Issue
Block a user