services refactor

This commit is contained in:
2025-12-17 23:37:51 +01:00
parent 1eaa338e86
commit f7a56a92ce
94 changed files with 1894 additions and 3194 deletions

File diff suppressed because it is too large Load Diff

View File

@@ -8,7 +8,7 @@ import { join } from 'path';
import { AppModule } from './app.module'; import { AppModule } from './app.module';
async function bootstrap() { async function bootstrap() {
const app = await NestFactory.create(AppModule); const app = await NestFactory.create(AppModule, process.env.GENERATE_OPENAPI ? { logger: false } : undefined);
// Swagger/OpenAPI configuration // Swagger/OpenAPI configuration
const config = new DocumentBuilder() const config = new DocumentBuilder()
@@ -35,6 +35,8 @@ async function bootstrap() {
const outputPath = join(__dirname, '../openapi.json'); const outputPath = join(__dirname, '../openapi.json');
writeFileSync(outputPath, JSON.stringify(document, null, 2)); writeFileSync(outputPath, JSON.stringify(document, null, 2));
console.log(`✅ OpenAPI spec generated at: ${outputPath}`); console.log(`✅ OpenAPI spec generated at: ${outputPath}`);
await app.close();
process.exit(0);
} }
await app.listen(3000); await app.listen(3000);

View File

@@ -1,14 +0,0 @@
/**
* Auto-generated DTO from OpenAPI spec
* This file is generated by scripts/generate-api-types.ts
* Do not edit manually - regenerate using: npm run api:sync-types
*/
export interface AllLeaguesWithCapacityAndScoringDTO {
type: [LeagueSummaryDTO] })
@IsArray()
@ValidateNested({ each: true })
@Type(() => LeagueSummaryDTO)
leagues: LeagueSummaryDTO[];
totalCount: number;
}

View File

@@ -1,14 +0,0 @@
/**
* Auto-generated DTO from OpenAPI spec
* This file is generated by scripts/generate-api-types.ts
* Do not edit manually - regenerate using: npm run api:sync-types
*/
export interface AllLeaguesWithCapacityDTO {
type: [LeagueWithCapacityDTO] })
@IsArray()
@ValidateNested({ each: true })
@Type(() => LeagueWithCapacityDTO)
leagues: LeagueWithCapacityDTO[];
totalCount: number;
}

View File

@@ -5,6 +5,8 @@
*/ */
export interface ApplyPenaltyCommandDTO { export interface ApplyPenaltyCommandDTO {
raceId: string;
driverId: string;
stewardId: string;
enum: string; enum: string;
required: string;
} }

View File

@@ -6,5 +6,4 @@
export interface ApproveJoinRequestOutputDTO { export interface ApproveJoinRequestOutputDTO {
success: boolean; success: boolean;
required: string;
} }

View File

@@ -4,10 +4,9 @@
* Do not edit manually - regenerate using: npm run api:sync-types * Do not edit manually - regenerate using: npm run api:sync-types
*/ */
import type { AuthenticatedUserDTO } from './AuthenticatedUserDTO';
export interface AuthSessionDTO { export interface AuthSessionDTO {
iracingCustomerId?: string; token: string;
primaryDriverId?: string; user: AuthenticatedUserDTO;
avatarUrl?: string;
returnTo?: string;
required: string;
} }

View File

@@ -5,9 +5,7 @@
*/ */
export interface AuthenticatedUserDTO { export interface AuthenticatedUserDTO {
iracingCustomerId?: string; userId: string;
primaryDriverId?: string; email: string;
avatarUrl?: string; displayName: string;
returnTo?: string;
required: string;
} }

View File

@@ -9,5 +9,4 @@ export interface CompleteOnboardingInputDTO {
lastName: string; lastName: string;
displayName: string; displayName: string;
country: string; country: string;
required: string;
} }

View File

@@ -6,5 +6,4 @@
export interface CompleteOnboardingOutputDTO { export interface CompleteOnboardingOutputDTO {
success: boolean; success: boolean;
required: string;
} }

View File

@@ -7,6 +7,4 @@
export interface CreateLeagueInputDTO { export interface CreateLeagueInputDTO {
name: string; name: string;
description: string; description: string;
enum: string;
ownerId: string;
} }

View File

@@ -7,5 +7,4 @@
export interface CreateSponsorInputDTO { export interface CreateSponsorInputDTO {
name: string; name: string;
contactEmail: string; contactEmail: string;
required: string;
} }

View File

@@ -1,11 +0,0 @@
/**
* Auto-generated DTO from OpenAPI spec
* This file is generated by scripts/generate-api-types.ts
* Do not edit manually - regenerate using: npm run api:sync-types
*/
export interface CreateSponsorOutputDTO {
sponsor: SponsorDTO;
type: SponsorDTO })
sponsor: SponsorDTO;
}

View File

@@ -5,5 +5,8 @@
*/ */
export interface DashboardDriverSummaryDTO { export interface DashboardDriverSummaryDTO {
nullable: string; id: string;
name: string;
country: string;
avatarUrl: string;
} }

View File

@@ -5,6 +5,6 @@
*/ */
export interface DashboardFeedItemSummaryDTO { export interface DashboardFeedItemSummaryDTO {
id: string;
enum: string; enum: string;
required: string;
} }

View File

@@ -5,6 +5,5 @@
*/ */
export interface DashboardFeedSummaryDTO { export interface DashboardFeedSummaryDTO {
type: [DashboardFeedItemSummaryDTO] }) notificationCount: number;
items!: DashboardFeedItemSummaryDTO[];
} }

View File

@@ -4,10 +4,9 @@
* Do not edit manually - regenerate using: npm run api:sync-types * Do not edit manually - regenerate using: npm run api:sync-types
*/ */
export interface DriverDto { export interface DashboardFriendSummaryDTO {
id: string; id: string;
name: string; name: string;
avatarUrl?: string; country: string;
iracingId?: string; avatarUrl: string;
rating?: number;
} }

View File

@@ -4,10 +4,10 @@
* Do not edit manually - regenerate using: npm run api:sync-types * Do not edit manually - regenerate using: npm run api:sync-types
*/ */
export interface CreatePaymentInputDTO { export interface DashboardLeagueStandingSummaryDTO {
enum: string;
amount: number;
payerId: string;
leagueId: string; leagueId: string;
required: string; leagueName: string;
position: number;
totalDrivers: number;
points: number;
} }

View File

@@ -1,12 +0,0 @@
/**
* Auto-generated DTO from OpenAPI spec
* This file is generated by scripts/generate-api-types.ts
* Do not edit manually - regenerate using: npm run api:sync-types
*/
export interface DashboardOverviewDTO {
nullable: true })
currentDriver!: DashboardDriverSummaryDTO null;
type: [DashboardRaceSummaryDTO] })
myUpcomingRaces!: DashboardRaceSummaryDTO[];
}

View File

@@ -5,5 +5,10 @@
*/ */
export interface DashboardRaceSummaryDTO { export interface DashboardRaceSummaryDTO {
enum: string; id: string;
leagueId: string;
leagueName: string;
track: string;
car: string;
scheduledAt: string;
} }

View File

@@ -0,0 +1,15 @@
/**
* Auto-generated DTO from OpenAPI spec
* This file is generated by scripts/generate-api-types.ts
* Do not edit manually - regenerate using: npm run api:sync-types
*/
export interface DashboardRecentResultDTO {
raceId: string;
raceName: string;
leagueId: string;
leagueName: string;
finishedAt: string;
position: number;
incidents: number;
}

View File

@@ -15,6 +15,4 @@ export interface DriverLeaderboardItemDTO {
podiums: number; podiums: number;
isActive: boolean; isActive: boolean;
rank: number; rank: number;
avatarUrl?: string;
nullable: string;
} }

View File

@@ -4,8 +4,8 @@
* Do not edit manually - regenerate using: npm run api:sync-types * Do not edit manually - regenerate using: npm run api:sync-types
*/ */
export interface CreatePaymentOutputDTO { export interface DriverRegistrationStatusDTO {
payment: PaymentDTO; isRegistered: boolean;
type: PaymentDTO }) raceId: string;
payment: PaymentDTO; driverId: string;
} }

View File

@@ -1,14 +0,0 @@
/**
* Auto-generated DTO from OpenAPI spec
* This file is generated by scripts/generate-api-types.ts
* Do not edit manually - regenerate using: npm run api:sync-types
*/
export interface DriversLeaderboardDTO {
drivers: DriverLeaderboardItemDTO[];
totalRaces: number;
totalWins: number;
activeCount: number;
type: [DriverLeaderboardItemDTO] })
drivers: DriverLeaderboardItemDTO[];
}

View File

@@ -5,8 +5,11 @@
*/ */
export interface FileProtestCommandDTO { export interface FileProtestCommandDTO {
raceId: string;
protestingDriverId: string;
accusedDriverId: string;
incident: Record<string, unknown>;
lap: number; lap: number;
description: string; description: string;
timeInRace: number; timeInRace?: number;
required: string;
} }

View File

@@ -1,11 +0,0 @@
/**
* Auto-generated DTO from OpenAPI spec
* This file is generated by scripts/generate-api-types.ts
* Do not edit manually - regenerate using: npm run api:sync-types
*/
export interface GetEntitySponsorshipPricingResultDTO {
pricing: SponsorshipPricingItemDTO[];
type: [SponsorshipPricingItemDTO] })
pricing: SponsorshipPricingItemDTO[];
}

View File

@@ -1,13 +0,0 @@
/**
* Auto-generated DTO from OpenAPI spec
* This file is generated by scripts/generate-api-types.ts
* Do not edit manually - regenerate using: npm run api:sync-types
*/
export interface GetLeagueAdminConfigOutputDTO {
type: () => LeagueConfigFormModelDTO, nullable: true })
@IsOptional()
@ValidateNested()
@Type(() => LeagueConfigFormModelDTO)
form: LeagueConfigFormModelDTO null;
}

View File

@@ -4,7 +4,7 @@
* Do not edit manually - regenerate using: npm run api:sync-types * Do not edit manually - regenerate using: npm run api:sync-types
*/ */
export interface RecordEngagementInputDTO { export interface GetRaceDetailParamsDTODTO {
enum: string; raceId: string;
required: string; driverId: string;
} }

View File

@@ -1,11 +0,0 @@
/**
* Auto-generated DTO from OpenAPI spec
* This file is generated by scripts/generate-api-types.ts
* Do not edit manually - regenerate using: npm run api:sync-types
*/
export interface GetSponsorsOutputDTO {
sponsors: SponsorDTO[];
type: [SponsorDTO] })
sponsors: SponsorDTO[];
}

View File

