fix: recover when sessions table is missing
This commit is contained in:
parent
c3c0c33339
commit
c1259f0bf5
@ -2,27 +2,10 @@ const crypto = require("crypto");
|
|||||||
const pool = require("../db/pool");
|
const pool = require("../db/pool");
|
||||||
const { SESSION_TTL_DAYS } = require("../utils/session-cookie");
|
const { SESSION_TTL_DAYS } = require("../utils/session-cookie");
|
||||||
|
|
||||||
function generateSessionId() {
|
const INSERT_SESSION_SQL = `INSERT INTO sessions (id, user_id, expires_at, user_agent)
|
||||||
if (typeof crypto.randomUUID === "function") {
|
|
||||||
return crypto.randomUUID().replace(/-/g, "") + crypto.randomBytes(8).toString("hex");
|
|
||||||
}
|
|
||||||
return crypto.randomBytes(32).toString("hex");
|
|
||||||
}
|
|
||||||
|
|
||||||
exports.createSession = async (userId, userAgent = null) => {
|
|
||||||
const id = generateSessionId();
|
|
||||||
const result = await pool.query(
|
|
||||||
`INSERT INTO sessions (id, user_id, expires_at, user_agent)
|
|
||||||
VALUES ($1, $2, NOW() + ($3 || ' days')::interval, $4)
|
VALUES ($1, $2, NOW() + ($3 || ' days')::interval, $4)
|
||||||
RETURNING id, user_id, created_at, expires_at`,
|
RETURNING id, user_id, created_at, expires_at`;
|
||||||
[id, userId, String(SESSION_TTL_DAYS), userAgent]
|
const SELECT_ACTIVE_SESSION_SQL = `SELECT
|
||||||
);
|
|
||||||
return result.rows[0];
|
|
||||||
};
|
|
||||||
|
|
||||||
exports.getActiveSessionWithUser = async (sessionId) => {
|
|
||||||
const result = await pool.query(
|
|
||||||
`SELECT
|
|
||||||
s.id,
|
s.id,
|
||||||
s.user_id,
|
s.user_id,
|
||||||
s.expires_at,
|
s.expires_at,
|
||||||
@ -31,26 +14,110 @@ exports.getActiveSessionWithUser = async (sessionId) => {
|
|||||||
FROM sessions s
|
FROM sessions s
|
||||||
JOIN users u ON u.id = s.user_id
|
JOIN users u ON u.id = s.user_id
|
||||||
WHERE s.id = $1
|
WHERE s.id = $1
|
||||||
AND s.expires_at > NOW()`,
|
AND s.expires_at > NOW()`;
|
||||||
[sessionId]
|
|
||||||
|
let ensureSessionsTablePromise = null;
|
||||||
|
|
||||||
|
function generateSessionId() {
|
||||||
|
if (typeof crypto.randomUUID === "function") {
|
||||||
|
return crypto.randomUUID().replace(/-/g, "") + crypto.randomBytes(8).toString("hex");
|
||||||
|
}
|
||||||
|
return crypto.randomBytes(32).toString("hex");
|
||||||
|
}
|
||||||
|
|
||||||
|
function isUndefinedTableError(error) {
|
||||||
|
return error && error.code === "42P01";
|
||||||
|
}
|
||||||
|
|
||||||
|
async function ensureSessionsTable() {
|
||||||
|
if (!ensureSessionsTablePromise) {
|
||||||
|
ensureSessionsTablePromise = (async () => {
|
||||||
|
await pool.query(`CREATE TABLE IF NOT EXISTS sessions (
|
||||||
|
id VARCHAR(128) PRIMARY KEY,
|
||||||
|
user_id INTEGER NOT NULL REFERENCES users(id) ON DELETE CASCADE,
|
||||||
|
created_at TIMESTAMPTZ NOT NULL DEFAULT NOW(),
|
||||||
|
expires_at TIMESTAMPTZ NOT NULL,
|
||||||
|
last_seen_at TIMESTAMPTZ NOT NULL DEFAULT NOW(),
|
||||||
|
user_agent TEXT
|
||||||
|
);`);
|
||||||
|
await pool.query(
|
||||||
|
"CREATE INDEX IF NOT EXISTS idx_sessions_user_id ON sessions(user_id);"
|
||||||
);
|
);
|
||||||
|
await pool.query(
|
||||||
|
"CREATE INDEX IF NOT EXISTS idx_sessions_expires_at ON sessions(expires_at);"
|
||||||
|
);
|
||||||
|
})().catch((error) => {
|
||||||
|
ensureSessionsTablePromise = null;
|
||||||
|
throw error;
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
await ensureSessionsTablePromise;
|
||||||
|
}
|
||||||
|
|
||||||
|
async function insertSession(id, userId, userAgent) {
|
||||||
|
const result = await pool.query(INSERT_SESSION_SQL, [
|
||||||
|
id,
|
||||||
|
userId,
|
||||||
|
String(SESSION_TTL_DAYS),
|
||||||
|
userAgent,
|
||||||
|
]);
|
||||||
|
return result.rows[0];
|
||||||
|
}
|
||||||
|
|
||||||
|
exports.createSession = async (userId, userAgent = null) => {
|
||||||
|
const id = generateSessionId();
|
||||||
|
try {
|
||||||
|
return await insertSession(id, userId, userAgent);
|
||||||
|
} catch (error) {
|
||||||
|
if (!isUndefinedTableError(error)) {
|
||||||
|
throw error;
|
||||||
|
}
|
||||||
|
|
||||||
|
await ensureSessionsTable();
|
||||||
|
return insertSession(id, userId, userAgent);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
exports.getActiveSessionWithUser = async (sessionId) => {
|
||||||
|
let result;
|
||||||
|
try {
|
||||||
|
result = await pool.query(SELECT_ACTIVE_SESSION_SQL, [sessionId]);
|
||||||
|
} catch (error) {
|
||||||
|
if (isUndefinedTableError(error)) {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
throw error;
|
||||||
|
}
|
||||||
|
|
||||||
const session = result.rows[0] || null;
|
const session = result.rows[0] || null;
|
||||||
if (!session) return null;
|
if (!session) return null;
|
||||||
|
|
||||||
|
try {
|
||||||
await pool.query(
|
await pool.query(
|
||||||
`UPDATE sessions
|
`UPDATE sessions
|
||||||
SET last_seen_at = NOW()
|
SET last_seen_at = NOW()
|
||||||
WHERE id = $1`,
|
WHERE id = $1`,
|
||||||
[sessionId]
|
[sessionId]
|
||||||
);
|
);
|
||||||
|
} catch (error) {
|
||||||
|
if (!isUndefinedTableError(error)) {
|
||||||
|
throw error;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
return session;
|
return session;
|
||||||
};
|
};
|
||||||
|
|
||||||
exports.deleteSession = async (sessionId) => {
|
exports.deleteSession = async (sessionId) => {
|
||||||
|
try {
|
||||||
await pool.query(
|
await pool.query(
|
||||||
`DELETE FROM sessions WHERE id = $1`,
|
`DELETE FROM sessions WHERE id = $1`,
|
||||||
[sessionId]
|
[sessionId]
|
||||||
);
|
);
|
||||||
|
} catch (error) {
|
||||||
|
if (!isUndefinedTableError(error)) {
|
||||||
|
throw error;
|
||||||
|
}
|
||||||
|
}
|
||||||
};
|
};
|
||||||
|
|||||||
Loading…
Reference in New Issue
Block a user