Files
gridpilot.gg/apps/website/lib/apiClient.ts
2025-12-17 14:04:11 +01:00

1161 lines
30 KiB
TypeScript

/**
* Domain-specific API Client for GridPilot Website
*
* This module provides a strongly-typed HTTP client for all API operations.
* The website should use these methods instead of directly importing core use cases.
*/
// ============================================================================
// Types - These mirror the API DTOs
// ============================================================================
// Common Types
export interface DriverDTO {
id: string;
name: string;
avatarUrl?: string;
iracingId?: string;
rating?: number;
}
export interface ProtestViewModel {
id: string;
raceId: string;
complainantId: string;
defendantId: string;
description: string;
status: string;
createdAt: string;
}
export interface LeagueMemberViewModel {
driverId: string;
driver?: DriverDTO;
role: string;
joinedAt: string;
}
export interface StandingEntryViewModel {
driverId: string;
driver?: DriverDTO;
position: number;
points: number;
wins: number;
podiums: number;
races: number;
}
export interface ScheduledRaceViewModel {
id: string;
name: string;
scheduledTime: string;
status: string;
trackName?: string;
}
// League Types
export interface LeagueSummaryViewModel {
id: string;
name: string;
description?: string;
logoUrl?: string;
coverImage?: string;
memberCount: number;
maxMembers: number;
isPublic: boolean;
ownerId: string;
ownerName?: string;
scoringType?: string;
status?: string;
}
export interface AllLeaguesWithCapacityViewModel {
leagues: LeagueSummaryViewModel[];
}
export interface LeagueStatsDto {
totalLeagues: number;
}
export interface LeagueJoinRequestViewModel {
id: string;
leagueId: string;
driverId: string;
requestedAt: Date;
message?: string;
}
export interface LeagueAdminPermissionsViewModel {
canManageMembers: boolean;
canManageRaces: boolean;
canManageSettings: boolean;
canManageProtests: boolean;
isOwner: boolean;
isAdmin: boolean;
}
export interface LeagueOwnerSummaryViewModel {
leagueId: string;
leagueName: string;
memberCount: number;
pendingRequests: number;
}
export interface LeagueConfigFormModelDto {
id: string;
name: string;
description?: string;
isPublic: boolean;
maxMembers: number;
// Add other config fields as needed
}
export interface LeagueAdminProtestsViewModel {
protests: ProtestViewModel[];
}
export interface LeagueSeasonSummaryViewModel {
id: string;
name: string;
startDate?: string;
endDate?: string;
status: string;
}
export interface LeagueMembershipsViewModel {
members: LeagueMemberViewModel[];
}
export interface LeagueStandingsViewModel {
standings: StandingEntryViewModel[];
}
export interface LeagueScheduleViewModel {
races: ScheduledRaceViewModel[];
}
export interface LeagueStatsViewModel {
leagueId: string;
totalRaces: number;
completedRaces: number;
scheduledRaces: number;
averageSOF?: number;
highestSOF?: number;
lowestSOF?: number;
}
export interface LeagueAdminViewModel {
config: LeagueConfigFormModelDto;
members: LeagueMemberViewModel[];
joinRequests: LeagueJoinRequestViewModel[];
}
export interface CreateLeagueInput {
name: string;
description?: string;
isPublic: boolean;
maxMembers: number;
ownerId: string;
}
export interface CreateLeagueOutput {
leagueId: string;
success: boolean;
}
// Driver Types
export interface DriverLeaderboardItemViewModel {
id: string;
name: string;
avatarUrl?: string;
rating: number;
wins: number;
races: number;
skillLevel: string;
}
export interface DriversLeaderboardViewModel {
drivers: DriverLeaderboardItemViewModel[];
}
export interface DriverStatsDto {
totalDrivers: number;
}
export interface CompleteOnboardingInput {
iracingId: string;
displayName: string;
}
export interface CompleteOnboardingOutput {
driverId: string;
success: boolean;
}
export interface DriverRegistrationStatusViewModel {
isRegistered: boolean;
raceId: string;
driverId: string;
}
// Team Types
export interface TeamSummaryViewModel {
id: string;
name: string;
logoUrl?: string;
memberCount: number;
rating: number;
}
export interface AllTeamsViewModel {
teams: TeamSummaryViewModel[];
}
export interface TeamMemberViewModel {
driverId: string;
driver?: DriverDTO;
role: string;
joinedAt: string;
}
export interface TeamJoinRequestItemViewModel {
id: string;
teamId: string;
driverId: string;
requestedAt: string;
message?: string;
}
export interface TeamDetailsViewModel {
id: string;
name: string;
description?: string;
logoUrl?: string;
memberCount: number;
ownerId: string;
members: TeamMemberViewModel[];
}
export interface TeamMembersViewModel {
members: TeamMemberViewModel[];
}
export interface TeamJoinRequestsViewModel {
requests: TeamJoinRequestItemViewModel[];
}
export interface DriverTeamViewModel {
teamId: string;
teamName: string;
role: string;
joinedAt: Date;
}
export interface CreateTeamInput {
name: string;
description?: string;
ownerId: string;
}
export interface CreateTeamOutput {
teamId: string;
success: boolean;
}
export interface UpdateTeamInput {
name?: string;
description?: string;
logoUrl?: string;
}
export interface UpdateTeamOutput {
success: boolean;
}
// Race Types
export interface RaceListItemViewModel {
id: string;
name: string;
leagueId: string;
leagueName: string;
scheduledTime: string;
status: string;
trackName?: string;
}
export interface AllRacesPageViewModel {
races: RaceListItemViewModel[];
}
export interface RaceStatsDto {
totalRaces: number;
}
// Race Management Types
export interface RaceDetailEntryViewModel {
id: string;
name: string;
country: string;
avatarUrl: string;
rating: number | null;
isCurrentUser: boolean;
}
export interface RaceDetailUserResultViewModel {
position: number;
startPosition: number;
incidents: number;
fastestLap: number;
positionChange: number;
isPodium: boolean;
isClean: boolean;
ratingChange: number | null;
}
export interface RaceDetailRaceViewModel {
id: string;
leagueId: string;
track: string;
car: string;
scheduledAt: string;
sessionType: string;
status: string;
strengthOfField: number | null;
registeredCount?: number;
maxParticipants?: number;
}
export interface RaceDetailLeagueViewModel {
id: string;
name: string;
description: string;
settings: {
maxDrivers?: number;
qualifyingFormat?: string;
};
}
export interface RaceDetailRegistrationViewModel {
isUserRegistered: boolean;
canRegister: boolean;
}
export interface RaceDetailViewModel {
race: RaceDetailRaceViewModel | null;
league: RaceDetailLeagueViewModel | null;
entryList: RaceDetailEntryViewModel[];
registration: RaceDetailRegistrationViewModel;
userResult: RaceDetailUserResultViewModel | null;
error?: string;
}
export interface RacesPageDataRaceViewModel {
id: string;
track: string;
car: string;
scheduledAt: string;
status: string;
leagueId: string;
leagueName: string;
strengthOfField: number | null;
isUpcoming: boolean;
isLive: boolean;
isPast: boolean;
}
export interface RacesPageDataViewModel {
races: RacesPageDataRaceViewModel[];
}
export interface RaceResultViewModel {
driverId: string;
driverName: string;
avatarUrl: string;
position: number;
startPosition: number;
incidents: number;
fastestLap: number;
positionChange: number;
isPodium: boolean;
isClean: boolean;
}
export interface RaceResultsDetailViewModel {
raceId: string;
track: string;
results: RaceResultViewModel[];
}
export interface RaceWithSOFViewModel {
id: string;
track: string;
strengthOfField: number | null;
}
export interface RaceProtestViewModel {
id: string;
protestingDriverId: string;
accusedDriverId: string;
incident: {
lap: number;
description: string;
};
status: string;
filedAt: string;
}
export interface RaceProtestsViewModel {
protests: RaceProtestViewModel[];
driverMap: Record<string, string>;
}
export interface RacePenaltyViewModel {
id: string;
driverId: string;
type: string;
value: number;
reason: string;
issuedBy: string;
issuedAt: string;
notes?: string;
}
export interface RacePenaltiesViewModel {
penalties: RacePenaltyViewModel[];
driverMap: Record<string, string>;
}
export interface RegisterForRaceParams {
leagueId: string;
driverId: string;
}
export interface WithdrawFromRaceParams {
driverId: string;
}
export interface ImportRaceResultsInput {
resultsFileContent: string;
}
export interface ImportRaceResultsSummaryViewModel {
success: boolean;
raceId: string;
driversProcessed: number;
resultsRecorded: number;
errors?: string[];
}
// Sponsor Types
export interface GetEntitySponsorshipPricingResultDto {
mainSlotPrice: number;
secondarySlotPrice: number;
currency: string;
}
export interface SponsorViewModel {
id: string;
name: string;
logoUrl?: string;
websiteUrl?: string;
}
export interface GetSponsorsOutput {
sponsors: SponsorViewModel[];
}
export interface CreateSponsorInput {
name: string;
logoUrl?: string;
websiteUrl?: string;
userId: string;
}
export interface CreateSponsorOutput {
sponsorId: string;
success: boolean;
}
export interface SponsorDashboardDTO {
sponsorId: string;
sponsorName: string;
totalSponsorships: number;
activeSponsorships: number;
totalInvestment: number;
}
export interface SponsorshipDetailViewModel {
id: string;
leagueId: string;
leagueName: string;
seasonId: string;
tier: 'main' | 'secondary';
status: string;
amount: number;
currency: string;
}
export interface SponsorSponsorshipsDTO {
sponsorId: string;
sponsorName: string;
sponsorships: SponsorshipDetailViewModel[];
}
// Media Types
export interface RequestAvatarGenerationInput {
driverId: string;
style?: string;
}
export interface RequestAvatarGenerationOutput {
success: boolean;
avatarUrl?: string;
error?: string;
}
// Analytics Types
export interface RecordPageViewInput {
path: string;
userId?: string;
sessionId?: string;
}
export interface RecordPageViewOutput {
success: boolean;
}
export interface RecordEngagementInput {
eventType: string;
eventData?: Record<string, unknown>;
userId?: string;
sessionId?: string;
}
export interface RecordEngagementOutput {
success: boolean;
}
// Auth Types
export interface LoginParams {
email: string;
password: string;
}
export interface SignupParams {
email: string;
password: string;
displayName: string;
}
export interface SessionData {
userId: string;
email: string;
displayName?: string;
driverId?: string;
isAuthenticated: boolean;
}
// Payments Types
export interface PaymentViewModel {
id: string;
amount: number;
currency: string;
status: string;
createdAt: string;
}
export interface GetPaymentsOutput {
payments: PaymentViewModel[];
}
export interface CreatePaymentInput {
amount: number;
currency: string;
leagueId: string;
driverId: string;
description?: string;
}
export interface CreatePaymentOutput {
paymentId: string;
success: boolean;
}
export interface MembershipFeeViewModel {
leagueId: string;
amount: number;
currency: string;
period: string;
}
export interface MemberPaymentViewModel {
driverId: string;
amount: number;
paidAt: string;
status: string;
}
export interface GetMembershipFeesOutput {
fees: MembershipFeeViewModel[];
memberPayments: MemberPaymentViewModel[];
}
export interface PrizeViewModel {
id: string;
name: string;
amount: number;
currency: string;
position?: number;
}
export interface GetPrizesOutput {
prizes: PrizeViewModel[];
}
export interface WalletTransactionViewModel {
id: string;
type: 'deposit' | 'withdrawal';
amount: number;
description?: string;
createdAt: string;
}
export interface WalletViewModel {
driverId: string;
balance: number;
currency: string;
transactions: WalletTransactionViewModel[];
}
export interface GetWalletOutput {
wallet: WalletViewModel;
}
// ============================================================================
// Base API Client
// ============================================================================
class BaseApiClient {
private baseUrl: string;
constructor(baseUrl: string) {
this.baseUrl = baseUrl;
}
protected async request<T>(method: string, path: string, data?: object): Promise<T> {
const headers: HeadersInit = {
'Content-Type': 'application/json',
};
const config: RequestInit = {
method,
headers,
credentials: 'include', // Include cookies for auth
};
if (data) {
config.body = JSON.stringify(data);
}
const response = await fetch(`${this.baseUrl}${path}`, config);
if (!response.ok) {
let errorData: { message?: string } = { message: response.statusText };
try {
errorData = await response.json();
} catch {
// Keep default error message
}
throw new Error(errorData.message || `API request failed with status ${response.status}`);
}
const text = await response.text();
if (!text) {
return null as T;
}
return JSON.parse(text) as T;
}
protected get<T>(path: string): Promise<T> {
return this.request<T>('GET', path);
}
protected post<T>(path: string, data: object): Promise<T> {
return this.request<T>('POST', path, data);
}
protected put<T>(path: string, data: object): Promise<T> {
return this.request<T>('PUT', path, data);
}
protected delete<T>(path: string): Promise<T> {
return this.request<T>('DELETE', path);
}
protected patch<T>(path: string, data: object): Promise<T> {
return this.request<T>('PATCH', path, data);
}
}
// ============================================================================
// Domain-Specific API Clients
// ============================================================================
class LeaguesApiClient extends BaseApiClient {
constructor(baseUrl: string) {
super(baseUrl);
}
/** Get all leagues with capacity information */
getAllWithCapacity(): Promise<AllLeaguesWithCapacityViewModel> {
return this.get<AllLeaguesWithCapacityViewModel>('/leagues/all-with-capacity');
}
/** Get total number of leagues */
getTotal(): Promise<LeagueStatsDto> {
return this.get<LeagueStatsDto>('/leagues/total-leagues');
}
/** Get league standings */
getStandings(leagueId: string): Promise<LeagueStandingsViewModel> {
return this.get<LeagueStandingsViewModel>(`/leagues/${leagueId}/standings`);
}
/** Get league schedule */
getSchedule(leagueId: string): Promise<LeagueScheduleViewModel> {
return this.get<LeagueScheduleViewModel>(`/leagues/${leagueId}/schedule`);
}
/** Get league stats */
getStats(leagueId: string): Promise<LeagueStatsViewModel> {
return this.get<LeagueStatsViewModel>(`/leagues/${leagueId}/stats`);
}
/** Get league memberships */
getMemberships(leagueId: string): Promise<LeagueMembershipsViewModel> {
return this.get<LeagueMembershipsViewModel>(`/leagues/${leagueId}/memberships`);
}
/** Get league join requests */
getJoinRequests(leagueId: string): Promise<LeagueJoinRequestViewModel[]> {
return this.get<LeagueJoinRequestViewModel[]>(`/leagues/${leagueId}/join-requests`);
}
/** Approve a join request */
approveJoinRequest(leagueId: string, requestId: string): Promise<{ success: boolean }> {
return this.post<{ success: boolean }>(`/leagues/${leagueId}/join-requests/approve`, { requestId });
}
/** Reject a join request */
rejectJoinRequest(leagueId: string, requestId: string, reason?: string): Promise<{ success: boolean }> {
return this.post<{ success: boolean }>(`/leagues/${leagueId}/join-requests/reject`, { requestId, reason });
}
/** Get league admin permissions */
getAdminPermissions(leagueId: string, performerDriverId: string): Promise<LeagueAdminPermissionsViewModel> {
return this.get<LeagueAdminPermissionsViewModel>(`/leagues/${leagueId}/permissions/${performerDriverId}`);
}
/** Get league owner summary */
getOwnerSummary(leagueId: string, ownerId: string): Promise<LeagueOwnerSummaryViewModel | null> {
return this.get<LeagueOwnerSummaryViewModel | null>(`/leagues/${leagueId}/owner-summary/${ownerId}`);
}
/** Get league full config */
getConfig(leagueId: string): Promise<LeagueConfigFormModelDto | null> {
return this.get<LeagueConfigFormModelDto | null>(`/leagues/${leagueId}/config`);
}
/** Get league protests */
getProtests(leagueId: string): Promise<LeagueAdminProtestsViewModel> {
return this.get<LeagueAdminProtestsViewModel>(`/leagues/${leagueId}/protests`);
}
/** Get league seasons */
getSeasons(leagueId: string): Promise<LeagueSeasonSummaryViewModel[]> {
return this.get<LeagueSeasonSummaryViewModel[]>(`/leagues/${leagueId}/seasons`);
}
/** Get league admin data */
getAdmin(leagueId: string): Promise<LeagueAdminViewModel> {
return this.get<LeagueAdminViewModel>(`/leagues/${leagueId}/admin`);
}
/** Create a new league */
create(input: CreateLeagueInput): Promise<CreateLeagueOutput> {
return this.post<CreateLeagueOutput>('/leagues', input);
}
/** Remove a member from league */
removeMember(leagueId: string, performerDriverId: string, targetDriverId: string): Promise<{ success: boolean }> {
return this.patch<{ success: boolean }>(`/leagues/${leagueId}/members/${targetDriverId}/remove`, { performerDriverId });
}
/** Update member role */
updateMemberRole(leagueId: string, performerDriverId: string, targetDriverId: string, newRole: string): Promise<{ success: boolean }> {
return this.patch<{ success: boolean }>(`/leagues/${leagueId}/members/${targetDriverId}/role`, { performerDriverId, newRole });
}
}
class DriversApiClient extends BaseApiClient {
constructor(baseUrl: string) {
super(baseUrl);
}
/** Get drivers leaderboard */
getLeaderboard(): Promise<DriversLeaderboardViewModel> {
return this.get<DriversLeaderboardViewModel>('/drivers/leaderboard');
}
/** Get total number of drivers */
getTotal(): Promise<DriverStatsDto> {
return this.get<DriverStatsDto>('/drivers/total-drivers');
}
/** Get current driver (based on session) */
getCurrent(): Promise<DriverDTO | null> {
return this.get<DriverDTO | null>('/drivers/current');
}
/** Complete driver onboarding */
completeOnboarding(input: CompleteOnboardingInput): Promise<CompleteOnboardingOutput> {
return this.post<CompleteOnboardingOutput>('/drivers/complete-onboarding', input);
}
/** Get driver registration status for a race */
getRegistrationStatus(driverId: string, raceId: string): Promise<DriverRegistrationStatusViewModel> {
return this.get<DriverRegistrationStatusViewModel>(`/drivers/${driverId}/races/${raceId}/registration-status`);
}
}
class TeamsApiClient extends BaseApiClient {
constructor(baseUrl: string) {
super(baseUrl);
}
/** Get all teams */
getAll(): Promise<AllTeamsViewModel> {
return this.get<AllTeamsViewModel>('/teams/all');
}
/** Get team details */
getDetails(teamId: string): Promise<TeamDetailsViewModel | null> {
return this.get<TeamDetailsViewModel | null>(`/teams/${teamId}`);
}
/** Get team members */
getMembers(teamId: string): Promise<TeamMembersViewModel> {
return this.get<TeamMembersViewModel>(`/teams/${teamId}/members`);
}
/** Get team join requests */
getJoinRequests(teamId: string): Promise<TeamJoinRequestsViewModel> {
return this.get<TeamJoinRequestsViewModel>(`/teams/${teamId}/join-requests`);
}
/** Approve a join request */
approveJoinRequest(teamId: string, requestId: string): Promise<{ success: boolean }> {
return this.post<{ success: boolean }>(`/teams/${teamId}/join-requests/approve`, { requestId });
}
/** Reject a join request */
rejectJoinRequest(teamId: string, requestId: string, reason?: string): Promise<{ success: boolean }> {
return this.post<{ success: boolean }>(`/teams/${teamId}/join-requests/reject`, { requestId, reason });
}
/** Create a new team */
create(input: CreateTeamInput): Promise<CreateTeamOutput> {
return this.post<CreateTeamOutput>('/teams', input);
}
/** Update team */
update(teamId: string, input: UpdateTeamInput): Promise<UpdateTeamOutput> {
return this.patch<UpdateTeamOutput>(`/teams/${teamId}`, input);
}
/** Get driver's team */
getDriverTeam(driverId: string): Promise<DriverTeamViewModel | null> {
return this.get<DriverTeamViewModel | null>(`/teams/driver/${driverId}`);
}
}
class RacesApiClient extends BaseApiClient {
constructor(baseUrl: string) {
super(baseUrl);
}
/** Get all races */
getAll(): Promise<AllRacesPageViewModel> {
return this.get<AllRacesPageViewModel>('/races/all');
}
/** Get total number of races */
getTotal(): Promise<RaceStatsDto> {
return this.get<RaceStatsDto>('/races/total-races');
}
/** Get races page data */
getPageData(): Promise<RacesPageDataViewModel> {
return this.get<RacesPageDataViewModel>('/races/page-data');
}
/** Get all races page data */
getAllPageData(): Promise<RacesPageDataViewModel> {
return this.get<RacesPageDataViewModel>('/races/all/page-data');
}
/** Get race detail */
getDetail(raceId: string, driverId: string): Promise<RaceDetailViewModel> {
return this.get<RaceDetailViewModel>(`/races/${raceId}?driverId=${driverId}`);
}
/** Get race results detail */
getResultsDetail(raceId: string): Promise<RaceResultsDetailViewModel> {
return this.get<RaceResultsDetailViewModel>(`/races/${raceId}/results`);
}
/** Get race with strength of field */
getWithSOF(raceId: string): Promise<RaceWithSOFViewModel> {
return this.get<RaceWithSOFViewModel>(`/races/${raceId}/sof`);
}
/** Get race protests */
getProtests(raceId: string): Promise<RaceProtestsViewModel> {
return this.get<RaceProtestsViewModel>(`/races/${raceId}/protests`);
}
/** Get race penalties */
getPenalties(raceId: string): Promise<RacePenaltiesViewModel> {
return this.get<RacePenaltiesViewModel>(`/races/${raceId}/penalties`);
}
/** Register for race */
register(raceId: string, params: RegisterForRaceParams): Promise<void> {
return this.post<void>(`/races/${raceId}/register`, params);
}
/** Withdraw from race */
withdraw(raceId: string, params: WithdrawFromRaceParams): Promise<void> {
return this.post<void>(`/races/${raceId}/withdraw`, params);
}
/** Cancel race */
cancel(raceId: string): Promise<void> {
return this.post<void>(`/races/${raceId}/cancel`, {});
}
/** Complete race */
complete(raceId: string): Promise<void> {
return this.post<void>(`/races/${raceId}/complete`, {});
}
/** Import race results */
importResults(raceId: string, input: ImportRaceResultsInput): Promise<ImportRaceResultsSummaryViewModel> {
return this.post<ImportRaceResultsSummaryViewModel>(`/races/${raceId}/import-results`, input);
}
}
class SponsorsApiClient extends BaseApiClient {
constructor(baseUrl: string) {
super(baseUrl);
}
/** Get sponsorship pricing */
getPricing(): Promise<GetEntitySponsorshipPricingResultDto> {
return this.get<GetEntitySponsorshipPricingResultDto>('/sponsors/pricing');
}
/** Get all sponsors */
getAll(): Promise<GetSponsorsOutput> {
return this.get<GetSponsorsOutput>('/sponsors');
}
/** Create a new sponsor */
create(input: CreateSponsorInput): Promise<CreateSponsorOutput> {
return this.post<CreateSponsorOutput>('/sponsors', input);
}
/** Get sponsor dashboard */
getDashboard(sponsorId: string): Promise<SponsorDashboardDTO | null> {
return this.get<SponsorDashboardDTO | null>(`/sponsors/dashboard/${sponsorId}`);
}
/** Get sponsor sponsorships */
getSponsorships(sponsorId: string): Promise<SponsorSponsorshipsDTO | null> {
return this.get<SponsorSponsorshipsDTO | null>(`/sponsors/${sponsorId}/sponsorships`);
}
}
class MediaApiClient extends BaseApiClient {
constructor(baseUrl: string) {
super(baseUrl);
}
/** Request avatar generation */
requestAvatarGeneration(input: RequestAvatarGenerationInput): Promise<RequestAvatarGenerationOutput> {
return this.post<RequestAvatarGenerationOutput>('/media/avatar/generate', input);
}
}
class AnalyticsApiClient extends BaseApiClient {
constructor(baseUrl: string) {
super(baseUrl);
}
/** Record a page view */
recordPageView(input: RecordPageViewInput): Promise<RecordPageViewOutput> {
return this.post<RecordPageViewOutput>('/analytics/page-view', input);
}
/** Record an engagement event */
recordEngagement(input: RecordEngagementInput): Promise<RecordEngagementOutput> {
return this.post<RecordEngagementOutput>('/analytics/engagement', input);
}
}
class AuthApiClient extends BaseApiClient {
constructor(baseUrl: string) {
super(baseUrl);
}
/** Sign up with email */
signup(params: SignupParams): Promise<SessionData> {
return this.post<SessionData>('/auth/signup', params);
}
/** Login with email */
login(params: LoginParams): Promise<SessionData> {
return this.post<SessionData>('/auth/login', params);
}
/** Get current session */
getSession(): Promise<SessionData | null> {
return this.get<SessionData | null>('/auth/session');
}
/** Logout */
logout(): Promise<void> {
return this.post<void>('/auth/logout', {});
}
/** Start iRacing auth redirect */
getIracingAuthUrl(returnTo?: string): string {
const baseUrl = process.env.NEXT_PUBLIC_API_BASE_URL || 'http://localhost:3001';
const params = returnTo ? `?returnTo=${encodeURIComponent(returnTo)}` : '';
return `${baseUrl}/auth/iracing/start${params}`;
}
}
class PaymentsApiClient extends BaseApiClient {
constructor(baseUrl: string) {
super(baseUrl);
}
/** Get payments */
getPayments(leagueId?: string, driverId?: string): Promise<GetPaymentsOutput> {
const params = new URLSearchParams();
if (leagueId) params.append('leagueId', leagueId);
if (driverId) params.append('driverId', driverId);
const query = params.toString();
return this.get<GetPaymentsOutput>(`/payments${query ? `?${query}` : ''}`);
}
/** Create a payment */
createPayment(input: CreatePaymentInput): Promise<CreatePaymentOutput> {
return this.post<CreatePaymentOutput>('/payments', input);
}
/** Update payment status */
updatePaymentStatus(paymentId: string, status: string): Promise<{ success: boolean }> {
return this.patch<{ success: boolean }>('/payments/status', { paymentId, status });
}
/** Get membership fees */
getMembershipFees(leagueId: string): Promise<GetMembershipFeesOutput> {
return this.get<GetMembershipFeesOutput>(`/payments/membership-fees?leagueId=${leagueId}`);
}
/** Upsert membership fee */
upsertMembershipFee(leagueId: string, amount: number, currency: string, period: string): Promise<{ success: boolean }> {
return this.post<{ success: boolean }>('/payments/membership-fees', { leagueId, amount, currency, period });
}
/** Update member payment */
updateMemberPayment(leagueId: string, driverId: string, amount: number, paidAt: string): Promise<{ success: boolean }> {
return this.patch<{ success: boolean }>('/payments/membership-fees/member-payment', { leagueId, driverId, amount, paidAt });
}
/** Get prizes */
getPrizes(leagueId?: string, seasonId?: string): Promise<GetPrizesOutput> {
const params = new URLSearchParams();
if (leagueId) params.append('leagueId', leagueId);
if (seasonId) params.append('seasonId', seasonId);
const query = params.toString();
return this.get<GetPrizesOutput>(`/payments/prizes${query ? `?${query}` : ''}`);
}
/** Create a prize */
createPrize(name: string, amount: number, currency: string, leagueId: string, position?: number): Promise<{ prizeId: string; success: boolean }> {
return this.post<{ prizeId: string; success: boolean }>('/payments/prizes', { name, amount, currency, leagueId, position });
}
/** Award a prize */
awardPrize(prizeId: string, driverId: string): Promise<{ success: boolean }> {
return this.patch<{ success: boolean }>('/payments/prizes/award', { prizeId, driverId });
}
/** Delete a prize */
deletePrize(prizeId: string): Promise<{ success: boolean }> {
return this.delete<{ success: boolean }>(`/payments/prizes?prizeId=${prizeId}`);
}
/** Get wallet */
getWallet(driverId: string): Promise<GetWalletOutput> {
return this.get<GetWalletOutput>(`/payments/wallets?driverId=${driverId}`);
}
/** Process wallet transaction */
processWalletTransaction(driverId: string, type: 'deposit' | 'withdrawal', amount: number, description?: string): Promise<{ success: boolean }> {
return this.post<{ success: boolean }>('/payments/wallets/transactions', { driverId, type, amount, description });
}
}
// ============================================================================
// Main API Client with Domain Namespaces
// ============================================================================
class ApiClient {
public readonly leagues: LeaguesApiClient;
public readonly drivers: DriversApiClient;
public readonly teams: TeamsApiClient;
public readonly races: RacesApiClient;
public readonly sponsors: SponsorsApiClient;
public readonly media: MediaApiClient;
public readonly analytics: AnalyticsApiClient;
public readonly auth: AuthApiClient;
public readonly payments: PaymentsApiClient;
constructor(baseUrl: string) {
this.leagues = new LeaguesApiClient(baseUrl);
this.drivers = new DriversApiClient(baseUrl);
this.teams = new TeamsApiClient(baseUrl);
this.races = new RacesApiClient(baseUrl);
this.sponsors = new SponsorsApiClient(baseUrl);
this.media = new MediaApiClient(baseUrl);
this.analytics = new AnalyticsApiClient(baseUrl);
this.auth = new AuthApiClient(baseUrl);
this.payments = new PaymentsApiClient(baseUrl);
}
}
// ============================================================================
// Singleton Instance
// ============================================================================
const API_BASE_URL = process.env.NEXT_PUBLIC_API_BASE_URL || 'http://localhost:3001';
export const apiClient = new ApiClient(API_BASE_URL);
// Default export for convenience
export default apiClient;