@@ -4,7 +4,7 @@
* Do not edit manually - regenerate using: npm run api:sync-types * Do not edit manually - regenerate using: npm run api:sync-types
*/ */
export interface ImportRaceResultsSummaryDTO { export interface ImportRaceResultsDTO {
errors?: string[]; raceId: string;
type: string[]; resultsFileContent: string;
} }

View File

@@ -1,13 +0,0 @@
/**
* Auto-generated DTO from OpenAPI spec
* This file is generated by scripts/generate-api-types.ts
* Do not edit manually - regenerate using: npm run api:sync-types
*/
export interface LeagueAdminConfigDTO {
type: () => LeagueConfigFormModelDTO, nullable: true })
@IsOptional()
@ValidateNested()
@Type(() => LeagueConfigFormModelDTO)
form: LeagueConfigFormModelDTO null;
}

View File

@@ -1,13 +0,0 @@
/**
* Auto-generated DTO from OpenAPI spec
* This file is generated by scripts/generate-api-types.ts
* Do not edit manually - regenerate using: npm run api:sync-types
*/
export interface LeagueAdminDTO {
type: [LeagueJoinRequestDTO] })
@IsArray()
@ValidateNested({ each: true })
@Type(() => LeagueJoinRequestDTO)
joinRequests: LeagueJoinRequestDTO[];
}

View File

@@ -1,13 +0,0 @@
/**
* Auto-generated DTO from OpenAPI spec
* This file is generated by scripts/generate-api-types.ts
* Do not edit manually - regenerate using: npm run api:sync-types
*/
export interface LeagueAdminProtestsDTO {
type: [ProtestDTO] })
@IsArray()
@ValidateNested({ each: true })
@Type(() => ProtestDTO)
protests: ProtestDTO[];
}

View File

@@ -7,5 +7,4 @@
export interface LeagueConfigFormModelBasicsDTO { export interface LeagueConfigFormModelBasicsDTO {
name: string; name: string;
description: string; description: string;
enum: string;
} }

View File

@@ -6,8 +6,4 @@
export interface LeagueConfigFormModelDTO { export interface LeagueConfigFormModelDTO {
leagueId: string; leagueId: string;
type: LeagueConfigFormModelBasicsDTO })
@ValidateNested()
@Type(() => LeagueConfigFormModelBasicsDTO)
basics: LeagueConfigFormModelBasicsDTO;
} }

View File

@@ -1,17 +0,0 @@
/**
* Auto-generated DTO from OpenAPI spec
* This file is generated by scripts/generate-api-types.ts
* Do not edit manually - regenerate using: npm run api:sync-types
*/
export interface LeagueConfigFormModelStewardingDTO {
enum: string;
required: string;
requireDefense: boolean;
defenseTimeLimit: number;
voteTimeLimit: number;
protestDeadlineHours: number;
stewardingClosesHours: number;
notifyAccusedOnProtest: boolean;
notifyOnVoteRequired: boolean;
}

View File

@@ -8,11 +8,6 @@ export interface LeagueJoinRequestDTO {
id: string; id: string;
leagueId: string; leagueId: string;
driverId: string; driverId: string;
/** Format: date-time */
requestedAt: string; requestedAt: string;
required: string;
type: () => DriverDto, required: false })
@IsOptional()
@ValidateNested()
@Type(() => DriverDto)
driver?: DriverDto;
} }

View File

@@ -6,10 +6,4 @@
export interface LeagueMemberDTO { export interface LeagueMemberDTO {
driverId: string; driverId: string;
type: () => DriverDto })
@ValidateNested()
@Type(() => DriverDto)
driver: DriverDto;
enum: string;
joinedAt: string;
} }

View File

@@ -1,13 +0,0 @@
/**
* Auto-generated DTO from OpenAPI spec
* This file is generated by scripts/generate-api-types.ts
* Do not edit manually - regenerate using: npm run api:sync-types
*/
export interface LeagueMembershipsDTO {
type: [LeagueMemberDTO] })
@IsArray()
@ValidateNested({ each: true })
@Type(() => LeagueMemberDTO)
members: LeagueMemberDTO[];
}

View File

@@ -1,13 +0,0 @@
/**
* Auto-generated DTO from OpenAPI spec
* This file is generated by scripts/generate-api-types.ts
* Do not edit manually - regenerate using: npm run api:sync-types
*/
export interface LeagueOwnerSummaryDTO {
type: () => DriverDto })
@ValidateNested()
@Type(() => DriverDto)
driver: DriverDto;
nullable: string;
}

View File

@@ -1,13 +0,0 @@
/**
* Auto-generated DTO from OpenAPI spec
* This file is generated by scripts/generate-api-types.ts
* Do not edit manually - regenerate using: npm run api:sync-types
*/
export interface LeagueScheduleDTO {
type: [RaceDto] })
@IsArray()
@ValidateNested({ each: true })
@Type(() => RaceDto)
races: RaceDto[];
}

View File

@@ -8,7 +8,4 @@ export interface LeagueSeasonSummaryDTO {
seasonId: string; seasonId: string;
name: string; name: string;
status: string; status: string;
required: string;
isPrimary: boolean;
isParallelActive: boolean;
} }

View File

@@ -6,10 +6,4 @@
export interface LeagueStandingDTO { export interface LeagueStandingDTO {
driverId: string; driverId: string;
type: () => DriverDto })
@ValidateNested()
@Type(() => DriverDto)
driver: DriverDto;
points: number;
rank: number;
} }

View File

@@ -1,13 +0,0 @@
/**
* Auto-generated DTO from OpenAPI spec
* This file is generated by scripts/generate-api-types.ts
* Do not edit manually - regenerate using: npm run api:sync-types
*/
export interface LeagueStandingsDTO {
type: [LeagueStandingDTO] })
@IsArray()
@ValidateNested({ each: true })
@Type(() => LeagueStandingDTO)
standings: LeagueStandingDTO[];
}

View File

@@ -7,9 +7,4 @@
export interface LeagueSummaryDTO { export interface LeagueSummaryDTO {
id: string; id: string;
name: string; name: string;
nullable: string;
memberCount: number;
maxMembers: number;
isPublic: boolean;
ownerId: string;
} }

View File

@@ -7,14 +7,4 @@
export interface LeagueWithCapacityDTO { export interface LeagueWithCapacityDTO {
id: string; id: string;
name: string; name: string;
nullable: string;
ownerId: string;
type: () => LeagueSettingsDTO })
@ValidateNested()
@Type(() => LeagueSettingsDTO)
settings: LeagueSettingsDTO;
createdAt: string;
usedSlots: number;
youtubeUrl: string;
websiteUrl: string;
} }

View File

@@ -5,42 +5,10 @@
*/ */
export interface MemberPaymentDto { export interface MemberPaymentDto {
payment: MemberPaymentDto;
fee: MembershipFeeDto;
payments: PaymentDto[];
prizes: PrizeDto[];
prize: PrizeDto;
wallet: WalletDto;
transactions: TransactionDto[];
transaction: TransactionDto;
id: string; id: string;
enum: string; feeId: string;
driverId: string;
amount: number; amount: number;
platformFee: number; platformFee: number;
netAmount: number; netAmount: number;
payerId: string;
leagueId: string;
required: string;
createdAt: string;
type: PaymentDto })
payment: PaymentDto;
paymentId: string;
enabled: boolean;
updatedAt: string;
feeId: string;
driverId: string;
dueDate: string;
seasonId: string;
position: number;
name: string;
awarded: boolean;
prizeId: string;
success: boolean;
balance: number;
totalRevenue: number;
totalPlatformFees: number;
totalWithdrawn: number;
currency: string;
walletId: string;
description: string;
} }

View File

@@ -5,42 +5,6 @@
*/ */
export interface MembershipFeeDto { export interface MembershipFeeDto {
payment: MemberPaymentDto;
fee: MembershipFeeDto;
payments: PaymentDto[];
prizes: PrizeDto[];
prize: PrizeDto;
wallet: WalletDto;
transactions: TransactionDto[];
transaction: TransactionDto;
id: string; id: string;
enum: string;
amount: number;
platformFee: number;
netAmount: number;
payerId: string;
leagueId: string; leagueId: string;
required: string;
createdAt: string;
type: PaymentDto })
payment: PaymentDto;
paymentId: string;
enabled: boolean;
updatedAt: string;
feeId: string;
driverId: string;
dueDate: string;
seasonId: string;
position: number;
name: string;
awarded: boolean;
prizeId: string;
success: boolean;
balance: number;
totalRevenue: number;
totalPlatformFees: number;
totalWithdrawn: number;
currency: string;
walletId: string;
description: string;
} }

View File

@@ -6,12 +6,4 @@
export interface PaymentDTO { export interface PaymentDTO {
id: string; id: string;
enum: string;
amount: number;
platformFee: number;
netAmount: number;
payerId: string;
leagueId: string;
required: string;
createdAt: string;
} }

View File

@@ -5,42 +5,10 @@
*/ */
export interface PrizeDto { export interface PrizeDto {
payment: MemberPaymentDto;
fee: MembershipFeeDto;
payments: PaymentDto[];
prizes: PrizeDto[];
prize: PrizeDto;
wallet: WalletDto;
transactions: TransactionDto[];
transaction: TransactionDto;
id: string; id: string;
enum: string;
amount: number;
platformFee: number;
netAmount: number;
payerId: string;
leagueId: string; leagueId: string;
required: string;
createdAt: string;
type: PaymentDto })
payment: PaymentDto;
paymentId: string;
enabled: boolean;
updatedAt: string;
feeId: string;
driverId: string;
dueDate: string;
seasonId: string; seasonId: string;
position: number; position: number;
name: string; name: string;
awarded: boolean; amount: number;
prizeId: string;
success: boolean;
balance: number;
totalRevenue: number;
totalPlatformFees: number;
totalWithdrawn: number;
currency: string;
walletId: string;
description: string;
} }

View File

@@ -5,16 +5,11 @@
*/ */
export interface ProtestDTO { export interface ProtestDTO {
TODO: protests are filed at race level but also managed on league level
export class ProtestDTO {
@ApiProperty()
@IsString()
id: string; id: string;
raceId: string; raceId: string;
protestingDriverId: string; protestingDriverId: string;
accusedDriverId: string; accusedDriverId: string;
/** Format: date-time */
submittedAt: string; submittedAt: string;
description: string; description: string;
enum: string;
} }

View File

@@ -5,6 +5,7 @@
*/ */
export interface QuickPenaltyCommandDTO { export interface QuickPenaltyCommandDTO {
enum: string; raceId: string;
required: string; driverId: string;
adminId: string;
} }

View File

@@ -4,6 +4,6 @@
* Do not edit manually - regenerate using: npm run api:sync-types * Do not edit manually - regenerate using: npm run api:sync-types
*/ */
export interface LeagueSettingsDTO { export interface RaceActionParamsDTO {
nullable: string; raceId: string;
} }

View File

@@ -5,6 +5,7 @@
*/ */
export interface RaceDTO { export interface RaceDTO {
leagueName?: string; id: string;
nullable: string; name: string;
date: string;
} }

View File

@@ -1,13 +0,0 @@
/**
* Auto-generated DTO from OpenAPI spec
* This file is generated by scripts/generate-api-types.ts
* Do not edit manually - regenerate using: npm run api:sync-types
*/
export interface RaceDetailDTO {
nullable: true })
race!: RaceDetailRaceDTO null;
type: [RaceDetailEntryDTO] })
entryList!: RaceDetailEntryDTO[];
required: string;
}

