Brand OTP SMS with business name and support local SMS proxy.

Append tenant Farsi name to verification SMS, allow optional domain on send-otp, and add SMS_PROXY_* for local delivery via production. Also include websites SSL sync agent/API wiring.

Co-authored-by: Cursor <cursoragent@cursor.com>
This commit is contained in:
Alireza Hassani
2026-08-08 23:29:56 +03:30
co-authored by Cursor
parent 77f3cb2a67
commit 8f05ee2b58
14 changed files with 385 additions and 14 deletions
+20 -6
View File
@@ -1,6 +1,7 @@
import {
BadRequestException,
ConflictException,
HttpException,
Injectable,
ServiceUnavailableException,
UnauthorizedException,
@@ -271,7 +272,7 @@ export class AuthService {
return { message: 'Password changed successfully' };
}
async sendOtp(cellNumber: string) {
async sendOtp(cellNumber: string, domain?: string) {
if (!this.sms.isEnabled()) {
return {
enabled: false,
@@ -288,15 +289,28 @@ export class AuthService {
throw new UnauthorizedException('Cell number is not registered');
}
let businessNameFa: string | undefined;
const host = domain?.trim();
if (host) {
try {
const business = await this.tenant.resolveBusinessByDomain(host);
businessNameFa =
business.nameFa?.trim() || business.name?.trim() || undefined;
} catch {
// Domain may be unknown — still send OTP without branding suffix
}
}
const code = this.generateOtpCode();
await this.redis.setOtp(cellNumber, code, OTP_TTL_SECONDS);
try {
await this.sms.sendVerificationCode(cellNumber, code);
} catch {
throw new ServiceUnavailableException(
'SMS provider is not configured yet',
);
await this.sms.sendVerificationCode(cellNumber, code, businessNameFa);
} catch (err) {
if (err instanceof HttpException) {
throw err;
}
throw new ServiceUnavailableException('Unable to send SMS');
}
return {