Vitron-Front/app/utils/token-manager.server.ts
Arda Samadi 0360780411 feat(admin): admin-panel foundation shell + overview (slice 0)
- /admin opted out of shopper chrome in MyLayout; /admin added to server
  protectedRoutes; useRequireAdmin() gates to superusers (UserProfile.isSuperuser).
- Admin shell (admin.tsx) with sidebar nav (Overview live; other sections stubbed)
  and a mobile top bar; Overview dashboard (KPI cards + 14-day orders chart) via
  use-admin-hooks.ts hitting /api/adminpanel/v1/overview/.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-07-25 12:00:49 +03:30

149 lines
4.0 KiB
TypeScript

import { redirect } from "@remix-run/node";
import {
isAccessTokenExpired,
isRefreshTokenExpired,
sessionStorage,
} from "~/sessions.server";
import { ApiApi, Configuration } from "../../src/api/types";
import { getInternalApiUrl } from "~/utils/api-config";
export interface User {
access_token: string;
refresh_token: string;
access_token_timestamp: number;
refresh_token_timestamp: number;
}
/**
* Token manager for handling access and refresh tokens
* - access_token expires after 1 week
* - refresh_token expires after 1 month
*/
const protectedRoutes = [
"/profile/bookmarks",
"/profile/following",
"/profile/threads",
"/profile/wallet",
"/profile/location",
"/profile/location/add",
"/profile/location/edit",
"/profile/orders",
"/profile/setting",
"/cart",
"/store",
"/admin",
];
export async function validateTokens(request: Request) {
const session = await sessionStorage.getSession(
request.headers.get("cookie"),
);
const user: User | null = session.get("user");
const isProtectedRoute = protectedRoutes.some((route) =>
request.url.includes(route),
);
// If no user data found, redirect to login
if (!user || !user.access_token) {
if (isProtectedRoute) {
throw redirect("/login", {
headers: {
"Set-Cookie": await sessionStorage.destroySession(session),
},
});
} else return { user: null };
}
// Check if refresh_token is expired
if (
user &&
user.refresh_token_timestamp &&
isRefreshTokenExpired(user.refresh_token_timestamp)
) {
// If refresh token is expired, clear session and redirect to login
if (isProtectedRoute) {
throw redirect("/login", {
headers: {
"Set-Cookie": await sessionStorage.destroySession(session),
},
});
} else return { user: null };
}
// Check if access_token is expired but refresh_token is still valid
if (
user &&
user.access_token_timestamp &&
isAccessTokenExpired(user.access_token_timestamp)
) {
try {
// Try to refresh the access token using the refresh token
const newTokens = await refreshAccessToken(user.refresh_token);
// Update session with new tokens and new timestamps
const currentTime = Date.now();
session.set("user", {
...user,
access_token: newTokens.access,
refresh_token: newTokens.refresh,
access_token_timestamp: currentTime,
refresh_token_timestamp: currentTime,
});
// Return the updated session and new tokens
return {
user: session.get("user"),
headers: {
"Set-Cookie": await sessionStorage.commitSession(session, {
expires: new Date(new Date().setMonth(new Date().getMonth() + 1)),
}),
},
};
} catch (error) {
// If refresh fails, redirect to login
console.error("Token refresh failed:", error);
if (isProtectedRoute) {
throw redirect("/login", {
headers: {
"Set-Cookie": await sessionStorage.destroySession(session),
},
});
} else return { user: null };
}
}
if (request.url.includes("/login")) throw redirect("/");
// If tokens are valid, return user data
return { user };
}
/**
* Function to refresh the access token using the refresh token
* This calls the API endpoint that handles token refresh
*/
async function refreshAccessToken(refreshToken: string) {
try {
const basePath = getInternalApiUrl();
const config = new Configuration({
basePath,
headers: {
"Content-Type": "application/json",
accept: "application/json",
},
});
const apiClient = new ApiApi(config);
const response = await apiClient.apiUserV1TokenRefreshCreate({
data: {
refresh: refreshToken,
},
});
// Validate the response data
if (!response.access || !response.refresh) {
throw new Error("Invalid refresh token response");
}
return response;
} catch (error) {
console.error("Error refreshing token:", error);
throw error;
}
}