significant refactor

This commit is contained in:
2025-08-30 01:42:23 -05:00
parent 7136f646a3
commit 052f53444e
106 changed files with 1994 additions and 1701 deletions

View File

@@ -1,4 +1,8 @@
import { createMiddleware, createServerFn, ServerFnResponseType } from "@tanstack/react-start";
import {
createMiddleware,
createServerFn,
ServerFnResponseType,
} from "@tanstack/react-start";
import { getWebRequest } from "@tanstack/react-start/server";
import { getSessionForSSR } from "supertokens-node/custom";
import UserRoles from "supertokens-node/recipe/userroles";
@@ -8,110 +12,134 @@ import { Logger } from "@/lib/logger";
import z from "zod";
import { refreshSession } from "supertokens-node/recipe/session";
const logger = new Logger('Middleware');
const logger = new Logger("Middleware");
export const verifySuperTokensSession = async (request: Request, response?: ServerFnResponseType) => {
const session = await getSessionForStart(request, { sessionRequired: false });
if (session?.needsRefresh && response) {
logger.info("Session refreshing...");
refreshSession(request, response);
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 verifySuperTokensSession = async (
request: Request,
response?: ServerFnResponseType
) => {
let session = await getSessionForStart(request, { sessionRequired: false });
export const superTokensRequestMiddleware = createMiddleware({ type: 'request' })
.server(async ({ next, request }) => {
const session = await verifySuperTokensSession(request);
if (!session.context.userAuthId) {
logger.error('Unauthenticated user in API call.', session.context)
throw new Error("Unauthenticated");
}
if (session?.needsRefresh) {
logger.info("Session refreshing...");
await refreshSession(request, response);
session = await getSessionForStart(request, { sessionRequired: false });
}
const userAuthId = session?.userId;
const context = {
userAuthId: session.context.userAuthId,
roles: session.context.roles,
metadata: session.context.metadata
}
if (!userAuthId || !session) {
logger.info("No authenticated user");
return { context: { userAuthId: null, roles: [] } };
}
return next({ context })
});
const { roles } = await fetchUserRoles(userAuthId);
const { metadata } = await UserMetadata.getUserMetadata(userAuthId);
export const superTokensFunctionMiddleware = createMiddleware({ type: 'function' })
.server(async ({ next, response }) => {
const request = getWebRequest();
const session = await verifySuperTokensSession(request, response);
return {
context: {
userAuthId,
roles,
metadata,
session: {
accessTokenPayload: session.accessTokenPayload,
sessionHandle: session.sessionHandle,
},
},
};
};
if (!session.context.userAuthId) {
logger.error('Unauthenticated user in server function.', session.context)
throw new Error("Unauthenticated");
}
export const superTokensRequestMiddleware = createMiddleware({
type: "request",
}).server(async ({ next, request }) => {
const session = await verifySuperTokensSession(request);
const context = {
userAuthId: session.context.userAuthId,
roles: session.context.roles,
metadata: session.context.metadata
}
if (!session.context.userAuthId) {
logger.error("Unauthenticated user in API call.", session.context);
throw new Error("Unauthenticated");
}
const context = {
userAuthId: session.context.userAuthId,
roles: session.context.roles,
metadata: session.context.metadata,
};
return next({ context });
});
export const superTokensFunctionMiddleware = createMiddleware({
type: "function",
}).server(async ({ next, response }) => {
const request = getWebRequest();
const session = await verifySuperTokensSession(request, response);
if (!session.context.userAuthId) {
logger.error("Unauthenticated user in server function.", session.context);
throw new Error("Unauthenticated");
}
const context = {
userAuthId: session.context.userAuthId,
roles: session.context.roles,
metadata: session.context.metadata,
};
return next({ context });
});
export const superTokensAdminFunctionMiddleware = createMiddleware({
type: "function",
}).server(async ({ next, response }) => {
const request = getWebRequest();
const session = await verifySuperTokensSession(request, response);
if (!session.context.userAuthId) {
logger.error("Unauthenticated user in admin function.", session.context);
throw new Error("Unauthenticated");
}
const context = {
userAuthId: session.context.userAuthId,
roles: session.context.roles,
metadata: session.context.metadata,
};
if (context.roles?.includes("Admin")) {
return next({ context });
})
}
export const superTokensAdminFunctionMiddleware = createMiddleware({ type: 'function' })
.server(async ({ next, response }) => {
const request = getWebRequest();
const session = await verifySuperTokensSession(request, response);
if (!session.context.userAuthId) {
logger.error('Unauthenticated user in admin function.', session.context)
throw new Error('Unauthenticated')
}
const context = {
userAuthId: session.context.userAuthId,
roles: session.context.roles,
metadata: session.context.metadata
}
if (context.roles?.includes('Admin')) {
return next(({ context }));
}
logger.error('Unauthorized user in admin function.', context);
throw new Error('Unauthorized');
})
logger.error("Unauthorized user in admin function.", context);
throw new Error("Unauthorized");
});
export const fetchUserRoles = async (userAuthId: string) => {
const response = await UserRoles.getRolesForUser("public", userAuthId);
return response;
}
};
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())
export const setUserMetadata = createServerFn({ method: "POST" })
.validator(
z
.object({
first_name: z
.string()
.min(2)
.max(20)
.regex(
/^[a-zA-Z0-9\s]+$/,
"First name must be 2-20 characters long and contain only letters and spaces"
),
last_name: z
.string()
.min(2)
.max(20)
.regex(
/^[a-zA-Z0-9\s]+$/,
"Last name must be 2-20 characters long and contain only letters and spaces"
),
player_id: z.string(),
})
.partial()
)
.middleware([superTokensFunctionMiddleware])
.handler(async ({ context, data }) => {
const { userAuthId, metadata } = context;
@@ -120,18 +148,18 @@ export const setUserMetadata = createServerFn({ method: 'POST' })
await UserMetadata.updateUserMetadata(userAuthId, {
first_name: data.first_name,
last_name: data.last_name,
player_id: data.player_id
player_id: data.player_id,
});
return {
metadata: {
...metadata,
...data
}
...data,
},
};
})
});
export const updateUserColorScheme = createServerFn({ method: 'POST' })
export const updateUserColorScheme = createServerFn({ method: "POST" })
.validator((data: string) => data)
.middleware([superTokensFunctionMiddleware])
.handler(async ({ context, data }) => {
@@ -139,18 +167,18 @@ export const updateUserColorScheme = createServerFn({ method: 'POST' })
if (!userAuthId) return;
await UserMetadata.updateUserMetadata(userAuthId, {
colorScheme: data
colorScheme: data,
});
return {
metadata: {
...metadata,
colorScheme: data
}
colorScheme: data,
},
};
})
});
export const updateUserAccentColor = createServerFn({ method: 'POST' })
export const updateUserAccentColor = createServerFn({ method: "POST" })
.validator((data: string) => data)
.middleware([superTokensFunctionMiddleware])
.handler(async ({ context, data }) => {
@@ -158,13 +186,13 @@ export const updateUserAccentColor = createServerFn({ method: 'POST' })
if (!userAuthId) return;
await UserMetadata.updateUserMetadata(userAuthId, {
accentColor: data
accentColor: data,
});
return {
metadata: {
...metadata,
accentColor: data
}
accentColor: data,
},
};
})
});

View File

@@ -5,8 +5,8 @@ import { serverEvents } from "@/lib/events/emitter";
export const testEvent = createServerFn()
.middleware([superTokensFunctionMiddleware])
.handler(async ({ context }) => {
serverEvents.emit('test', {
type: 'test',
serverEvents.emit("test", {
type: "test",
userId: context.userAuthId,
});
});