View File

@@ -5,5 +5,8 @@
*/ */
export interface RaceDetailEntryDTO { export interface RaceDetailEntryDTO {
nullable: string; id: string;
name: string;
country: string;
avatarUrl: string;
} }

View File

@@ -5,6 +5,10 @@
*/ */
export interface RaceDetailLeagueDTO { export interface RaceDetailLeagueDTO {
maxDrivers: number; id: string;
qualifyingFormat: string; name: string;
description: string;
settings: Record<string, unknown>;
maxDrivers?: number;
qualifyingFormat?: string;
} }

View File

@@ -5,6 +5,11 @@
*/ */
export interface RaceDetailRaceDTO { export interface RaceDetailRaceDTO {
nullable: string; id: string;
required: string; leagueId: string;
track: string;
car: string;
scheduledAt: string;
sessionType: string;
status: string;
} }

View File

@@ -0,0 +1,10 @@
/**
* Auto-generated DTO from OpenAPI spec
* This file is generated by scripts/generate-api-types.ts
* Do not edit manually - regenerate using: npm run api:sync-types
*/
export interface RaceDetailRegistrationDTO {
isUserRegistered: boolean;
canRegister: boolean;
}

View File

@@ -5,5 +5,11 @@
*/ */
export interface RaceDetailUserResultDTO { export interface RaceDetailUserResultDTO {
nullable: string; position: number;
startPosition: number;
incidents: number;
fastestLap: number;
positionChange: number;
isPodium: boolean;
isClean: boolean;
} }

View File

@@ -1,10 +0,0 @@
/**
* Auto-generated DTO from OpenAPI spec
* This file is generated by scripts/generate-api-types.ts
* Do not edit manually - regenerate using: npm run api:sync-types
*/
export interface RacePenaltiesDTO {
type: [RacePenaltyDTO] })
penalties!: RacePenaltyDTO[];
}

View File

@@ -5,6 +5,11 @@
*/ */
export interface RacePenaltyDTO { export interface RacePenaltyDTO {
notes?: string; id: string;
nullable: string; driverId: string;
type: string;
value: number;
reason: string;
issuedBy: string;
issuedAt: string;
} }

View File

@@ -5,6 +5,10 @@
*/ */
export interface RaceProtestDTO { export interface RaceProtestDTO {
id: string;
protestingDriverId: string;
accusedDriverId: string;
incident: Record<string, unknown>;
lap: number; lap: number;
description: string; description: string;
} }

View File

@@ -1,10 +0,0 @@
/**
* Auto-generated DTO from OpenAPI spec
* This file is generated by scripts/generate-api-types.ts
* Do not edit manually - regenerate using: npm run api:sync-types
*/
export interface RaceProtestsDTO {
type: [RaceProtestDto] })
protests!: RaceProtestDto[];
}

View File

@@ -0,0 +1,18 @@
/**
* Auto-generated DTO from OpenAPI spec
* This file is generated by scripts/generate-api-types.ts
* Do not edit manually - regenerate using: npm run api:sync-types
*/
export interface RaceResultDTO {
driverId: string;
driverName: string;
avatarUrl: string;
position: number;
startPosition: number;
incidents: number;
fastestLap: number;
positionChange: number;
isPodium: boolean;
isClean: boolean;
}

View File

@@ -5,6 +5,6 @@
*/ */
export interface RaceResultsDetailDTO { export interface RaceResultsDetailDTO {
type: [RaceResultDto] }) raceId: string;
results!: RaceResultDto[]; track: string;
} }

View File

@@ -4,6 +4,6 @@
* Do not edit manually - regenerate using: npm run api:sync-types * Do not edit manually - regenerate using: npm run api:sync-types
*/ */
export interface AllRacesPageDTO { export interface RaceStatsDTO {
type: string[]; totalRaces: number;
} }

View File

@@ -5,5 +5,6 @@
*/ */
export interface RaceWithSOFDTO { export interface RaceWithSOFDTO {
nullable: string; id: string;
track: string;
} }

View File

@@ -1,10 +0,0 @@
/**
* Auto-generated DTO from OpenAPI spec
* This file is generated by scripts/generate-api-types.ts
* Do not edit manually - regenerate using: npm run api:sync-types
*/
export interface RacesPageDataDTO {
type: [RacesPageDataRaceDto] })
races!: RacesPageDataRaceDto[];
}

View File

@@ -5,5 +5,11 @@
*/ */
export interface RacesPageDataRaceDTO { export interface RacesPageDataRaceDTO {
nullable: string; id: string;
track: string;
car: string;
scheduledAt: string;
status: string;
leagueId: string;
leagueName: string;
} }

View File

@@ -4,7 +4,7 @@
* Do not edit manually - regenerate using: npm run api:sync-types * Do not edit manually - regenerate using: npm run api:sync-types
*/ */
export interface LeagueConfigFormModelDropPolicyDTO { export interface RecordEngagementOutputDTO {
enum: string; eventId: string;
required: string; engagementWeight: number;
} }

View File

@@ -4,7 +4,6 @@
* Do not edit manually - regenerate using: npm run api:sync-types * Do not edit manually - regenerate using: npm run api:sync-types
*/ */
export interface RecordPageViewInputDTO { export interface RecordPageViewOutputDTO {
enum: string; pageViewId: string;
required: string;
} }

View File

@@ -0,0 +1,11 @@
/**
* Auto-generated DTO from OpenAPI spec
* This file is generated by scripts/generate-api-types.ts
* Do not edit manually - regenerate using: npm run api:sync-types
*/
export interface RegisterForRaceParamsDTO {
raceId: string;
leagueId: string;
driverId: string;
}

View File

@@ -6,5 +6,4 @@
export interface RejectJoinRequestOutputDTO { export interface RejectJoinRequestOutputDTO {
success: boolean; success: boolean;
required: string;
} }

View File

@@ -1,11 +0,0 @@
/**
* Auto-generated DTO from OpenAPI spec
* This file is generated by scripts/generate-api-types.ts
* Do not edit manually - regenerate using: npm run api:sync-types
*/
export interface RequestAvatarGenerationOutputDTO {
avatarUrls?: string[];
type: string;
required: string;
}

View File

@@ -0,0 +1,10 @@
/**
* Auto-generated DTO from OpenAPI spec
* This file is generated by scripts/generate-api-types.ts
* Do not edit manually - regenerate using: npm run api:sync-types
*/
export interface RequestProtestDefenseCommandDTO {
protestId: string;
stewardId: string;
}

View File

@@ -8,7 +8,4 @@ export interface SeasonDTO {
seasonId: string; seasonId: string;
name: string; name: string;
leagueId: string; leagueId: string;
required: string;
enum: string;
isPrimary: boolean;
} }

View File

@@ -5,11 +5,6 @@
*/ */
export interface SponsorDashboardDTO { export interface SponsorDashboardDTO {
metrics: SponsorDashboardMetricsDTO;
sponsoredLeagues: SponsoredLeagueDTO[];
investment: SponsorDashboardInvestmentDTO;
sponsorId: string; sponsorId: string;
sponsorName: string; sponsorName: string;
type: SponsorDashboardMetricsDTO })
metrics: SponsorDashboardMetricsDTO;
} }

View File

@@ -5,14 +5,6 @@
*/ */
export interface SponsorSponsorshipsDTO { export interface SponsorSponsorshipsDTO {
sponsorships: SponsorshipDetailDTO[];
sponsorId: string; sponsorId: string;
sponsorName: string; sponsorName: string;
type: [SponsorshipDetailDTO] })
sponsorships: SponsorshipDetailDTO[];
summary: string;
activeSponsorships: number;
totalInvestment: number;
totalPlatformFees: number;
currency: string;
} }

View File

@@ -7,8 +7,4 @@
export interface SponsoredLeagueDTO { export interface SponsoredLeagueDTO {
id: string; id: string;
name: string; name: string;
enum: string;
drivers: number;
races: number;
impressions: number;
} }

View File

@@ -5,20 +5,9 @@
*/ */
export interface SponsorshipDetailDTO { export interface SponsorshipDetailDTO {
createdAt: string;
id: string; id: string;
leagueId: string; leagueId: string;
leagueName: string; leagueName: string;
seasonId: string; seasonId: string;
seasonName: string; seasonName: string;
required: string;
enum: string;
pricing: string;
currency: string;
platformFee: string;
netAmount: string;
metrics: string;
races: number;
completedRaces: number;
impressions: number;
} }

View File

@@ -1,29 +0,0 @@
/**
* Auto-generated DTO from OpenAPI spec
* This file is generated by scripts/generate-api-types.ts
* Do not edit manually - regenerate using: npm run api:sync-types
*/
export interface TeamListItemViewModel {
id: string;
name: string;
tag: string;
description: string;
memberCount: number;
leagues: string;
specialization?: string;
region?: string;
languages?: string;
teams: string[];
totalCount: number;
ownerId: string;
createdAt?: string;
role: string;
joinedAt: string;
isActive: boolean;
team: string;
membership: string;
isOwner: boolean;
canManage: boolean;
success: boolean;
}

View File

@@ -5,42 +5,6 @@
*/ */
export interface TransactionDto { export interface TransactionDto {
payment: MemberPaymentDto;
fee: MembershipFeeDto;
payments: PaymentDto[];
prizes: PrizeDto[];
prize: PrizeDto;
wallet: WalletDto;
transactions: TransactionDto[];
transaction: TransactionDto;
id: string; id: string;
enum: string;
amount: number;
platformFee: number;
netAmount: number;
payerId: string;
leagueId: string;
required: string;
createdAt: string;
type: PaymentDto })
payment: PaymentDto;
paymentId: string;
enabled: boolean;
updatedAt: string;
feeId: string;
driverId: string;
dueDate: string;
seasonId: string;
position: number;
name: string;
awarded: boolean;
prizeId: string;
success: boolean;
balance: number;
totalRevenue: number;
totalPlatformFees: number;
totalWithdrawn: number;
currency: string;
walletId: string; walletId: string;
description: string;
} }

