forked from Yara724/api
Merge pull request 'YARA-834' (#36) from s.yahyaee/yara724-api:main into main
Reviewed-on: Yara724/api#36
This commit is contained in:
49
package-lock.json
generated
49
package-lock.json
generated
@@ -14,6 +14,7 @@
|
||||
"@nestjs-modules/mailer": "^1.8.1",
|
||||
"@nestjs/axios": "^3.1.3",
|
||||
"@nestjs/common": "^10.4.15",
|
||||
"@nestjs/config": "^4.0.4",
|
||||
"@nestjs/core": "^10.4.15",
|
||||
"@nestjs/jwt": "^10.2.0",
|
||||
"@nestjs/mapped-types": "*",
|
||||
@@ -4651,6 +4652,39 @@
|
||||
}
|
||||
}
|
||||
},
|
||||
"node_modules/@nestjs/config": {
|
||||
"version": "4.0.4",
|
||||
"resolved": "https://registry.npmjs.org/@nestjs/config/-/config-4.0.4.tgz",
|
||||
"integrity": "sha512-CJPjNitr0bAufSEnRe2N+JbnVmMmDoo6hvKCPzXgZoGwJSmp/dZPk9f/RMbuD/+Q1ZJPjwsRpq0vxna++Knwow==",
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"dotenv": "17.4.1",
|
||||
"dotenv-expand": "12.0.3",
|
||||
"lodash": "4.18.1"
|
||||
},
|
||||
"peerDependencies": {
|
||||
"@nestjs/common": "^10.0.0 || ^11.0.0",
|
||||
"rxjs": "^7.1.0"
|
||||
}
|
||||
},
|
||||
"node_modules/@nestjs/config/node_modules/dotenv": {
|
||||
"version": "17.4.1",
|
||||
"resolved": "https://registry.npmjs.org/dotenv/-/dotenv-17.4.1.tgz",
|
||||
"integrity": "sha512-k8DaKGP6r1G30Lx8V4+pCsLzKr8vLmV2paqEj1Y55GdAgJuIqpRp5FfajGF8KtwMxCz9qJc6wUIJnm053d/WCw==",
|
||||
"license": "BSD-2-Clause",
|
||||
"engines": {
|
||||
"node": ">=12"
|
||||
},
|
||||
"funding": {
|
||||
"url": "https://dotenvx.com"
|
||||
}
|
||||
},
|
||||
"node_modules/@nestjs/config/node_modules/lodash": {
|
||||
"version": "4.18.1",
|
||||
"resolved": "https://registry.npmjs.org/lodash/-/lodash-4.18.1.tgz",
|
||||
"integrity": "sha512-dMInicTPVE8d1e5otfwmmjlxkZoUpiVLwyeTdUsi/Caj/gfzzblBcCE5sRHV/AsjuCmxWrte2TNGSYuCeCq+0Q==",
|
||||
"license": "MIT"
|
||||
},
|
||||
"node_modules/@nestjs/core": {
|
||||
"version": "10.4.15",
|
||||
"resolved": "https://registry.npmjs.org/@nestjs/core/-/core-10.4.15.tgz",
|
||||
@@ -8441,6 +8475,21 @@
|
||||
"url": "https://dotenvx.com"
|
||||
}
|
||||
},
|
||||
"node_modules/dotenv-expand": {
|
||||
"version": "12.0.3",
|
||||
"resolved": "https://registry.npmjs.org/dotenv-expand/-/dotenv-expand-12.0.3.tgz",
|
||||
"integrity": "sha512-uc47g4b+4k/M/SeaW1y4OApx+mtLWl92l5LMPP0GNXctZqELk+YGgOPIIC5elYmUH4OuoK3JLhuRUYegeySiFA==",
|
||||
"license": "BSD-2-Clause",
|
||||
"dependencies": {
|
||||
"dotenv": "^16.4.5"
|
||||
},
|
||||
"engines": {
|
||||
"node": ">=12"
|
||||
},
|
||||
"funding": {
|
||||
"url": "https://dotenvx.com"
|
||||
}
|
||||
},
|
||||
"node_modules/dunder-proto": {
|
||||
"version": "1.0.1",
|
||||
"resolved": "https://registry.npmjs.org/dunder-proto/-/dunder-proto-1.0.1.tgz",
|
||||
|
||||
@@ -25,6 +25,7 @@
|
||||
"@nestjs-modules/mailer": "^1.8.1",
|
||||
"@nestjs/axios": "^3.1.3",
|
||||
"@nestjs/common": "^10.4.15",
|
||||
"@nestjs/config": "^4.0.4",
|
||||
"@nestjs/core": "^10.4.15",
|
||||
"@nestjs/jwt": "^10.2.0",
|
||||
"@nestjs/mapped-types": "*",
|
||||
|
||||
@@ -13,8 +13,7 @@ import { LoginDtoRs } from "src/auth/dto/user/login.dto";
|
||||
import { UserDbService } from "src/users/entities/db-service/user.db.service";
|
||||
import { HashService } from "src/utils/hash/hash.service";
|
||||
import { OtpService } from "src/utils/otp/otp.service";
|
||||
import { SmsManagerService } from "src/utils/sms-manager/sms-manager.service";
|
||||
import { describeSmsError } from "src/utils/sms-manager/sms-provider.exception";
|
||||
import { SmsOrchestrationService } from "src/sms-orchestration/sms-orchestration.service";
|
||||
|
||||
// TODO FIX REGISTER TO USER.SERVICE AND AUTH IN THIS MODULE
|
||||
@Injectable()
|
||||
@@ -26,7 +25,7 @@ export class UserAuthService {
|
||||
private readonly userDbService: UserDbService,
|
||||
private readonly hashService: HashService,
|
||||
private readonly otpCreator: OtpService,
|
||||
private readonly smsManagerService: SmsManagerService,
|
||||
private readonly smsOrchestrationService: SmsOrchestrationService,
|
||||
) {}
|
||||
|
||||
async validateUser(username: string, pass: string): Promise<any> {
|
||||
@@ -118,26 +117,19 @@ export class UserAuthService {
|
||||
}
|
||||
|
||||
private async smsSender(otp: string, mobile: string) {
|
||||
try {
|
||||
await this.smsManagerService.verifyLookUp({
|
||||
token: otp,
|
||||
template: process.env.AUTH_SMS_TEMPLATE,
|
||||
receptor: mobile,
|
||||
});
|
||||
this.logger.log(
|
||||
`Auth OTP SMS accepted by provider phone=${mobile} otp=${otp}`,
|
||||
const ok = await this.smsOrchestrationService.sendAuthOtp(
|
||||
mobile,
|
||||
otp,
|
||||
process.env.AUTH_SMS_TEMPLATE,
|
||||
);
|
||||
} catch (err) {
|
||||
this.logger.error(
|
||||
`Auth OTP SMS failed phone=${mobile} otp=${otp} ${describeSmsError(err)}`,
|
||||
);
|
||||
if (err instanceof HttpException) {
|
||||
throw err;
|
||||
}
|
||||
if (!ok) {
|
||||
throw new HttpException(
|
||||
"auth sms send failed",
|
||||
HttpStatus.BAD_GATEWAY,
|
||||
);
|
||||
}
|
||||
this.logger.log(
|
||||
`Auth OTP SMS accepted by provider phone=${mobile} otp=${otp}`,
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -12,7 +12,7 @@ import { UsersModule } from "src/users/users.module";
|
||||
import { HashModule } from "src/utils/hash/hash.module";
|
||||
// import { MailModule } from "src/utils/mail/mail.module";
|
||||
import { OtpModule } from "src/utils/otp/otp.module";
|
||||
import { SmsManagerModule } from "src/utils/sms-manager/sms-manager.module";
|
||||
import { SmsOrchestrationModule } from "src/sms-orchestration/sms-orchestration.module";
|
||||
|
||||
@Module({
|
||||
imports: [
|
||||
@@ -22,7 +22,7 @@ import { SmsManagerModule } from "src/utils/sms-manager/sms-manager.module";
|
||||
HashModule,
|
||||
OtpModule,
|
||||
PassportModule,
|
||||
SmsManagerModule,
|
||||
SmsOrchestrationModule,
|
||||
JwtModule.register({
|
||||
signOptions: { expiresIn: "1h" },
|
||||
global: true,
|
||||
|
||||
@@ -35,9 +35,14 @@ export class ResendRequestDto {
|
||||
description: "Array of resend requests (can include first party, second party, or both)",
|
||||
example: [
|
||||
{
|
||||
partyId: "507f1f77bcf86cd799439011",
|
||||
requestedItems: ["drivingLicense", "carCertificate"],
|
||||
description: "Please resend with better quality",
|
||||
partyId: "681b3b3fb237e5856429e444",
|
||||
requestedItems: ["drivingLicense"],
|
||||
description: "DESC TEST1",
|
||||
},
|
||||
{
|
||||
partyId: "68b6ba552a6e897df34e0f90",
|
||||
requestedItems: ["carCertificate"],
|
||||
description: "DESC TEST2",
|
||||
},
|
||||
],
|
||||
})
|
||||
|
||||
@@ -10,9 +10,9 @@ import { BlameRequestDbService } from "src/request-management/entities/db-servic
|
||||
import { SandHubModule } from "src/sand-hub/sand-hub.module";
|
||||
import { UsersModule } from "src/users/users.module";
|
||||
import { CronModule } from "src/utils/cron/cron.module";
|
||||
import { SmsManagerModule } from "src/utils/sms-manager/sms-manager.module";
|
||||
import { PublicIdModule } from "src/utils/public-id/public-id.module";
|
||||
import { HashModule } from "src/utils/hash/hash.module";
|
||||
import { SmsOrchestrationModule } from "src/sms-orchestration/sms-orchestration.module";
|
||||
import { WorkflowStepManagementModule } from "src/workflow-step-management/workflow-step-management.module";
|
||||
import { AuthModule } from "src/auth/auth.module";
|
||||
import { BlameDocumentDbService } from "./entities/db-service/blame-document.db.service";
|
||||
@@ -45,7 +45,7 @@ import { RegistrarInitiatedController } from "./registrar-initiated.controller";
|
||||
UsersModule,
|
||||
ClientModule,
|
||||
SandHubModule,
|
||||
SmsManagerModule,
|
||||
SmsOrchestrationModule,
|
||||
PublicIdModule,
|
||||
HashModule,
|
||||
WorkflowStepManagementModule,
|
||||
|
||||
@@ -39,8 +39,7 @@ import { StepsEnum } from "src/Types&Enums/blame-request-management/steps.enum";
|
||||
import { ExpertDbService } from "src/users/entities/db-service/expert.db.service";
|
||||
import { UserDbService } from "src/users/entities/db-service/user.db.service";
|
||||
import { AutoCloseRequestService } from "src/utils/cron/cron.service";
|
||||
import { SmsManagerService } from "src/utils/sms-manager/sms-manager.service";
|
||||
import { describeSmsError } from "src/utils/sms-manager/sms-provider.exception";
|
||||
import { SmsOrchestrationService } from "src/sms-orchestration/sms-orchestration.service";
|
||||
import {
|
||||
DescriptionDto,
|
||||
LocationDto,
|
||||
@@ -308,7 +307,7 @@ export class RequestManagementService {
|
||||
private readonly clientService: ClientService,
|
||||
private readonly blameVoiceDbService: BlameVoiceDbService,
|
||||
private readonly userSign: UserSignDbService,
|
||||
private readonly smsManagerService: SmsManagerService,
|
||||
private readonly smsOrchestrationService: SmsOrchestrationService,
|
||||
private readonly expertDbService: ExpertDbService,
|
||||
private readonly autoCloseRequestService: AutoCloseRequestService,
|
||||
private readonly claimRequestManagementDbService: ClaimRequestManagementDbService,
|
||||
@@ -1305,21 +1304,14 @@ export class RequestManagementService {
|
||||
metadata: { secondPartyPhone, expertInitiatedLink: true },
|
||||
} as any);
|
||||
await (req as any).save();
|
||||
const url = `${process.env.URL}/${frontendRoute}?token=${requestId}`;
|
||||
try {
|
||||
await this.smsManagerService.verifyLookUp({
|
||||
token: url,
|
||||
template: "yara724-invite-link",
|
||||
receptor: secondPartyPhone,
|
||||
});
|
||||
this.logger.log(
|
||||
`[SMS] Invitation resent to ${secondPartyPhone} for expert-initiated request ${req.publicId}`,
|
||||
const url = this.smsOrchestrationService.buildInviteLink(
|
||||
frontendRoute,
|
||||
requestId,
|
||||
);
|
||||
} catch (err) {
|
||||
this.logger.error(
|
||||
`[SMS] Failed to send invitation to ${secondPartyPhone}: ${describeSmsError(err)}`,
|
||||
await this.smsOrchestrationService.sendInviteLink(
|
||||
secondPartyPhone,
|
||||
url,
|
||||
);
|
||||
}
|
||||
return {
|
||||
requestId: req._id,
|
||||
publicId: req.publicId,
|
||||
@@ -1362,22 +1354,11 @@ export class RequestManagementService {
|
||||
await (req as any).save();
|
||||
|
||||
// Send SMS invitation
|
||||
const url = `${process.env.URL}/${frontendRoute}?token=${requestId}`;
|
||||
try {
|
||||
await this.smsManagerService.verifyLookUp({
|
||||
token: url,
|
||||
template: "yara724-invite-link",
|
||||
receptor: phoneNumber,
|
||||
});
|
||||
this.logger.log(
|
||||
`[SMS] Invitation sent to ${phoneNumber} for request ${req.publicId}`,
|
||||
const url = this.smsOrchestrationService.buildInviteLink(
|
||||
frontendRoute,
|
||||
requestId,
|
||||
);
|
||||
} catch (err) {
|
||||
this.logger.error(
|
||||
`[SMS] Failed to send invitation to ${phoneNumber}: ${describeSmsError(err)}`,
|
||||
);
|
||||
// Don't block the request flow if SMS fails
|
||||
}
|
||||
await this.smsOrchestrationService.sendInviteLink(phoneNumber, url);
|
||||
|
||||
return {
|
||||
requestId: req._id,
|
||||
@@ -2295,21 +2276,11 @@ export class RequestManagementService {
|
||||
}
|
||||
|
||||
// Send the SMS after the database update is complete.
|
||||
const URL = `${process.env.URL}/${frontendRoutes}?token=${requestId}`;
|
||||
try {
|
||||
await this.smsManagerService.verifyLookUp({
|
||||
token: URL,
|
||||
template: "yara724-invite-link",
|
||||
receptor: phoneNumber,
|
||||
});
|
||||
this.logger.log(
|
||||
`[SMS] Invite link verifyLookUp ok receptor=${phoneNumber}`,
|
||||
const URL = this.smsOrchestrationService.buildInviteLink(
|
||||
frontendRoutes,
|
||||
requestId,
|
||||
);
|
||||
} catch (er) {
|
||||
this.logger.error(
|
||||
`[SMS] Invite link failed receptor=${phoneNumber}: ${describeSmsError(er)}`,
|
||||
);
|
||||
}
|
||||
await this.smsOrchestrationService.sendInviteLink(phoneNumber, URL);
|
||||
|
||||
return { url: URL };
|
||||
}
|
||||
@@ -2838,19 +2809,13 @@ export class RequestManagementService {
|
||||
|
||||
// TODO SMS Sending
|
||||
if (phoneNumberToNotify) {
|
||||
const message = `متاسفانه طرف مقابل با نظر کارشناس مخالفت کرد. فرآیند آنلاین این پرونده بسته شده است و جهت پیگیری حضوری اقدام نمایید.`;
|
||||
try {
|
||||
await this.smsManagerService.sendMessage({
|
||||
receptor: phoneNumberToNotify,
|
||||
message,
|
||||
});
|
||||
} catch (err) {
|
||||
this.logger.error(
|
||||
`[SMS] PartiesDisagree notify failed receptor=${phoneNumberToNotify}: ${describeSmsError(err)}`,
|
||||
await this.smsOrchestrationService.sendTextByKey(
|
||||
phoneNumberToNotify,
|
||||
"parties_disagree_notify",
|
||||
"متاسفانه طرف مقابل با نظر کارشناس مخالفت کرد. فرآیند آنلاین این پرونده بسته شده است و جهت پیگیری حضوری اقدام نمایید.",
|
||||
);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private async handlePostReplyStateChanges(
|
||||
updatedReq: any,
|
||||
@@ -3028,18 +2993,16 @@ export class RequestManagementService {
|
||||
: originalReq.firstPartyDetails?.firstPartyPhoneNumber;
|
||||
|
||||
if (phoneNumber) {
|
||||
// TODO FIX SMS SENDING FOR PARTIES
|
||||
try {
|
||||
await this.smsManagerService.sendMessage({
|
||||
receptor: phoneNumber,
|
||||
const key =
|
||||
message.includes("24 ساعت")
|
||||
? "one_party_accepted_wait_24h"
|
||||
: "one_party_signed_wait_signature";
|
||||
await this.smsOrchestrationService.sendTextByKey(
|
||||
phoneNumber,
|
||||
key,
|
||||
message,
|
||||
});
|
||||
} catch (err) {
|
||||
this.logger.error(
|
||||
`[SMS] One-party-replied notify failed receptor=${phoneNumber}: ${describeSmsError(err)}`,
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
await this.autoCloseRequestService.scheduleAutoClose({
|
||||
requestId: updatedReq._id.toString(),
|
||||
@@ -3764,33 +3727,21 @@ export class RequestManagementService {
|
||||
"URL environment variable is not configured",
|
||||
);
|
||||
}
|
||||
const linkUrl = `${process.env.URL}/${dto.frontendRoute}?token=${requestId}`;
|
||||
const typeToken =
|
||||
req.type === BlameRequestType.CAR_BODY ? "بدنه" : "ثالث";
|
||||
const linkUrl = this.smsOrchestrationService.buildInviteLink(
|
||||
dto.frontendRoute,
|
||||
requestId,
|
||||
);
|
||||
const expertName =
|
||||
`${expert?.lastName || ""}`.trim() || "کارشناس";
|
||||
|
||||
const sentTo: { role: string; phoneNumber: string; smsSent: boolean }[] = [];
|
||||
const template = "yara-field-expert-link";
|
||||
const sendSms = async (phone: string, role: PartyRole): Promise<boolean> => {
|
||||
try {
|
||||
await this.smsManagerService.verifyLookUp({
|
||||
template,
|
||||
return this.smsOrchestrationService.sendFieldExpertLink({
|
||||
receptor: phone,
|
||||
token: typeToken,
|
||||
token2: expertName,
|
||||
token3: linkUrl,
|
||||
type: req.type,
|
||||
expertLastName: expertName,
|
||||
link: linkUrl,
|
||||
});
|
||||
this.logger.log(
|
||||
`[SMS] Field-expert link sent role=${role} receptor=${phone} template=${template}`,
|
||||
);
|
||||
return true;
|
||||
} catch (er) {
|
||||
this.logger.error(
|
||||
`[SMS] Field-expert link failed role=${role} receptor=${phone}: ${describeSmsError(er)}`,
|
||||
);
|
||||
return false;
|
||||
}
|
||||
};
|
||||
|
||||
const firstIdx = this.getPartyIndex(req, PartyRole.FIRST);
|
||||
@@ -3816,7 +3767,12 @@ export class RequestManagementService {
|
||||
actorName: `${expert.firstName || ""} ${expert.lastName || ""}`.trim(),
|
||||
actorType: "field_expert",
|
||||
},
|
||||
metadata: { linkUrl, sentTo, template, typeToken, frontendRoute: dto.frontendRoute },
|
||||
metadata: {
|
||||
linkUrl,
|
||||
sentTo,
|
||||
template: "yara-field-expert-link",
|
||||
frontendRoute: dto.frontendRoute,
|
||||
},
|
||||
});
|
||||
await (req as any).save();
|
||||
|
||||
|
||||
@@ -0,0 +1,24 @@
|
||||
import { Injectable } from "@nestjs/common";
|
||||
import { InjectModel } from "@nestjs/mongoose";
|
||||
import { FilterQuery, Model } from "mongoose";
|
||||
import { SmsText, SmsTextDocument } from "../schema/sms-text.schema";
|
||||
|
||||
@Injectable()
|
||||
export class SmsTextDbService {
|
||||
constructor(
|
||||
@InjectModel(SmsText.name)
|
||||
private readonly smsTextModel: Model<SmsTextDocument>,
|
||||
) {}
|
||||
|
||||
async findOne(filter: FilterQuery<SmsText>): Promise<SmsTextDocument | null> {
|
||||
return this.smsTextModel.findOne(filter);
|
||||
}
|
||||
|
||||
async upsert(key: string, text: string): Promise<void> {
|
||||
await this.smsTextModel.updateOne(
|
||||
{ key },
|
||||
{ $setOnInsert: { key, text, active: true } },
|
||||
{ upsert: true },
|
||||
);
|
||||
}
|
||||
}
|
||||
17
src/sms-orchestration/entities/schema/sms-text.schema.ts
Normal file
17
src/sms-orchestration/entities/schema/sms-text.schema.ts
Normal file
@@ -0,0 +1,17 @@
|
||||
import { Prop, Schema, SchemaFactory } from "@nestjs/mongoose";
|
||||
import { HydratedDocument } from "mongoose";
|
||||
|
||||
@Schema({ collection: "sms-texts", timestamps: true, versionKey: false })
|
||||
export class SmsText {
|
||||
@Prop({ required: true, unique: true, index: true })
|
||||
key: string;
|
||||
|
||||
@Prop({ required: true })
|
||||
text: string;
|
||||
|
||||
@Prop({ default: true })
|
||||
active: boolean;
|
||||
}
|
||||
|
||||
export type SmsTextDocument = HydratedDocument<SmsText>;
|
||||
export const SmsTextSchema = SchemaFactory.createForClass(SmsText);
|
||||
18
src/sms-orchestration/provider/sms-gateway.module.ts
Normal file
18
src/sms-orchestration/provider/sms-gateway.module.ts
Normal file
@@ -0,0 +1,18 @@
|
||||
import { KavenegarModule } from "@fraybabak/kavenegar_nest";
|
||||
import { Module } from "@nestjs/common";
|
||||
import * as dotenv from "dotenv";
|
||||
import { SmsGatewayService } from "./sms-gateway.service";
|
||||
|
||||
dotenv.config();
|
||||
dotenv.config({ path: `.${process.env.NODE_ENV}.env` });
|
||||
|
||||
@Module({
|
||||
imports: [
|
||||
KavenegarModule.forRoot({
|
||||
apikey: process.env.SMS_API_KEY || "",
|
||||
}),
|
||||
],
|
||||
providers: [SmsGatewayService],
|
||||
exports: [SmsGatewayService],
|
||||
})
|
||||
export class SmsGatewayModule {}
|
||||
@@ -14,7 +14,6 @@ import {
|
||||
|
||||
export interface SendMessage {
|
||||
message: string;
|
||||
// sender: string;
|
||||
receptor: string;
|
||||
}
|
||||
|
||||
@@ -27,8 +26,8 @@ export interface VerifyLookUpMessage {
|
||||
}
|
||||
|
||||
@Injectable()
|
||||
export class SmsManagerService {
|
||||
private readonly logger = new Logger(SmsManagerService.name);
|
||||
export class SmsGatewayService {
|
||||
private readonly logger = new Logger(SmsGatewayService.name);
|
||||
|
||||
constructor(private readonly sender: KavenegarService) {}
|
||||
|
||||
16
src/sms-orchestration/sms-orchestration.module.ts
Normal file
16
src/sms-orchestration/sms-orchestration.module.ts
Normal file
@@ -0,0 +1,16 @@
|
||||
import { Module } from "@nestjs/common";
|
||||
import { MongooseModule } from "@nestjs/mongoose";
|
||||
import { SmsTextDbService } from "./entities/db-service/sms-text.db.service";
|
||||
import { SmsText, SmsTextSchema } from "./entities/schema/sms-text.schema";
|
||||
import { SmsGatewayModule } from "./provider/sms-gateway.module";
|
||||
import { SmsOrchestrationService } from "./sms-orchestration.service";
|
||||
|
||||
@Module({
|
||||
imports: [
|
||||
SmsGatewayModule,
|
||||
MongooseModule.forFeature([{ name: SmsText.name, schema: SmsTextSchema }]),
|
||||
],
|
||||
providers: [SmsTextDbService, SmsOrchestrationService],
|
||||
exports: [SmsOrchestrationService],
|
||||
})
|
||||
export class SmsOrchestrationModule {}
|
||||
111
src/sms-orchestration/sms-orchestration.service.ts
Normal file
111
src/sms-orchestration/sms-orchestration.service.ts
Normal file
@@ -0,0 +1,111 @@
|
||||
import { Injectable, Logger, OnModuleInit } from "@nestjs/common";
|
||||
import { BlameRequestType } from "src/Types&Enums/blame-request-management/blameRequestType.enum";
|
||||
import { SmsTextDbService } from "./entities/db-service/sms-text.db.service";
|
||||
import { SmsGatewayService } from "./provider/sms-gateway.service";
|
||||
import { describeSmsError } from "./provider/sms-provider.exception";
|
||||
|
||||
type TemplateArgs = {
|
||||
template: string;
|
||||
receptor: string;
|
||||
token: string;
|
||||
token2?: string;
|
||||
token3?: string;
|
||||
};
|
||||
|
||||
@Injectable()
|
||||
export class SmsOrchestrationService implements OnModuleInit {
|
||||
private readonly logger = new Logger(SmsOrchestrationService.name);
|
||||
|
||||
constructor(
|
||||
private readonly smsGatewayService: SmsGatewayService,
|
||||
private readonly smsTextDbService: SmsTextDbService,
|
||||
) {}
|
||||
|
||||
async onModuleInit(): Promise<void> {
|
||||
await this.smsTextDbService.upsert(
|
||||
"parties_disagree_notify",
|
||||
"متاسفانه طرف مقابل با نظر کارشناس مخالفت کرد. فرآیند آنلاین این پرونده بسته شده است و جهت پیگیری حضوری اقدام نمایید.",
|
||||
);
|
||||
await this.smsTextDbService.upsert(
|
||||
"one_party_signed_wait_signature",
|
||||
"طرف مقابل توافق را امضا کرده است. لطفا جهت نهایی کردن پرونده، امضای خود را ثبت نمایید.",
|
||||
);
|
||||
await this.smsTextDbService.upsert(
|
||||
"one_party_accepted_wait_24h",
|
||||
"طرف مقابل نظر کارشناس را قبول و امضا کرده است. در طی 24 ساعت آینده فرصت دارید تا پرونده را تکمیل کرده یا پرونده بسته خواهد شد.",
|
||||
);
|
||||
}
|
||||
|
||||
buildInviteLink(frontendRoute: string, requestId: string): string {
|
||||
return `${process.env.URL}/${frontendRoute}?token=${requestId}`;
|
||||
}
|
||||
|
||||
async sendInviteLink(phoneNumber: string, link: string): Promise<boolean> {
|
||||
return this.sendTemplate({
|
||||
template: "yara724-invite-link",
|
||||
receptor: phoneNumber,
|
||||
token: link,
|
||||
});
|
||||
}
|
||||
|
||||
async sendAuthOtp(
|
||||
mobile: string,
|
||||
otp: string,
|
||||
template?: string,
|
||||
): Promise<boolean> {
|
||||
return this.sendTemplate({
|
||||
template: template || process.env.AUTH_SMS_TEMPLATE || "",
|
||||
receptor: mobile,
|
||||
token: otp,
|
||||
});
|
||||
}
|
||||
|
||||
async sendFieldExpertLink(params: {
|
||||
receptor: string;
|
||||
type: BlameRequestType;
|
||||
expertLastName: string;
|
||||
link: string;
|
||||
}): Promise<boolean> {
|
||||
return this.sendTemplate({
|
||||
template: "yara-field-expert-link",
|
||||
receptor: params.receptor,
|
||||
token: params.type === BlameRequestType.CAR_BODY ? "بدنه" : "ثالث",
|
||||
token2: params.expertLastName || "کارشناس",
|
||||
token3: params.link,
|
||||
});
|
||||
}
|
||||
|
||||
async sendTextByKey(
|
||||
receptor: string,
|
||||
key: string,
|
||||
fallbackText: string,
|
||||
): Promise<boolean> {
|
||||
const row = await this.smsTextDbService.findOne({ key, active: true });
|
||||
const message = row?.text?.trim() || fallbackText;
|
||||
try {
|
||||
await this.smsGatewayService.sendMessage({ receptor, message });
|
||||
this.logger.log(`[SMS] text sent key=${key} receptor=${receptor}`);
|
||||
return true;
|
||||
} catch (err) {
|
||||
this.logger.error(
|
||||
`[SMS] text send failed key=${key} receptor=${receptor}: ${describeSmsError(err)}`,
|
||||
);
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
private async sendTemplate(args: TemplateArgs): Promise<boolean> {
|
||||
try {
|
||||
await this.smsGatewayService.verifyLookUp(args);
|
||||
this.logger.log(
|
||||
`[SMS] template sent template=${args.template} receptor=${args.receptor}`,
|
||||
);
|
||||
return true;
|
||||
} catch (err) {
|
||||
this.logger.error(
|
||||
`[SMS] template send failed template=${args.template} receptor=${args.receptor}: ${describeSmsError(err)}`,
|
||||
);
|
||||
return false;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,15 +0,0 @@
|
||||
import { KavenegarModule } from "@fraybabak/kavenegar_nest";
|
||||
import { Module } from "@nestjs/common";
|
||||
import { SmsManagerService } from "./sms-manager.service";
|
||||
|
||||
@Module({
|
||||
imports: [
|
||||
KavenegarModule.forRoot({
|
||||
apikey:
|
||||
"75776C717969412B4B52306A5956462F4A714E6F6C65544D6A2B654B7566786E",
|
||||
}),
|
||||
],
|
||||
providers: [SmsManagerService],
|
||||
exports: [SmsManagerService],
|
||||
})
|
||||
export class SmsManagerModule {}
|
||||
Reference in New Issue
Block a user