Интеграция с sms.ru
This commit is contained in:
parent
053bd699f3
commit
1461d16983
|
|
@ -1,4 +1,5 @@
|
||||||
import { Module } from '@nestjs/common';
|
import { Module } from '@nestjs/common';
|
||||||
|
import { ConfigModule } from '@nestjs/config';
|
||||||
import { AppController } from './app.controller';
|
import { AppController } from './app.controller';
|
||||||
import { AppService } from './app.service';
|
import { AppService } from './app.service';
|
||||||
import { YachtsModule } from './yachts/yachts.module';
|
import { YachtsModule } from './yachts/yachts.module';
|
||||||
|
|
@ -10,7 +11,16 @@ import { ReviewsModule } from './reviews/reviews.module';
|
||||||
import { ReservationsModule } from './reservations/reservations.module';
|
import { ReservationsModule } from './reservations/reservations.module';
|
||||||
|
|
||||||
@Module({
|
@Module({
|
||||||
imports: [YachtsModule, CatalogModule, AuthModule, UsersModule, FilesModule, ReviewsModule, ReservationsModule],
|
imports: [
|
||||||
|
ConfigModule.forRoot({ isGlobal: true }),
|
||||||
|
YachtsModule,
|
||||||
|
CatalogModule,
|
||||||
|
AuthModule,
|
||||||
|
UsersModule,
|
||||||
|
FilesModule,
|
||||||
|
ReviewsModule,
|
||||||
|
ReservationsModule,
|
||||||
|
],
|
||||||
controllers: [AppController],
|
controllers: [AppController],
|
||||||
providers: [AppService],
|
providers: [AppService],
|
||||||
})
|
})
|
||||||
|
|
|
||||||
|
|
@ -1,4 +1,5 @@
|
||||||
import { Module } from '@nestjs/common';
|
import { Module } from '@nestjs/common';
|
||||||
|
import { ConfigModule } from '@nestjs/config';
|
||||||
import { AuthService } from './auth.service';
|
import { AuthService } from './auth.service';
|
||||||
import { AuthController } from './auth.controller';
|
import { AuthController } from './auth.controller';
|
||||||
import { UsersModule } from '../users/users.module';
|
import { UsersModule } from '../users/users.module';
|
||||||
|
|
@ -10,6 +11,7 @@ import { RefreshTokenStoreService } from './refresh-token-store.service';
|
||||||
|
|
||||||
@Module({
|
@Module({
|
||||||
imports: [
|
imports: [
|
||||||
|
ConfigModule,
|
||||||
UsersModule,
|
UsersModule,
|
||||||
JwtModule.register({
|
JwtModule.register({
|
||||||
secret: jwtConstants.secret,
|
secret: jwtConstants.secret,
|
||||||
|
|
|
||||||
|
|
@ -1,7 +1,26 @@
|
||||||
import { Injectable, Logger } from '@nestjs/common';
|
import { Injectable, Logger } from '@nestjs/common';
|
||||||
|
import { ConfigService } from '@nestjs/config';
|
||||||
|
|
||||||
export const SMS_SERVICE = 'SmsService';
|
export const SMS_SERVICE = 'SmsService';
|
||||||
|
|
||||||
|
const SMS_RU_SEND_URL = 'https://sms.ru/sms/send';
|
||||||
|
|
||||||
|
/** Ответ API sms.ru (фрагмент для одного номера). */
|
||||||
|
interface SmsRuSmsItem {
|
||||||
|
status: string;
|
||||||
|
status_code: number;
|
||||||
|
sms_id?: string;
|
||||||
|
status_text?: string;
|
||||||
|
}
|
||||||
|
|
||||||
|
/** Ответ API sms.ru при json=1. */
|
||||||
|
interface SmsRuResponse {
|
||||||
|
status: string;
|
||||||
|
status_code: number;
|
||||||
|
sms?: Record<string, SmsRuSmsItem>;
|
||||||
|
balance?: number;
|
||||||
|
}
|
||||||
|
|
||||||
/** Интерфейс для отправки SMS. В проде подставьте реализацию (SMS.ru, Twilio и т.д.). */
|
/** Интерфейс для отправки SMS. В проде подставьте реализацию (SMS.ru, Twilio и т.д.). */
|
||||||
export interface ISmsSender {
|
export interface ISmsSender {
|
||||||
sendVerificationCode(phone: string, code: string): Promise<void>;
|
sendVerificationCode(phone: string, code: string): Promise<void>;
|
||||||
|
|
@ -10,10 +29,67 @@ export interface ISmsSender {
|
||||||
@Injectable()
|
@Injectable()
|
||||||
export class SmsService implements ISmsSender {
|
export class SmsService implements ISmsSender {
|
||||||
private readonly logger = new Logger(SmsService.name);
|
private readonly logger = new Logger(SmsService.name);
|
||||||
|
private readonly apiId: string | undefined;
|
||||||
|
private readonly isDev: boolean;
|
||||||
|
|
||||||
|
constructor(private readonly configService: ConfigService) {
|
||||||
|
this.apiId = this.configService.get<string>('SMS_RU_API_ID');
|
||||||
|
this.isDev = this.configService.get<string>('NODE_ENV') === 'development';
|
||||||
|
if (!this.apiId && !this.isDev) {
|
||||||
|
this.logger.warn(
|
||||||
|
'SMS_RU_API_ID не задан — SMS не отправляются, код верификации только логируется',
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
async sendVerificationCode(phone: string, code: string): Promise<void> {
|
async sendVerificationCode(phone: string, code: string): Promise<void> {
|
||||||
// Заглушка: в разработке только логируем. Подключите реальный провайдер SMS.
|
if (this.isDev) {
|
||||||
this.logger.log(`[SMS] Код для ${phone}: ${code}`);
|
this.logger.log(`[dev] Код верификации для ${phone}: ${code}`);
|
||||||
// await this.smsProvider.send(phone, `Ваш код: ${code}`);
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
const msg = `Ваш код: ${code}`;
|
||||||
|
const to = this.toSmsRuPhone(phone);
|
||||||
|
|
||||||
|
if (!this.apiId) {
|
||||||
|
this.logger.log(`[SMS stub] Код для ${phone}: ${code}`);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
const url = new URL(SMS_RU_SEND_URL);
|
||||||
|
url.searchParams.set('api_id', this.apiId);
|
||||||
|
url.searchParams.set('to', to);
|
||||||
|
url.searchParams.set('msg', msg);
|
||||||
|
url.searchParams.set('json', '1');
|
||||||
|
|
||||||
|
try {
|
||||||
|
const res = await fetch(url.toString());
|
||||||
|
const data = (await res.json()) as SmsRuResponse;
|
||||||
|
|
||||||
|
if (data.status !== 'OK' || data.status_code !== 100) {
|
||||||
|
this.logger.error(`SMS.ru: ошибка запроса status=${data.status} status_code=${data.status_code}`);
|
||||||
|
throw new Error(`SMS.ru: ${data.status_code}`);
|
||||||
|
}
|
||||||
|
|
||||||
|
const item = data.sms?.[to];
|
||||||
|
if (item?.status !== 'OK') {
|
||||||
|
const text = item?.status_text ?? 'неизвестная ошибка';
|
||||||
|
this.logger.error(`SMS.ru: не удалось отправить на ${to}: ${text}`);
|
||||||
|
throw new Error(`SMS.ru: ${text}`);
|
||||||
|
}
|
||||||
|
|
||||||
|
this.logger.log(`SMS отправлен на ${phone}, код=${code}, sms_id=${item.sms_id ?? '—'}`);
|
||||||
|
} catch (err) {
|
||||||
|
this.logger.error(`Ошибка отправки SMS на ${phone}:`, err);
|
||||||
|
throw err;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/** Номер в формате sms.ru: только цифры, например 79255070602. */
|
||||||
|
private toSmsRuPhone(phone: string): string {
|
||||||
|
const digits = phone.replace(/\D/g, '');
|
||||||
|
if (digits.length === 10 && digits.startsWith('9')) return '7' + digits;
|
||||||
|
if (digits.length === 11 && digits.startsWith('7')) return digits;
|
||||||
|
return digits;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
Loading…
Reference in New Issue