Includes mobile auth, get_user/get_version, SMS wallet parity endpoints, appearance backfill, and related Prisma migrations. Co-authored-by: Cursor <cursoragent@cursor.com>
84 lines
2.5 KiB
TypeScript
84 lines
2.5 KiB
TypeScript
import { Injectable, Logger } from '@nestjs/common';
|
|
import { ConfigService } from '@nestjs/config';
|
|
import axios from 'axios';
|
|
import { normalizeMobile } from '../common/mobile.util';
|
|
|
|
export type CentralSmsResult = {
|
|
success: boolean;
|
|
raw?: unknown;
|
|
error?: string;
|
|
};
|
|
|
|
@Injectable()
|
|
export class CentralSmsService {
|
|
private readonly logger = new Logger(CentralSmsService.name);
|
|
|
|
constructor(private readonly config: ConfigService) {}
|
|
|
|
async send(phone: string, text: string): Promise<CentralSmsResult> {
|
|
const to = normalizeMobile(phone);
|
|
const message = text.trim();
|
|
|
|
if (!to) {
|
|
return { success: false, error: 'شماره موبایل نامعتبر است' };
|
|
}
|
|
if (!message) {
|
|
return { success: false, error: 'متن پیامک خالی است' };
|
|
}
|
|
|
|
const url =
|
|
this.config.get<string>('CENTRAL_SMS_URL') ||
|
|
'http://185.231.112.101/api/createsms';
|
|
const secret = this.config.get<string>('CENTRAL_SMS_SECRET')?.trim();
|
|
if (!secret) {
|
|
this.logger.error('CENTRAL_SMS_SECRET is not configured');
|
|
return { success: false, error: 'سرویس پیامک پیکربندی نشده است' };
|
|
}
|
|
const host =
|
|
this.config.get<string>('CENTRAL_SMS_HOST') || 'superadmin';
|
|
const platform =
|
|
this.config.get<string>('CENTRAL_SMS_PLATFORM') || 'superadmin';
|
|
|
|
try {
|
|
const response = await axios.post(
|
|
url,
|
|
{
|
|
phone: to,
|
|
host,
|
|
text: message,
|
|
platform,
|
|
},
|
|
{
|
|
timeout: 15000,
|
|
headers: {
|
|
'Content-Type': 'application/json',
|
|
secret_key: secret,
|
|
},
|
|
// Prefer IPv4 like Hamkar curl CURLOPT_IPRESOLVE_V4
|
|
family: 4,
|
|
validateStatus: () => true,
|
|
},
|
|
);
|
|
|
|
const body = response.data;
|
|
let success = response.status >= 200 && response.status < 300;
|
|
|
|
if (body && typeof body === 'object') {
|
|
if (body.success === false || body.ok === false) success = false;
|
|
const status = String(body.status ?? '').toLowerCase();
|
|
if (['error', 'failed', 'fail'].includes(status)) success = false;
|
|
}
|
|
|
|
if (!success) {
|
|
this.logger.warn(`Central SMS failed to ${to} (HTTP ${response.status})`);
|
|
}
|
|
|
|
return { success, raw: body };
|
|
} catch (error: any) {
|
|
const msg = error?.message || 'SMS send error';
|
|
this.logger.error(`Central SMS exception to ${to}: ${msg}`);
|
|
return { success: false, error: msg };
|
|
}
|
|
}
|
|
}
|