forked from Yara724/api
275 lines
8.7 KiB
TypeScript
275 lines
8.7 KiB
TypeScript
import {
|
|
Body,
|
|
Controller,
|
|
Get,
|
|
Param,
|
|
Patch,
|
|
Post,
|
|
Query,
|
|
Req,
|
|
Res,
|
|
UseGuards,
|
|
} from "@nestjs/common";
|
|
import type { Response } from "express";
|
|
import {
|
|
ApiBody,
|
|
ApiAcceptedResponse,
|
|
ApiOperation,
|
|
ApiResponse,
|
|
ApiTags,
|
|
ApiBearerAuth,
|
|
} from "@nestjs/swagger";
|
|
import { ActorAuthService } from "src/auth/auth-services/actor.auth.service";
|
|
import { CaptchaChallengeService } from "src/captcha/captcha-challenge.service";
|
|
import { CaptchaResponseDto } from "src/auth/dto/captcha-response.dto";
|
|
import { GetCaptchaImageQueryDto } from "src/auth/dto/get-captcha-image-query.dto";
|
|
import {
|
|
ForgetPasswordSendCodeDto,
|
|
ForgetPasswordVerifyCodeDto,
|
|
} from "src/auth/dto/actor/forget-password.actor.dto";
|
|
import { LoginActorDto } from "src/auth/dto/actor/login.actor.dto";
|
|
import { ActorEditUserProfileDto } from "src/auth/dto/actor/profile.actor.dto";
|
|
import { CreateFieldExpertDto } from "src/auth/dto/actor/create-field-expert.actor.dto";
|
|
import { CreateRegistrarDto } from "src/auth/dto/actor/create-registrar.actor.dto";
|
|
import {
|
|
GenuineRegisterDto,
|
|
InsurerRegisterDto,
|
|
LegalRegisterDto,
|
|
} from "src/auth/dto/actor/register.actor.dto";
|
|
import { LocalActorAuthGuard } from "src/auth/guards/actor-local.guard";
|
|
import { Roles } from "src/decorators/roles.decorator";
|
|
import { CurrentUser } from "src/decorators/user.decorator";
|
|
|
|
@Controller("actor")
|
|
@ApiTags("actor")
|
|
export class ActorAuthController {
|
|
constructor(
|
|
private readonly actorAuthService: ActorAuthService,
|
|
private readonly captchaChallengeService: CaptchaChallengeService,
|
|
) {}
|
|
|
|
@Get("captcha")
|
|
@ApiOperation({
|
|
summary: "Get a login captcha",
|
|
description:
|
|
"Issues a new captcha challenge. Returns `captchaId`, `image`, and `expiresAt`. " +
|
|
"Send `captchaId` and the typed characters as `captcha` on POST /actor/login.\n\n" +
|
|
"Optional `format=raw` returns image/svg+xml for browser preview (same captchaId is in JSON when omitted).",
|
|
})
|
|
@ApiResponse({ status: 200, type: CaptchaResponseDto })
|
|
async getCaptcha(
|
|
@Query() query: GetCaptchaImageQueryDto,
|
|
@Res({ passthrough: true }) res: Response,
|
|
) {
|
|
const result = await this.captchaChallengeService.issue();
|
|
|
|
if (query.format === "raw") {
|
|
const svg = await this.captchaChallengeService.getImageById(
|
|
result.captchaId,
|
|
);
|
|
res.setHeader("X-Captcha-Id", result.captchaId);
|
|
res.type("image/svg+xml");
|
|
res.send(svg);
|
|
return;
|
|
}
|
|
|
|
return result;
|
|
}
|
|
|
|
@Get("captcha/:captchaId/image")
|
|
@ApiOperation({
|
|
summary: "View captcha image by id",
|
|
description: "Returns raw SVG for a previously issued captcha challenge.",
|
|
})
|
|
async getCaptchaImage(
|
|
@Param("captchaId") captchaId: string,
|
|
@Res({ passthrough: true }) res: Response,
|
|
) {
|
|
const svg = await this.captchaChallengeService.getImageById(captchaId);
|
|
res.type("image/svg+xml");
|
|
res.send(svg);
|
|
}
|
|
|
|
/**
|
|
* @deprecated Use the unified actor onboarding flow instead. This endpoint
|
|
* will be removed in a future release.
|
|
*/
|
|
@Post("register/genuine")
|
|
@ApiOperation({
|
|
deprecated: true,
|
|
summary: "[DEPRECATED] Genuine actor registration",
|
|
description:
|
|
"Deprecated — kept only for legacy clients. Use the unified actor onboarding flow instead. Will be removed.",
|
|
})
|
|
@ApiBody({ type: GenuineRegisterDto })
|
|
async registerGenuine(@Body() body: GenuineRegisterDto) {
|
|
return await this.actorAuthService.genuineRegister(body);
|
|
}
|
|
|
|
/**
|
|
* @deprecated Use the unified actor onboarding flow instead. This endpoint
|
|
* will be removed in a future release.
|
|
*/
|
|
@Post("register/legal")
|
|
@ApiOperation({
|
|
deprecated: true,
|
|
summary: "[DEPRECATED] Legal actor registration",
|
|
description:
|
|
"Deprecated — kept only for legacy clients. Use the unified actor onboarding flow instead. Will be removed.",
|
|
})
|
|
@ApiBody({ type: LegalRegisterDto })
|
|
async registerLegal(@Body() body: LegalRegisterDto) {
|
|
return await this.actorAuthService.legalRegister(body);
|
|
}
|
|
|
|
@Post("register/insurer")
|
|
@ApiBody({ type: InsurerRegisterDto })
|
|
async registerInsurer(@Body() body: InsurerRegisterDto) {
|
|
return await this.actorAuthService.insurerRegister(body);
|
|
}
|
|
|
|
/** Mock: create a field expert for testing. Make private later. */
|
|
@Post("create-field-expert")
|
|
@ApiBody({ type: CreateFieldExpertDto })
|
|
@ApiAcceptedResponse()
|
|
async createFieldExpert(@Body() body: CreateFieldExpertDto) {
|
|
return await this.actorAuthService.createFieldExpertMock(body);
|
|
}
|
|
|
|
/** Mock: create a registrar for testing. Make private later. */
|
|
@Post("create-registrar")
|
|
@ApiBody({ type: CreateRegistrarDto })
|
|
@ApiAcceptedResponse()
|
|
async createRegistrar(@Body() body: CreateRegistrarDto) {
|
|
return await this.actorAuthService.createRegistrarMock(body);
|
|
}
|
|
|
|
@UseGuards(LocalActorAuthGuard)
|
|
@Post("login")
|
|
@Roles()
|
|
@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 password and one of `username` / `email` / `nationalCode`. On success the response contains the JWT pair and the resolved profile.',
|
|
})
|
|
@ApiBody({
|
|
type: LoginActorDto,
|
|
description:
|
|
"Login payload. Pick one of the swagger examples below to see the exact body shape per role.",
|
|
examples: {
|
|
company: {
|
|
summary: "Insurer / company portal",
|
|
description:
|
|
"Sample tenant credentials for the insurer (company) panel.",
|
|
value: {
|
|
role: "company",
|
|
username: "saman_insurer@gmail.com",
|
|
password: "123321",
|
|
captchaId: "f47ac10b-58cc-4372-a567-0e02b2c3d479",
|
|
captcha: "a7bx2",
|
|
},
|
|
},
|
|
expert: {
|
|
summary: "Blame expert panel",
|
|
description: "Sample credentials for a blame (`expert`) account.",
|
|
value: {
|
|
role: "expert",
|
|
username: "blame@gmail.com",
|
|
password: "123321",
|
|
captchaId: "f47ac10b-58cc-4372-a567-0e02b2c3d479",
|
|
captcha: "a7bx2",
|
|
},
|
|
},
|
|
damage_expert: {
|
|
summary: "Damage expert (claim) panel",
|
|
description: "Sample credentials for a damage-expert account.",
|
|
value: {
|
|
role: "damage_expert",
|
|
username: "claim@gmail.com",
|
|
password: "123321",
|
|
captchaId: "f47ac10b-58cc-4372-a567-0e02b2c3d479",
|
|
captcha: "a7bx2",
|
|
},
|
|
},
|
|
field_expert: {
|
|
summary: "Field expert panel",
|
|
description:
|
|
"Login with email+password or nationalCode+password for seeded Parsian field experts.",
|
|
value: {
|
|
role: "field_expert",
|
|
nationalCode: "0051967839",
|
|
password: "Parsian@724",
|
|
captchaId: "f47ac10b-58cc-4372-a567-0e02b2c3d479",
|
|
captcha: "a7bx2",
|
|
},
|
|
},
|
|
},
|
|
})
|
|
@ApiResponse({
|
|
status: 404,
|
|
description: "No actor account exists for the given email and role",
|
|
})
|
|
@ApiResponse({
|
|
status: 401,
|
|
description: "Wrong password or role mismatch",
|
|
})
|
|
@ApiAcceptedResponse()
|
|
async login(@Req() req: { user: Record<string, unknown> }) {
|
|
return req.user;
|
|
}
|
|
|
|
@Post("forget-password")
|
|
@ApiBody({
|
|
type: ForgetPasswordSendCodeDto,
|
|
description: "send otp when call this api",
|
|
})
|
|
@ApiAcceptedResponse()
|
|
@ApiResponse({ type: ForgetPasswordSendCodeDto })
|
|
async forgetPassword(@Body() body: ForgetPasswordSendCodeDto) {
|
|
return await this.actorAuthService.forgetPasswordSendMail(body.email);
|
|
}
|
|
|
|
@Post("forget-password-verify")
|
|
@ApiBody({
|
|
type: ForgetPasswordVerifyCodeDto,
|
|
description: "send otp when call this api",
|
|
})
|
|
@ApiAcceptedResponse()
|
|
@ApiResponse({ type: ForgetPasswordVerifyCodeDto })
|
|
async forgetPasswordVerify(@Body() body: ForgetPasswordVerifyCodeDto) {
|
|
const { email, otp, newPassword } = body;
|
|
return await this.actorAuthService.forgetPasswordVerify(
|
|
email,
|
|
otp,
|
|
newPassword,
|
|
);
|
|
}
|
|
|
|
@Get("register/form/states/list")
|
|
async getStates() {
|
|
return await this.actorAuthService.getStates();
|
|
}
|
|
|
|
@Get("register/form/cities/list/:stateId")
|
|
async getCities(@Param("stateId") stateId: number) {
|
|
return await this.actorAuthService.getCities(stateId);
|
|
}
|
|
|
|
@Get("/profile")
|
|
@UseGuards(LocalActorAuthGuard)
|
|
@ApiBearerAuth()
|
|
async getProfile(@CurrentUser() user) {
|
|
return await this.actorAuthService.getProfiles(user);
|
|
}
|
|
|
|
@Patch("/profile")
|
|
@UseGuards(LocalActorAuthGuard)
|
|
@ApiBearerAuth()
|
|
async editProfile(
|
|
@CurrentUser() user,
|
|
@Body() update: ActorEditUserProfileDto,
|
|
) {
|
|
return await this.actorAuthService.modifyProfile(user, update);
|
|
}
|
|
}
|