import PocketBase from "pocketbase"; import { createPlayersService } from "./services/players"; import { createTournamentsService } from "./services/tournaments"; import { createTeamsService } from "./services/teams"; import { createMatchesService } from "./services/matches"; import { createReactionsService } from "./services/reactions"; import { createActivitiesService } from "./services/activities"; import { createBadgesService } from "./services/badges"; import dotenv from 'dotenv'; 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 = async (url, options) => { await this.authPromise; 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, "Cache-Control": "no-cache, no-store, must-revalidate", Pragma: "no-cache", Expires: "0", }; return { url, options }; }; this.pb.autoCancellation(false); Object.assign(this, createPlayersService(this.pb)); Object.assign(this, createTeamsService(this.pb)); Object.assign(this, createTournamentsService(this.pb)); Object.assign(this, createMatchesService(this.pb)); Object.assign(this, createReactionsService(this.pb)); Object.assign(this, createActivitiesService(this.pb)); Object.assign(this, createBadgesService(this.pb)); this.authPromise = this.authenticate(); this.authPromise.then(() => { this.startTokenRefresh(); }); } private async authenticate() { try { await this.pb .collection("_superusers") .authWithPassword( process.env.POCKETBASE_ADMIN_EMAIL!, process.env.POCKETBASE_ADMIN_PASSWORD! ); } catch (error) { console.error('Failed to authenticate PocketBase admin:', error); throw error; } } private async refreshAuth() { try { await this.pb.collection("_superusers").authRefresh(); } 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); } }); } } } interface AdminClient extends PocketBaseAdminClient, ReturnType, ReturnType, ReturnType, ReturnType, ReturnType, ReturnType, ReturnType { authPromise: Promise; } export const pbAdmin = new PocketBaseAdminClient() as AdminClient;