perf upgrade
This commit is contained in:
@@ -0,0 +1,141 @@
|
||||
import { redirect } from "@tanstack/react-router";
|
||||
import UserRoles from "supertokens-node/recipe/userroles";
|
||||
import UserMetadata from "supertokens-node/recipe/usermetadata";
|
||||
import { getSessionForStart } from "@/lib/supertokens/recipes/start-session";
|
||||
import { Logger } from "@/lib/logger";
|
||||
import { pbAdmin } from "@/lib/pocketbase/client";
|
||||
import { PUBLIC_ROUTES } from "./supertokens";
|
||||
import type { Player } from "@/features/players/types";
|
||||
|
||||
const logger = new Logger("Middleware");
|
||||
|
||||
const ROLES_METADATA_TTL_MS = 60 * 1000;
|
||||
const rolesMetadataCache = new Map<
|
||||
string,
|
||||
{ roles: string[]; metadata: any; expiresAt: number }
|
||||
>();
|
||||
|
||||
export const invalidateUserCache = (userAuthId: string) => {
|
||||
rolesMetadataCache.delete(userAuthId);
|
||||
};
|
||||
|
||||
export const fetchUserRoles = async (userAuthId: string) => {
|
||||
const response = await UserRoles.getRolesForUser("public", userAuthId);
|
||||
return response;
|
||||
};
|
||||
|
||||
const getRolesAndMetadata = async (userAuthId: string) => {
|
||||
const now = Date.now();
|
||||
const cached = rolesMetadataCache.get(userAuthId);
|
||||
if (cached && cached.expiresAt > now) {
|
||||
return cached;
|
||||
}
|
||||
|
||||
const [{ roles }, { metadata }] = await Promise.all([
|
||||
fetchUserRoles(userAuthId),
|
||||
UserMetadata.getUserMetadata(userAuthId),
|
||||
]);
|
||||
|
||||
const entry = { roles, metadata, expiresAt: now + ROLES_METADATA_TTL_MS };
|
||||
rolesMetadataCache.set(userAuthId, entry);
|
||||
return entry;
|
||||
};
|
||||
|
||||
export const updateUserMetadataFields = async (
|
||||
userAuthId: string,
|
||||
fields: Record<string, any>
|
||||
) => {
|
||||
await UserMetadata.updateUserMetadata(userAuthId, fields);
|
||||
invalidateUserCache(userAuthId);
|
||||
};
|
||||
|
||||
const verifySuperTokensSession = async (request: Request) => {
|
||||
let session = await getSessionForStart(request, { sessionRequired: false });
|
||||
|
||||
if (session?.needsRefresh) {
|
||||
logger.info("Session needs refresh - redirecting to client");
|
||||
return { context: { session: { tryRefresh: true } } };
|
||||
}
|
||||
|
||||
const userAuthId = session?.userId;
|
||||
|
||||
if (!userAuthId || !session) {
|
||||
logger.info("No authenticated user");
|
||||
return { context: { userAuthId: null, roles: [] } };
|
||||
}
|
||||
|
||||
const { roles, metadata } = await getRolesAndMetadata(userAuthId);
|
||||
|
||||
return {
|
||||
context: {
|
||||
userAuthId,
|
||||
roles,
|
||||
metadata,
|
||||
phone: session.phone,
|
||||
session: {
|
||||
accessTokenPayload: session.accessTokenPayload,
|
||||
sessionHandle: session.sessionHandle,
|
||||
},
|
||||
},
|
||||
};
|
||||
};
|
||||
|
||||
const LAST_ACTIVITY_THROTTLE_MS = 5 * 60 * 1000;
|
||||
const lastActivityWriteAt = new Map<string, number>();
|
||||
|
||||
export const getSessionContextCore = async (
|
||||
request: Request,
|
||||
options?: { isServerFunction?: boolean }
|
||||
) => {
|
||||
const session = await verifySuperTokensSession(request);
|
||||
|
||||
if (session.context.session?.tryRefresh) {
|
||||
if (options?.isServerFunction) {
|
||||
throw new Error("SESSION_REFRESH_REQUIRED");
|
||||
}
|
||||
|
||||
const url = new URL(request.url);
|
||||
|
||||
if (PUBLIC_ROUTES.some((route) => url.pathname.startsWith(route))) {
|
||||
throw new Error("Unauthenticated");
|
||||
}
|
||||
|
||||
const from = url.pathname + url.search;
|
||||
|
||||
throw redirect({
|
||||
to: "/refresh-session",
|
||||
search: from === "/" ? {} : { redirect: from },
|
||||
});
|
||||
}
|
||||
|
||||
if (!session.context.userAuthId) {
|
||||
logger.error("Unauthenticated user", session.context);
|
||||
throw new Error("Unauthenticated");
|
||||
}
|
||||
|
||||
let player: Player | null = null;
|
||||
|
||||
try {
|
||||
player = await pbAdmin.getPlayerByAuthId(session.context.userAuthId);
|
||||
if (player) {
|
||||
const now = Date.now();
|
||||
const lastWrite = lastActivityWriteAt.get(session.context.userAuthId) ?? 0;
|
||||
if (now - lastWrite > LAST_ACTIVITY_THROTTLE_MS) {
|
||||
lastActivityWriteAt.set(session.context.userAuthId, now);
|
||||
await pbAdmin.updatePlayer(player.id, {
|
||||
last_activity: new Date().toISOString(),
|
||||
});
|
||||
}
|
||||
}
|
||||
} catch (error) {
|
||||
logger.error("Failed to update player last_activity", error);
|
||||
}
|
||||
|
||||
return {
|
||||
userAuthId: session.context.userAuthId,
|
||||
roles: session.context.roles,
|
||||
metadata: session.context.metadata,
|
||||
phone: session.context.phone,
|
||||
player,
|
||||
};
|
||||
};
|
||||
Reference in New Issue
Block a user