website refactor
This commit is contained in:
@@ -16,6 +16,7 @@ export interface BaseApiClientOptions {
|
||||
timeout?: number;
|
||||
retry?: boolean;
|
||||
retryConfig?: typeof DEFAULT_RETRY_CONFIG;
|
||||
allowUnauthenticated?: boolean;
|
||||
}
|
||||
|
||||
export class BaseApiClient {
|
||||
|
||||
@@ -28,7 +28,7 @@ export class DriversApiClient extends BaseApiClient {
|
||||
|
||||
/** Get current driver (based on session) */
|
||||
getCurrent(): Promise<GetDriverOutputDTO | null> {
|
||||
return this.get<GetDriverOutputDTO | null>('/drivers/current');
|
||||
return this.get<GetDriverOutputDTO | null>('/drivers/current', { allowUnauthenticated: true });
|
||||
}
|
||||
|
||||
/** Get driver registration status for a specific race */
|
||||
|
||||
24
apps/website/lib/builders/view-data/HomeViewDataBuilder.ts
Normal file
24
apps/website/lib/builders/view-data/HomeViewDataBuilder.ts
Normal file
@@ -0,0 +1,24 @@
|
||||
import type { HomeViewData } from '@/templates/HomeTemplate';
|
||||
import type { HomeDataDTO } from '@/lib/types/dtos/HomeDataDTO';
|
||||
|
||||
/**
|
||||
* HomeViewDataBuilder
|
||||
*
|
||||
* Transforms HomeDataDTO to HomeViewData.
|
||||
*/
|
||||
export class HomeViewDataBuilder {
|
||||
/**
|
||||
* Build HomeViewData from HomeDataDTO
|
||||
*
|
||||
* @param apiDto - The API DTO
|
||||
* @returns HomeViewData
|
||||
*/
|
||||
static build(apiDto: HomeDataDTO): HomeViewData {
|
||||
return {
|
||||
isAlpha: apiDto.isAlpha,
|
||||
upcomingRaces: apiDto.upcomingRaces,
|
||||
topLeagues: apiDto.topLeagues,
|
||||
teams: apiDto.teams,
|
||||
};
|
||||
}
|
||||
}
|
||||
@@ -30,8 +30,10 @@ export function getWebsiteApiBaseUrl(): string {
|
||||
);
|
||||
}
|
||||
|
||||
const isDocker = process.env.DOCKER === 'true';
|
||||
|
||||
const fallback =
|
||||
process.env.NODE_ENV === 'development'
|
||||
process.env.NODE_ENV === 'development' && !isDocker
|
||||
? 'http://localhost:3001'
|
||||
: 'http://api:3000';
|
||||
|
||||
|
||||
@@ -9,6 +9,8 @@
|
||||
* Client: Reads from session context or provides mock implementation
|
||||
*/
|
||||
|
||||
import { getWebsiteApiBaseUrl } from '../config/apiBaseUrl';
|
||||
|
||||
// Server-side implementation
|
||||
export class FeatureFlagService {
|
||||
private flags: Set<string>;
|
||||
@@ -41,11 +43,10 @@ export class FeatureFlagService {
|
||||
|
||||
/**
|
||||
* Factory method to create service by fetching from API
|
||||
* Fetches from ${NEXT_PUBLIC_API_BASE_URL || 'http://localhost:3001'}/features
|
||||
* On error, returns empty flags (secure by default)
|
||||
*/
|
||||
static async fromAPI(): Promise<FeatureFlagService> {
|
||||
const baseUrl = process.env.NEXT_PUBLIC_API_BASE_URL || 'http://localhost:3001';
|
||||
const baseUrl = getWebsiteApiBaseUrl();
|
||||
const url = `${baseUrl}/features`;
|
||||
|
||||
try {
|
||||
|
||||
50
apps/website/lib/page-queries/HomePageQuery.ts
Normal file
50
apps/website/lib/page-queries/HomePageQuery.ts
Normal file
@@ -0,0 +1,50 @@
|
||||
import { Result } from '@/lib/contracts/Result';
|
||||
import { HomeService } from '@/lib/services/home/HomeService';
|
||||
import type { HomeViewData } from '@/templates/HomeTemplate';
|
||||
import type { PageQuery } from '@/lib/contracts/page-queries/PageQuery';
|
||||
import { HomeViewDataBuilder } from '@/lib/builders/view-data/HomeViewDataBuilder';
|
||||
|
||||
/**
|
||||
* HomePageQuery
|
||||
*
|
||||
* Server-side data fetcher for the home page.
|
||||
* Returns Result<HomeViewData, string>
|
||||
*/
|
||||
export class HomePageQuery implements PageQuery<HomeViewData, void, string> {
|
||||
/**
|
||||
* Execute the home page query
|
||||
*
|
||||
* @returns Result with HomeViewData or error
|
||||
*/
|
||||
async execute(): Promise<Result<HomeViewData, string>> {
|
||||
try {
|
||||
const service = new HomeService();
|
||||
const result = await service.getHomeData();
|
||||
|
||||
if (result.isErr()) {
|
||||
return Result.err('Error');
|
||||
}
|
||||
|
||||
const viewData = HomeViewDataBuilder.build(result.unwrap());
|
||||
return Result.ok(viewData);
|
||||
} catch (error) {
|
||||
console.error('HomePageQuery failed:', error);
|
||||
return Result.err('Error');
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Static execute for convenience
|
||||
*/
|
||||
static async execute(): Promise<Result<HomeViewData, string>> {
|
||||
return new HomePageQuery().execute();
|
||||
}
|
||||
|
||||
/**
|
||||
* Check if user should be redirected to dashboard
|
||||
*/
|
||||
static async shouldRedirectToDashboard(): Promise<boolean> {
|
||||
const service = new HomeService();
|
||||
return service.shouldRedirectToDashboard();
|
||||
}
|
||||
}
|
||||
@@ -42,7 +42,10 @@ export interface ParsedWizardParams {
|
||||
}
|
||||
|
||||
export class SearchParamParser {
|
||||
private static getParam(params: URLSearchParams | Record<string, string | string[] | undefined>, key: string): string | null {
|
||||
private static getParam(params: URLSearchParams | Record<string, string | string[] | undefined> | undefined | null, key: string): string | null {
|
||||
if (!params) {
|
||||
return null;
|
||||
}
|
||||
if (params instanceof URLSearchParams) {
|
||||
return params.get(key);
|
||||
}
|
||||
@@ -54,7 +57,7 @@ export class SearchParamParser {
|
||||
}
|
||||
|
||||
// Parse auth parameters
|
||||
static parseAuth(params: URLSearchParams | Record<string, string | string[] | undefined>): Result<ParsedAuthParams, string> {
|
||||
static parseAuth(params: URLSearchParams | Record<string, string | string[] | undefined> | undefined | null): Result<ParsedAuthParams, string> {
|
||||
const errors: string[] = [];
|
||||
|
||||
const returnTo = this.getParam(params, 'returnTo');
|
||||
@@ -95,7 +98,7 @@ export class SearchParamParser {
|
||||
}
|
||||
|
||||
// Parse sponsor parameters
|
||||
static parseSponsor(params: URLSearchParams | Record<string, string | string[] | undefined>): Result<ParsedSponsorParams, string> {
|
||||
static parseSponsor(params: URLSearchParams | Record<string, string | string[] | undefined> | undefined | null): Result<ParsedSponsorParams, string> {
|
||||
const errors: string[] = [];
|
||||
|
||||
const type = this.getParam(params, 'type');
|
||||
@@ -117,7 +120,7 @@ export class SearchParamParser {
|
||||
}
|
||||
|
||||
// Parse pagination parameters
|
||||
static parsePagination(params: URLSearchParams | Record<string, string | string[] | undefined>): Result<ParsedPaginationParams, string> {
|
||||
static parsePagination(params: URLSearchParams | Record<string, string | string[] | undefined> | undefined | null): Result<ParsedPaginationParams, string> {
|
||||
const result: ParsedPaginationParams = {};
|
||||
const errors: string[] = [];
|
||||
|
||||
@@ -157,7 +160,7 @@ export class SearchParamParser {
|
||||
}
|
||||
|
||||
// Parse sorting parameters
|
||||
static parseSorting(params: URLSearchParams | Record<string, string | string[] | undefined>): Result<ParsedSortingParams, string> {
|
||||
static parseSorting(params: URLSearchParams | Record<string, string | string[] | undefined> | undefined | null): Result<ParsedSortingParams, string> {
|
||||
const errors: string[] = [];
|
||||
|
||||
const order = this.getParam(params, 'order');
|
||||
@@ -179,7 +182,7 @@ export class SearchParamParser {
|
||||
}
|
||||
|
||||
// Parse filter parameters
|
||||
static parseFilters(params: URLSearchParams | Record<string, string | string[] | undefined>): Result<ParsedFilterParams, string> {
|
||||
static parseFilters(params: URLSearchParams | Record<string, string | string[] | undefined> | undefined | null): Result<ParsedFilterParams, string> {
|
||||
return Result.ok({
|
||||
status: this.getParam(params, 'status'),
|
||||
role: this.getParam(params, 'role'),
|
||||
@@ -188,14 +191,14 @@ export class SearchParamParser {
|
||||
}
|
||||
|
||||
// Parse wizard parameters
|
||||
static parseWizard(params: URLSearchParams | Record<string, string | string[] | undefined>): Result<ParsedWizardParams, string> {
|
||||
static parseWizard(params: URLSearchParams | Record<string, string | string[] | undefined> | undefined | null): Result<ParsedWizardParams, string> {
|
||||
return Result.ok({
|
||||
step: this.getParam(params, 'step'),
|
||||
});
|
||||
}
|
||||
|
||||
// Parse all parameters at once
|
||||
static parseAll(params: URLSearchParams | Record<string, string | string[] | undefined>): Result<
|
||||
static parseAll(params: URLSearchParams | Record<string, string | string[] | undefined> | undefined | null): Result<
|
||||
{
|
||||
auth: ParsedAuthParams;
|
||||
sponsor: ParsedSponsorParams;
|
||||
|
||||
@@ -14,11 +14,17 @@ import { ConsoleErrorReporter } from '@/lib/infrastructure/logging/ConsoleErrorR
|
||||
import { getWebsiteApiBaseUrl } from '@/lib/config/apiBaseUrl';
|
||||
|
||||
// DTO types
|
||||
import type { HomeViewData } from '@/templates/HomeTemplate';
|
||||
import { Result } from '@/lib/contracts/Result';
|
||||
import type { Service } from '@/lib/contracts/services/Service';
|
||||
import type { HomeDataDTO } from '@/lib/types/dtos/HomeDataDTO';
|
||||
|
||||
export class HomeService {
|
||||
async getHomeData(): Promise<Result<HomeViewData, Error>> {
|
||||
/**
|
||||
* HomeService
|
||||
*
|
||||
* @server-safe
|
||||
*/
|
||||
export class HomeService implements Service {
|
||||
async getHomeData(): Promise<Result<HomeDataDTO, Error>> {
|
||||
try {
|
||||
// Manual wiring: construct dependencies explicitly
|
||||
const baseUrl = getWebsiteApiBaseUrl();
|
||||
|
||||
20
apps/website/lib/types/dtos/HomeDataDTO.ts
Normal file
20
apps/website/lib/types/dtos/HomeDataDTO.ts
Normal file
@@ -0,0 +1,20 @@
|
||||
export interface HomeDataDTO {
|
||||
isAlpha: boolean;
|
||||
upcomingRaces: Array<{
|
||||
id: string;
|
||||
track: string;
|
||||
car: string;
|
||||
formattedDate: string;
|
||||
}>;
|
||||
topLeagues: Array<{
|
||||
id: string;
|
||||
name: string;
|
||||
description: string;
|
||||
}>;
|
||||
teams: Array<{
|
||||
id: string;
|
||||
name: string;
|
||||
description: string;
|
||||
logoUrl?: string;
|
||||
}>;
|
||||
}
|
||||
Reference in New Issue
Block a user