1
0
forked from Yara724/api

Merge pull request 'Fixing link for some documents' (#120) from s.yahyaee/yara724-api:main into main

Reviewed-on: Yara724/api#120
This commit is contained in:
2026-06-13 17:25:25 +03:30
9 changed files with 78 additions and 20 deletions

View File

@@ -1 +1,15 @@
# api # api
Current JWT payload:
```json
{
"username": "saman_insurer@gmail.com",
"sub": "6a144979799f3c63aa63f67c",
"fullName": "بیمه گر سامان",
"role": "company",
"userType": "UserType",
"clientKey": "67f0fd0e53868dc1ff8a2738",
"iat": 1781339791,
"exp": 1781343391
}
```

View File

@@ -15,7 +15,7 @@ import { isAxiosError } from "axios";
import { unlink } from "node:fs/promises"; import { unlink } from "node:fs/promises";
import { existsSync } from "node:fs"; import { existsSync } from "node:fs";
import { AiService } from "src/ai/ai.service"; import { AiService } from "src/ai/ai.service";
import { buildFileLink } from "src/helpers/urlCreator"; import { buildFileLink, resolveStoredFileUrl } from "src/helpers/urlCreator";
import { RequestManagementDbService } from "src/request-management/entities/db-service/request-management.db.service"; import { RequestManagementDbService } from "src/request-management/entities/db-service/request-management.db.service";
import { ReqBlameStatus } from "src/Types&Enums/blame-request-management/status.enum"; import { ReqBlameStatus } from "src/Types&Enums/blame-request-management/status.enum";
import { ReqClaimStatus } from "src/Types&Enums/claim-request-management/status.enum"; import { ReqClaimStatus } from "src/Types&Enums/claim-request-management/status.enum";
@@ -6644,7 +6644,7 @@ export class ClaimRequestManagementService {
) as { url?: string; path?: string } | undefined; ) as { url?: string; path?: string } | undefined;
carAngles[k] = { carAngles[k] = {
captured: !!cap, captured: !!cap,
url: cap?.url || (cap?.path ? buildFileLink(cap.path) : undefined), url: resolveStoredFileUrl(cap),
}; };
} }

View File

