This commit is contained in:
yohlo
2025-08-20 22:35:40 -05:00
commit f51c278cd3
169 changed files with 8173 additions and 0 deletions

160
src/utils/supertokens.ts Normal file
View File

@@ -0,0 +1,160 @@
import { createMiddleware, createServerFn } from "@tanstack/react-start";
import { getWebRequest } from "@tanstack/react-start/server";
import { getSessionForSSR } from "supertokens-node/custom";
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 z from "zod";
const logger = new Logger('Middleware');
const verifySuperTokensSession = async (request: Request) => {
const session = await getSessionForStart(request, { sessionRequired: false });
if (session?.needsRefresh) {
logger.info("Session needs refresh");
return { context: { needsRefresh: true } };
}
const userAuthId = session?.userId;
if (!userAuthId) {
logger.info("No authenticated user");
return { context: { userAuthId: null, roles: [] } };
}
const { roles } = await fetchUserRoles(userAuthId);
const { metadata } = await UserMetadata.getUserMetadata(userAuthId);
return {
context: {
userAuthId,
roles,
metadata,
session: {
accessTokenPayload: session.accessTokenPayload,
sessionHandle: session.sessionHandle
}
}
};
}
export const superTokensRequestMiddleware = createMiddleware({ type: 'request' })
.server(async ({ next, request }) => {
const context = await verifySuperTokensSession(request);
return next(context as any);
});
export const superTokensFunctionMiddleware = createMiddleware({ type: 'function' })
.server(async ({ next }) => {
const request = getWebRequest();
const context = await verifySuperTokensSession(request);
return next(context as any);
})
export const superTokensRoleFunctionMiddleware = createMiddleware({ type: 'function' })
.server(async ({ next, context }) => {
const { roles } = context as any;
return next(({ context: { roles } }));
})
export const superTokensAdminFunctionMiddleware = createMiddleware({ type: 'function' })
.server(async ({ next }) => {
const request = getWebRequest();
const session = await verifySuperTokensSession(request);
const { roles } = session?.context as any;
if (roles?.includes('Admin')) {
return next(({ context: { roles } }));
}
logger.error('User reached admin function without admin role', next);
throw new Error('Unauthorized');
})
export const fetchUserRoles = async (userAuthId: string) => {
const response = await UserRoles.getRolesForUser("public", userAuthId);
return response;
}
export const fetchSuperTokensAuth = createServerFn({ method: 'GET' }).handler(async () => {
const request = getWebRequest();
const session = await getSessionForSSR(request);
const userAuthId = session?.accessTokenPayload?.sub;
if (!userAuthId) return;
const { roles } = await fetchUserRoles(userAuthId);
const { metadata } = await UserMetadata.getUserMetadata(userAuthId);
return {
userAuthId,
hasToken: session.hasToken,
roles,
metadata
}
})
export const setUserMetadata = createServerFn({ method: 'POST' })
.validator(z.object({
first_name: z.string().min(3).max(20).regex(/^[a-zA-Z0-9\s]+$/, "First name must be 3-20 characters long and contain only letters and spaces"),
last_name: z.string().min(3).max(20).regex(/^[a-zA-Z0-9\s]+$/, "Last name must be 3-20 characters long and contain only letters and spaces"),
player_id: z.string(),
}).partial())
.middleware([superTokensFunctionMiddleware])
.handler(async ({ context, data }) => {
const { userAuthId, metadata } = context as any;
if (!userAuthId) return;
await UserMetadata.updateUserMetadata(userAuthId, {
first_name: data.first_name,
last_name: data.last_name,
player_id: data.player_id
});
return {
metadata: {
...metadata,
...data
}
};
})
export const updateUserColorScheme = createServerFn({ method: 'POST' })
.validator((data: string) => data)
.middleware([superTokensFunctionMiddleware])
.handler(async ({ context, data }) => {
const { userAuthId, metadata } = context as any;
if (!userAuthId) return;
await UserMetadata.updateUserMetadata(userAuthId, {
colorScheme: data
});
return {
metadata: {
...metadata,
colorScheme: data
}
};
})
export const updateUserAccentColor = createServerFn({ method: 'POST' })
.validator((data: string) => data)
.middleware([superTokensFunctionMiddleware])
.handler(async ({ context, data }) => {
const { userAuthId, metadata } = context as any;
if (!userAuthId) return;
await UserMetadata.updateUserMetadata(userAuthId, {
accentColor: data
});
return {
metadata: {
...metadata,
accentColor: data
}
};
})

12
src/utils/test-event.ts Normal file
View File

@@ -0,0 +1,12 @@
import { createServerFn } from "@tanstack/react-start";
import { superTokensFunctionMiddleware } from "./supertokens";
import { serverEvents } from "@/lib/events/emitter";
export const testEvent = createServerFn()
.middleware([superTokensFunctionMiddleware])
.handler(async ({ context }) => {
serverEvents.emit('test', {
type: 'test',
userId: (context as any).userAuthId,
});
});