Tidied up the project

This commit is contained in:
2026-05-30 08:59:57 +03:30
parent 5c372947dd
commit 10df869efb
19 changed files with 7064 additions and 4620 deletions

View File

@@ -1,5 +1,5 @@
import { KavenegarService } from "@fraybabak/kavenegar_nest";
import { Injectable, Logger } from "@nestjs/common";
import { KavenegarService } from "./kavenegar.service";
import {
isKavenegarSuccess,
KavenegarNormalized,
@@ -21,29 +21,27 @@ export class KavenegarSmsGateway {
async sendMessage(data: SendMessage) {
try {
const body = await this.sender.Send({
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);
}
}
@@ -51,14 +49,17 @@ export class KavenegarSmsGateway {
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,
@@ -66,19 +67,21 @@ export class KavenegarSmsGateway {
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}`,
);
this.logger.error(`Kavenegar transport error ${detail}`);
throw new SmsTransportException(
"verifyLookup",
{ receptor: data.receptor, template: data.template },
{
receptor: data.receptor,
template: data.template,
},
e,
);
}
@@ -90,9 +93,10 @@ export class KavenegarSmsGateway {
template: string | undefined,
normalized: KavenegarNormalized,
) {
const t = template != null && template !== "" ? ` template=${template}` : "";
const t = template != null ? ` template=${template}` : "";
this.logger.warn(
`Kavenegar ${op} rejected receptor=${receptor}${t} providerStatus=${normalized.httpLikeStatus ?? "unknown"} providerMessage=${normalized.message ?? "n/a"} body=${normalized.raw}`,
`Kavenegar ${op} rejected receptor=${receptor}${t} providerStatus=${normalized.httpLikeStatus}`,
);
}
}

View File

@@ -0,0 +1,45 @@
import { HttpService } from "@nestjs/axios";
import { Injectable } from "@nestjs/common";
import { ConfigService } from "@nestjs/config";
import { firstValueFrom } from "rxjs";
@Injectable()
export class KavenegarService {
private readonly apiKey: string;
private readonly baseUrl: string;
constructor(
private readonly http: HttpService,
private readonly configService: ConfigService,
) {
this.apiKey = this.configService.get<string>("SMS_API_KEY", "");
this.baseUrl = `https://api.kavenegar.com/v1/${this.apiKey}`;
}
async send(data: { receptor: string; message: string; sender?: string }) {
const response = await firstValueFrom(
this.http.post(`${this.baseUrl}/sms/send.json`, null, {
params: data,
}),
);
return response.data;
}
async verifyLookup(data: {
receptor: string;
token: string;
template: string;
token2?: string;
token3?: string;
}) {
const response = await firstValueFrom(
this.http.get(`${this.baseUrl}/verify/lookup.json`, {
params: data,
}),
);
return response.data;
}
}

View File

@@ -1,16 +1,20 @@
import { Injectable, Logger } from "@nestjs/common";
import axios, { isAxiosError } from "axios";
import { isAxiosError } from "axios";
import { buildParsianTemplateMessage } from "./parsian-template-messages";
import {
SmsProviderException,
SmsTransportException,
} from "./sms-provider.exception";
import { SendMessage, VerifyLookUpMessage } from "./sms-gateway.types";
import { firstValueFrom } from "rxjs";
import { HttpService } from "@nestjs/axios";
@Injectable()
export class ParsianSmsGateway {
private readonly logger = new Logger(ParsianSmsGateway.name);
constructor(private readonly httpService: HttpService) {}
async sendMessage(data: SendMessage) {
try {
const body = await this.requestSend(data.receptor, data.message);
@@ -19,7 +23,11 @@ export class ParsianSmsGateway {
} catch (e) {
if (e instanceof SmsProviderException) throw e;
const detail = isAxiosError(e)
? { status: e.response?.status, data: e.response?.data, message: e.message }
? {
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)}`,
@@ -39,7 +47,11 @@ export class ParsianSmsGateway {
} catch (e) {
if (e instanceof SmsProviderException) throw e;
const detail = isAxiosError(e)
? { status: e.response?.status, data: e.response?.data, message: e.message }
? {
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)}`,
@@ -60,7 +72,10 @@ export class ParsianSmsGateway {
};
}
private async requestSend(receptor: string, message: string): Promise<unknown> {
private async requestSend(
receptor: string,
message: string,
): Promise<unknown> {
const baseUrl = process.env.PARSIAN_SMS_URL;
if (!baseUrl?.trim()) {
throw new SmsTransportException(
@@ -71,7 +86,9 @@ export class ParsianSmsGateway {
}
const url = `${baseUrl}=${receptor}&Message=${encodeURIComponent(message)}`;
const response = await axios.get(url, { headers: this.parsianHeaders() });
const response = await firstValueFrom(
this.httpService.get(url, { headers: this.parsianHeaders() }),
);
if (response.status < 200 || response.status >= 300) {
throw new SmsProviderException("send", {

View File

@@ -1,20 +1,16 @@
import { KavenegarModule } from "@fraybabak/kavenegar_nest";
import { HttpModule } from "@nestjs/axios";
import { Module } from "@nestjs/common";
import * as dotenv from "dotenv";
import { ConfigModule } from "@nestjs/config";
import { KavenegarService } from "./kavenegar.service";
import { KavenegarSmsGateway } from "./kavenegar-sms.gateway";
import { ParsianSmsGateway } from "./parsian-sms.gateway";
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 || "",
}),
],
imports: [HttpModule, ConfigModule],
providers: [
KavenegarService,
KavenegarSmsGateway,
ParsianSmsGateway,
SmsGatewayService,