From 0910f11228fbb91cf339a922d3a1e392c7413c08 Mon Sep 17 00:00:00 2001 From: yohlo Date: Wed, 1 Oct 2025 21:34:59 -0500 Subject: [PATCH] pb refresh, profile refresh update --- src/app/routes/_authed/profile.$playerId.tsx | 11 ++- src/lib/pocketbase/client.ts | 80 ++++++++++++++++++-- 2 files changed, 83 insertions(+), 8 deletions(-) diff --git a/src/app/routes/_authed/profile.$playerId.tsx b/src/app/routes/_authed/profile.$playerId.tsx index d88e56f..f9bf474 100644 --- a/src/app/routes/_authed/profile.$playerId.tsx +++ b/src/app/routes/_authed/profile.$playerId.tsx @@ -1,3 +1,4 @@ +import { badgeKeys, badgeQueries } from "@/features/badges/queries"; import Profile from "@/features/players/components/profile"; import HeaderSkeleton from "@/features/players/components/profile/header-skeleton"; import ProfileSkeleton from "@/features/players/components/profile/skeleton"; @@ -24,6 +25,14 @@ export const Route = createFileRoute("/_authed/profile/$playerId")({ queryClient, playerQueries.matches(params.playerId) ), + prefetchServerQuery( + queryClient, + playerQueries.stats(params.playerId) + ), + prefetchServerQuery( + queryClient, + badgeQueries.playerBadges(params.playerId) + ), ]); }, loader: ({ params, context }) => ({ @@ -34,7 +43,7 @@ export const Route = createFileRoute("/_authed/profile/$playerId")({ context?.auth.user.id === params.playerId ? "/settings" : undefined, }, withPadding: false, - refresh: [playerKeys.details(params.playerId), playerKeys.matches(params.playerId), playerKeys.stats(params.playerId)], + refresh: [playerKeys.details(params.playerId), playerKeys.matches(params.playerId), playerKeys.stats(params.playerId), badgeKeys.playerBadges(params.playerId)], }), component: () => { const { playerId } = Route.useParams(); diff --git a/src/lib/pocketbase/client.ts b/src/lib/pocketbase/client.ts index b11ebd9..d3ce67b 100644 --- a/src/lib/pocketbase/client.ts +++ b/src/lib/pocketbase/client.ts @@ -12,11 +12,21 @@ dotenv.config(); class PocketBaseAdminClient { private pb: PocketBase; public authPromise: Promise; + private refreshInterval: NodeJS.Timeout | null = null; constructor() { this.pb = new PocketBase(process.env.POCKETBASE_URL); - this.pb.beforeSend = (url, options) => { + this.pb.beforeSend = async (url, options) => { + if (this.pb.authStore.isValid && this.isTokenExpiringSoon()) { + try { + await this.refreshAuth(); + } catch (error) { + console.error('Failed to refresh admin token, re-authenticating:', error); + await this.authenticate(); + } + } + options.cache = "no-store"; options.headers = { ...options.headers, @@ -39,16 +49,72 @@ class PocketBaseAdminClient { Object.assign(this, createReactionsService(this.pb)); Object.assign(this, createActivitiesService(this.pb)); Object.assign(this, createBadgesService(this.pb)); + + this.startTokenRefresh(); }); } private async authenticate() { - await this.pb - .collection("_superusers") - .authWithPassword( - process.env.POCKETBASE_ADMIN_EMAIL!, - process.env.POCKETBASE_ADMIN_PASSWORD! - ); + try { + await this.pb + .collection("_superusers") + .authWithPassword( + process.env.POCKETBASE_ADMIN_EMAIL!, + process.env.POCKETBASE_ADMIN_PASSWORD! + ); + console.log('PocketBase admin authenticated successfully'); + } catch (error) { + console.error('Failed to authenticate PocketBase admin:', error); + throw error; + } + } + + private async refreshAuth() { + try { + await this.pb.collection("_superusers").authRefresh(); + console.log('PocketBase admin token refreshed'); + } catch (error) { + console.error('Failed to refresh PocketBase admin token:', error); + throw error; + } + } + + private isTokenExpiringSoon(): boolean { + if (!this.pb.authStore.token) return false; + + try { + const payload = JSON.parse(atob(this.pb.authStore.token.split('.')[1])); + const expiresAt = payload.exp * 1000; + const now = Date.now(); + const fiveMinutes = 5 * 60 * 1000; + + return expiresAt - now < fiveMinutes; + } catch { + return false; + } + } + + private startTokenRefresh() { + this.refreshInterval = setInterval(async () => { + try { + await this.refreshAuth(); + } catch (error) { + console.error('Periodic token refresh failed, re-authenticating:', error); + try { + await this.authenticate(); + } catch (authError) { + console.error('Re-authentication failed:', authError); + } + } + }, 10 * 60 * 1000); + + if (typeof process !== 'undefined') { + process.on('beforeExit', () => { + if (this.refreshInterval) { + clearInterval(this.refreshInterval); + } + }); + } } }