View File

@@ -8,5 +8,4 @@ export interface UpdateLeagueMemberRoleInputDTO {
leagueId: string; leagueId: string;
performerDriverId: string; performerDriverId: string;
targetDriverId: string; targetDriverId: string;
enum: string;
} }

View File

@@ -6,5 +6,4 @@
export interface UpdatePaymentStatusInputDTO { export interface UpdatePaymentStatusInputDTO {
paymentId: string; paymentId: string;
enum: string;
} }

View File

@@ -1,11 +0,0 @@
/**
* Auto-generated DTO from OpenAPI spec
* This file is generated by scripts/generate-api-types.ts
* Do not edit manually - regenerate using: npm run api:sync-types
*/
export interface UpdatePaymentStatusOutputDTO {
payment: PaymentDTO;
type: PaymentDTO })
payment: PaymentDTO;
}

View File

@@ -5,42 +5,13 @@
*/ */
export interface WalletDto { export interface WalletDto {
payment: MemberPaymentDto;
fee: MembershipFeeDto;
payments: PaymentDto[];
prizes: PrizeDto[];
prize: PrizeDto;
wallet: WalletDto;
transactions: TransactionDto[];
transaction: TransactionDto;
id: string; id: string;
enum: string;
amount: number;
platformFee: number;
netAmount: number;
payerId: string;
leagueId: string; leagueId: string;
required: string;
createdAt: string;
type: PaymentDto })
payment: PaymentDto;
paymentId: string;
enabled: boolean;
updatedAt: string;
feeId: string;
driverId: string;
dueDate: string;
seasonId: string;
position: number;
name: string;
awarded: boolean;
prizeId: string;
success: boolean;
balance: number; balance: number;
totalRevenue: number; totalRevenue: number;
totalPlatformFees: number; totalPlatformFees: number;
totalWithdrawn: number; totalWithdrawn: number;
/** Format: date-time */
createdAt: string;
currency: string; currency: string;
walletId: string;
description: string;
} }

View File

@@ -4,7 +4,7 @@
* Do not edit manually - regenerate using: npm run api:sync-types * Do not edit manually - regenerate using: npm run api:sync-types
*/ */
export interface CreateDriverOutputDto { export interface WithdrawFromRaceParamsDTO {
success?: boolean; raceId: string;
driverId?: string; driverId: string;
} }

View File

@@ -0,0 +1,469 @@
/**
* This file was auto-generated by openapi-typescript.
* Do not make direct changes to the file.
*/
export type paths = Record<string, never>;
export type webhooks = Record<string, never>;
export interface components {
schemas: {
SponsorshipPricingItemDTO: {
id: string;
level: string;
price: number;
currency: string;
};
SponsorshipDetailDTO: {
id: string;
leagueId: string;
leagueName: string;
seasonId: string;
seasonName: string;
};
SponsoredLeagueDTO: {
id: string;
name: string;
};
SponsorSponsorshipsDTO: {
sponsorId: string;
sponsorName: string;
};
SponsorDashboardMetricsDTO: {
impressions: number;
impressionsChange: number;
uniqueViewers: number;
viewersChange: number;
races: number;
drivers: number;
exposure: number;
exposureChange: number;
};
SponsorDashboardInvestmentDTO: {
activeSponsorships: number;
totalInvestment: number;
costPerThousandViews: number;
};
SponsorDashboardDTO: {
sponsorId: string;
sponsorName: string;
};
GetSponsorSponsorshipsQueryParamsDTO: {
sponsorId: string;
};
GetSponsorDashboardQueryParamsDTO: {
sponsorId: string;
};
CreateSponsorInputDTO: {
name: string;
contactEmail: string;
};
UpdatePaymentStatusInputDTO: {
paymentId: string;
};
PaymentDto: {
id: string;
};
MembershipFeeDto: {
id: string;
leagueId: string;
};
MemberPaymentDto: {
id: string;
feeId: string;
driverId: string;
amount: number;
platformFee: number;
netAmount: number;
};
PrizeDto: {
id: string;
leagueId: string;
seasonId: string;
position: number;
name: string;
amount: number;
};
WalletDto: {
id: string;
leagueId: string;
balance: number;
totalRevenue: number;
totalPlatformFees: number;
totalWithdrawn: number;
/** Format: date-time */
createdAt: string;
currency: string;
};
TransactionDto: {
id: string;
walletId: string;
};
PaymentDTO: {
id: string;
};
WithdrawFromRaceParamsDTO: {
raceId: string;
driverId: string;
};
RequestProtestDefenseCommandDTO: {
protestId: string;
stewardId: string;
};
RegisterForRaceParamsDTO: {
raceId: string;
leagueId: string;
driverId: string;
};
RacesPageDataRaceDTO: {
id: string;
track: string;
car: string;
scheduledAt: string;
status: string;
leagueId: string;
leagueName: string;
};
RaceWithSOFDTO: {
id: string;
track: string;
};
RaceStatsDTO: {
totalRaces: number;
};
RaceResultsDetailDTO: {
raceId: string;
track: string;
};
RaceResultDTO: {
driverId: string;
driverName: string;
avatarUrl: string;
position: number;
startPosition: number;
incidents: number;
fastestLap: number;
positionChange: number;
isPodium: boolean;
isClean: boolean;
};
RaceProtestDTO: {
id: string;
protestingDriverId: string;
accusedDriverId: string;
incident: Record<string, never>;
lap: number;
description: string;
};
RacePenaltyDTO: {
id: string;
driverId: string;
type: string;
value: number;
reason: string;
issuedBy: string;
issuedAt: string;
};
RaceDetailUserResultDTO: {
position: number;
startPosition: number;
incidents: number;
fastestLap: number;
positionChange: number;
isPodium: boolean;
isClean: boolean;
};
RaceDetailRegistrationDTO: {
isUserRegistered: boolean;
canRegister: boolean;
};
RaceDetailRaceDTO: {
id: string;
leagueId: string;
track: string;
car: string;
scheduledAt: string;
sessionType: string;
status: string;
};
RaceDetailLeagueDTO: {
id: string;
name: string;
description: string;
settings: Record<string, never>;
maxDrivers?: number;
qualifyingFormat?: string;
};
RaceDetailEntryDTO: {
id: string;
name: string;
country: string;
avatarUrl: string;
};
RaceDTO: {
id: string;
name: string;
date: string;
};
RaceActionParamsDTO: {
raceId: string;
};
QuickPenaltyCommandDTO: {
raceId: string;
driverId: string;
adminId: string;
};
ImportRaceResultsDTO: {
raceId: string;
resultsFileContent: string;
};
GetRaceDetailParamsDTODTO: {
raceId: string;
driverId: string;
};
FileProtestCommandDTO: {
raceId: string;
protestingDriverId: string;
accusedDriverId: string;
incident: Record<string, never>;
lap: number;
description: string;
timeInRace?: number;
};
DashboardRecentResultDTO: {
raceId: string;
raceName: string;
leagueId: string;
leagueName: string;
finishedAt: string;
position: number;
incidents: number;
};
DashboardRaceSummaryDTO: {
id: string;
leagueId: string;
leagueName: string;
track: string;
car: string;
scheduledAt: string;
};
DashboardLeagueStandingSummaryDTO: {
leagueId: string;
leagueName: string;
position: number;
totalDrivers: number;
points: number;
};
DashboardFriendSummaryDTO: {
id: string;
name: string;
country: string;
avatarUrl: string;
};
DashboardFeedSummaryDTO: {
notificationCount: number;
};
DashboardFeedItemSummaryDTO: {
id: string;
enum: string;
};
DashboardDriverSummaryDTO: {
id: string;
name: string;
country: string;
avatarUrl: string;
};
ApplyPenaltyCommandDTO: {
raceId: string;
driverId: string;
stewardId: string;
enum: string;
};
RequestAvatarGenerationInputDTO: {
userId: string;
facePhotoData: string;
suitColor: string;
};
UpdateLeagueMemberRoleOutputDTO: {
success: boolean;
};
UpdateLeagueMemberRoleInputDTO: {
leagueId: string;
performerDriverId: string;
targetDriverId: string;
};
SeasonDTO: {
seasonId: string;
name: string;
leagueId: string;
};
RemoveLeagueMemberOutputDTO: {
success: boolean;
};
RemoveLeagueMemberInputDTO: {
leagueId: string;
performerDriverId: string;
targetDriverId: string;
};
RejectJoinRequestOutputDTO: {
success: boolean;
};
RejectJoinRequestInputDTO: {
requestId: string;
leagueId: string;
};
ProtestDTO: {
id: string;
raceId: string;
protestingDriverId: string;
accusedDriverId: string;
/** Format: date-time */
submittedAt: string;
description: string;
};
LeagueWithCapacityDTO: {
id: string;
name: string;
};
LeagueSummaryDTO: {
id: string;
name: string;
};
LeagueStatsDTO: {
totalMembers: number;
totalRaces: number;
averageRating: number;
};
LeagueStandingDTO: {
driverId: string;
};
LeagueSeasonSummaryDTO: {
seasonId: string;
name: string;
status: string;
};
LeagueMemberDTO: {
driverId: string;
};
LeagueJoinRequestDTO: {
id: string;
leagueId: string;
driverId: string;
/** Format: date-time */
requestedAt: string;
};
LeagueConfigFormModelTimingsDTO: {
raceDayOfWeek: string;
raceTimeHour: number;
raceTimeMinute: number;
};
LeagueConfigFormModelStructureDTO: {
mode: string;
};
LeagueConfigFormModelScoringDTO: {
type: string;
points: number;
};
LeagueConfigFormModelDTO: {
leagueId: string;
};
LeagueConfigFormModelBasicsDTO: {
name: string;
description: string;
};
LeagueAdminPermissionsDTO: {
canRemoveMember: boolean;
canUpdateRoles: boolean;
};
GetLeagueSeasonsQueryDTO: {
leagueId: string;
};
GetLeagueProtestsQueryDTO: {
leagueId: string;
};
GetLeagueOwnerSummaryQueryDTO: {
ownerId: string;
leagueId: string;
};
GetLeagueJoinRequestsQueryDTO: {
leagueId: string;
};
GetLeagueAdminPermissionsInputDTO: {
leagueId: string;
performerDriverId: string;
};
GetLeagueAdminConfigQueryDTO: {
leagueId: string;
};
CreateLeagueOutputDTO: {
leagueId: string;
success: boolean;
};
CreateLeagueInputDTO: {
name: string;
description: string;
};
ApproveJoinRequestOutputDTO: {
success: boolean;
};
ApproveJoinRequestInputDTO: {
requestId: string;
leagueId: string;
};
GetDriverRegistrationStatusQueryDTO: {
raceId: string;
driverId: string;
};
DriverStatsDTO: {
totalDrivers: number;
};
DriverRegistrationStatusDTO: {
isRegistered: boolean;
raceId: string;
driverId: string;
};
DriverLeaderboardItemDTO: {
id: string;
name: string;
rating: number;
skillLevel: string;
nationality: string;
racesCompleted: number;
wins: number;
podiums: number;
isActive: boolean;
rank: number;
};
CompleteOnboardingOutputDTO: {
success: boolean;
};
CompleteOnboardingInputDTO: {
firstName: string;
lastName: string;
displayName: string;
country: string;
};
AuthenticatedUserDTO: {
userId: string;
email: string;
displayName: string;
};
AuthSessionDTO: {
token: string;
user: components["schemas"]["AuthenticatedUserDTO"];
};
RecordPageViewOutputDTO: {
pageViewId: string;
};
RecordEngagementOutputDTO: {
eventId: string;
engagementWeight: number;
};
};
responses: never;
parameters: never;
requestBodies: never;
headers: never;
pathItems: never;
}
export type $defs = Record<string, never>;
export type operations = Record<string, never>;

