api client refactor
This commit is contained in:
24
apps/website/lib/api/analytics/AnalyticsApiClient.ts
Normal file
24
apps/website/lib/api/analytics/AnalyticsApiClient.ts
Normal file
@@ -0,0 +1,24 @@
|
||||
import { BaseApiClient } from '../base/BaseApiClient';
|
||||
import type {
|
||||
RecordPageViewInputDto,
|
||||
RecordPageViewOutputDto,
|
||||
RecordEngagementInputDto,
|
||||
RecordEngagementOutputDto,
|
||||
} from '../../dtos';
|
||||
|
||||
/**
|
||||
* Analytics API Client
|
||||
*
|
||||
* Handles all analytics-related API operations.
|
||||
*/
|
||||
export class AnalyticsApiClient extends BaseApiClient {
|
||||
/** Record a page view */
|
||||
recordPageView(input: RecordPageViewInputDto): Promise<RecordPageViewOutputDto> {
|
||||
return this.post<RecordPageViewOutputDto>('/analytics/page-view', input);
|
||||
}
|
||||
|
||||
/** Record an engagement event */
|
||||
recordEngagement(input: RecordEngagementInputDto): Promise<RecordEngagementOutputDto> {
|
||||
return this.post<RecordEngagementOutputDto>('/analytics/engagement', input);
|
||||
}
|
||||
}
|
||||
40
apps/website/lib/api/auth/AuthApiClient.ts
Normal file
40
apps/website/lib/api/auth/AuthApiClient.ts
Normal file
@@ -0,0 +1,40 @@
|
||||
import { BaseApiClient } from '../base/BaseApiClient';
|
||||
import type {
|
||||
LoginParamsDto,
|
||||
SignupParamsDto,
|
||||
SessionDataDto,
|
||||
} from '../../dtos';
|
||||
|
||||
/**
|
||||
* Auth API Client
|
||||
*
|
||||
* Handles all authentication-related API operations.
|
||||
*/
|
||||
export class AuthApiClient extends BaseApiClient {
|
||||
/** Sign up with email */
|
||||
signup(params: SignupParamsDto): Promise<SessionDataDto> {
|
||||
return this.post<SessionDataDto>('/auth/signup', params);
|
||||
}
|
||||
|
||||
/** Login with email */
|
||||
login(params: LoginParamsDto): Promise<SessionDataDto> {
|
||||
return this.post<SessionDataDto>('/auth/login', params);
|
||||
}
|
||||
|
||||
/** Get current session */
|
||||
getSession(): Promise<SessionDataDto | null> {
|
||||
return this.get<SessionDataDto | 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}`;
|
||||
}
|
||||
}
|
||||
68
apps/website/lib/api/base/BaseApiClient.ts
Normal file
68
apps/website/lib/api/base/BaseApiClient.ts
Normal file
@@ -0,0 +1,68 @@
|
||||
/**
|
||||
* Base API Client for HTTP operations
|
||||
*
|
||||
* Provides generic HTTP methods with common request/response handling,
|
||||
* error handling, and authentication.
|
||||
*/
|
||||
|
||||
export 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);
|
||||
}
|
||||
}
|
||||
29
apps/website/lib/api/drivers/DriversApiClient.ts
Normal file
29
apps/website/lib/api/drivers/DriversApiClient.ts
Normal file
@@ -0,0 +1,29 @@
|
||||
import { BaseApiClient } from '../base/BaseApiClient';
|
||||
import type {
|
||||
DriversLeaderboardDto,
|
||||
CompleteOnboardingInputDto,
|
||||
CompleteOnboardingOutputDto,
|
||||
DriverDto,
|
||||
} from '../../dtos';
|
||||
|
||||
/**
|
||||
* Drivers API Client
|
||||
*
|
||||
* Handles all driver-related API operations.
|
||||
*/
|
||||
export class DriversApiClient extends BaseApiClient {
|
||||
/** Get drivers leaderboard */
|
||||
getLeaderboard(): Promise<DriversLeaderboardDto> {
|
||||
return this.get<DriversLeaderboardDto>('/drivers/leaderboard');
|
||||
}
|
||||
|
||||
/** Complete driver onboarding */
|
||||
completeOnboarding(input: CompleteOnboardingInputDto): Promise<CompleteOnboardingOutputDto> {
|
||||
return this.post<CompleteOnboardingOutputDto>('/drivers/complete-onboarding', input);
|
||||
}
|
||||
|
||||
/** Get current driver (based on session) */
|
||||
getCurrent(): Promise<DriverDto | null> {
|
||||
return this.get<DriverDto | null>('/drivers/current');
|
||||
}
|
||||
}
|
||||
46
apps/website/lib/api/index.ts
Normal file
46
apps/website/lib/api/index.ts
Normal file
@@ -0,0 +1,46 @@
|
||||
import { LeaguesApiClient } from './leagues/LeaguesApiClient';
|
||||
import { RacesApiClient } from './races/RacesApiClient';
|
||||
import { DriversApiClient } from './drivers/DriversApiClient';
|
||||
import { TeamsApiClient } from './teams/TeamsApiClient';
|
||||
import { SponsorsApiClient } from './sponsors/SponsorsApiClient';
|
||||
import { MediaApiClient } from './media/MediaApiClient';
|
||||
import { AnalyticsApiClient } from './analytics/AnalyticsApiClient';
|
||||
import { AuthApiClient } from './auth/AuthApiClient';
|
||||
import { PaymentsApiClient } from './payments/PaymentsApiClient';
|
||||
|
||||
/**
|
||||
* Main API Client
|
||||
*
|
||||
* Orchestrates all domain-specific API clients with consistent configuration.
|
||||
*/
|
||||
export class ApiClient {
|
||||
public readonly leagues: LeaguesApiClient;
|
||||
public readonly races: RacesApiClient;
|
||||
public readonly drivers: DriversApiClient;
|
||||
public readonly teams: TeamsApiClient;
|
||||
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.races = new RacesApiClient(baseUrl);
|
||||
this.drivers = new DriversApiClient(baseUrl);
|
||||
this.teams = new TeamsApiClient(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 api = new ApiClient(API_BASE_URL);
|
||||
52
apps/website/lib/api/leagues/LeaguesApiClient.ts
Normal file
52
apps/website/lib/api/leagues/LeaguesApiClient.ts
Normal file
@@ -0,0 +1,52 @@
|
||||
import { BaseApiClient } from '../base/BaseApiClient';
|
||||
import type {
|
||||
AllLeaguesWithCapacityDto,
|
||||
LeagueStatsDto,
|
||||
LeagueStandingsDto,
|
||||
LeagueScheduleDto,
|
||||
LeagueMembershipsDto,
|
||||
CreateLeagueInputDto,
|
||||
CreateLeagueOutputDto,
|
||||
} from '../../dtos';
|
||||
|
||||
/**
|
||||
* Leagues API Client
|
||||
*
|
||||
* Handles all league-related API operations.
|
||||
*/
|
||||
export class LeaguesApiClient extends BaseApiClient {
|
||||
/** Get all leagues with capacity information */
|
||||
getAllWithCapacity(): Promise<AllLeaguesWithCapacityDto> {
|
||||
return this.get<AllLeaguesWithCapacityDto>('/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<LeagueStandingsDto> {
|
||||
return this.get<LeagueStandingsDto>(`/leagues/${leagueId}/standings`);
|
||||
}
|
||||
|
||||
/** Get league schedule */
|
||||
getSchedule(leagueId: string): Promise<LeagueScheduleDto> {
|
||||
return this.get<LeagueScheduleDto>(`/leagues/${leagueId}/schedule`);
|
||||
}
|
||||
|
||||
/** Get league memberships */
|
||||
getMemberships(leagueId: string): Promise<LeagueMembershipsDto> {
|
||||
return this.get<LeagueMembershipsDto>(`/leagues/${leagueId}/memberships`);
|
||||
}
|
||||
|
||||
/** Create a new league */
|
||||
create(input: CreateLeagueInputDto): Promise<CreateLeagueOutputDto> {
|
||||
return this.post<CreateLeagueOutputDto>('/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 });
|
||||
}
|
||||
}
|
||||
17
apps/website/lib/api/media/MediaApiClient.ts
Normal file
17
apps/website/lib/api/media/MediaApiClient.ts
Normal file
@@ -0,0 +1,17 @@
|
||||
import { BaseApiClient } from '../base/BaseApiClient';
|
||||
import type {
|
||||
RequestAvatarGenerationInputDto,
|
||||
RequestAvatarGenerationOutputDto,
|
||||
} from '../../dtos';
|
||||
|
||||
/**
|
||||
* Media API Client
|
||||
*
|
||||
* Handles all media-related API operations.
|
||||
*/
|
||||
export class MediaApiClient extends BaseApiClient {
|
||||
/** Request avatar generation */
|
||||
requestAvatarGeneration(input: RequestAvatarGenerationInputDto): Promise<RequestAvatarGenerationOutputDto> {
|
||||
return this.post<RequestAvatarGenerationOutputDto>('/media/avatar/generate', input);
|
||||
}
|
||||
}
|
||||
49
apps/website/lib/api/payments/PaymentsApiClient.ts
Normal file
49
apps/website/lib/api/payments/PaymentsApiClient.ts
Normal file
@@ -0,0 +1,49 @@
|
||||
import { BaseApiClient } from '../base/BaseApiClient';
|
||||
import type {
|
||||
GetPaymentsOutputDto,
|
||||
CreatePaymentInputDto,
|
||||
CreatePaymentOutputDto,
|
||||
GetMembershipFeesOutputDto,
|
||||
GetPrizesOutputDto,
|
||||
GetWalletOutputDto,
|
||||
} from '../../dtos';
|
||||
|
||||
/**
|
||||
* Payments API Client
|
||||
*
|
||||
* Handles all payment-related API operations.
|
||||
*/
|
||||
export class PaymentsApiClient extends BaseApiClient {
|
||||
/** Get payments */
|
||||
getPayments(leagueId?: string, driverId?: string): Promise<GetPaymentsOutputDto> {
|
||||
const params = new URLSearchParams();
|
||||
if (leagueId) params.append('leagueId', leagueId);
|
||||
if (driverId) params.append('driverId', driverId);
|
||||
const query = params.toString();
|
||||
return this.get<GetPaymentsOutputDto>(`/payments${query ? `?${query}` : ''}`);
|
||||
}
|
||||
|
||||
/** Create a payment */
|
||||
createPayment(input: CreatePaymentInputDto): Promise<CreatePaymentOutputDto> {
|
||||
return this.post<CreatePaymentOutputDto>('/payments', input);
|
||||
}
|
||||
|
||||
/** Get membership fees */
|
||||
getMembershipFees(leagueId: string): Promise<GetMembershipFeesOutputDto> {
|
||||
return this.get<GetMembershipFeesOutputDto>(`/payments/membership-fees?leagueId=${leagueId}`);
|
||||
}
|
||||
|
||||
/** Get prizes */
|
||||
getPrizes(leagueId?: string, seasonId?: string): Promise<GetPrizesOutputDto> {
|
||||
const params = new URLSearchParams();
|
||||
if (leagueId) params.append('leagueId', leagueId);
|
||||
if (seasonId) params.append('seasonId', seasonId);
|
||||
const query = params.toString();
|
||||
return this.get<GetPrizesOutputDto>(`/payments/prizes${query ? `?${query}` : ''}`);
|
||||
}
|
||||
|
||||
/** Get wallet */
|
||||
getWallet(driverId: string): Promise<GetWalletOutputDto> {
|
||||
return this.get<GetWalletOutputDto>(`/payments/wallets?driverId=${driverId}`);
|
||||
}
|
||||
}
|
||||
53
apps/website/lib/api/races/RacesApiClient.ts
Normal file
53
apps/website/lib/api/races/RacesApiClient.ts
Normal file
@@ -0,0 +1,53 @@
|
||||
import { BaseApiClient } from '../base/BaseApiClient';
|
||||
import type {
|
||||
RaceStatsDto,
|
||||
RacesPageDataDto,
|
||||
RaceDetailDto,
|
||||
RaceResultsDetailDto,
|
||||
RaceWithSOFDto,
|
||||
RegisterForRaceInputDto,
|
||||
ImportRaceResultsInputDto,
|
||||
ImportRaceResultsSummaryDto,
|
||||
} from '../../dtos';
|
||||
|
||||
/**
|
||||
* Races API Client
|
||||
*
|
||||
* Handles all race-related API operations.
|
||||
*/
|
||||
export class RacesApiClient extends BaseApiClient {
|
||||
/** Get total number of races */
|
||||
getTotal(): Promise<RaceStatsDto> {
|
||||
return this.get<RaceStatsDto>('/races/total-races');
|
||||
}
|
||||
|
||||
/** Get races page data */
|
||||
getPageData(): Promise<RacesPageDataDto> {
|
||||
return this.get<RacesPageDataDto>('/races/page-data');
|
||||
}
|
||||
|
||||
/** Get race detail */
|
||||
getDetail(raceId: string, driverId: string): Promise<RaceDetailDto> {
|
||||
return this.get<RaceDetailDto>(`/races/${raceId}?driverId=${driverId}`);
|
||||
}
|
||||
|
||||
/** Get race results detail */
|
||||
getResultsDetail(raceId: string): Promise<RaceResultsDetailDto> {
|
||||
return this.get<RaceResultsDetailDto>(`/races/${raceId}/results`);
|
||||
}
|
||||
|
||||
/** Get race with strength of field */
|
||||
getWithSOF(raceId: string): Promise<RaceWithSOFDto> {
|
||||
return this.get<RaceWithSOFDto>(`/races/${raceId}/sof`);
|
||||
}
|
||||
|
||||
/** Register for race */
|
||||
register(raceId: string, input: RegisterForRaceInputDto): Promise<void> {
|
||||
return this.post<void>(`/races/${raceId}/register`, input);
|
||||
}
|
||||
|
||||
/** Import race results */
|
||||
importResults(raceId: string, input: ImportRaceResultsInputDto): Promise<ImportRaceResultsSummaryDto> {
|
||||
return this.post<ImportRaceResultsSummaryDto>(`/races/${raceId}/import-results`, input);
|
||||
}
|
||||
}
|
||||
41
apps/website/lib/api/sponsors/SponsorsApiClient.ts
Normal file
41
apps/website/lib/api/sponsors/SponsorsApiClient.ts
Normal file
@@ -0,0 +1,41 @@
|
||||
import { BaseApiClient } from '../base/BaseApiClient';
|
||||
import type {
|
||||
GetEntitySponsorshipPricingResultDto,
|
||||
GetSponsorsOutputDto,
|
||||
CreateSponsorInputDto,
|
||||
CreateSponsorOutputDto,
|
||||
SponsorDashboardDto,
|
||||
SponsorSponsorshipsDto,
|
||||
} from '../../dtos';
|
||||
|
||||
/**
|
||||
* Sponsors API Client
|
||||
*
|
||||
* Handles all sponsor-related API operations.
|
||||
*/
|
||||
export class SponsorsApiClient extends BaseApiClient {
|
||||
/** Get sponsorship pricing */
|
||||
getPricing(): Promise<GetEntitySponsorshipPricingResultDto> {
|
||||
return this.get<GetEntitySponsorshipPricingResultDto>('/sponsors/pricing');
|
||||
}
|
||||
|
||||
/** Get all sponsors */
|
||||
getAll(): Promise<GetSponsorsOutputDto> {
|
||||
return this.get<GetSponsorsOutputDto>('/sponsors');
|
||||
}
|
||||
|
||||
/** Create a new sponsor */
|
||||
create(input: CreateSponsorInputDto): Promise<CreateSponsorOutputDto> {
|
||||
return this.post<CreateSponsorOutputDto>('/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`);
|
||||
}
|
||||
}
|
||||
54
apps/website/lib/api/teams/TeamsApiClient.ts
Normal file
54
apps/website/lib/api/teams/TeamsApiClient.ts
Normal file
@@ -0,0 +1,54 @@
|
||||
import { BaseApiClient } from '../base/BaseApiClient';
|
||||
import type {
|
||||
AllTeamsDto,
|
||||
TeamDetailsDto,
|
||||
TeamMembersDto,
|
||||
TeamJoinRequestsDto,
|
||||
CreateTeamInputDto,
|
||||
CreateTeamOutputDto,
|
||||
UpdateTeamInputDto,
|
||||
UpdateTeamOutputDto,
|
||||
DriverTeamDto,
|
||||
} from '../../dtos';
|
||||
|
||||
/**
|
||||
* Teams API Client
|
||||
*
|
||||
* Handles all team-related API operations.
|
||||
*/
|
||||
export class TeamsApiClient extends BaseApiClient {
|
||||
/** Get all teams */
|
||||
getAll(): Promise<AllTeamsDto> {
|
||||
return this.get<AllTeamsDto>('/teams/all');
|
||||
}
|
||||
|
||||
/** Get team details */
|
||||
getDetails(teamId: string): Promise<TeamDetailsDto | null> {
|
||||
return this.get<TeamDetailsDto | null>(`/teams/${teamId}`);
|
||||
}
|
||||
|
||||
/** Get team members */
|
||||
getMembers(teamId: string): Promise<TeamMembersDto> {
|
||||
return this.get<TeamMembersDto>(`/teams/${teamId}/members`);
|
||||
}
|
||||
|
||||
/** Get team join requests */
|
||||
getJoinRequests(teamId: string): Promise<TeamJoinRequestsDto> {
|
||||
return this.get<TeamJoinRequestsDto>(`/teams/${teamId}/join-requests`);
|
||||
}
|
||||
|
||||
/** Create a new team */
|
||||
create(input: CreateTeamInputDto): Promise<CreateTeamOutputDto> {
|
||||
return this.post<CreateTeamOutputDto>('/teams', input);
|
||||
}
|
||||
|
||||
/** Update team */
|
||||
update(teamId: string, input: UpdateTeamInputDto): Promise<UpdateTeamOutputDto> {
|
||||
return this.patch<UpdateTeamOutputDto>(`/teams/${teamId}`, input);
|
||||
}
|
||||
|
||||
/** Get driver's team */
|
||||
getDriverTeam(driverId: string): Promise<DriverTeamDto | null> {
|
||||
return this.get<DriverTeamDto | null>(`/teams/driver/${driverId}`);
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user