fix issues
This commit is contained in:
@@ -1,3 +1,4 @@
|
||||
import type { Provider } from '@nestjs/common';
|
||||
import { Module } from '@nestjs/common';
|
||||
import { InMemoryAdminPersistenceModule } from '../../persistence/inmemory/InMemoryAdminPersistenceModule';
|
||||
import { AdminService } from './AdminService';
|
||||
@@ -7,6 +8,7 @@ import { DashboardStatsPresenter } from './presenters/DashboardStatsPresenter';
|
||||
import { AuthModule } from '../auth/AuthModule';
|
||||
import { ListUsersUseCase } from '@core/admin/application/use-cases/ListUsersUseCase';
|
||||
import { GetDashboardStatsUseCase } from './use-cases/GetDashboardStatsUseCase';
|
||||
import { InitializationLogger } from '../../shared/logging/InitializationLogger';
|
||||
import type { UseCaseOutputPort } from '@core/shared/application/UseCaseOutputPort';
|
||||
import type { ListUsersResult } from '@core/admin/application/use-cases/ListUsersUseCase';
|
||||
import type { DashboardStatsResult } from './use-cases/GetDashboardStatsUseCase';
|
||||
@@ -16,38 +18,80 @@ export const ADMIN_USER_REPOSITORY_TOKEN = 'IAdminUserRepository';
|
||||
export const LIST_USERS_OUTPUT_PORT_TOKEN = 'ListUsersOutputPort';
|
||||
export const DASHBOARD_STATS_OUTPUT_PORT_TOKEN = 'DashboardStatsOutputPort';
|
||||
|
||||
const initLogger = InitializationLogger.getInstance();
|
||||
|
||||
const adminProviders: Provider[] = [
|
||||
AdminService,
|
||||
ListUsersPresenter,
|
||||
DashboardStatsPresenter,
|
||||
{
|
||||
provide: LIST_USERS_OUTPUT_PORT_TOKEN,
|
||||
useExisting: ListUsersPresenter,
|
||||
},
|
||||
{
|
||||
provide: DASHBOARD_STATS_OUTPUT_PORT_TOKEN,
|
||||
useExisting: DashboardStatsPresenter,
|
||||
},
|
||||
{
|
||||
provide: ListUsersUseCase,
|
||||
useFactory: (
|
||||
repository: IAdminUserRepository,
|
||||
output: UseCaseOutputPort<ListUsersResult>,
|
||||
) => new ListUsersUseCase(repository, output),
|
||||
inject: [ADMIN_USER_REPOSITORY_TOKEN, LIST_USERS_OUTPUT_PORT_TOKEN],
|
||||
},
|
||||
{
|
||||
provide: GetDashboardStatsUseCase,
|
||||
useFactory: (
|
||||
repository: IAdminUserRepository,
|
||||
output: UseCaseOutputPort<DashboardStatsResult>,
|
||||
) => new GetDashboardStatsUseCase(repository, output),
|
||||
inject: [ADMIN_USER_REPOSITORY_TOKEN, DASHBOARD_STATS_OUTPUT_PORT_TOKEN],
|
||||
},
|
||||
];
|
||||
|
||||
// Diagnostics: Nest will crash with "metatype is not a constructor" if any provider resolves
|
||||
// to a non-constructable value (e.g. undefined import, object, etc.).
|
||||
for (const provider of adminProviders) {
|
||||
// Class providers are functions at runtime.
|
||||
if (typeof provider === 'function') {
|
||||
continue;
|
||||
}
|
||||
|
||||
// Custom providers should be objects with a `provide` token.
|
||||
if (!provider || typeof provider !== 'object' || !('provide' in provider)) {
|
||||
initLogger.error(
|
||||
`[AdminModule] Invalid provider entry (expected class or provider object): ${String(provider)}`,
|
||||
);
|
||||
continue;
|
||||
}
|
||||
|
||||
const token = (provider as { provide: unknown }).provide;
|
||||
const tokenLabel = typeof token === 'function' ? token.name : String(token);
|
||||
|
||||
if ('useClass' in provider) {
|
||||
const useClass = (provider as { useClass?: unknown }).useClass;
|
||||
if (typeof useClass !== 'function') {
|
||||
initLogger.error(
|
||||
`[AdminModule] Provider "${tokenLabel}" has non-constructable useClass: ${String(useClass)}`,
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
if ('useExisting' in provider) {
|
||||
const useExisting = (provider as { useExisting?: unknown }).useExisting;
|
||||
if (typeof useExisting !== 'function' && typeof useExisting !== 'string' && typeof useExisting !== 'symbol') {
|
||||
initLogger.warn(
|
||||
`[AdminModule] Provider "${tokenLabel}" has suspicious useExisting: ${String(useExisting)}`,
|
||||
);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@Module({
|
||||
imports: [InMemoryAdminPersistenceModule, AuthModule],
|
||||
controllers: [AdminController],
|
||||
providers: [
|
||||
AdminService,
|
||||
ListUsersPresenter,
|
||||
DashboardStatsPresenter,
|
||||
{
|
||||
provide: LIST_USERS_OUTPUT_PORT_TOKEN,
|
||||
useExisting: ListUsersPresenter,
|
||||
},
|
||||
{
|
||||
provide: DASHBOARD_STATS_OUTPUT_PORT_TOKEN,
|
||||
useExisting: DashboardStatsPresenter,
|
||||
},
|
||||
{
|
||||
provide: ListUsersUseCase,
|
||||
useFactory: (
|
||||
repository: IAdminUserRepository,
|
||||
output: UseCaseOutputPort<ListUsersResult>,
|
||||
) => new ListUsersUseCase(repository, output),
|
||||
inject: [ADMIN_USER_REPOSITORY_TOKEN, LIST_USERS_OUTPUT_PORT_TOKEN],
|
||||
},
|
||||
{
|
||||
provide: GetDashboardStatsUseCase,
|
||||
useFactory: (
|
||||
repository: IAdminUserRepository,
|
||||
output: UseCaseOutputPort<DashboardStatsResult>,
|
||||
) => new GetDashboardStatsUseCase(repository, output),
|
||||
inject: [ADMIN_USER_REPOSITORY_TOKEN, DASHBOARD_STATS_OUTPUT_PORT_TOKEN],
|
||||
},
|
||||
],
|
||||
providers: [...adminProviders],
|
||||
exports: [AdminService],
|
||||
})
|
||||
export class AdminModule {}
|
||||
export class AdminModule {}
|
||||
|
||||
@@ -25,15 +25,25 @@ export class AuthSessionDTO {
|
||||
|
||||
export class SignupParamsDTO {
|
||||
@ApiProperty()
|
||||
@IsEmail()
|
||||
email!: string;
|
||||
|
||||
@ApiProperty()
|
||||
@IsString()
|
||||
@MinLength(8)
|
||||
password!: string;
|
||||
|
||||
@ApiProperty()
|
||||
@IsString()
|
||||
@MinLength(2)
|
||||
displayName!: string;
|
||||
|
||||
@ApiProperty({ required: false })
|
||||
iracingCustomerId?: string;
|
||||
|
||||
@ApiProperty({ required: false })
|
||||
primaryDriverId?: string;
|
||||
|
||||
@ApiProperty({ required: false, nullable: true })
|
||||
avatarUrl?: string | null;
|
||||
}
|
||||
|
||||
@@ -11,4 +11,4 @@ import { DashboardProviders } from './DashboardProviders';
|
||||
providers: [DashboardService, ...DashboardProviders],
|
||||
exports: [DashboardService],
|
||||
})
|
||||
export class DashboardModule {}
|
||||
export class DashboardModule {}
|
||||
|
||||
@@ -20,20 +20,34 @@ import { DashboardOverviewUseCase } from '@core/racing/application/use-cases/Das
|
||||
import { ConsoleLogger } from '@adapters/logging/ConsoleLogger';
|
||||
import { InMemoryImageServiceAdapter } from '@adapters/media/ports/InMemoryImageServiceAdapter';
|
||||
import { DashboardOverviewPresenter } from './presenters/DashboardOverviewPresenter';
|
||||
import { DashboardService } from './DashboardService';
|
||||
import {
|
||||
DASHBOARD_OVERVIEW_OUTPUT_PORT_TOKEN,
|
||||
DASHBOARD_OVERVIEW_USE_CASE_TOKEN,
|
||||
DRIVER_REPOSITORY_TOKEN,
|
||||
IMAGE_SERVICE_TOKEN,
|
||||
LEAGUE_MEMBERSHIP_REPOSITORY_TOKEN,
|
||||
LEAGUE_REPOSITORY_TOKEN,
|
||||
LOGGER_TOKEN,
|
||||
RACE_REGISTRATION_REPOSITORY_TOKEN,
|
||||
RACE_REPOSITORY_TOKEN,
|
||||
RESULT_REPOSITORY_TOKEN,
|
||||
STANDING_REPOSITORY_TOKEN,
|
||||
} from './DashboardTokens';
|
||||
|
||||
// Define injection tokens
|
||||
export const LOGGER_TOKEN = 'Logger';
|
||||
export const DRIVER_REPOSITORY_TOKEN = 'IDriverRepository';
|
||||
export const RACE_REPOSITORY_TOKEN = 'IRaceRepository';
|
||||
export const RESULT_REPOSITORY_TOKEN = 'IResultRepository';
|
||||
export const LEAGUE_REPOSITORY_TOKEN = 'ILeagueRepository';
|
||||
export const STANDING_REPOSITORY_TOKEN = 'IStandingRepository';
|
||||
export const LEAGUE_MEMBERSHIP_REPOSITORY_TOKEN = 'ILeagueMembershipRepository';
|
||||
export const RACE_REGISTRATION_REPOSITORY_TOKEN = 'IRaceRegistrationRepository';
|
||||
export const IMAGE_SERVICE_TOKEN = 'IImageServicePort';
|
||||
export const DASHBOARD_OVERVIEW_USE_CASE_TOKEN = 'DashboardOverviewUseCase';
|
||||
export const DASHBOARD_OVERVIEW_OUTPUT_PORT_TOKEN = 'DashboardOverviewOutputPort';
|
||||
// Re-export tokens for convenience (legacy imports)
|
||||
export {
|
||||
DASHBOARD_OVERVIEW_OUTPUT_PORT_TOKEN,
|
||||
DASHBOARD_OVERVIEW_USE_CASE_TOKEN,
|
||||
DRIVER_REPOSITORY_TOKEN,
|
||||
IMAGE_SERVICE_TOKEN,
|
||||
LEAGUE_MEMBERSHIP_REPOSITORY_TOKEN,
|
||||
LEAGUE_REPOSITORY_TOKEN,
|
||||
LOGGER_TOKEN,
|
||||
RACE_REGISTRATION_REPOSITORY_TOKEN,
|
||||
RACE_REPOSITORY_TOKEN,
|
||||
RESULT_REPOSITORY_TOKEN,
|
||||
STANDING_REPOSITORY_TOKEN,
|
||||
} from './DashboardTokens';
|
||||
|
||||
export const DashboardProviders: Provider[] = [
|
||||
DashboardOverviewPresenter,
|
||||
@@ -93,19 +107,4 @@ export const DashboardProviders: Provider[] = [
|
||||
DASHBOARD_OVERVIEW_OUTPUT_PORT_TOKEN,
|
||||
],
|
||||
},
|
||||
{
|
||||
provide: DashboardService,
|
||||
useFactory: (
|
||||
logger: Logger,
|
||||
dashboardOverviewUseCase: DashboardOverviewUseCase,
|
||||
presenter: DashboardOverviewPresenter,
|
||||
imageService: ImageServicePort,
|
||||
) => new DashboardService(logger, dashboardOverviewUseCase, presenter, imageService),
|
||||
inject: [
|
||||
LOGGER_TOKEN,
|
||||
DASHBOARD_OVERVIEW_USE_CASE_TOKEN,
|
||||
DASHBOARD_OVERVIEW_OUTPUT_PORT_TOKEN,
|
||||
IMAGE_SERVICE_TOKEN,
|
||||
],
|
||||
},
|
||||
];
|
||||
];
|
||||
|
||||
@@ -7,8 +7,13 @@ import { DashboardOverviewPresenter } from './presenters/DashboardOverviewPresen
|
||||
import type { Logger } from '@core/shared/application/Logger';
|
||||
import type { ImageServicePort } from '@core/media/application/ports/ImageServicePort';
|
||||
|
||||
// Tokens
|
||||
import { DASHBOARD_OVERVIEW_USE_CASE_TOKEN, LOGGER_TOKEN, DASHBOARD_OVERVIEW_OUTPUT_PORT_TOKEN, IMAGE_SERVICE_TOKEN } from './DashboardProviders';
|
||||
// Tokens (standalone to avoid circular imports)
|
||||
import {
|
||||
DASHBOARD_OVERVIEW_OUTPUT_PORT_TOKEN,
|
||||
DASHBOARD_OVERVIEW_USE_CASE_TOKEN,
|
||||
IMAGE_SERVICE_TOKEN,
|
||||
LOGGER_TOKEN,
|
||||
} from './DashboardTokens';
|
||||
|
||||
@Injectable()
|
||||
export class DashboardService {
|
||||
@@ -228,4 +233,4 @@ export class DashboardService {
|
||||
friends: [],
|
||||
};
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
16
apps/api/src/domain/dashboard/DashboardTokens.ts
Normal file
16
apps/api/src/domain/dashboard/DashboardTokens.ts
Normal file
@@ -0,0 +1,16 @@
|
||||
// Dashboard injection tokens
|
||||
// NOTE: kept in a standalone file to avoid circular imports between
|
||||
// providers and services.
|
||||
|
||||
export const LOGGER_TOKEN = 'Logger';
|
||||
export const DRIVER_REPOSITORY_TOKEN = 'IDriverRepository';
|
||||
export const RACE_REPOSITORY_TOKEN = 'IRaceRepository';
|
||||
export const RESULT_REPOSITORY_TOKEN = 'IResultRepository';
|
||||
export const LEAGUE_REPOSITORY_TOKEN = 'ILeagueRepository';
|
||||
export const STANDING_REPOSITORY_TOKEN = 'IStandingRepository';
|
||||
export const LEAGUE_MEMBERSHIP_REPOSITORY_TOKEN = 'ILeagueMembershipRepository';
|
||||
export const RACE_REGISTRATION_REPOSITORY_TOKEN = 'IRaceRegistrationRepository';
|
||||
export const IMAGE_SERVICE_TOKEN = 'IImageServicePort';
|
||||
export const DASHBOARD_OVERVIEW_USE_CASE_TOKEN = 'DashboardOverviewUseCase';
|
||||
export const DASHBOARD_OVERVIEW_OUTPUT_PORT_TOKEN = 'DashboardOverviewOutputPort';
|
||||
|
||||
@@ -87,13 +87,17 @@ async function bootstrap() {
|
||||
|
||||
// Handle uncaught errors
|
||||
process.on('uncaughtException', (error) => {
|
||||
console.error('🚨 Uncaught Exception:', error.message);
|
||||
console.error('🚨 Uncaught Exception:', error.stack ?? error.message);
|
||||
process.exit(1);
|
||||
});
|
||||
|
||||
process.on('unhandledRejection', (reason: unknown) => {
|
||||
console.error('🚨 Unhandled Rejection:', reason instanceof Error ? reason.message : reason);
|
||||
if (reason instanceof Error) {
|
||||
console.error('🚨 Unhandled Rejection:', reason.stack ?? reason.message);
|
||||
} else {
|
||||
console.error('🚨 Unhandled Rejection:', reason);
|
||||
}
|
||||
process.exit(1);
|
||||
});
|
||||
|
||||
bootstrap();
|
||||
bootstrap();
|
||||
|
||||
Reference in New Issue
Block a user