Files
gridpilot.gg/apps/api/src/development/use-cases/DemoLoginUseCase.ts
2025-12-31 19:55:43 +01:00

122 lines
4.8 KiB
TypeScript

import { EmailAddress } from '@core/identity/domain/value-objects/EmailAddress';
import { UserId } from '@core/identity/domain/value-objects/UserId';
import { User } from '@core/identity/domain/entities/User';
import { IAuthRepository } from '@core/identity/domain/repositories/IAuthRepository';
import { IPasswordHashingService } from '@core/identity/domain/services/PasswordHashingService';
import { Result } from '@core/shared/application/Result';
import type { ApplicationErrorCode } from '@core/shared/errors/ApplicationErrorCode';
import type { UseCaseOutputPort, Logger, UseCase } from '@core/shared/application';
export type DemoLoginInput = {
role: 'driver' | 'sponsor' | 'league-owner' | 'league-steward' | 'league-admin' | 'system-owner' | 'super-admin';
};
export type DemoLoginResult = {
user: User;
};
export type DemoLoginErrorCode = 'DEMO_NOT_ALLOWED' | 'REPOSITORY_ERROR';
export type DemoLoginApplicationError = ApplicationErrorCode<DemoLoginErrorCode, { message: string }>;
/**
* Application Use Case: DemoLoginUseCase
*
* Provides demo login functionality for development environments.
* Creates demo users with predefined credentials.
*
* ⚠️ DEVELOPMENT ONLY - Should be disabled in production
*/
export class DemoLoginUseCase implements UseCase<DemoLoginInput, void, DemoLoginErrorCode> {
constructor(
private readonly authRepo: IAuthRepository,
private readonly passwordService: IPasswordHashingService,
private readonly logger: Logger,
private readonly output: UseCaseOutputPort<DemoLoginResult>,
) {}
async execute(input: DemoLoginInput): Promise<Result<void, DemoLoginApplicationError>> {
// Security check: Only allow in development
if (process.env.NODE_ENV !== 'development' && process.env.ALLOW_DEMO_LOGIN !== 'true') {
return Result.err({
code: 'DEMO_NOT_ALLOWED',
details: { message: 'Demo login is only available in development environment' },
});
}
try {
// Generate demo user email and display name based on role
const roleConfig = {
'driver': { email: 'demo.driver@example.com', name: 'John Demo', primaryDriverId: true },
'sponsor': { email: 'demo.sponsor@example.com', name: 'Jane Sponsor', primaryDriverId: false },
'league-owner': { email: 'demo.owner@example.com', name: 'Alex Owner', primaryDriverId: true },
'league-steward': { email: 'demo.steward@example.com', name: 'Sam Steward', primaryDriverId: true },
'league-admin': { email: 'demo.admin@example.com', name: 'Taylor Admin', primaryDriverId: true },
'system-owner': { email: 'demo.systemowner@example.com', name: 'System Owner', primaryDriverId: true },
'super-admin': { email: 'demo.superadmin@example.com', name: 'Super Admin', primaryDriverId: true },
};
const config = roleConfig[input.role];
const emailVO = EmailAddress.create(config.email);
// Check if demo user already exists
let user = await this.authRepo.findByEmail(emailVO);
if (!user) {
// Create new demo user
this.logger.info('[DemoLoginUseCase] Creating new demo user', { role: input.role });
const userId = UserId.create();
// Use a fixed demo password and hash it
const demoPassword = 'Demo1234!';
const hashedPassword = await this.passwordService.hash(demoPassword);
// Import PasswordHash and create proper object
const passwordHashModule = await import('@core/identity/domain/value-objects/PasswordHash');
const passwordHash = passwordHashModule.PasswordHash.fromHash(hashedPassword);
const userProps: any = {
id: userId,
displayName: config.name,
email: config.email,
passwordHash,
};
if (config.primaryDriverId) {
userProps.primaryDriverId = `demo-${input.role}-${userId.value}`;
// Add avatar URL for demo users with primary driver
// Use the same format as seeded drivers: /media/default/neutral-default-avatar
userProps.avatarUrl = '/media/default/neutral-default-avatar';
}
user = User.create(userProps);
await this.authRepo.save(user);
} else {
this.logger.info('[DemoLoginUseCase] Using existing demo user', {
role: input.role,
userId: user.getId().value
});
}
this.output.present({ user });
return Result.ok(undefined);
} catch (error) {
const message =
error instanceof Error && error.message
? error.message
: 'Failed to execute DemoLoginUseCase';
this.logger.error('DemoLoginUseCase.execute failed', error instanceof Error ? error : undefined, {
input,
});
return Result.err({
code: 'REPOSITORY_ERROR',
details: { message },
});
}
}
}