diff --git a/src/app/routes/__root.tsx b/src/app/routes/__root.tsx
index feda413..0459715 100644
--- a/src/app/routes/__root.tsx
+++ b/src/app/routes/__root.tsx
@@ -65,7 +65,9 @@ export const Route = createRootRouteWithContext<{
errorComponent: (props) => {
return (
-
+
+
+
);
},
diff --git a/src/app/routes/refresh-session.tsx b/src/app/routes/refresh-session.tsx
index 8ab33a8..47bc840 100644
--- a/src/app/routes/refresh-session.tsx
+++ b/src/app/routes/refresh-session.tsx
@@ -18,10 +18,21 @@ function RouteComponent() {
const urlParams = new URLSearchParams(window.location.search)
const redirect = urlParams.get('redirect')
- if (redirect && !redirect.startsWith('/_serverFn')) {
+ const isServerFunction = redirect && (
+ redirect.startsWith('_serverFn') ||
+ redirect.startsWith('api/') ||
+ redirect.includes('_serverFn')
+ );
+
+ if (redirect && !isServerFunction) {
window.location.href = decodeURIComponent(redirect)
} else {
- window.location.href = '/'
+ const referrer = document.referrer;
+ const referrerUrl = referrer && !referrer.includes('/_serverFn') && !referrer.includes('/api/')
+ ? referrer
+ : '/';
+
+ window.location.href = referrerUrl;
}
} else {
window.location.href = '/login'
diff --git a/src/features/players/queries.ts b/src/features/players/queries.ts
index 730896a..c73a930 100644
--- a/src/features/players/queries.ts
+++ b/src/features/players/queries.ts
@@ -34,7 +34,18 @@ export const useMe = () => {
queryFn,
options: {
staleTime: 0,
- refetchOnMount: true
+ refetchOnMount: true,
+ retry: (failureCount, error: any) => {
+ if (error?.response?.status === 401) {
+ const errorData = error?.response?.data;
+ if (errorData?.error === "SESSION_REFRESH_REQUIRED") {
+ const currentUrl = window.location.pathname + window.location.search;
+ window.location.href = `/refresh-session?redirect=${encodeURIComponent(currentUrl)}`;
+ return false;
+ }
+ }
+ return failureCount < 3;
+ }
}
});
};
diff --git a/src/features/players/server.ts b/src/features/players/server.ts
index ec2f2ee..7edb775 100644
--- a/src/features/players/server.ts
+++ b/src/features/players/server.ts
@@ -23,8 +23,7 @@ export const fetchMe = createServerFn()
metadata: context.metadata
};
} catch (error: any) {
- // If getSessionContext throws (unauthenticated or redirect), return empty state
- logger.info('fetchMe: No authenticated user or redirect needed');
+ logger.info('fetchMe: Session error', error.message);
return { user: undefined, roles: [], metadata: {} };
}
})
diff --git a/src/lib/tanstack-query/hooks/use-server-mutation.ts b/src/lib/tanstack-query/hooks/use-server-mutation.ts
index d04fe27..a6e3ccf 100644
--- a/src/lib/tanstack-query/hooks/use-server-mutation.ts
+++ b/src/lib/tanstack-query/hooks/use-server-mutation.ts
@@ -23,16 +23,34 @@ export function useServerMutation(
return useMutation({
...mutationOptions,
mutationFn: async (variables: TVariables) => {
- const result = await mutationFn(variables);
-
- if (!result.success) {
- if (showErrorToast) {
- toast.error(result.error.userMessage);
+ try {
+ const result = await mutationFn(variables);
+
+ if (!result.success) {
+ if (showErrorToast) {
+ toast.error(result.error.userMessage);
+ }
+ throw new Error(result.error.userMessage);
}
- throw new Error(result.error.userMessage);
+
+ return result.data;
+ } catch (error: any) {
+ if (error?.response?.status === 401) {
+ try {
+ const errorData = typeof error.response.data === 'string'
+ ? JSON.parse(error.response.data)
+ : error.response.data;
+
+ if (errorData?.error === "SESSION_REFRESH_REQUIRED") {
+ const currentUrl = window.location.pathname + window.location.search;
+ window.location.href = `/refresh-session?redirect=${encodeURIComponent(currentUrl)}`;
+ throw new Error("SESSION_REFRESH_REQUIRED");
+ }
+ } catch (parseError) {}
+ }
+
+ throw error;
}
-
- return result.data;
},
onSuccess: (data, variables, context) => {
if (showSuccessToast && successMessage) {
diff --git a/src/utils/supertokens.ts b/src/utils/supertokens.ts
index 13d2509..5842f66 100644
--- a/src/utils/supertokens.ts
+++ b/src/utils/supertokens.ts
@@ -70,10 +70,14 @@ export const verifySuperTokensSession = async (
};
};
-export const getSessionContext = async (request: Request): Promise => {
+export const getSessionContext = async (request: Request, options?: { isServerFunction?: boolean }): Promise => {
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);
const from = encodeURIComponent(url.pathname + url.search);
throw redirect({
@@ -107,22 +111,56 @@ export const superTokensFunctionMiddleware = createMiddleware({
type: "function",
}).server(async ({ next, response }) => {
const request = getWebRequest();
- const context = await getSessionContext(request);
- return next({ context });
+
+ try {
+ const context = await getSessionContext(request, { isServerFunction: true });
+ return next({ context });
+ } catch (error: any) {
+ if (error.message === "SESSION_REFRESH_REQUIRED") {
+ throw new Response(
+ JSON.stringify({
+ error: "SESSION_REFRESH_REQUIRED",
+ message: "Session needs to be refreshed"
+ }),
+ {
+ status: 401,
+ headers: { "Content-Type": "application/json" }
+ }
+ );
+ }
+ throw error;
+ }
});
export const superTokensAdminFunctionMiddleware = createMiddleware({
type: "function",
}).server(async ({ next }) => {
const request = getWebRequest();
- const context = await getSessionContext(request);
+
+ try {
+ const context = await getSessionContext(request, { isServerFunction: true });
- if (context.roles?.includes("Admin")) {
- return next({ context });
+ if (context.roles?.includes("Admin")) {
+ return next({ context });
+ }
+
+ logger.error("Unauthorized user in admin function.", context);
+ throw new Error("Unauthorized");
+ } catch (error: any) {
+ if (error.message === "SESSION_REFRESH_REQUIRED") {
+ throw new Response(
+ JSON.stringify({
+ error: "SESSION_REFRESH_REQUIRED",
+ message: "Session needs to be refreshed"
+ }),
+ {
+ status: 401,
+ headers: { "Content-Type": "application/json" }
+ }
+ );
+ }
+ throw error;
}
-
- logger.error("Unauthorized user in admin function.", context);
- throw new Error("Unauthorized");
});
export const fetchUserRoles = async (userAuthId: string) => {