forked from Yara724/api
Merge pull request 'main' (#90) from s.yahyaee/yara724-api:main into main
Reviewed-on: Yara724/api#90
This commit is contained in:
@@ -84,8 +84,10 @@ import * as Joi from "joi";
|
|||||||
useFactory: (configService: ConfigService) => {
|
useFactory: (configService: ConfigService) => {
|
||||||
return {
|
return {
|
||||||
uri: `mongodb://${configService.get<string>("MONGO_USER")}:${configService.get<string>("MONGO_PASS")}@${configService.get<string>("MONGO_HOST")}:${configService.get<string>("MONGO_PORT")}/${configService.get<string>("MONGO_DB_NAME")}?authSource=admin&${configService.get<string>("MONGO_OPTIONS")}`,
|
uri: `mongodb://${configService.get<string>("MONGO_USER")}:${configService.get<string>("MONGO_PASS")}@${configService.get<string>("MONGO_HOST")}:${configService.get<string>("MONGO_PORT")}/${configService.get<string>("MONGO_DB_NAME")}?authSource=admin&${configService.get<string>("MONGO_OPTIONS")}`,
|
||||||
tls: true,
|
tls: configService.get<string>("MONGO_TLS") === "true",
|
||||||
tlsAllowInvalidCertificates: true,
|
tlsAllowInvalidCertificates:
|
||||||
|
configService.get<string>("MONGO_TLS_ALLOW_INVALID_CERTS") ===
|
||||||
|
"true",
|
||||||
autoIndex: configService.get<string>("NODE_ENV") !== "production",
|
autoIndex: configService.get<string>("NODE_ENV") !== "production",
|
||||||
connectionFactory: (connection) => {
|
connectionFactory: (connection) => {
|
||||||
applyIranFaTimestampPlugin(connection);
|
applyIranFaTimestampPlugin(connection);
|
||||||
|
|||||||
@@ -30,6 +30,7 @@ export class UserAuthController {
|
|||||||
linkToken: body.linkToken,
|
linkToken: body.linkToken,
|
||||||
linkContext: body.linkContext,
|
linkContext: body.linkContext,
|
||||||
});
|
});
|
||||||
|
|
||||||
if (res) {
|
if (res) {
|
||||||
throw new HttpException(res, HttpStatus.ACCEPTED);
|
throw new HttpException(res, HttpStatus.ACCEPTED);
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -34,9 +34,9 @@ export function userAuthErrorBody(code: UserAuthErrorCode) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
export function throwUserAuthError(code: UserAuthErrorCode): never {
|
export function throwUserAuthError(code: UserAuthErrorCode): never {
|
||||||
if (code === UserAuthErrorCode.OTP_REQUEST_TOO_SOON) {
|
// if (code === UserAuthErrorCode.OTP_REQUEST_TOO_SOON) {
|
||||||
throw new BadRequestException(userAuthErrorBody(code));
|
// throw new BadRequestException(userAuthErrorBody(code));
|
||||||
}
|
// }
|
||||||
|
|
||||||
if (code === UserAuthErrorCode.LINK_MOBILE_MISMATCH) {
|
if (code === UserAuthErrorCode.LINK_MOBILE_MISMATCH) {
|
||||||
throw new ForbiddenException(userAuthErrorBody(code));
|
throw new ForbiddenException(userAuthErrorBody(code));
|
||||||
|
|||||||
@@ -139,7 +139,8 @@ export class UserAuthService {
|
|||||||
}
|
}
|
||||||
|
|
||||||
if (isOtpExpiryActive(userExist.otpExpire, nowMs)) {
|
if (isOtpExpiryActive(userExist.otpExpire, nowMs)) {
|
||||||
throwUserAuthError(UserAuthErrorCode.OTP_REQUEST_TOO_SOON);
|
// throwUserAuthError(UserAuthErrorCode.OTP_REQUEST_TOO_SOON);
|
||||||
|
return new LoginDtoRs(userExist, "OTP Still valid");
|
||||||
}
|
}
|
||||||
|
|
||||||
await this.smsSender(otp, canonicalMobile);
|
await this.smsSender(otp, canonicalMobile);
|
||||||
|
|||||||
@@ -35,7 +35,9 @@ export class UserLoginDto {
|
|||||||
}
|
}
|
||||||
|
|
||||||
export class LoginDtoRs extends UserModel {
|
export class LoginDtoRs extends UserModel {
|
||||||
|
@ApiProperty({ type: "string" })
|
||||||
message: string;
|
message: string;
|
||||||
|
|
||||||
@ApiProperty({
|
@ApiProperty({
|
||||||
example: "09226187419",
|
example: "09226187419",
|
||||||
type: "string",
|
type: "string",
|
||||||
@@ -61,8 +63,10 @@ export class LoginDtoRs extends UserModel {
|
|||||||
username: string;
|
username: string;
|
||||||
mobile: string;
|
mobile: string;
|
||||||
nationalCode: string;
|
nationalCode: string;
|
||||||
constructor(loginData) {
|
|
||||||
|
constructor(loginData, message: string = "") {
|
||||||
super();
|
super();
|
||||||
this.mobile = loginData.mobile;
|
this.mobile = loginData.mobile;
|
||||||
|
this.message = message;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -8,9 +8,11 @@ import {
|
|||||||
CaptchaChallenge,
|
CaptchaChallenge,
|
||||||
CaptchaChallengeSchema,
|
CaptchaChallengeSchema,
|
||||||
} from "./entities/schema/captcha-challenge.schema";
|
} from "./entities/schema/captcha-challenge.schema";
|
||||||
|
import { ConfigModule } from "@nestjs/config";
|
||||||
|
|
||||||
@Module({
|
@Module({
|
||||||
imports: [
|
imports: [
|
||||||
|
ConfigModule,
|
||||||
HashModule,
|
HashModule,
|
||||||
MongooseModule.forFeature([
|
MongooseModule.forFeature([
|
||||||
{ name: CaptchaChallenge.name, schema: CaptchaChallengeSchema },
|
{ name: CaptchaChallenge.name, schema: CaptchaChallengeSchema },
|
||||||
|
|||||||
@@ -1,14 +1,17 @@
|
|||||||
import { Injectable } from "@nestjs/common";
|
import { Injectable } from "@nestjs/common";
|
||||||
|
import { ConfigService } from "@nestjs/config";
|
||||||
import * as svgCaptcha from "svg-captcha";
|
import * as svgCaptcha from "svg-captcha";
|
||||||
|
|
||||||
export interface GeneratedCaptcha {
|
export interface GeneratedCaptcha {
|
||||||
text: string;
|
text?: string;
|
||||||
image: string;
|
image: string;
|
||||||
expiresAt: number;
|
expiresAt: number;
|
||||||
}
|
}
|
||||||
|
|
||||||
@Injectable()
|
@Injectable()
|
||||||
export class CaptchaService {
|
export class CaptchaService {
|
||||||
|
constructor(private readonly configService: ConfigService) {}
|
||||||
|
|
||||||
generate(): GeneratedCaptcha {
|
generate(): GeneratedCaptcha {
|
||||||
const captcha = svgCaptcha.create({
|
const captcha = svgCaptcha.create({
|
||||||
size: 5,
|
size: 5,
|
||||||
@@ -21,6 +24,13 @@ export class CaptchaService {
|
|||||||
fontSize: 52,
|
fontSize: 52,
|
||||||
});
|
});
|
||||||
|
|
||||||
|
if (this.configService.get<string>("NODE_ENV") === "production") {
|
||||||
|
return {
|
||||||
|
image: `data:image/svg+xml;base64,${Buffer.from(captcha.data, "utf8").toString("base64")}`,
|
||||||
|
expiresAt: this.buildExpireAt(),
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
return {
|
return {
|
||||||
text: captcha.text,
|
text: captcha.text,
|
||||||
image: `data:image/svg+xml;base64,${Buffer.from(captcha.data, "utf8").toString("base64")}`,
|
image: `data:image/svg+xml;base64,${Buffer.from(captcha.data, "utf8").toString("base64")}`,
|
||||||
|
|||||||
@@ -3850,7 +3850,7 @@ export class ExpertClaimService {
|
|||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
// Build damaged parts list (aligned with normalized selected parts)
|
// Build damaged parts list (aligned with normalized selected parts and enriched with evaluation context)
|
||||||
const carTypeExpert = claim.vehicle?.carType as
|
const carTypeExpert = claim.vehicle?.carType as
|
||||||
| ClaimVehicleTypeV2
|
| ClaimVehicleTypeV2
|
||||||
| undefined;
|
| undefined;
|
||||||
@@ -3860,6 +3860,13 @@ export class ExpertClaimService {
|
|||||||
(claim.damage as any)?.selectedOuterParts,
|
(claim.damage as any)?.selectedOuterParts,
|
||||||
);
|
);
|
||||||
const damagedPartsDataExpert = claim.media?.damagedParts as any;
|
const damagedPartsDataExpert = claim.media?.damagedParts as any;
|
||||||
|
|
||||||
|
// Extract optimization lookups from the DB document safely
|
||||||
|
const evaluationBlock = (claim as any).evaluation;
|
||||||
|
const priceDropLines = evaluationBlock?.priceDrop?.partLines || [];
|
||||||
|
const objectionParts = evaluationBlock?.objection?.objectionParts || [];
|
||||||
|
const newObjectionParts = evaluationBlock?.objection?.newParts || [];
|
||||||
|
|
||||||
const damagedParts = selectedNormExpert.map((sp, index) => {
|
const damagedParts = selectedNormExpert.map((sp, index) => {
|
||||||
const ck = sp.catalogKey ?? catalogLikeKeyFromPart(sp);
|
const ck = sp.catalogKey ?? catalogLikeKeyFromPart(sp);
|
||||||
const cap = getDamagedPartCaptureBlob(
|
const cap = getDamagedPartCaptureBlob(
|
||||||
@@ -3867,6 +3874,22 @@ export class ExpertClaimService {
|
|||||||
ck,
|
ck,
|
||||||
selectedNormExpert,
|
selectedNormExpert,
|
||||||
) as { url?: string; path?: string } | undefined;
|
) as { url?: string; path?: string } | undefined;
|
||||||
|
|
||||||
|
// 1. Cross-reference Price Drop items via partId or name match
|
||||||
|
const matchingPriceDrop = priceDropLines.find(
|
||||||
|
(line: any) =>
|
||||||
|
line.partId === sp.id ||
|
||||||
|
line.priceDropPartKey?.toLowerCase() === sp.name?.toLowerCase(),
|
||||||
|
);
|
||||||
|
|
||||||
|
// 2. Evaluate if this specific part is flag-linked within an active user objection
|
||||||
|
const isObjected = objectionParts.some(
|
||||||
|
(objPart: any) => objPart.id === sp.id || objPart.name === sp.name,
|
||||||
|
);
|
||||||
|
const isNewAddedPart = newObjectionParts.some(
|
||||||
|
(newPart: any) => newPart.id === sp.id || newPart.name === sp.name,
|
||||||
|
);
|
||||||
|
|
||||||
return {
|
return {
|
||||||
index,
|
index,
|
||||||
partId: catalogPartIdFromSelectedPart(sp),
|
partId: catalogPartIdFromSelectedPart(sp),
|
||||||
@@ -3880,6 +3903,15 @@ export class ExpertClaimService {
|
|||||||
selectedNormExpert,
|
selectedNormExpert,
|
||||||
),
|
),
|
||||||
url: cap?.url || (cap?.path ? buildFileLink(cap.path) : undefined),
|
url: cap?.url || (cap?.path ? buildFileLink(cap.path) : undefined),
|
||||||
|
|
||||||
|
// --- Added Consolidated Production Enrichments ---
|
||||||
|
severity: matchingPriceDrop ? matchingPriceDrop.severity : null,
|
||||||
|
coefficient: matchingPriceDrop ? matchingPriceDrop.coefficient : null,
|
||||||
|
objectionStatus: {
|
||||||
|
hasObjection: isObjected,
|
||||||
|
isNewlyAddedByObjection: isNewAddedPart,
|
||||||
|
// You can map extra parameters here if the front-end requires reason texts
|
||||||
|
},
|
||||||
};
|
};
|
||||||
});
|
});
|
||||||
|
|
||||||
|
|||||||
@@ -1068,10 +1068,19 @@ export class RequestManagementService {
|
|||||||
const companyCode = inquiryMapped?.CompanyCode;
|
const companyCode = inquiryMapped?.CompanyCode;
|
||||||
const client =
|
const client =
|
||||||
await this.clientService.findClientWithCompanyCode(+companyCode);
|
await this.clientService.findClientWithCompanyCode(+companyCode);
|
||||||
|
|
||||||
if (!client) {
|
if (!client) {
|
||||||
throw new HttpException("Client not found", HttpStatus.CONFLICT);
|
await this.clientService.addClient({
|
||||||
|
clientName,
|
||||||
|
clientCode: companyCode,
|
||||||
|
useExpertMode: "legal",
|
||||||
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// if (!client) {
|
||||||
|
// throw new HttpException("Client not found", HttpStatus.CONFLICT);
|
||||||
|
// }
|
||||||
|
|
||||||
// Persist inquiry/body data into new model
|
// Persist inquiry/body data into new model
|
||||||
if (!party.person) party.person = {} as any;
|
if (!party.person) party.person = {} as any;
|
||||||
const resolvedClientId = (client as any)?._id ?? (client as any)?._doc?._id;
|
const resolvedClientId = (client as any)?._id ?? (client as any)?._doc?._id;
|
||||||
|
|||||||
Reference in New Issue
Block a user