Fixed registrar and field expert

This commit is contained in:
SepehrYahyaee
2026-06-17 16:39:30 +03:30
parent a4eb98258b
commit bc5be99b59
19 changed files with 942 additions and 161 deletions

View File

@@ -150,7 +150,7 @@ export class ActorAuthController {
@ApiOperation({
summary: "Actor login (returns access + refresh tokens)",
description:
'Authenticate any non-end-user actor (insurer/company, blame expert, damage expert, registrar, field expert, admin). Submit `role` as an array — e.g. `["damage_expert"]` — together with the actor\'s email/`username` and password. On success the response contains the JWT pair and the resolved profile.',
'Authenticate any non-end-user actor (insurer/company, blame expert, damage expert, registrar, field expert, admin). Submit `role` as an array — e.g. `["damage_expert"]` — together with password and one of `username` / `email` / `nationalCode`. On success the response contains the JWT pair and the resolved profile.',
})
@ApiBody({
type: LoginActorDto,
@@ -193,11 +193,12 @@ export class ActorAuthController {
},
field_expert: {
summary: "Field expert panel",
description: "Sample credentials for a field-expert account.",
description:
"Login with email+password or nationalCode+password for seeded Parsian field experts.",
value: {
role: "field_expert",
username: "fieldexpert@gmail.com",
password: "123321",
nationalCode: "0051967839",
password: "Parsian@724",
captchaId: "f47ac10b-58cc-4372-a567-0e02b2c3d479",
captcha: "a7bx2",
},

View File

@@ -63,7 +63,7 @@ export class ActorAuthService {
res = await this.expertDbService.findOne({
_id: new Types.ObjectId(userId),
});
else res = await this.expertDbService.findOne({ email: username });
else res = await this.findActorByLoginIdentifier(this.expertDbService, username);
break;
case RoleEnum.DAMAGE_EXPERT:
if (username == null && userId)
@@ -71,14 +71,18 @@ export class ActorAuthService {
_id: new Types.ObjectId(userId),
});
else
res = await this.damageExpertDbService.findOne({ email: username });
res = await this.findActorByLoginIdentifier(
this.damageExpertDbService,
username,
);
break;
case RoleEnum.FIELD_EXPERT:
if (username == null && userId)
res = await this.fieldExpertDbService.findOne({
_id: new Types.ObjectId(userId),
});
else res = await this.fieldExpertDbService.findOne({ email: username });
else
res = await this.fieldExpertDbService.findByLoginIdentifier(username);
break;
case RoleEnum.REGISTRAR:
if (username == null && userId)
@@ -111,13 +115,27 @@ export class ActorAuthService {
}
parseActorLoginUsername(body: Record<string, unknown>): string {
const username = body?.username ?? body?.email;
const username = body?.username ?? body?.email ?? body?.nationalCode;
if (typeof username !== "string" || !username.trim()) {
throw new BadRequestException("username (email) is required");
throw new BadRequestException(
"username, email, or nationalCode is required",
);
}
return username.trim();
}
private async findActorByLoginIdentifier(
dbService: { findOne: (filter: any) => Promise<any> },
identifier: string,
) {
const id = identifier.trim();
const or: Record<string, string>[] = [{ email: id }, { username: id }];
if (/^\d{10}$/.test(id)) {
or.push({ nationalCode: id });
}
return dbService.findOne({ $or: or });
}
issueActorTokens(actor: {
_id: Types.ObjectId;
username?: string;
@@ -129,12 +147,13 @@ export class ActorAuthService {
clientKey?: Types.ObjectId | string | null;
}) {
const payload = {
username: actor.username || actor.email,
username:
actor.username || actor.email || (actor as any).nationalCode || null,
sub: actor._id,
fullName: `${actor.firstName || ""} ${actor.lastName || ""}`.trim(),
role: actor.role || "User",
userType: actor.userType || "UserType",
clientKey: actor.clientKey ?? null,
clientKey: actor.clientKey ? String(actor.clientKey) : null,
};
const access_token = this.jwtService.sign(payload, {

View File

@@ -1,13 +1,34 @@
import { ApiProperty } from "@nestjs/swagger";
import { IsNotEmpty, IsString, MaxLength } from "class-validator";
import { ApiProperty, ApiPropertyOptional } from "@nestjs/swagger";
import { IsNotEmpty, IsOptional, IsString, MaxLength } from "class-validator";
import { RoleEnum } from "src/Types&Enums/role.enum";
export class LoginActorDto {
@ApiProperty({ example: RoleEnum, type: "array", description: "LOGIN_DTO" })
role: RoleEnum[];
@ApiProperty({})
username: string;
@ApiPropertyOptional({
description:
"Actor email or username. For field experts you may also send nationalCode instead.",
})
@IsOptional()
@IsString()
username?: string;
@ApiPropertyOptional({
description: "Alias for username when logging in with email.",
})
@IsOptional()
@IsString()
email?: string;
@ApiPropertyOptional({
example: "4311402422",
description:
"10-digit national ID. Alternative login identifier for actors (especially field experts without email).",
})
@IsOptional()
@IsString()
nationalCode?: string;
@ApiProperty({})
password: string;