forked from Yara724/api
YARA-941
This commit is contained in:
@@ -1,7 +1,7 @@
|
|||||||
export enum ClaimRequiredDocumentType {
|
export enum ClaimRequiredDocumentType {
|
||||||
// Car green card
|
// Car green card
|
||||||
CAR_GREEN_CARD = "car_green_card",
|
CAR_GREEN_CARD = "car_green_card",
|
||||||
|
CAR_CERTIFICATE = "car_certificate",
|
||||||
/** National ID card (or similar); may be requested on resend even if not in the initial upload set. */
|
/** National ID card (or similar); may be requested on resend even if not in the initial upload set. */
|
||||||
NATIONAL_CARD = "national_card",
|
NATIONAL_CARD = "national_card",
|
||||||
|
|
||||||
|
|||||||
@@ -77,14 +77,14 @@ dotenv.config({ path: `.${process.env.NODE_ENV}.env` });
|
|||||||
provide: APP_INTERCEPTOR,
|
provide: APP_INTERCEPTOR,
|
||||||
useClass: UnicodeDigitsNormalizeInterceptor,
|
useClass: UnicodeDigitsNormalizeInterceptor,
|
||||||
},
|
},
|
||||||
// {
|
{
|
||||||
// provide: APP_PIPE,
|
provide: APP_PIPE,
|
||||||
// useValue: new ValidationPipe({
|
useValue: new ValidationPipe({
|
||||||
// transform: true,
|
transform: true,
|
||||||
// whitelist: true,
|
whitelist: true,
|
||||||
// forbidNonWhitelisted: false,
|
forbidNonWhitelisted: false,
|
||||||
// }),
|
}),
|
||||||
// },
|
},
|
||||||
],
|
],
|
||||||
})
|
})
|
||||||
export class AppModule {}
|
export class AppModule {}
|
||||||
|
|||||||
@@ -8,6 +8,7 @@ import {
|
|||||||
LightCondition,
|
LightCondition,
|
||||||
} from "src/Types&Enums/blame-request-management/accident-conditions.enum";
|
} from "src/Types&Enums/blame-request-management/accident-conditions.enum";
|
||||||
import { BlameRequestType } from "src/Types&Enums/blame-request-management/blameRequestType.enum";
|
import { BlameRequestType } from "src/Types&Enums/blame-request-management/blameRequestType.enum";
|
||||||
|
import { IsEnum } from "class-validator";
|
||||||
|
|
||||||
export class InitialFormDto {
|
export class InitialFormDto {
|
||||||
@ApiProperty({ required: false, default: false })
|
@ApiProperty({ required: false, default: false })
|
||||||
@@ -161,16 +162,17 @@ export class LocationDto {
|
|||||||
lon: number;
|
lon: number;
|
||||||
}
|
}
|
||||||
|
|
||||||
export class CreateRequestDto {
|
// export class CreateRequestDto {
|
||||||
@ApiProperty({ type: String, required: false })
|
// @ApiProperty({ type: String, required: false })
|
||||||
type?: "THIRD_PARTY" | "CAR_BODY";
|
// type?: "THIRD_PARTY" | "CAR_BODY";
|
||||||
}
|
// }
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* V2 create blame request (new model `BlameRequest`).
|
* V2 create blame request (new model `BlameRequest`).
|
||||||
*/
|
*/
|
||||||
export class CreateBlameRequestDtoV2 {
|
export class CreateBlameRequestDtoV2 {
|
||||||
@ApiProperty({ enum: BlameRequestType })
|
@IsEnum(BlameRequestType)
|
||||||
|
@ApiProperty({ enum: BlameRequestType, description: "Type of blame request" })
|
||||||
type: BlameRequestType;
|
type: BlameRequestType;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -36,7 +36,6 @@ import { BlameDocumentType } from "./entities/schema/blame-document.schema";
|
|||||||
import {
|
import {
|
||||||
CarBodyFormDto,
|
CarBodyFormDto,
|
||||||
CarBodySecondForm,
|
CarBodySecondForm,
|
||||||
CreateRequestDto,
|
|
||||||
DescriptionDto,
|
DescriptionDto,
|
||||||
InitialFormDto,
|
InitialFormDto,
|
||||||
LocationDto,
|
LocationDto,
|
||||||
@@ -53,18 +52,18 @@ export class RequestManagementController {
|
|||||||
private readonly requestManagementService: RequestManagementService,
|
private readonly requestManagementService: RequestManagementService,
|
||||||
) {}
|
) {}
|
||||||
|
|
||||||
@ApiOperation({ deprecated: true })
|
// @ApiOperation({ deprecated: true })
|
||||||
@Post()
|
// @Post()
|
||||||
@UseGuards(GlobalGuard)
|
// @UseGuards(GlobalGuard)
|
||||||
async createRequest(
|
// async createRequest(
|
||||||
@CurrentUser() user,
|
// @CurrentUser() user,
|
||||||
@Body() createRequestDto: CreateRequestDto,
|
// @Body() createRequestDto: CreateRequestDto,
|
||||||
) {
|
// ) {
|
||||||
return await this.requestManagementService.createRequest(
|
// return await this.requestManagementService.createRequest(
|
||||||
user,
|
// user,
|
||||||
createRequestDto.type,
|
// createRequestDto.type,
|
||||||
);
|
// );
|
||||||
}
|
// }
|
||||||
|
|
||||||
@ApiOperation({ deprecated: true })
|
@ApiOperation({ deprecated: true })
|
||||||
@Post("/initial-form/:requestId")
|
@Post("/initial-form/:requestId")
|
||||||
|
|||||||
98
src/sms-orchestration/provider/kavenegar-sms.gateway.ts
Normal file
98
src/sms-orchestration/provider/kavenegar-sms.gateway.ts
Normal file
@@ -0,0 +1,98 @@
|
|||||||
|
import { KavenegarService } from "@fraybabak/kavenegar_nest";
|
||||||
|
import { Injectable, Logger } from "@nestjs/common";
|
||||||
|
import {
|
||||||
|
isKavenegarSuccess,
|
||||||
|
KavenegarNormalized,
|
||||||
|
normalizeKavenegarBody,
|
||||||
|
safeJsonStringify,
|
||||||
|
unwrapKavenegarTransportError,
|
||||||
|
} from "./kavenegar-response";
|
||||||
|
import {
|
||||||
|
SmsProviderException,
|
||||||
|
SmsTransportException,
|
||||||
|
} from "./sms-provider.exception";
|
||||||
|
import { SendMessage, VerifyLookUpMessage } from "./sms-gateway.types";
|
||||||
|
|
||||||
|
@Injectable()
|
||||||
|
export class KavenegarSmsGateway {
|
||||||
|
private readonly logger = new Logger(KavenegarSmsGateway.name);
|
||||||
|
|
||||||
|
constructor(private readonly sender: KavenegarService) {}
|
||||||
|
|
||||||
|
async sendMessage(data: SendMessage) {
|
||||||
|
try {
|
||||||
|
const body = await this.sender.Send({
|
||||||
|
sender: "10008663",
|
||||||
|
...data,
|
||||||
|
});
|
||||||
|
if (!isKavenegarSuccess(body)) {
|
||||||
|
const normalized = normalizeKavenegarBody(body);
|
||||||
|
this.logProviderRejection("send", data.receptor, undefined, normalized);
|
||||||
|
throw new SmsProviderException("send", {
|
||||||
|
receptor: data.receptor,
|
||||||
|
providerBody: body,
|
||||||
|
normalized,
|
||||||
|
});
|
||||||
|
}
|
||||||
|
this.logger.log(
|
||||||
|
`Kavenegar Send ok receptor=${data.receptor} status=200`,
|
||||||
|
);
|
||||||
|
return body;
|
||||||
|
} catch (e) {
|
||||||
|
if (e instanceof SmsProviderException) throw e;
|
||||||
|
const detail = safeJsonStringify(unwrapKavenegarTransportError(e));
|
||||||
|
this.logger.error(
|
||||||
|
`Kavenegar Send transport error receptor=${data.receptor} detail=${detail}`,
|
||||||
|
);
|
||||||
|
throw new SmsTransportException("send", { receptor: data.receptor }, e);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
async verifyLookUp(data: VerifyLookUpMessage) {
|
||||||
|
try {
|
||||||
|
const body = await this.sender.verifyLookup(data);
|
||||||
|
if (!isKavenegarSuccess(body)) {
|
||||||
|
const normalized = normalizeKavenegarBody(body);
|
||||||
|
this.logProviderRejection(
|
||||||
|
"verifyLookUp",
|
||||||
|
data.receptor,
|
||||||
|
data.template,
|
||||||
|
normalized,
|
||||||
|
);
|
||||||
|
throw new SmsProviderException("verifyLookup", {
|
||||||
|
receptor: data.receptor,
|
||||||
|
template: data.template,
|
||||||
|
providerBody: body,
|
||||||
|
normalized,
|
||||||
|
});
|
||||||
|
}
|
||||||
|
this.logger.log(
|
||||||
|
`Kavenegar verifyLookUp ok receptor=${data.receptor} template=${data.template} status=200`,
|
||||||
|
);
|
||||||
|
return body;
|
||||||
|
} catch (e) {
|
||||||
|
if (e instanceof SmsProviderException) throw e;
|
||||||
|
const detail = safeJsonStringify(unwrapKavenegarTransportError(e));
|
||||||
|
this.logger.error(
|
||||||
|
`Kavenegar verifyLookUp transport error receptor=${data.receptor} template=${data.template} detail=${detail}`,
|
||||||
|
);
|
||||||
|
throw new SmsTransportException(
|
||||||
|
"verifyLookup",
|
||||||
|
{ receptor: data.receptor, template: data.template },
|
||||||
|
e,
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private logProviderRejection(
|
||||||
|
op: "send" | "verifyLookUp",
|
||||||
|
receptor: string,
|
||||||
|
template: string | undefined,
|
||||||
|
normalized: KavenegarNormalized,
|
||||||
|
) {
|
||||||
|
const t = template != null && template !== "" ? ` template=${template}` : "";
|
||||||
|
this.logger.warn(
|
||||||
|
`Kavenegar ${op} rejected receptor=${receptor}${t} providerStatus=${normalized.httpLikeStatus ?? "unknown"} providerMessage=${normalized.message ?? "n/a"} body=${normalized.raw}`,
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
||||||
90
src/sms-orchestration/provider/parsian-sms.gateway.ts
Normal file
90
src/sms-orchestration/provider/parsian-sms.gateway.ts
Normal file
@@ -0,0 +1,90 @@
|
|||||||
|
import { Injectable, Logger } from "@nestjs/common";
|
||||||
|
import axios, { isAxiosError } from "axios";
|
||||||
|
import { buildParsianTemplateMessage } from "./parsian-template-messages";
|
||||||
|
import {
|
||||||
|
SmsProviderException,
|
||||||
|
SmsTransportException,
|
||||||
|
} from "./sms-provider.exception";
|
||||||
|
import { SendMessage, VerifyLookUpMessage } from "./sms-gateway.types";
|
||||||
|
|
||||||
|
@Injectable()
|
||||||
|
export class ParsianSmsGateway {
|
||||||
|
private readonly logger = new Logger(ParsianSmsGateway.name);
|
||||||
|
|
||||||
|
async sendMessage(data: SendMessage) {
|
||||||
|
try {
|
||||||
|
const body = await this.requestSend(data.receptor, data.message);
|
||||||
|
this.logger.log(`Parsian Send ok receptor=${data.receptor}`);
|
||||||
|
return body;
|
||||||
|
} catch (e) {
|
||||||
|
if (e instanceof SmsProviderException) throw e;
|
||||||
|
const detail = isAxiosError(e)
|
||||||
|
? { status: e.response?.status, data: e.response?.data, message: e.message }
|
||||||
|
: e;
|
||||||
|
this.logger.error(
|
||||||
|
`Parsian Send transport error receptor=${data.receptor} detail=${JSON.stringify(detail)}`,
|
||||||
|
);
|
||||||
|
throw new SmsTransportException("send", { receptor: data.receptor }, e);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
async verifyLookUp(data: VerifyLookUpMessage) {
|
||||||
|
const message = buildParsianTemplateMessage(data);
|
||||||
|
try {
|
||||||
|
const body = await this.requestSend(data.receptor, message);
|
||||||
|
this.logger.log(
|
||||||
|
`Parsian verifyLookUp ok receptor=${data.receptor} template=${data.template}`,
|
||||||
|
);
|
||||||
|
return body;
|
||||||
|
} catch (e) {
|
||||||
|
if (e instanceof SmsProviderException) throw e;
|
||||||
|
const detail = isAxiosError(e)
|
||||||
|
? { status: e.response?.status, data: e.response?.data, message: e.message }
|
||||||
|
: e;
|
||||||
|
this.logger.error(
|
||||||
|
`Parsian verifyLookUp transport error receptor=${data.receptor} template=${data.template} detail=${JSON.stringify(detail)}`,
|
||||||
|
);
|
||||||
|
throw new SmsTransportException(
|
||||||
|
"verifyLookup",
|
||||||
|
{ receptor: data.receptor, template: data.template },
|
||||||
|
e,
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private parsianHeaders() {
|
||||||
|
return {
|
||||||
|
"Content-Type": "application/json",
|
||||||
|
"X-PACKAGE-API-KEY": process.env.PARSIAN_API_KEY ?? "",
|
||||||
|
Authorization: `Basic ${process.env.PARSIAN_BASIC_TOKEN ?? ""}`,
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
private async requestSend(receptor: string, message: string): Promise<unknown> {
|
||||||
|
const baseUrl = process.env.PARSIAN_SMS_URL;
|
||||||
|
if (!baseUrl?.trim()) {
|
||||||
|
throw new SmsTransportException(
|
||||||
|
"send",
|
||||||
|
{ receptor },
|
||||||
|
new Error("PARSIAN_SMS_URL is not configured"),
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
const url = `${baseUrl}=${receptor}&Message=${encodeURIComponent(message)}`;
|
||||||
|
const response = await axios.get(url, { headers: this.parsianHeaders() });
|
||||||
|
|
||||||
|
if (response.status < 200 || response.status >= 300) {
|
||||||
|
throw new SmsProviderException("send", {
|
||||||
|
receptor,
|
||||||
|
providerBody: response.data,
|
||||||
|
normalized: {
|
||||||
|
httpLikeStatus: response.status,
|
||||||
|
message: String(response.statusText ?? ""),
|
||||||
|
raw: JSON.stringify(response.data),
|
||||||
|
},
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
return response.data;
|
||||||
|
}
|
||||||
|
}
|
||||||
65
src/sms-orchestration/provider/parsian-template-messages.ts
Normal file
65
src/sms-orchestration/provider/parsian-template-messages.ts
Normal file
@@ -0,0 +1,65 @@
|
|||||||
|
import { VerifyLookUpMessage } from "./sms-gateway.types";
|
||||||
|
|
||||||
|
const TOKEN_KEYS = ["token", "token2", "token3", "token10"] as const;
|
||||||
|
|
||||||
|
/** Kavenegar template name → plain-text body for Parsian (placeholders: {token}, {token2}, …). */
|
||||||
|
const PARSIAN_TEMPLATE_BODIES: Record<string, string> = {
|
||||||
|
"yara724-invite-link":
|
||||||
|
"کاربر محترم، لینک ورود به پرونده یارا724:\nشناسه: {token}\n{token2}",
|
||||||
|
"yara-field-expert-link":
|
||||||
|
"کارشناس میدانی بیمه {token} ({token2}) پرونده شما را بررسی میکند.\nلینک: {token3}",
|
||||||
|
"yara-blame-agreement":
|
||||||
|
"پرونده تصادف شما ({token}) آماده امضاست.\nلینک: {token2}",
|
||||||
|
"yara-claim-link":
|
||||||
|
"پرونده خسارت شما ({token}) آماده ثبت است.\nلینک: {token2}",
|
||||||
|
"yara-expert-lock":
|
||||||
|
"کارشناس ({token3}) پرونده {token} ({token2}) را در حال بررسی دارد.",
|
||||||
|
"yara-resend-documents":
|
||||||
|
"لطفاً مدارک پرونده {token} ({token2}) را مجدداً ارسال کنید.\nلینک: {token3}",
|
||||||
|
"yara-signature":
|
||||||
|
"امضای پرونده {token} ({token2}) توسط کارشناس {token3} بررسی شد.\nلینک: {token10}",
|
||||||
|
};
|
||||||
|
|
||||||
|
function applyTokens(
|
||||||
|
pattern: string,
|
||||||
|
data: Pick<
|
||||||
|
VerifyLookUpMessage,
|
||||||
|
"token" | "token2" | "token3" | "token10"
|
||||||
|
>,
|
||||||
|
): string {
|
||||||
|
let out = pattern;
|
||||||
|
for (const key of TOKEN_KEYS) {
|
||||||
|
const value = data[key];
|
||||||
|
if (value != null && value !== "") {
|
||||||
|
out = out.split(`{${key}}`).join(value);
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
return out.replace(/\{token\d*\}/g, "").trim();
|
||||||
|
}
|
||||||
|
|
||||||
|
export function buildParsianTemplateMessage(
|
||||||
|
data: VerifyLookUpMessage,
|
||||||
|
): string {
|
||||||
|
const authTemplate = process.env.AUTH_SMS_TEMPLATE?.trim();
|
||||||
|
if (authTemplate && data.template === authTemplate) {
|
||||||
|
const authMessage =
|
||||||
|
process.env.AUTH_SMS_MESSAGE?.trim() ??
|
||||||
|
"کاربر محترم رمز یکبار مصرف شما:\nCode: {token}";
|
||||||
|
return applyTokens(authMessage, data);
|
||||||
|
}
|
||||||
|
|
||||||
|
const body = PARSIAN_TEMPLATE_BODIES[data.template];
|
||||||
|
if (body) {
|
||||||
|
return applyTokens(body, data);
|
||||||
|
}
|
||||||
|
|
||||||
|
const parts = [
|
||||||
|
`پیام سیستم (${data.template})`,
|
||||||
|
data.token ? `کد: ${data.token}` : "",
|
||||||
|
data.token2 ?? "",
|
||||||
|
data.token3 ?? "",
|
||||||
|
data.token10 ?? "",
|
||||||
|
].filter(Boolean);
|
||||||
|
return parts.join("\n");
|
||||||
|
}
|
||||||
@@ -1,6 +1,8 @@
|
|||||||
import { KavenegarModule } from "@fraybabak/kavenegar_nest";
|
import { KavenegarModule } from "@fraybabak/kavenegar_nest";
|
||||||
import { Module } from "@nestjs/common";
|
import { Module } from "@nestjs/common";
|
||||||
import * as dotenv from "dotenv";
|
import * as dotenv from "dotenv";
|
||||||
|
import { KavenegarSmsGateway } from "./kavenegar-sms.gateway";
|
||||||
|
import { ParsianSmsGateway } from "./parsian-sms.gateway";
|
||||||
import { SmsGatewayService } from "./sms-gateway.service";
|
import { SmsGatewayService } from "./sms-gateway.service";
|
||||||
|
|
||||||
dotenv.config();
|
dotenv.config();
|
||||||
@@ -12,7 +14,11 @@ dotenv.config({ path: `.${process.env.NODE_ENV}.env` });
|
|||||||
apikey: process.env.SMS_API_KEY || "",
|
apikey: process.env.SMS_API_KEY || "",
|
||||||
}),
|
}),
|
||||||
],
|
],
|
||||||
providers: [SmsGatewayService],
|
providers: [
|
||||||
|
KavenegarSmsGateway,
|
||||||
|
ParsianSmsGateway,
|
||||||
|
SmsGatewayService,
|
||||||
|
],
|
||||||
exports: [SmsGatewayService],
|
exports: [SmsGatewayService],
|
||||||
})
|
})
|
||||||
export class SmsGatewayModule {}
|
export class SmsGatewayModule {}
|
||||||
|
|||||||
@@ -1,112 +1,33 @@
|
|||||||
import { KavenegarService } from "@fraybabak/kavenegar_nest";
|
import { Injectable, Logger, OnModuleInit } from "@nestjs/common";
|
||||||
import { Injectable, Logger } from "@nestjs/common";
|
import { KavenegarSmsGateway } from "./kavenegar-sms.gateway";
|
||||||
import {
|
import { ParsianSmsGateway } from "./parsian-sms.gateway";
|
||||||
isKavenegarSuccess,
|
import { getSmsProvider } from "./sms-provider.config";
|
||||||
KavenegarNormalized,
|
import { SendMessage, VerifyLookUpMessage } from "./sms-gateway.types";
|
||||||
normalizeKavenegarBody,
|
|
||||||
safeJsonStringify,
|
|
||||||
unwrapKavenegarTransportError,
|
|
||||||
} from "./kavenegar-response";
|
|
||||||
import {
|
|
||||||
SmsProviderException,
|
|
||||||
SmsTransportException,
|
|
||||||
} from "./sms-provider.exception";
|
|
||||||
|
|
||||||
export interface SendMessage {
|
export type { SendMessage, VerifyLookUpMessage } from "./sms-gateway.types";
|
||||||
message: string;
|
|
||||||
receptor: string;
|
|
||||||
}
|
|
||||||
|
|
||||||
export interface VerifyLookUpMessage {
|
|
||||||
template: string;
|
|
||||||
token: string;
|
|
||||||
token2?: string;
|
|
||||||
token3?: string;
|
|
||||||
token10?: string;
|
|
||||||
receptor: string;
|
|
||||||
}
|
|
||||||
|
|
||||||
@Injectable()
|
@Injectable()
|
||||||
export class SmsGatewayService {
|
export class SmsGatewayService implements OnModuleInit {
|
||||||
private readonly logger = new Logger(SmsGatewayService.name);
|
private readonly logger = new Logger(SmsGatewayService.name);
|
||||||
|
|
||||||
constructor(private readonly sender: KavenegarService) {}
|
constructor(
|
||||||
|
private readonly kavenegar: KavenegarSmsGateway,
|
||||||
|
private readonly parsian: ParsianSmsGateway,
|
||||||
|
) {}
|
||||||
|
|
||||||
|
onModuleInit() {
|
||||||
|
this.logger.log(`SMS provider active: ${getSmsProvider()}`);
|
||||||
|
}
|
||||||
|
|
||||||
async sendMessage(data: SendMessage) {
|
async sendMessage(data: SendMessage) {
|
||||||
try {
|
return this.activeGateway().sendMessage(data);
|
||||||
const body = await this.sender.Send({
|
|
||||||
sender: "10008663",
|
|
||||||
...data,
|
|
||||||
});
|
|
||||||
if (!isKavenegarSuccess(body)) {
|
|
||||||
const normalized = normalizeKavenegarBody(body);
|
|
||||||
this.logProviderRejection("send", data.receptor, undefined, normalized);
|
|
||||||
throw new SmsProviderException("send", {
|
|
||||||
receptor: data.receptor,
|
|
||||||
providerBody: body,
|
|
||||||
normalized,
|
|
||||||
});
|
|
||||||
}
|
|
||||||
this.logger.log(
|
|
||||||
`Kavenegar Send ok receptor=${data.receptor} status=200`,
|
|
||||||
);
|
|
||||||
return body;
|
|
||||||
} catch (e) {
|
|
||||||
if (e instanceof SmsProviderException) throw e;
|
|
||||||
const detail = safeJsonStringify(unwrapKavenegarTransportError(e));
|
|
||||||
this.logger.error(
|
|
||||||
`Kavenegar Send transport error receptor=${data.receptor} detail=${detail}`,
|
|
||||||
);
|
|
||||||
throw new SmsTransportException("send", { receptor: data.receptor }, e);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
async verifyLookUp(data: VerifyLookUpMessage) {
|
async verifyLookUp(data: VerifyLookUpMessage) {
|
||||||
try {
|
return this.activeGateway().verifyLookUp(data);
|
||||||
console.log(data)
|
|
||||||
const body = await this.sender.verifyLookup(data);
|
|
||||||
if (!isKavenegarSuccess(body)) {
|
|
||||||
const normalized = normalizeKavenegarBody(body);
|
|
||||||
this.logProviderRejection(
|
|
||||||
"verifyLookUp",
|
|
||||||
data.receptor,
|
|
||||||
data.template,
|
|
||||||
normalized,
|
|
||||||
);
|
|
||||||
throw new SmsProviderException("verifyLookup", {
|
|
||||||
receptor: data.receptor,
|
|
||||||
template: data.template,
|
|
||||||
providerBody: body,
|
|
||||||
normalized,
|
|
||||||
});
|
|
||||||
}
|
|
||||||
this.logger.log(
|
|
||||||
`Kavenegar verifyLookUp ok receptor=${data.receptor} template=${data.template} status=200`,
|
|
||||||
);
|
|
||||||
return body;
|
|
||||||
} catch (e) {
|
|
||||||
if (e instanceof SmsProviderException) throw e;
|
|
||||||
const detail = safeJsonStringify(unwrapKavenegarTransportError(e));
|
|
||||||
this.logger.error(
|
|
||||||
`Kavenegar verifyLookUp transport error receptor=${data.receptor} template=${data.template} detail=${detail}`,
|
|
||||||
);
|
|
||||||
throw new SmsTransportException(
|
|
||||||
"verifyLookup",
|
|
||||||
{ receptor: data.receptor, template: data.template },
|
|
||||||
e,
|
|
||||||
);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
private logProviderRejection(
|
private activeGateway(): KavenegarSmsGateway | ParsianSmsGateway {
|
||||||
op: "send" | "verifyLookUp",
|
return getSmsProvider() === "parsian" ? this.parsian : this.kavenegar;
|
||||||
receptor: string,
|
|
||||||
template: string | undefined,
|
|
||||||
normalized: KavenegarNormalized,
|
|
||||||
) {
|
|
||||||
const t = template != null && template !== "" ? ` template=${template}` : "";
|
|
||||||
this.logger.warn(
|
|
||||||
`Kavenegar ${op} rejected receptor=${receptor}${t} providerStatus=${normalized.httpLikeStatus ?? "unknown"} providerMessage=${normalized.message ?? "n/a"} body=${normalized.raw}`,
|
|
||||||
);
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
13
src/sms-orchestration/provider/sms-gateway.types.ts
Normal file
13
src/sms-orchestration/provider/sms-gateway.types.ts
Normal file
@@ -0,0 +1,13 @@
|
|||||||
|
export interface SendMessage {
|
||||||
|
message: string;
|
||||||
|
receptor: string;
|
||||||
|
}
|
||||||
|
|
||||||
|
export interface VerifyLookUpMessage {
|
||||||
|
template: string;
|
||||||
|
token: string;
|
||||||
|
token2?: string;
|
||||||
|
token3?: string;
|
||||||
|
token10?: string;
|
||||||
|
receptor: string;
|
||||||
|
}
|
||||||
18
src/sms-orchestration/provider/sms-provider.config.ts
Normal file
18
src/sms-orchestration/provider/sms-provider.config.ts
Normal file
@@ -0,0 +1,18 @@
|
|||||||
|
export type SmsProviderId = "kavenegar" | "parsian";
|
||||||
|
|
||||||
|
/** Reads `SMS` (e.g. `parsian`) or `SMS_PROVIDER`; defaults to kavenegar. */
|
||||||
|
export function getSmsProvider(): SmsProviderId {
|
||||||
|
const raw = (
|
||||||
|
process.env.SMS ??
|
||||||
|
process.env.SMS_PROVIDER ??
|
||||||
|
"kavenegar"
|
||||||
|
)
|
||||||
|
.trim()
|
||||||
|
.toLowerCase();
|
||||||
|
if (raw === "parsian") return "parsian";
|
||||||
|
return "kavenegar";
|
||||||
|
}
|
||||||
|
|
||||||
|
export function isParsianSmsProvider(): boolean {
|
||||||
|
return getSmsProvider() === "parsian";
|
||||||
|
}
|
||||||
Reference in New Issue
Block a user