View File

@@ -0,0 +1,99 @@
/**
* Auto-generated DTO type exports
* This file is generated by scripts/generate-api-types.ts
* Do not edit manually - regenerate using: npm run api:sync-types
*/
// Re-export all schema types from the generated OpenAPI types
export type { components, paths, operations } from './api';
// Re-export individual DTO types
export type { SponsorshipPricingItemDTO } from './SponsorshipPricingItemDTO';
export type { SponsorshipDetailDTO } from './SponsorshipDetailDTO';
export type { SponsoredLeagueDTO } from './SponsoredLeagueDTO';
export type { SponsorSponsorshipsDTO } from './SponsorSponsorshipsDTO';
export type { SponsorDashboardMetricsDTO } from './SponsorDashboardMetricsDTO';
export type { SponsorDashboardInvestmentDTO } from './SponsorDashboardInvestmentDTO';
export type { SponsorDashboardDTO } from './SponsorDashboardDTO';
export type { GetSponsorSponsorshipsQueryParamsDTO } from './GetSponsorSponsorshipsQueryParamsDTO';
export type { GetSponsorDashboardQueryParamsDTO } from './GetSponsorDashboardQueryParamsDTO';
export type { CreateSponsorInputDTO } from './CreateSponsorInputDTO';
export type { UpdatePaymentStatusInputDTO } from './UpdatePaymentStatusInputDTO';
export type { PaymentDto } from './PaymentDto';
export type { MembershipFeeDto } from './MembershipFeeDto';
export type { MemberPaymentDto } from './MemberPaymentDto';
export type { PrizeDto } from './PrizeDto';
export type { WalletDto } from './WalletDto';
export type { TransactionDto } from './TransactionDto';
export type { PaymentDTO } from './PaymentDTO';
export type { WithdrawFromRaceParamsDTO } from './WithdrawFromRaceParamsDTO';
export type { RequestProtestDefenseCommandDTO } from './RequestProtestDefenseCommandDTO';
export type { RegisterForRaceParamsDTO } from './RegisterForRaceParamsDTO';
export type { RacesPageDataRaceDTO } from './RacesPageDataRaceDTO';
export type { RaceWithSOFDTO } from './RaceWithSOFDTO';
export type { RaceStatsDTO } from './RaceStatsDTO';
export type { RaceResultsDetailDTO } from './RaceResultsDetailDTO';
export type { RaceResultDTO } from './RaceResultDTO';
export type { RaceProtestDTO } from './RaceProtestDTO';
export type { RacePenaltyDTO } from './RacePenaltyDTO';
export type { RaceDetailUserResultDTO } from './RaceDetailUserResultDTO';
export type { RaceDetailRegistrationDTO } from './RaceDetailRegistrationDTO';
export type { RaceDetailRaceDTO } from './RaceDetailRaceDTO';
export type { RaceDetailLeagueDTO } from './RaceDetailLeagueDTO';
export type { RaceDetailEntryDTO } from './RaceDetailEntryDTO';
export type { RaceDTO } from './RaceDTO';
export type { RaceActionParamsDTO } from './RaceActionParamsDTO';
export type { QuickPenaltyCommandDTO } from './QuickPenaltyCommandDTO';
export type { ImportRaceResultsDTO } from './ImportRaceResultsDTO';
export type { GetRaceDetailParamsDTODTO } from './GetRaceDetailParamsDTODTO';
export type { FileProtestCommandDTO } from './FileProtestCommandDTO';
export type { DashboardRecentResultDTO } from './DashboardRecentResultDTO';
export type { DashboardRaceSummaryDTO } from './DashboardRaceSummaryDTO';
export type { DashboardLeagueStandingSummaryDTO } from './DashboardLeagueStandingSummaryDTO';
export type { DashboardFriendSummaryDTO } from './DashboardFriendSummaryDTO';
export type { DashboardFeedSummaryDTO } from './DashboardFeedSummaryDTO';
export type { DashboardFeedItemSummaryDTO } from './DashboardFeedItemSummaryDTO';
export type { DashboardDriverSummaryDTO } from './DashboardDriverSummaryDTO';
export type { ApplyPenaltyCommandDTO } from './ApplyPenaltyCommandDTO';
export type { RequestAvatarGenerationInputDTO } from './RequestAvatarGenerationInputDTO';
export type { UpdateLeagueMemberRoleOutputDTO } from './UpdateLeagueMemberRoleOutputDTO';
export type { UpdateLeagueMemberRoleInputDTO } from './UpdateLeagueMemberRoleInputDTO';
export type { SeasonDTO } from './SeasonDTO';
export type { RemoveLeagueMemberOutputDTO } from './RemoveLeagueMemberOutputDTO';
export type { RemoveLeagueMemberInputDTO } from './RemoveLeagueMemberInputDTO';
export type { RejectJoinRequestOutputDTO } from './RejectJoinRequestOutputDTO';
export type { RejectJoinRequestInputDTO } from './RejectJoinRequestInputDTO';
export type { ProtestDTO } from './ProtestDTO';
export type { LeagueWithCapacityDTO } from './LeagueWithCapacityDTO';
export type { LeagueSummaryDTO } from './LeagueSummaryDTO';
export type { LeagueStatsDTO } from './LeagueStatsDTO';
export type { LeagueStandingDTO } from './LeagueStandingDTO';
export type { LeagueSeasonSummaryDTO } from './LeagueSeasonSummaryDTO';
export type { LeagueMemberDTO } from './LeagueMemberDTO';
export type { LeagueJoinRequestDTO } from './LeagueJoinRequestDTO';
export type { LeagueConfigFormModelTimingsDTO } from './LeagueConfigFormModelTimingsDTO';
export type { LeagueConfigFormModelStructureDTO } from './LeagueConfigFormModelStructureDTO';
export type { LeagueConfigFormModelScoringDTO } from './LeagueConfigFormModelScoringDTO';
export type { LeagueConfigFormModelDTO } from './LeagueConfigFormModelDTO';
export type { LeagueConfigFormModelBasicsDTO } from './LeagueConfigFormModelBasicsDTO';
export type { LeagueAdminPermissionsDTO } from './LeagueAdminPermissionsDTO';
export type { GetLeagueSeasonsQueryDTO } from './GetLeagueSeasonsQueryDTO';
export type { GetLeagueProtestsQueryDTO } from './GetLeagueProtestsQueryDTO';
export type { GetLeagueOwnerSummaryQueryDTO } from './GetLeagueOwnerSummaryQueryDTO';
export type { GetLeagueJoinRequestsQueryDTO } from './GetLeagueJoinRequestsQueryDTO';
export type { GetLeagueAdminPermissionsInputDTO } from './GetLeagueAdminPermissionsInputDTO';
export type { GetLeagueAdminConfigQueryDTO } from './GetLeagueAdminConfigQueryDTO';
export type { CreateLeagueOutputDTO } from './CreateLeagueOutputDTO';
export type { CreateLeagueInputDTO } from './CreateLeagueInputDTO';
export type { ApproveJoinRequestOutputDTO } from './ApproveJoinRequestOutputDTO';
export type { ApproveJoinRequestInputDTO } from './ApproveJoinRequestInputDTO';
export type { GetDriverRegistrationStatusQueryDTO } from './GetDriverRegistrationStatusQueryDTO';
export type { DriverStatsDTO } from './DriverStatsDTO';
export type { DriverRegistrationStatusDTO } from './DriverRegistrationStatusDTO';
export type { DriverLeaderboardItemDTO } from './DriverLeaderboardItemDTO';
export type { CompleteOnboardingOutputDTO } from './CompleteOnboardingOutputDTO';
export type { CompleteOnboardingInputDTO } from './CompleteOnboardingInputDTO';
export type { AuthenticatedUserDTO } from './AuthenticatedUserDTO';
export type { AuthSessionDTO } from './AuthSessionDTO';
export type { RecordPageViewOutputDTO } from './RecordPageViewOutputDTO';
export type { RecordEngagementOutputDTO } from './RecordEngagementOutputDTO';

View File

@@ -25,7 +25,7 @@
"docker:prod:logs": "docker-compose -f docker-compose.prod.yml logs -f", "docker:prod:logs": "docker-compose -f docker-compose.prod.yml logs -f",
"docker:prod:clean": "docker-compose -f docker-compose.prod.yml down -v", "docker:prod:clean": "docker-compose -f docker-compose.prod.yml down -v",
"api:build": "npm run build --workspace=@gridpilot/api", "api:build": "npm run build --workspace=@gridpilot/api",
"api:generate-spec": "tsx scripts/generate-openapi-from-dtos.ts", "api:generate-spec": "tsx scripts/generate-openapi-spec.ts",
"api:generate-types": "tsx scripts/generate-api-types.ts", "api:generate-types": "tsx scripts/generate-api-types.ts",
"api:sync-types": "npm run api:generate-spec && npm run api:generate-types", "api:sync-types": "npm run api:generate-spec && npm run api:generate-types",
"test": "vitest run \"$@\"", "test": "vitest run \"$@\"",

View File

