76 lines
1.8 KiB
TypeScript
76 lines
1.8 KiB
TypeScript
import "server-only";
|
|
|
|
import { eq } from "drizzle-orm";
|
|
import { db, User } from "@/lib/db";
|
|
import { redis } from "@/lib/redis";
|
|
|
|
const MEMORY_TTL_MS = 60_000;
|
|
const REDIS_TTL_SEC = 60;
|
|
const memory = new Map<number, { version: number; expiresAt: number }>();
|
|
|
|
function redisKey(userId: number): string {
|
|
return `jwtver:${userId}`;
|
|
}
|
|
|
|
/**
|
|
* Cached `users.website_jwt_version` for Auth.js JWT validation.
|
|
* Avoids a DB round-trip on every authenticated request.
|
|
*/
|
|
export async function getCachedJwtVersion(
|
|
userId: number,
|
|
): Promise<number | null> {
|
|
if (!Number.isInteger(userId) || userId <= 0) return null;
|
|
|
|
const now = Date.now();
|
|
const hit = memory.get(userId);
|
|
if (hit && hit.expiresAt > now) return hit.version;
|
|
|
|
if (redis) {
|
|
try {
|
|
const raw = await redis.get(redisKey(userId));
|
|
if (raw !== null && raw !== undefined) {
|
|
const version = Number.parseInt(raw, 10);
|
|
if (Number.isFinite(version)) {
|
|
memory.set(userId, { version, expiresAt: now + MEMORY_TTL_MS });
|
|
return version;
|
|
}
|
|
}
|
|
} catch {
|
|
/* fall through to DB */
|
|
}
|
|
}
|
|
|
|
try {
|
|
const [row] = await db
|
|
.select({ websiteJwtVersion: User.websiteJwtVersion })
|
|
.from(User)
|
|
.where(eq(User.id, userId))
|
|
.limit(1);
|
|
if (!row) return null;
|
|
const version = row.websiteJwtVersion;
|
|
memory.set(userId, { version, expiresAt: now + MEMORY_TTL_MS });
|
|
if (redis) {
|
|
try {
|
|
await redis.setex(redisKey(userId), REDIS_TTL_SEC, String(version));
|
|
} catch {
|
|
/* non-critical */
|
|
}
|
|
}
|
|
return version;
|
|
} catch {
|
|
return null;
|
|
}
|
|
}
|
|
|
|
/** Call after bumping website_jwt_version so other instances drop sessions ASAP. */
|
|
export async function invalidateJwtVersionCache(userId: number): Promise<void> {
|
|
memory.delete(userId);
|
|
if (redis) {
|
|
try {
|
|
await redis.del(redisKey(userId));
|
|
} catch {
|
|
/* non-critical */
|
|
}
|
|
}
|
|
}
|