forked from Shared/esg
Implement normalized error handling with Persian translations across all exception types, replace legacy NestJS exceptions with AppException, and add unit, integration, and smoke tests. - Add error catalog with gateway-owned codes and Persian messages - Introduce AppException wrapping normalized error envelopes - Add translateError helper for automatic messageFa population - Remove claims module and update provider error normalization - Add unit tests for error contracts and helper functions - Add integration tests for admin and inquiry endpoints - Add smoke tests for real provider connectivity - Add test support utilities (auth mocks, assertions, app factory)
68 lines
2.2 KiB
TypeScript
68 lines
2.2 KiB
TypeScript
import { Injectable, Logger } from '@nestjs/common';
|
|
import { InjectModel } from '@nestjs/mongoose';
|
|
import { Model } from 'mongoose';
|
|
import { randomBytes } from 'crypto';
|
|
import { ClientType } from '../common/enums/client-type.enum';
|
|
import { Role } from '../common/enums/role.enum';
|
|
import { PasswordService } from '../auth/services/password.service';
|
|
import { User, UserDocument } from '../users/schemas/user.schema';
|
|
import { UserMapper } from '../users/mappers/user.mapper';
|
|
import { UserResponseDto } from '../users/dto/user-response.dto';
|
|
import { AppException } from '../common/exceptions/app-exception';
|
|
|
|
export interface CreateSuperAdminInput {
|
|
fullName: string;
|
|
username: string;
|
|
email: string;
|
|
password: string;
|
|
}
|
|
|
|
@Injectable()
|
|
export class CreateSuperAdminService {
|
|
private readonly logger = new Logger(CreateSuperAdminService.name);
|
|
|
|
constructor(
|
|
@InjectModel(User.name) private readonly userModel: Model<UserDocument>,
|
|
private readonly passwordService: PasswordService,
|
|
) {}
|
|
|
|
async create(input: CreateSuperAdminInput): Promise<UserResponseDto> {
|
|
if (input.password.length < 8) {
|
|
throw new AppException('PASSWORD_TOO_SHORT');
|
|
}
|
|
|
|
const username = input.username.toLowerCase().trim();
|
|
const email = input.email.toLowerCase().trim();
|
|
|
|
const existing = await this.userModel.findOne({ $or: [{ username }, { email }] });
|
|
if (existing) {
|
|
throw new AppException('USERNAME_OR_EMAIL_EXISTS');
|
|
}
|
|
|
|
const superAdminExists = await this.userModel.exists({ role: Role.SUPER_ADMIN });
|
|
if (superAdminExists) {
|
|
this.logger.warn('A SUPER_ADMIN already exists; creating another.');
|
|
}
|
|
|
|
const passwordHash = await this.passwordService.hash(input.password);
|
|
|
|
const user = await this.userModel.create({
|
|
fullName: input.fullName.trim(),
|
|
username,
|
|
email,
|
|
password: passwordHash,
|
|
role: Role.SUPER_ADMIN,
|
|
clientType: ClientType.INTERNAL,
|
|
appName: 'inquiry-gateway',
|
|
clientName: 'Internal',
|
|
isActive: true,
|
|
isBlocked: false,
|
|
allowedInquiries: [],
|
|
apiKey: `esg_${randomBytes(32).toString('hex')}`,
|
|
passwordChangedAt: new Date(),
|
|
});
|
|
|
|
return UserMapper.toResponseDto(user);
|
|
}
|
|
}
|