@@ -1,113 +1,238 @@
#!/usr/bin/env tsx #!/usr/bin/env tsx
/**
* Generate TypeScript types from OpenAPI spec
*
* This script uses openapi-typescript to generate proper TypeScript types
* from the OpenAPI spec that NestJS/Swagger generates.
*
* Usage:
* 1. First generate the OpenAPI spec: npm run api:generate-spec
* 2. Then generate types: npm run api:generate-types
*
* Or use: npm run api:sync-types (runs both)
*/
import { execSync } from 'child_process';
import fs from 'fs/promises'; import fs from 'fs/promises';
import path from 'path'; import path from 'path';
async function generateIndividualDTOs() { async function generateTypes() {
const openapiPath = path.join(__dirname, '../apps/api/openapi.json'); const openapiPath = path.join(__dirname, '../apps/api/openapi.json');
const outputDir = path.join(__dirname, '../apps/website/lib/types/generated'); const outputDir = path.join(__dirname, '../apps/website/lib/types/generated');
const outputFile = path.join(outputDir, 'api.ts');
console.log('🔄 Generating individual DTO files from OpenAPI spec...'); console.log('🔄 Generating TypeScript types from OpenAPI spec...');
// Check if OpenAPI spec exists
try { try {
// Check if OpenAPI spec exists
await fs.access(openapiPath); await fs.access(openapiPath);
} catch { } catch {
console.error(`❌ OpenAPI spec not found at: ${openapiPath}`); console.error(`❌ OpenAPI spec not found at: ${openapiPath}`);
console.error('Run "npm run api:generate-spec" first'); console.error('Run "npm run api:generate-spec" first to generate the OpenAPI spec from NestJS');
process.exit(1); process.exit(1);
} }
// Ensure output directory exists
await fs.mkdir(outputDir, { recursive: true });
try { try {
// Read the OpenAPI spec // Use openapi-typescript to generate types
const specContent = await fs.readFile(openapiPath, 'utf-8'); console.log('📝 Running openapi-typescript...');
const spec = JSON.parse(specContent); execSync(`npx openapi-typescript "${openapiPath}" -o "${outputFile}"`, {
stdio: 'inherit',
// Ensure output directory exists cwd: path.join(__dirname, '..')
await fs.mkdir(outputDir, { recursive: true }); });
// Extract schemas from the spec
const schemas = spec.components?.schemas || {};
console.log(`📝 Found ${Object.keys(schemas).length} schemas to generate`);
// Generate individual files for each schema
for (const [schemaName, schema] of Object.entries(schemas)) {
if (typeof schema === 'object' && schema !== null) {
const fileName = `${schemaName}.ts`;
const filePath = path.join(outputDir, fileName);
// Convert OpenAPI schema to TypeScript interface
const tsInterface = generateTypeScriptInterface(schemaName, schema);
await fs.writeFile(filePath, tsInterface);
console.log(`✅ Generated ${fileName}`);
}
}
console.log(`🎉 Generated ${Object.keys(schemas).length} DTO files in ${outputDir}`);
console.log(`✅ TypeScript types generated at: ${outputFile}`);
// Generate individual DTO files
await generateIndividualDtoFiles(openapiPath, outputDir);
} catch (error) { } catch (error) {
console.error('❌ Failed to generate DTOs:', error); console.error('❌ Failed to generate types:', error);
process.exit(1); process.exit(1);
} }
} }
function generateTypeScriptInterface(name: string, schema: any): string { async function generateIndividualDtoFiles(openapiPath: string, outputDir: string) {
const properties = schema.properties || {}; console.log('📝 Generating individual DTO files...');
const required = schema.required || [];
const specContent = await fs.readFile(openapiPath, 'utf-8');
const spec = JSON.parse(specContent);
const schemas = spec.components?.schemas || {};
const schemaNames = Object.keys(schemas);
// Generate individual files for each schema
for (const schemaName of schemaNames) {
const schema = schemas[schemaName];
const fileName = `${schemaName}.ts`;
const filePath = path.join(outputDir, fileName);
const fileContent = generateDtoFileContent(schemaName, schema, schemas);
await fs.writeFile(filePath, fileContent);
console.log(` ✅ Generated ${fileName}`);
}
// Generate index file that re-exports all DTOs
let indexContent = `/**
* Auto-generated DTO type exports
* This file is generated by scripts/generate-api-types.ts
* Do not edit manually - regenerate using: npm run api:sync-types
*/
let interfaceContent = `/** // Re-export all schema types from the generated OpenAPI types
export type { components, paths, operations } from './api';
// Re-export individual DTO types
`;
for (const schemaName of schemaNames) {
indexContent += `export type { ${schemaName} } from './${schemaName}';\n`;
}
const indexPath = path.join(outputDir, 'index.ts');
await fs.writeFile(indexPath, indexContent);
console.log(`✅ Generated ${schemaNames.length} individual DTO files and index at: ${outputDir}`);
}
function generateDtoFileContent(schemaName: string, schema: any, allSchemas: Record<string, any>): string {
// Collect dependencies (referenced DTOs)
const dependencies = new Set<string>();
collectDependencies(schema, dependencies, allSchemas);
dependencies.delete(schemaName); // Remove self-reference
let content = `/**
* Auto-generated DTO from OpenAPI spec * Auto-generated DTO from OpenAPI spec
* This file is generated by scripts/generate-api-types.ts * This file is generated by scripts/generate-api-types.ts
* Do not edit manually - regenerate using: npm run api:sync-types * Do not edit manually - regenerate using: npm run api:sync-types
*/ */
export interface ${name} {\n`; `;
for (const [propName, propSchema] of Object.entries(properties)) { // Add imports for dependencies
const isRequired = required.includes(propName); for (const dep of dependencies) {
const optionalMark = isRequired ? '' : '?'; content += `import type { ${dep} } from './${dep}';\n`;
const type = openApiTypeToTypeScript(propSchema); }
interfaceContent += ` ${propName}${optionalMark}: ${type};\n`; if (dependencies.size > 0) {
content += '\n';
} }
interfaceContent += '}\n'; // Generate interface
content += `export interface ${schemaName} {\n`;
return interfaceContent;
const properties = schema.properties || {};
const required = new Set(schema.required || []);
for (const [propName, propSchema] of Object.entries(properties)) {
const isRequired = required.has(propName);
const optionalMark = isRequired ? '' : '?';
const typeStr = schemaToTypeString(propSchema as any);
// Add JSDoc comment for format
if ((propSchema as any).format) {
content += ` /** Format: ${(propSchema as any).format} */\n`;
}
content += ` ${propName}${optionalMark}: ${typeStr};\n`;
}
content += '}\n';
return content;
} }
function openApiTypeToTypeScript(schema: any): string { function collectDependencies(schema: any, deps: Set<string>, allSchemas: Record<string, any>): void {
if (!schema) return;
if (schema.$ref) { if (schema.$ref) {
// Handle references
const refName = schema.$ref.split('/').pop(); const refName = schema.$ref.split('/').pop();
return refName; if (refName && allSchemas[refName]) {
deps.add(refName);
}
return;
} }
if (schema.type === 'array' && schema.items) {
collectDependencies(schema.items, deps, allSchemas);
return;
}
if (schema.properties) {
for (const propSchema of Object.values(schema.properties)) {
collectDependencies(propSchema, deps, allSchemas);
}
}
if (schema.oneOf) {
for (const subSchema of schema.oneOf) {
collectDependencies(subSchema, deps, allSchemas);
}
}
if (schema.anyOf) {
for (const subSchema of schema.anyOf) {
collectDependencies(subSchema, deps, allSchemas);
}
}
if (schema.allOf) {
for (const subSchema of schema.allOf) {
collectDependencies(subSchema, deps, allSchemas);
}
}
}
function schemaToTypeString(schema: any): string {
if (!schema) return 'unknown';
if (schema.$ref) {
return schema.$ref.split('/').pop() || 'unknown';
}
if (schema.type === 'array') { if (schema.type === 'array') {
const itemType = openApiTypeToTypeScript(schema.items); const itemType = schemaToTypeString(schema.items);
return `${itemType}[]`; return `${itemType}[]`;
} }
if (schema.type === 'object') { if (schema.type === 'object') {
// For complex objects, we'll use a generic Record for now if (schema.properties) {
return 'Record<string, any>'; // Inline object type
const props = Object.entries(schema.properties)
.map(([key, val]) => `${key}: ${schemaToTypeString(val as any)}`)
.join('; ');
return `{ ${props} }`;
}
return 'Record<string, unknown>';
} }
if (schema.oneOf) {
return schema.oneOf.map((s: any) => schemaToTypeString(s)).join(' | ');
}
if (schema.anyOf) {
return schema.anyOf.map((s: any) => schemaToTypeString(s)).join(' | ');
}
if (schema.enum) {
return schema.enum.map((v: any) => JSON.stringify(v)).join(' | ');
}
switch (schema.type) { switch (schema.type) {
case 'string': case 'string':
if (schema.format === 'date-time') {
return 'string'; // Keep as string for now, could be Date
}
return 'string'; return 'string';
case 'number': case 'number':
case 'integer': case 'integer':
return 'number'; return 'number';
case 'boolean': case 'boolean':
return 'boolean'; return 'boolean';
case 'null':
return 'null';
default: default:
return 'any'; return 'unknown';
} }
} }
generateIndividualDTOs().catch(console.error); generateTypes().catch(console.error);

View File