@@ -22,7 +22,9 @@ export class EnvironmentVariables {
message: "NODE_ENV should be development or production", message: "NODE_ENV should be development or production",
}) })
NODE_ENV: Environment; NODE_ENV: Environment;
// --------------------------------------------------------- // // --------------------------------------------------------- //
@IsNumber( @IsNumber(
{ {
allowNaN: false, allowNaN: false,
@@ -36,7 +38,9 @@ export class EnvironmentVariables {
@Min(0, { message: "PORT must be between 0 and 65535" }) @Min(0, { message: "PORT must be between 0 and 65535" })
@Max(65535, { message: "PORT must be between 0 and 65535" }) @Max(65535, { message: "PORT must be between 0 and 65535" })
PORT: number; PORT: number;
// --------------------------------------------------------- // // --------------------------------------------------------- //
@IsString({ message: "MONGO_URI must be string" }) @IsString({ message: "MONGO_URI must be string" })
@IsUrl( @IsUrl(
{ {
@@ -48,23 +52,32 @@ export class EnvironmentVariables {
}, },
) )
MONGO_URI: string; MONGO_URI: string;
// --------------------------------------------------------- // // --------------------------------------------------------- //
@IsBooleanString({ message: "MONGO_TLS must be boolean" }) @IsBooleanString({ message: "MONGO_TLS must be boolean" })
MONGO_TLS: string; MONGO_TLS: string;
// --------------------------------------------------------- // // --------------------------------------------------------- //
@IsBooleanString({ message: "MONGO_TLS_ALLOW_INVALID_CERTS must be boolean" }) @IsBooleanString({ message: "MONGO_TLS_ALLOW_INVALID_CERTS must be boolean" })
MONGO_TLS_ALLOW_INVALID_CERTS: string; MONGO_TLS_ALLOW_INVALID_CERTS: string;
// --------------------------------------------------------- // // --------------------------------------------------------- //
@IsString({ message: "JWT_SECRET must be string" }) @IsString({ message: "JWT_SECRET must be string" })
@MinLength(32, { @MinLength(32, {
message: "JWT_SECRET must be at least 32 characters", message: "JWT_SECRET must be at least 32 characters",
}) })
JWT_SECRET: string; JWT_SECRET: string;
// --------------------------------------------------------- // // --------------------------------------------------------- //
@Matches(/^\d+(ms|s|m|h|d|w|y)$/, { @Matches(/^\d+(ms|s|m|h|d|w|y)$/, {
message: "JWT_EXPIRY must be in the correct format", message: "JWT_EXPIRY must be in the correct format",
}) })
JWT_EXPIRY: StringValue; JWT_EXPIRY: StringValue;
// --------------------------------------------------------- // // --------------------------------------------------------- //
// @IsString({ message: "HASH_PEPPER must be string" }) // @IsString({ message: "HASH_PEPPER must be string" })
// @MinLength(32, { // @MinLength(32, {

View File

@@ -1,5 +1,6 @@
// claim-damaged-part.enricher.ts // claim-damaged-part.enricher.ts
import { resolveStoredFileUrl } from "src/helpers/urlCreator";
import { import {
DamagedPartCaptureStatus, DamagedPartCaptureStatus,
EnrichedDamagedPart, EnrichedDamagedPart,
@@ -14,6 +15,9 @@ export function buildEnrichedDamagedParts(params: {
hasDamagedPartCapture: (data: any, key: string, parts: any[]) => boolean; hasDamagedPartCapture: (data: any, key: string, parts: any[]) => boolean;
catalogLikeKeyFromPart: (part: any) => string; catalogLikeKeyFromPart: (part: any) => string;
buildFileLink: (path: string) => string; buildFileLink: (path: string) => string;
resolveStoredFileUrl?: (
stored?: { url?: string; path?: string } | null,
) => string | undefined;
}): EnrichedDamagedPart[] { }): EnrichedDamagedPart[] {
const { const {
selectedParts, selectedParts,
@@ -24,6 +28,7 @@ export function buildEnrichedDamagedParts(params: {
hasDamagedPartCapture, hasDamagedPartCapture,
catalogLikeKeyFromPart, catalogLikeKeyFromPart,
buildFileLink, buildFileLink,
resolveStoredFileUrl: resolveUrl = resolveStoredFileUrl,
} = params; } = params;
const priceDropLines: any[] = evaluationBlock?.priceDrop?.partLines ?? []; const priceDropLines: any[] = evaluationBlock?.priceDrop?.partLines ?? [];
@@ -109,17 +114,13 @@ export function buildEnrichedDamagedParts(params: {
: false); : false);
const capUrl = const capUrl =
cap?.url || resolveUrl(cap) ||
(cap?.path ? buildFileLink(cap.path) : undefined) ||
(sp.id == null && Array.isArray(damagedPartsData) (sp.id == null && Array.isArray(damagedPartsData)
? (() => { ? (() => {
const match = (damagedPartsData as any[]).find( const match = (damagedPartsData as any[]).find(
(d) => d.name === sp.name && d.side === sp.side && d.path, (d) => d.name === sp.name && d.side === sp.side && d.path,
); );
return ( return resolveUrl(match);
match?.url ||
(match?.path ? buildFileLink(match.path) : undefined)
);
})() })()
: undefined); : undefined);

View File

@@ -20,7 +20,7 @@ import { ClaimSignDbService } from "src/claim-request-management/entites/db-serv
import { DamageImageDbService } from "src/claim-request-management/entites/db-service/damage-image.db.service"; import { DamageImageDbService } from "src/claim-request-management/entites/db-service/damage-image.db.service";
import { VideoCaptureDbService } from "src/claim-request-management/entites/db-service/video-capture.db.service"; import { VideoCaptureDbService } from "src/claim-request-management/entites/db-service/video-capture.db.service";
import { ClientDbService } from "src/client/entities/db-service/client.db.service"; import { ClientDbService } from "src/client/entities/db-service/client.db.service";
import { buildFileLink } from "src/helpers/urlCreator"; import { buildFileLink, resolveStoredFileUrl } from "src/helpers/urlCreator";
import { toJalaliDateAndTime } from "src/helpers/date-jalali"; import { toJalaliDateAndTime } from "src/helpers/date-jalali";
import { BlameDocumentDbService } from "src/request-management/entities/db-service/blame-document.db.service"; import { BlameDocumentDbService } from "src/request-management/entities/db-service/blame-document.db.service";
import { BlameVideoDbService } from "src/request-management/entities/db-service/blame-video.db.service"; import { BlameVideoDbService } from "src/request-management/entities/db-service/blame-video.db.service";
@@ -3941,7 +3941,7 @@ export class ExpertClaimService {
) as { url?: string; path?: string } | undefined; ) as { url?: string; path?: string } | undefined;
carAngles[k] = { carAngles[k] = {
captured: !!cap, captured: !!cap,
url: cap?.url || (cap?.path ? buildFileLink(cap.path) : undefined), url: resolveStoredFileUrl(cap),
}; };
} }
@@ -3965,6 +3965,7 @@ export class ExpertClaimService {
hasDamagedPartCapture, hasDamagedPartCapture,
catalogLikeKeyFromPart, catalogLikeKeyFromPart,
buildFileLink, buildFileLink,
resolveStoredFileUrl,
}); });
// Vehicle payload — fall back to blame inquiry if claim vehicle is sparse // Vehicle payload — fall back to blame inquiry if claim vehicle is sparse

View File

@@ -33,7 +33,7 @@ import { BlameVideoDbService } from "src/request-management/entities/db-service/
import { BlameVoiceDbService } from "src/request-management/entities/db-service/blame.voice.db.service"; import { BlameVoiceDbService } from "src/request-management/entities/db-service/blame.voice.db.service";
import { VideoCaptureDbService } from "src/claim-request-management/entites/db-service/video-capture.db.service"; import { VideoCaptureDbService } from "src/claim-request-management/entites/db-service/video-capture.db.service";
import { ClientDbService } from "src/client/entities/db-service/client.db.service"; import { ClientDbService } from "src/client/entities/db-service/client.db.service";
import { buildFileLink } from "src/helpers/urlCreator"; import { buildFileLink, resolveStoredFileUrl } from "src/helpers/urlCreator";
import { toJalaliDateAndTime } from "src/helpers/date-jalali"; import { toJalaliDateAndTime } from "src/helpers/date-jalali";
import { enrichBlamePartiesForAgreementView } from "src/helpers/blame-party-agreement-decision"; import { enrichBlamePartiesForAgreementView } from "src/helpers/blame-party-agreement-decision";
import { BlameRequestType } from "src/Types&Enums/blame-request-management/blameRequestType.enum"; import { BlameRequestType } from "src/Types&Enums/blame-request-management/blameRequestType.enum";
@@ -422,7 +422,7 @@ export class ExpertInsurerService {
ck, ck,
selectedNormExpert, selectedNormExpert,
) as { url?: string; path?: string; fileName?: string } | undefined; ) as { url?: string; path?: string; fileName?: string } | undefined;
const url = cap?.url || (cap?.path ? buildFileLink(cap.path) : undefined); const url = resolveStoredFileUrl(cap);
const link = url; const link = url;
return { return {
index, index,
@@ -633,7 +633,7 @@ export class ExpertInsurerService {
) as { url?: string; path?: string } | undefined; ) as { url?: string; path?: string } | undefined;
carAngles[k] = { carAngles[k] = {
captured: !!cap, captured: !!cap,
url: cap?.url || (cap?.path ? buildFileLink(cap.path) : undefined), url: resolveStoredFileUrl(cap),
}; };
} }

View File

@@ -9,6 +9,7 @@ import {
} from "./outer-damage-parts"; } from "./outer-damage-parts";
import { ClaimVehicleTypeV2 } from "src/static/outer-car-parts-catalog"; import { ClaimVehicleTypeV2 } from "src/static/outer-car-parts-catalog";
import { canonicalizeResendDocumentKey } from "./claim-resend-document-keys"; import { canonicalizeResendDocumentKey } from "./claim-resend-document-keys";
import { resolveStoredFileUrl } from "./urlCreator";
export type ExpertResendCarPartV2 = { export type ExpertResendCarPartV2 = {
id?: number | null; id?: number | null;
@@ -161,8 +162,7 @@ export function mapExpertResendCarPartsForClient(
const capturedFromStore = const capturedFromStore =
typeof stored.captured === "boolean" ? stored.captured : undefined; typeof stored.captured === "boolean" ? stored.captured : undefined;
const url = const url =
cap?.url || resolveStoredFileUrl(cap);
(cap?.path && options.buildUrl ? options.buildUrl(cap.path) : undefined);
return { return {
id: sp.id, id: sp.id,

View File

@@ -1,9 +1,38 @@
type StoredFileCapture = {
url?: string;
path?: string;
};
/**
* Build a public file URL from a relative storage path (e.g. `files/...`).
* Uses the current `URL` env so deployments with custom routing stay correct.
*/
export function buildFileLink(path: string): string { export function buildFileLink(path: string): string {
const baseUrl = process.env.URL; const normalized = String(path ?? "").trim();
if(process.env.NODE_ENV === 'local'){ if (!normalized) return normalized;
return baseUrl + "/" + path; if (/^https?:\/\//i.test(normalized)) {
}else{ return normalized;
return baseUrl + "/api/" + path;
} }
const baseUrl = (process.env.URL ?? "").replace(/\/+$/, "");
const relativePath = normalized.replace(/^\/+/, "");
if (process.env.NODE_ENV === "local") {
return `${baseUrl}/${relativePath}`;
}
return `${baseUrl}/api/${relativePath}`;
}
/**
* Resolve a file URL from persisted capture metadata.
* Prefer rebuilding from `path` so stale baked-in URLs (e.g. user-portal prefix)
* do not leak into expert/other API responses.
*/
export function resolveStoredFileUrl(
stored?: StoredFileCapture | null,
): string | undefined {
const path = stored?.path?.trim();
if (path) {
return buildFileLink(path);
}
return stored?.url;
} }