generated from nholtware/service-template-express
feat: passport-local authentication, story ownership
Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com>
This commit is contained in:
parent
0ae3f6109b
commit
afbc5e5254
16 changed files with 995 additions and 73 deletions
70
src/lib/session-store.ts
Normal file
70
src/lib/session-store.ts
Normal file
|
|
@ -0,0 +1,70 @@
|
|||
import session from "express-session";
|
||||
import type { Db } from "./db.js";
|
||||
|
||||
/** Postgres-backed sessions so every pod sees the same login. */
|
||||
export class PgSessionStore extends session.Store {
|
||||
private readonly timer: NodeJS.Timeout;
|
||||
|
||||
constructor(
|
||||
private readonly db: Db,
|
||||
private readonly ttlMs: number,
|
||||
) {
|
||||
super();
|
||||
// Expired rows are ignored on read; sweep them occasionally.
|
||||
this.timer = setInterval(() => {
|
||||
void this.db
|
||||
.query("DELETE FROM sessions WHERE expire < now()")
|
||||
.catch(() => {});
|
||||
}, 10 * 60_000);
|
||||
this.timer.unref();
|
||||
}
|
||||
|
||||
private expiry(sess: session.SessionData): Date {
|
||||
const exp = sess.cookie?.expires;
|
||||
return exp ? new Date(exp) : new Date(Date.now() + this.ttlMs);
|
||||
}
|
||||
|
||||
get(
|
||||
sid: string,
|
||||
cb: (err?: unknown, sess?: session.SessionData | null) => void,
|
||||
) {
|
||||
this.db
|
||||
.query<{ sess: session.SessionData }>(
|
||||
"SELECT sess FROM sessions WHERE sid = $1 AND expire > now()",
|
||||
[sid],
|
||||
)
|
||||
.then((r) => cb(null, r.rows[0]?.sess ?? null), cb);
|
||||
}
|
||||
|
||||
set(sid: string, sess: session.SessionData, cb?: (err?: unknown) => void) {
|
||||
this.db
|
||||
.query(
|
||||
`INSERT INTO sessions (sid, sess, expire) VALUES ($1, $2, $3)
|
||||
ON CONFLICT (sid) DO UPDATE SET sess = EXCLUDED.sess, expire = EXCLUDED.expire`,
|
||||
[sid, JSON.stringify(sess), this.expiry(sess)],
|
||||
)
|
||||
.then(
|
||||
() => cb?.(),
|
||||
(e) => cb?.(e),
|
||||
);
|
||||
}
|
||||
|
||||
touch(sid: string, sess: session.SessionData, cb?: (err?: unknown) => void) {
|
||||
this.db
|
||||
.query("UPDATE sessions SET expire = $2 WHERE sid = $1", [
|
||||
sid,
|
||||
this.expiry(sess),
|
||||
])
|
||||
.then(
|
||||
() => cb?.(),
|
||||
(e) => cb?.(e),
|
||||
);
|
||||
}
|
||||
|
||||
destroy(sid: string, cb?: (err?: unknown) => void) {
|
||||
this.db.query("DELETE FROM sessions WHERE sid = $1", [sid]).then(
|
||||
() => cb?.(),
|
||||
(e) => cb?.(e),
|
||||
);
|
||||
}
|
||||
}
|
||||
Loading…
Add table
Add a link
Reference in a new issue