@@ -1,283 +0,0 @@
#!/usr/bin/env tsx
import fs from 'fs/promises';
import path from 'path';
import { glob } from 'glob';
// Comprehensive OpenAPI spec generator that scans all DTO files
async function generateComprehensiveOpenAPISpec() {
console.log('🔄 Generating comprehensive OpenAPI spec from all DTO files...');
const schemas: Record<string, any> = {};
// Find all DTO files in the API
const dtoFiles = await glob('apps/api/src/domain/*/dtos/**/*.ts', {
cwd: path.join(__dirname, '..')
});
console.log(`📁 Found ${dtoFiles.length} DTO files to process`);
for (const dtoFile of dtoFiles) {
const filePath = path.join(__dirname, '..', dtoFile);
await processDTOFile(filePath, schemas);
}
// Also check for DTOs in other locations
const additionalDtoFiles = await glob('apps/api/src/domain/*/*.ts', {
cwd: path.join(__dirname, '..')
});
for (const dtoFile of additionalDtoFiles) {
if (dtoFile.includes('dto') || dtoFile.includes('DTO')) {
const filePath = path.join(__dirname, '..', dtoFile);
await processDTOFile(filePath, schemas);
}
}
const spec = {
openapi: '3.0.0',
info: {
title: 'GridPilot API',
description: 'GridPilot API documentation',
version: '1.0.0'
},
paths: {},
components: {
schemas
}
};
const outputPath = path.join(__dirname, '../apps/api/openapi.json');
await fs.writeFile(outputPath, JSON.stringify(spec, null, 2));
console.log(`✅ Comprehensive OpenAPI spec generated with ${Object.keys(schemas).length} schemas at: ${outputPath}`);
}
async function processDTOFile(filePath: string, schemas: Record<string, any>) {
try {
const content = await fs.readFile(filePath, 'utf-8');
// Extract all class and interface definitions
const classMatches = content.match(/export (?:class|interface) (\w+(?:DTO|Dto))/g);
if (!classMatches) {
// Debug: check if file has any export statements
if (content.includes('export')) {
console.log(`📄 ${filePath} has exports but no DTO classes`);
}
return;
}
console.log(`📄 Processing ${filePath} - found ${classMatches.length} DTO classes`);
for (const classMatch of classMatches) {
const classNameMatch = classMatch.match(/export (?:class|interface) (\w+(?:DTO|Dto))/);
if (classNameMatch) {
const className = classNameMatch[1];
console.log(` 🔍 Extracting schema for ${className}`);
const schema = extractSchemaFromClass(content, className);
if (schema && Object.keys(schema.properties || {}).length > 0) {
schemas[className] = schema;
console.log(` ✅ Added schema for ${className} with ${Object.keys(schema.properties).length} properties`);
} else {
console.log(` ⚠️ No schema generated for ${className}`);
}
}
}
} catch (error) {
// File can't be read, continue
console.warn(`⚠️ Could not process ${filePath}:`, error.message);
}
}
function extractSchemaFromClass(content: string, className: string): any | null {
const properties: Record<string, any> = {};
const required: string[] = [];
// Extract @ApiProperty decorated properties from NestJS DTOs
// Pattern: @ApiProperty(...) followed by property declaration
const lines = content.split('\n');
let i = 0;
while (i < lines.length) {
const line = lines[i].trim();
// Look for @ApiProperty decorator
if (line.startsWith('@ApiProperty(')) {
const decoratorMatch = line.match(/@ApiProperty\(([^)]*)\)/);
if (decoratorMatch) {
const decoratorContent = decoratorMatch[1];
// Find the property declaration (could be on next line)
let propertyLine = line;
if (!line.includes(';')) {
i++;
if (i < lines.length) {
propertyLine = lines[i].trim();
}
}
// Extract property name and type from declaration
const propertyMatch = propertyLine.match(/(\w+)\s*\??:\s*([^;]+);/);
if (propertyMatch) {
const propertyName = propertyMatch[1];
const propertyType = propertyMatch[2].trim();
// Check if property is required
const isOptional = propertyName.includes('?') ||
decoratorContent.includes('required: false') ||
decoratorContent.includes('nullable: true') ||
propertyLine.includes('@IsOptional()');
if (!isOptional) {
required.push(propertyName);
}
// Try to extract type from decorator first, then fall back to property type
let schemaType = extractTypeFromDecorator(decoratorContent);
if (!schemaType) {
schemaType = mapTypeToSchema(propertyType);
}
properties[propertyName] = schemaType;
}
}
}
i++;
}
// Also extract interface properties (for existing interfaces)
const interfacePropertyRegex = /(\w+)\s*\??:\s*([^;]+);/g;
let match;
while ((match = interfacePropertyRegex.exec(content)) !== null) {
const propertyName = match[1];
const propertyType = match[2].trim();
if (!properties[propertyName]) {
// Check if property is required
if (!propertyName.includes('?')) {
required.push(propertyName);
}
properties[propertyName] = mapTypeToSchema(propertyType);
}
}
if (Object.keys(properties).length === 0) {
return null;
}
const schema: any = {
type: 'object',
properties
};
if (required.length > 0) {
schema.required = required;
}
return schema;
}
function extractTypeFromDecorator(decoratorContent: string): any | null {
// Extract type information from @ApiProperty decorator
// Examples:
// @ApiProperty({ type: String })
// @ApiProperty({ type: [SomeDTO] })
// @ApiProperty({ type: () => SomeDTO })
if (decoratorContent.includes('type:')) {
// Simple type extraction - this is a simplified version
// In a real implementation, you'd want proper AST parsing
if (decoratorContent.includes('[String]') || decoratorContent.includes('[string]')) {
return { type: 'array', items: { type: 'string' } };
}
if (decoratorContent.includes('[Number]') || decoratorContent.includes('[number]')) {
return { type: 'array', items: { type: 'number' } };
}
if (decoratorContent.includes('String') || decoratorContent.includes('string')) {
return { type: 'string' };
}
if (decoratorContent.includes('Number') || decoratorContent.includes('number')) {
return { type: 'number' };
}
if (decoratorContent.includes('Boolean') || decoratorContent.includes('boolean')) {
return { type: 'boolean' };
}
// For complex types with references
const refMatch = decoratorContent.match(/type:\s*\[?(\w+DTO)\]?/);
if (refMatch) {
const refType = refMatch[1];
if (decoratorContent.includes('[')) {
return { type: 'array', items: { $ref: `#/components/schemas/${refType}` } };
} else {
return { $ref: `#/components/schemas/${refType}` };
}
}
// For function types like type: () => SomeDTO
const funcMatch = decoratorContent.match(/type:\s*\(\)\s*=>\s*(\w+DTO)/);
if (funcMatch) {
return { $ref: `#/components/schemas/${funcMatch[1]}` };
}
}
return null;
}
function mapTypeToSchema(type: string): any {
// Clean up the type
type = type.replace(/[|;]/g, '').trim();
// Handle array types
if (type.endsWith('[]')) {
const itemType = type.slice(0, -2);
return {
type: 'array',
items: mapTypeToSchema(itemType)
};
}
// Handle union types (simplified)
if (type.includes('|')) {
const types = type.split('|').map(t => t.trim());
if (types.length === 2 && types.includes('null')) {
// Nullable type
const nonNullType = types.find(t => t !== 'null');
return mapTypeToSchema(nonNullType!);
}
return {
oneOf: types.map(t => mapTypeToSchema(t))
};
}
// Handle basic types
switch (type.toLowerCase()) {
case 'string':
return { type: 'string' };
case 'number':
case 'bigint':
return { type: 'number' };
case 'boolean':
return { type: 'boolean' };
case 'date':
return { type: 'string', format: 'date-time' };
case 'any':
case 'unknown':
return {};
default:
// For complex types, assume they're other DTOs
if (type.includes('DTO') || type.includes('Dto')) {
return { $ref: `#/components/schemas/${type}` };
}
// For other types, use string as fallback
return { type: 'string' };
}
}
generateComprehensiveOpenAPISpec().catch(console.error);

View File

