This commit is contained in:
2025-12-31 19:55:43 +01:00
parent 8260bf7baf
commit 167e82a52b
66 changed files with 5124 additions and 228 deletions

View File

@@ -4,6 +4,9 @@ import { LoginParamsDTO } from '../../types/generated/LoginParamsDTO';
import { SignupParamsDTO } from '../../types/generated/SignupParamsDTO';
import { LoginWithIracingCallbackParamsDTO } from '../../types/generated/LoginWithIracingCallbackParamsDTO';
import { IracingAuthRedirectResultDTO } from '../../types/generated/IracingAuthRedirectResultDTO';
import { ForgotPasswordDTO } from '../../types/generated/ForgotPasswordDTO';
import { ResetPasswordDTO } from '../../types/generated/ResetPasswordDTO';
import { DemoLoginDTO } from '../../types/generated/DemoLoginDTO';
/**
* Auth API Client
@@ -58,4 +61,19 @@ export class AuthApiClient extends BaseApiClient {
}
return this.get<AuthSessionDTO>(`/auth/iracing/callback?${query.toString()}`);
}
/** Forgot password - send reset link */
forgotPassword(params: ForgotPasswordDTO): Promise<{ message: string; magicLink?: string }> {
return this.post<{ message: string; magicLink?: string }>('/auth/forgot-password', params);
}
/** Reset password with token */
resetPassword(params: ResetPasswordDTO): Promise<{ message: string }> {
return this.post<{ message: string }>('/auth/reset-password', params);
}
/** Demo login (development only) */
demoLogin(params: DemoLoginDTO): Promise<AuthSessionDTO> {
return this.post<AuthSessionDTO>('/auth/demo-login', params);
}
}

View File

@@ -63,7 +63,24 @@ export function isAlpha(): boolean {
* Get list of public routes that are always accessible
*/
export function getPublicRoutes(): readonly string[] {
return ['/', '/api/signup'] as const;
return [
'/',
'/api/signup',
'/api/auth/signup',
'/api/auth/login',
'/api/auth/forgot-password',
'/api/auth/reset-password',
'/api/auth/demo-login',
'/api/auth/session',
'/api/auth/logout',
'/auth/login',
'/auth/signup',
'/auth/forgot-password',
'/auth/reset-password',
'/auth/iracing',
'/auth/iracing/start',
'/auth/iracing/callback',
] as const;
}
/**

View File

@@ -3,6 +3,9 @@ import { SessionViewModel } from '../../view-models/SessionViewModel';
import type { LoginParamsDTO } from '../../types/generated/LoginParamsDTO';
import type { SignupParamsDTO } from '../../types/generated/SignupParamsDTO';
import type { LoginWithIracingCallbackParamsDTO } from '../../types/generated/LoginWithIracingCallbackParamsDTO';
import type { ForgotPasswordDTO } from '../../types/generated/ForgotPasswordDTO';
import type { ResetPasswordDTO } from '../../types/generated/ResetPasswordDTO';
import type { DemoLoginDTO } from '../../types/generated/DemoLoginDTO';
/**
* Auth Service
@@ -68,4 +71,38 @@ export class AuthService {
throw error;
}
}
/**
* Forgot password - send reset link
*/
async forgotPassword(params: ForgotPasswordDTO): Promise<{ message: string; magicLink?: string }> {
try {
return await this.apiClient.forgotPassword(params);
} catch (error) {
throw error;
}
}
/**
* Reset password with token
*/
async resetPassword(params: ResetPasswordDTO): Promise<{ message: string }> {
try {
return await this.apiClient.resetPassword(params);
} catch (error) {
throw error;
}
}
/**
* Demo login (development only)
*/
async demoLogin(params: DemoLoginDTO): Promise<SessionViewModel> {
try {
const dto = await this.apiClient.demoLogin(params);
return new SessionViewModel(dto.user);
} catch (error) {
throw error;
}
}
}

View File

@@ -9,4 +9,6 @@ export interface AuthenticatedUserDTO {
userId: string;
email: string;
displayName: string;
primaryDriverId?: string;
avatarUrl?: string | null;
}

View File

@@ -0,0 +1,3 @@
export interface DemoLoginDTO {
role: 'driver' | 'sponsor';
}

View File

@@ -0,0 +1,3 @@
export interface ForgotPasswordDTO {
email: string;
}

View File

@@ -0,0 +1,4 @@
export interface ResetPasswordDTO {
token: string;
newPassword: string;
}

View File

@@ -4,18 +4,22 @@ export class SessionViewModel {
userId: string;
email: string;
displayName: string;
avatarUrl?: string | null;
constructor(dto: AuthenticatedUserDTO) {
this.userId = dto.userId;
this.email = dto.email;
this.displayName = dto.displayName;
const anyDto = dto as unknown as { primaryDriverId?: unknown; driverId?: unknown };
const anyDto = dto as unknown as { primaryDriverId?: unknown; driverId?: unknown; avatarUrl?: unknown };
if (typeof anyDto.primaryDriverId === 'string' && anyDto.primaryDriverId) {
this.driverId = anyDto.primaryDriverId;
} else if (typeof anyDto.driverId === 'string' && anyDto.driverId) {
this.driverId = anyDto.driverId;
}
if (anyDto.avatarUrl !== undefined) {
this.avatarUrl = anyDto.avatarUrl as string | null;
}
}
// Note: The generated DTO doesn't have these fields
@@ -32,12 +36,14 @@ export class SessionViewModel {
email: string;
displayName: string;
primaryDriverId?: string | null;
avatarUrl?: string | null;
} {
return {
userId: this.userId,
email: this.email,
displayName: this.displayName,
primaryDriverId: this.driverId ?? null,
avatarUrl: this.avatarUrl,
};
}