@@ -1,39 +1,63 @@
#!/usr/bin/env tsx #!/usr/bin/env tsx
import fs from 'fs/promises'; /**
import path from 'path'; * Generate OpenAPI spec from NestJS DTO classes
*
* This script scans all DTO files and extracts OpenAPI schema definitions
* using the @nestjs/swagger metadata.
*/
import 'reflect-metadata';
import * as fs from 'fs/promises';
import * as path from 'path';
import { glob } from 'glob'; import { glob } from 'glob';
// Comprehensive OpenAPI spec generator that scans all DTO files // OpenAPI schema types
async function generateComprehensiveOpenAPISpec() { interface OpenAPISchema {
console.log('🔄 Generating comprehensive OpenAPI spec from all DTO files...'); type?: string;
format?: string;
$ref?: string;
items?: OpenAPISchema;
properties?: Record<string, OpenAPISchema>;
required?: string[];
enum?: string[];
nullable?: boolean;
description?: string;
}
const schemas: Record<string, any> = {}; interface OpenAPISpec {
openapi: string;
info: {
title: string;
description: string;
version: string;
};
paths: Record<string, any>;
components: {
schemas: Record<string, OpenAPISchema>;
};
}
// Find all DTO files in the API async function generateSpec() {
console.log('🔄 Generating OpenAPI spec from DTO files...');
const schemas: Record<string, OpenAPISchema> = {};
// Find all DTO files
const dtoFiles = await glob('apps/api/src/domain/*/dtos/**/*.ts', { const dtoFiles = await glob('apps/api/src/domain/*/dtos/**/*.ts', {
cwd: path.join(__dirname, '..') cwd: process.cwd()
}); });
console.log(`📁 Found ${dtoFiles.length} DTO files to process`); console.log(`📁 Found ${dtoFiles.length} DTO files to process`);
for (const dtoFile of dtoFiles) { for (const dtoFile of dtoFiles) {
const filePath = path.join(__dirname, '..', dtoFile); try {
await processDTOFile(filePath, schemas); await processDTOFile(path.join(process.cwd(), dtoFile), schemas);
} } catch (e: any) {
console.warn(`⚠️ Could not process ${dtoFile}: ${e.message}`);
// Also check for DTOs in other locations
const additionalDtoFiles = await glob('apps/api/src/domain/*/*.ts', {
cwd: path.join(__dirname, '..')
});
for (const dtoFile of additionalDtoFiles) {
if (dtoFile.includes('dto') || dtoFile.includes('DTO')) {
const filePath = path.join(__dirname, '..', dtoFile);
await processDTOFile(filePath, schemas);
} }
} }
const spec = { const spec: OpenAPISpec = {
openapi: '3.0.0', openapi: '3.0.0',
info: { info: {
title: 'GridPilot API', title: 'GridPilot API',
@@ -46,218 +70,183 @@ async function generateComprehensiveOpenAPISpec() {
} }
}; };
const outputPath = path.join(__dirname, '../apps/api/openapi.json'); const outputPath = path.join(process.cwd(), 'apps/api/openapi.json');
await fs.writeFile(outputPath, JSON.stringify(spec, null, 2)); await fs.writeFile(outputPath, JSON.stringify(spec, null, 2));
console.log(`✅ OpenAPI spec generated with ${Object.keys(schemas).length} schemas at: ${outputPath}`);
console.log(`✅ Comprehensive OpenAPI spec generated with ${Object.keys(schemas).length} schemas at: ${outputPath}`);
} }
async function processDTOFile(filePath: string, schemas: Record<string, any>) { async function processDTOFile(filePath: string, schemas: Record<string, OpenAPISchema>) {
try { const content = await fs.readFile(filePath, 'utf-8');
const content = await fs.readFile(filePath, 'utf-8');
// Extract all class and interface definitions // Find class definitions with DTO suffix
const classMatches = content.match(/export (?:class|interface) (\w+(?:DTO|Dto))/g); const classRegex = /export\s+class\s+(\w+(?:DTO|Dto))\s*(?:extends\s+\w+)?\s*\{([^}]*(?:\{[^}]*\}[^}]*)*)\}/gs;
if (!classMatches) { let classMatch;
// Debug: check if file has any export statements
if (content.includes('export')) { while ((classMatch = classRegex.exec(content)) !== null) {
console.log(`📄 ${filePath} has exports but no DTO classes`); const className = classMatch[1];
} const classBody = classMatch[2];
return;
console.log(` 📝 Processing ${className}`);
const schema = extractSchemaFromClassBody(classBody, content);
if (schema && Object.keys(schema.properties || {}).length > 0) {
schemas[className] = schema;
console.log(` ✅ Added ${className} with ${Object.keys(schema.properties || {}).length} properties`);
} }
console.log(`📄 Processing ${filePath} - found ${classMatches.length} DTO classes`);
for (const classMatch of classMatches) {
const classNameMatch = classMatch.match(/export (?:class|interface) (\w+(?:DTO|Dto))/);
if (classNameMatch) {
const className = classNameMatch[1];
console.log(` 🔍 Extracting schema for ${className}`);
const schema = extractSchemaFromClass(content, className);
if (schema && Object.keys(schema.properties || {}).length > 0) {
schemas[className] = schema;
console.log(` ✅ Added schema for ${className} with ${Object.keys(schema.properties).length} properties`);
} else {
console.log(` ⚠️ No schema generated for ${className}`);
}
}
}
} catch (error) {
// File can't be read, continue
console.warn(`⚠️ Could not process ${filePath}:`, error.message);
} }
} }
function extractSchemaFromClass(content: string, className: string): any | null { function extractSchemaFromClassBody(classBody: string, fullContent: string): OpenAPISchema {
const properties: Record<string, any> = {}; const properties: Record<string, OpenAPISchema> = {};
const required: string[] = []; const required: string[] = [];
// Extract @ApiProperty decorated properties from NestJS DTOs // Split by lines and process each property
// Pattern: @ApiProperty(...) followed by property declaration const lines = classBody.split('\n');
const lines = content.split('\n'); let currentDecorators: string[] = [];
let i = 0;
while (i < lines.length) { for (let i = 0; i < lines.length; i++) {
const line = lines[i].trim(); const line = lines[i].trim();
// Look for @ApiProperty decorator // Skip empty lines and comments
if (line.startsWith('@ApiProperty(')) { if (!line || line.startsWith('//') || line.startsWith('/*') || line.startsWith('*')) {
const decoratorMatch = line.match(/@ApiProperty\(([^)]*)\)/); continue;
if (decoratorMatch) {
const decoratorContent = decoratorMatch[1];
// Find the property declaration (could be on next line)
let propertyLine = line;
if (!line.includes(';')) {
i++;
if (i < lines.length) {
propertyLine = lines[i].trim();
}
}
// Extract property name and type from declaration
const propertyMatch = propertyLine.match(/(\w+)\s*\??:\s*([^;]+);/);
if (propertyMatch) {
const propertyName = propertyMatch[1];
const propertyType = propertyMatch[2].trim();
// Check if property is required
const isOptional = propertyName.includes('?') ||
decoratorContent.includes('required: false') ||
decoratorContent.includes('nullable: true') ||
propertyLine.includes('@IsOptional()');
if (!isOptional) {
required.push(propertyName);
}
// Try to extract type from decorator first, then fall back to property type
let schemaType = extractTypeFromDecorator(decoratorContent);
if (!schemaType) {
schemaType = mapTypeToSchema(propertyType);
}
properties[propertyName] = schemaType;
}
}
} }
i++; // Collect decorators
} if (line.startsWith('@')) {
currentDecorators.push(line);
continue;
}
// Also extract interface properties (for existing interfaces) // Check if this is a property declaration
const interfacePropertyRegex = /(\w+)\s*\??:\s*([^;]+);/g; const propertyMatch = line.match(/^(\w+)(\?)?[!]?\s*:\s*(.+?)\s*;?\s*$/);
let match; if (propertyMatch) {
while ((match = interfacePropertyRegex.exec(content)) !== null) { const [, propName, optional, propType] = propertyMatch;
const propertyName = match[1];
const propertyType = match[2].trim();
if (!properties[propertyName]) { // Skip if propName is a TypeScript/decorator keyword
// Check if property is required if (['constructor', 'private', 'public', 'protected', 'static', 'readonly'].includes(propName)) {
if (!propertyName.includes('?')) { currentDecorators = [];
required.push(propertyName); continue;
} }
properties[propertyName] = mapTypeToSchema(propertyType); // Determine if required
const hasApiProperty = currentDecorators.some(d => d.includes('@ApiProperty'));
const isOptional = !!optional ||
currentDecorators.some(d => d.includes('required: false') || d.includes('@IsOptional'));
const isNullable = currentDecorators.some(d => d.includes('nullable: true'));
if (!isOptional && !isNullable) {
required.push(propName);
}
// Extract type from @ApiProperty decorator if present
let schema = extractTypeFromDecorators(currentDecorators, propType);
properties[propName] = schema;
currentDecorators = [];
} }
} }
if (Object.keys(properties).length === 0) { const result: OpenAPISchema = {
return null;
}
const schema: any = {
type: 'object', type: 'object',
properties properties
}; };
if (required.length > 0) { if (required.length > 0) {
schema.required = required; result.required = required;
} }
return schema; return result;
} }
function extractTypeFromDecorator(decoratorContent: string): any | null { function extractTypeFromDecorators(decorators: string[], tsType: string): OpenAPISchema {
// Extract type information from @ApiProperty decorator // Join all decorators to search across them
// Examples: const decoratorStr = decorators.join(' ');
// @ApiProperty({ type: String })
// @ApiProperty({ type: [SomeDTO] })
// @ApiProperty({ type: () => SomeDTO })
if (decoratorContent.includes('type:')) { // Check for @ApiProperty type specification
// Simple type extraction - this is a simplified version const apiPropertyMatch = decoratorStr.match(/@ApiProperty\s*\(\s*\{([^}]*)\}\s*\)/s);
// In a real implementation, you'd want proper AST parsing
if (apiPropertyMatch) {
const apiPropertyContent = apiPropertyMatch[1];
if (decoratorContent.includes('[String]') || decoratorContent.includes('[string]')) { // Check for array type: type: [SomeDTO]
return { type: 'array', items: { type: 'string' } }; const arrayTypeMatch = apiPropertyContent.match(/type:\s*\[\s*(\w+)\s*\]/);
if (arrayTypeMatch) {
const itemType = arrayTypeMatch[1];
return {
type: 'array',
items: mapTypeToSchema(itemType)
};
} }
if (decoratorContent.includes('[Number]') || decoratorContent.includes('[number]')) { // Check for function type: type: () => SomeDTO
return { type: 'array', items: { type: 'number' } }; const funcTypeMatch = apiPropertyContent.match(/type:\s*\(\)\s*=>\s*(\w+)/);
if (funcTypeMatch) {
return mapTypeToSchema(funcTypeMatch[1]);
} }
if (decoratorContent.includes('String') || decoratorContent.includes('string')) { // Check for direct type reference: type: SomeDTO
return { type: 'string' }; const directTypeMatch = apiPropertyContent.match(/type:\s*(\w+)(?:\s*[,}])/);
if (directTypeMatch && !['String', 'Number', 'Boolean', 'string', 'number', 'boolean'].includes(directTypeMatch[1])) {
return mapTypeToSchema(directTypeMatch[1]);
} }
if (decoratorContent.includes('Number') || decoratorContent.includes('number')) { // Check for enum
return { type: 'number' }; const enumMatch = apiPropertyContent.match(/enum:\s*(\w+)/);
if (enumMatch) {
return { type: 'string' }; // Simplify enum to string
} }
if (decoratorContent.includes('Boolean') || decoratorContent.includes('boolean')) { // Check for nullable
return { type: 'boolean' }; if (apiPropertyContent.includes('nullable: true')) {
} const baseSchema = mapTypeToSchema(tsType);
baseSchema.nullable = true;
// For complex types with references return baseSchema;
const refMatch = decoratorContent.match(/type:\s*\[?(\w+DTO)\]?/);
if (refMatch) {
const refType = refMatch[1];
if (decoratorContent.includes('[')) {
return { type: 'array', items: { $ref: `#/components/schemas/${refType}` } };
} else {
return { $ref: `#/components/schemas/${refType}` };
}
}
// For function types like type: () => SomeDTO
const funcMatch = decoratorContent.match(/type:\s*\(\)\s*=>\s*(\w+DTO)/);
if (funcMatch) {
return { $ref: `#/components/schemas/${funcMatch[1]}` };
} }
} }
return null; // Fall back to TypeScript type
return mapTypeToSchema(tsType);
} }
function mapTypeToSchema(type: string): any { function mapTypeToSchema(type: string): OpenAPISchema {
// Clean up the type // Clean up the type
type = type.replace(/[|;]/g, '').trim(); type = type.replace(/[;!]/g, '').trim();
// Handle union with null
if (type.includes('| null') || type.includes('null |')) {
const baseType = type.replace(/\|\s*null/g, '').replace(/null\s*\|/g, '').trim();
const schema = mapTypeToSchema(baseType);
schema.nullable = true;
return schema;
}
// Handle array types // Handle array types
if (type.endsWith('[]')) { if (type.endsWith('[]')) {
const itemType = type.slice(0, -2); const itemType = type.slice(0, -2).trim();
return { return {
type: 'array', type: 'array',
items: mapTypeToSchema(itemType) items: mapTypeToSchema(itemType)
}; };
} }
// Handle union types (simplified) // Handle Array<T> syntax
if (type.includes('|')) { const arrayGenericMatch = type.match(/^Array<(.+)>$/);
const types = type.split('|').map(t => t.trim()); if (arrayGenericMatch) {
if (types.length === 2 && types.includes('null')) {
// Nullable type
const nonNullType = types.find(t => t !== 'null');
return mapTypeToSchema(nonNullType!);
}
return { return {
oneOf: types.map(t => mapTypeToSchema(t)) type: 'array',
items: mapTypeToSchema(arrayGenericMatch[1])
}; };
} }
// Handle basic types // Handle object literal types (inline objects)
switch (type.toLowerCase()) { if (type.startsWith('{')) {
return { type: 'object' };
}
// Handle primitive types
const lowerType = type.toLowerCase();
switch (lowerType) {
case 'string': case 'string':
return { type: 'string' }; return { type: 'string' };
case 'number': case 'number':
@@ -269,15 +258,17 @@ function mapTypeToSchema(type: string): any {
return { type: 'string', format: 'date-time' }; return { type: 'string', format: 'date-time' };
case 'any': case 'any':
case 'unknown': case 'unknown':
return {}; case 'object':
default: return { type: 'object' };
// For complex types, assume they're other DTOs
if (type.includes('DTO') || type.includes('Dto')) {
return { $ref: `#/components/schemas/${type}` };
}
// For other types, use string as fallback
return { type: 'string' };
} }
// Handle DTO references
if (type.endsWith('DTO') || type.endsWith('Dto')) {
return { $ref: `#/components/schemas/${type}` };
}
// Default to string for unknown types
return { type: 'string' };
} }
generateComprehensiveOpenAPISpec().catch(console.error); generateSpec().catch(console.error);