Compare commits
No commits in common. "a4b9f46afd04152236ffbe15f10da6240d5e4439" and "3a10dd136b5b0769ceb23b325ada7459a881b223" have entirely different histories.
a4b9f46afd
...
3a10dd136b
@ -21,7 +21,6 @@
|
|||||||
},
|
},
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"@nestjs-modules/mailer": "^2.0.2",
|
"@nestjs-modules/mailer": "^2.0.2",
|
||||||
"@nestjs/axios": "^4.0.1",
|
|
||||||
"@nestjs/common": "^11.0.1",
|
"@nestjs/common": "^11.0.1",
|
||||||
"@nestjs/config": "^4.0.3",
|
"@nestjs/config": "^4.0.3",
|
||||||
"@nestjs/core": "^11.0.1",
|
"@nestjs/core": "^11.0.1",
|
||||||
@ -32,7 +31,6 @@
|
|||||||
"@teacinema/core": "^1.0.9",
|
"@teacinema/core": "^1.0.9",
|
||||||
"amqp-connection-manager": "^5.0.0",
|
"amqp-connection-manager": "^5.0.0",
|
||||||
"amqplib": "^0.10.9",
|
"amqplib": "^0.10.9",
|
||||||
"axios": "^1.13.5",
|
|
||||||
"handlebars": "^4.7.8",
|
"handlebars": "^4.7.8",
|
||||||
"reflect-metadata": "^0.2.2",
|
"reflect-metadata": "^0.2.2",
|
||||||
"rxjs": "^7.8.1",
|
"rxjs": "^7.8.1",
|
||||||
|
|||||||
@ -5,7 +5,6 @@ import configuration from './config/configuration'
|
|||||||
import { MailModule } from './infra/mail/mail.module'
|
import { MailModule } from './infra/mail/mail.module'
|
||||||
import { RmqModule } from './infra/rmq/rmq.module'
|
import { RmqModule } from './infra/rmq/rmq.module'
|
||||||
import { NotificationsModule } from './modules/notifications/notifications.module'
|
import { NotificationsModule } from './modules/notifications/notifications.module'
|
||||||
import { SmsModule } from './infra/sms/sms.module';
|
|
||||||
|
|
||||||
@Module({
|
@Module({
|
||||||
imports: [
|
imports: [
|
||||||
@ -16,8 +15,7 @@ import { SmsModule } from './infra/sms/sms.module';
|
|||||||
}),
|
}),
|
||||||
RmqModule,
|
RmqModule,
|
||||||
NotificationsModule,
|
NotificationsModule,
|
||||||
MailModule,
|
MailModule
|
||||||
SmsModule
|
|
||||||
]
|
]
|
||||||
})
|
})
|
||||||
export class AppModule {}
|
export class AppModule {}
|
||||||
|
|||||||
@ -25,10 +25,6 @@ export default () => {
|
|||||||
password: env.SMTP_PASSWORD,
|
password: env.SMTP_PASSWORD,
|
||||||
fromAddress: env.SMTP_FROM_ADDRESS,
|
fromAddress: env.SMTP_FROM_ADDRESS,
|
||||||
secure: env.SMTP_SECURE
|
secure: env.SMTP_SECURE
|
||||||
},
|
|
||||||
exolve: {
|
|
||||||
apiKey: env.EXOLVE_API_KEY,
|
|
||||||
sender: env.EXOLVE_SENDER
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@ -1,10 +0,0 @@
|
|||||||
import { ConfigService } from '@nestjs/config'
|
|
||||||
|
|
||||||
import { SmsOptions } from '../../infra/sms/interfaces'
|
|
||||||
|
|
||||||
export function getExolveConfig(configService: ConfigService): SmsOptions {
|
|
||||||
return {
|
|
||||||
apiKey: configService.get('exolve.apiKey') ?? '',
|
|
||||||
sender: configService.get('exolve.sender') ?? ''
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@ -1,2 +1 @@
|
|||||||
export * from './mailer.factory'
|
export * from './mailer.factory'
|
||||||
export * from './exolve.factory'
|
|
||||||
|
|||||||
@ -21,8 +21,5 @@ export default z.object({
|
|||||||
SMTP_SECURE: z
|
SMTP_SECURE: z
|
||||||
.string()
|
.string()
|
||||||
.default('false')
|
.default('false')
|
||||||
.transform(value => value === 'true'),
|
.transform(value => value === 'true')
|
||||||
// MTS Exolve
|
|
||||||
EXOLVE_API_KEY: z.string().nonempty(),
|
|
||||||
EXOLVE_SENDER: z.string().nonempty()
|
|
||||||
})
|
})
|
||||||
|
|||||||
@ -19,13 +19,4 @@ export class MailService {
|
|||||||
html
|
html
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
public async sendEmailChange(email: string, code: string) {
|
|
||||||
const html = this.templateService.render('email-change', { code })
|
|
||||||
await this.transporter.sendMail({
|
|
||||||
to: email,
|
|
||||||
subject: 'Подтверждение смены почты',
|
|
||||||
html
|
|
||||||
})
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|||||||
@ -1,14 +0,0 @@
|
|||||||
<!doctype html>
|
|
||||||
<html lang="en">
|
|
||||||
<head>
|
|
||||||
<meta charset="UTF-8">
|
|
||||||
<meta name="viewport"
|
|
||||||
content="width=device-width, user-scalable=no, initial-scale=1.0, maximum-scale=1.0, minimum-scale=1.0">
|
|
||||||
<meta http-equiv="X-UA-Compatible" content="ie=edge">
|
|
||||||
<title>Подтверждение смены почты</title>
|
|
||||||
</head>
|
|
||||||
<body>
|
|
||||||
<h1>Подтверждение смены почты</h1>
|
|
||||||
<p>Ваш код подтверждения для смены почты: {{code}}</p>
|
|
||||||
</body>
|
|
||||||
</html>
|
|
||||||
@ -1 +0,0 @@
|
|||||||
export * from './sms.constants'
|
|
||||||
@ -1 +0,0 @@
|
|||||||
export const SMS_OPTIONS = Symbol('SMS_OPTIONS');
|
|
||||||
@ -1,3 +0,0 @@
|
|||||||
export * from './sms-options.interface'
|
|
||||||
export * from './sms-async-options'
|
|
||||||
export * from './send-sms.interface'
|
|
||||||
@ -1,10 +0,0 @@
|
|||||||
export interface SendSmsRequest {
|
|
||||||
sender?: string
|
|
||||||
destination: string
|
|
||||||
text: string
|
|
||||||
}
|
|
||||||
|
|
||||||
export interface SendSmsResponse {
|
|
||||||
message_id: string
|
|
||||||
template_resource_id: string
|
|
||||||
}
|
|
||||||
@ -1,8 +0,0 @@
|
|||||||
import { FactoryProvider, ModuleMetadata } from '@nestjs/common'
|
|
||||||
|
|
||||||
import { SmsOptions } from './sms-options.interface'
|
|
||||||
|
|
||||||
export interface SmsAsyncOptions extends Pick<ModuleMetadata, 'imports'> {
|
|
||||||
useFactory: (...args: any[]) => Promise<SmsOptions> | SmsOptions
|
|
||||||
inject?: FactoryProvider['inject']
|
|
||||||
}
|
|
||||||
@ -1,4 +0,0 @@
|
|||||||
export interface SmsOptions {
|
|
||||||
apiKey: string;
|
|
||||||
sender: string;
|
|
||||||
}
|
|
||||||
@ -1,40 +0,0 @@
|
|||||||
import { HttpModule } from '@nestjs/axios'
|
|
||||||
import { DynamicModule, Module } from '@nestjs/common'
|
|
||||||
|
|
||||||
import { SMS_OPTIONS } from './constants'
|
|
||||||
import { SmsAsyncOptions, SmsOptions } from './interfaces'
|
|
||||||
import { SmsService } from './sms.service'
|
|
||||||
|
|
||||||
@Module({})
|
|
||||||
export class SmsModule {
|
|
||||||
public static register(options: SmsOptions): DynamicModule {
|
|
||||||
return {
|
|
||||||
module: SmsModule,
|
|
||||||
imports: [HttpModule],
|
|
||||||
providers: [
|
|
||||||
SmsService,
|
|
||||||
{
|
|
||||||
provide: SMS_OPTIONS,
|
|
||||||
useValue: options
|
|
||||||
}
|
|
||||||
],
|
|
||||||
exports: [SmsService]
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
public static registerAsync(options: SmsAsyncOptions): DynamicModule {
|
|
||||||
return {
|
|
||||||
module: SmsModule,
|
|
||||||
imports: [HttpModule, ...(options.imports ?? [])],
|
|
||||||
providers: [
|
|
||||||
SmsService,
|
|
||||||
{
|
|
||||||
provide: SMS_OPTIONS,
|
|
||||||
useFactory: options.useFactory,
|
|
||||||
inject: options.inject ?? []
|
|
||||||
}
|
|
||||||
],
|
|
||||||
exports: [SmsService]
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@ -1,108 +0,0 @@
|
|||||||
import { HttpService } from '@nestjs/axios'
|
|
||||||
import { Inject, Injectable, Logger } from '@nestjs/common'
|
|
||||||
import {
|
|
||||||
catchError,
|
|
||||||
delay,
|
|
||||||
firstValueFrom,
|
|
||||||
retryWhen,
|
|
||||||
scan,
|
|
||||||
throwError,
|
|
||||||
timeout
|
|
||||||
} from 'rxjs'
|
|
||||||
|
|
||||||
import { SMS_OPTIONS } from './constants'
|
|
||||||
import type { SendSmsRequest, SendSmsResponse, SmsOptions } from './interfaces'
|
|
||||||
|
|
||||||
@Injectable()
|
|
||||||
export class SmsService {
|
|
||||||
private readonly BASE_URL: string
|
|
||||||
private readonly logger = new Logger(SmsService.name)
|
|
||||||
|
|
||||||
constructor(
|
|
||||||
private readonly httpService: HttpService,
|
|
||||||
@Inject(SMS_OPTIONS) private readonly options: SmsOptions
|
|
||||||
) {
|
|
||||||
this.BASE_URL = 'https://api.exolve.ru'
|
|
||||||
}
|
|
||||||
|
|
||||||
public async sendOtp(phone: string, code: string) {
|
|
||||||
return this.send({
|
|
||||||
destination: phone,
|
|
||||||
text: `Ваш код подтверждения: ${code}`
|
|
||||||
})
|
|
||||||
}
|
|
||||||
|
|
||||||
public async sendPhoneChanged(phone: string, code: string) {
|
|
||||||
return this.send({
|
|
||||||
destination: phone,
|
|
||||||
text: `Ваш код подтвердления для изменения номера телефона: ${code}`
|
|
||||||
})
|
|
||||||
}
|
|
||||||
|
|
||||||
public send(data: SendSmsRequest): Promise<SendSmsResponse> {
|
|
||||||
const payload = {
|
|
||||||
number: data.sender ?? this.options.sender,
|
|
||||||
destination: data.destination.replace('+', ''),
|
|
||||||
text: data.text
|
|
||||||
}
|
|
||||||
|
|
||||||
return this.request<SendSmsResponse>(
|
|
||||||
'POST',
|
|
||||||
'/messaging/v1/SendSMS',
|
|
||||||
payload
|
|
||||||
)
|
|
||||||
}
|
|
||||||
|
|
||||||
private async request<T>(
|
|
||||||
method: 'GET' | 'POST',
|
|
||||||
path: string,
|
|
||||||
body: any
|
|
||||||
): Promise<T> {
|
|
||||||
const url = `${this.BASE_URL}${path}`
|
|
||||||
|
|
||||||
try {
|
|
||||||
const request = this.httpService
|
|
||||||
.request<T>({
|
|
||||||
url,
|
|
||||||
method,
|
|
||||||
data: body,
|
|
||||||
headers: {
|
|
||||||
'Content-Type': 'application/json',
|
|
||||||
Authorization: `Bearer ${this.options.apiKey}`
|
|
||||||
}
|
|
||||||
})
|
|
||||||
.pipe(
|
|
||||||
timeout(7000),
|
|
||||||
retryWhen(errors =>
|
|
||||||
errors.pipe(
|
|
||||||
scan((retryCount, error) => {
|
|
||||||
if (retryCount >= 2) {
|
|
||||||
throw error
|
|
||||||
}
|
|
||||||
|
|
||||||
this.logger.warn(
|
|
||||||
`Retry request (${method} ${path}): ${retryCount + 1}/3`
|
|
||||||
)
|
|
||||||
|
|
||||||
return retryCount + 1
|
|
||||||
}, 0),
|
|
||||||
delay(500)
|
|
||||||
)
|
|
||||||
),
|
|
||||||
catchError(error => {
|
|
||||||
const detail = error.response?.data ?? error.message ?? error
|
|
||||||
this.logger.error(
|
|
||||||
`Exolve API error (${method} ${path})\n${JSON.stringify(detail)}`
|
|
||||||
)
|
|
||||||
return throwError(() => error)
|
|
||||||
})
|
|
||||||
)
|
|
||||||
|
|
||||||
const response = await firstValueFrom(request)
|
|
||||||
return response.data
|
|
||||||
} catch (e) {
|
|
||||||
this.logger.error(`Request failed (${method} ${path})\n${e.message}`)
|
|
||||||
throw e
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@ -1,10 +1,6 @@
|
|||||||
import { Controller, Logger } from '@nestjs/common'
|
import { Controller } from '@nestjs/common'
|
||||||
import { Ctx, EventPattern, Payload, RmqContext } from '@nestjs/microservices'
|
import { Ctx, EventPattern, Payload, RmqContext } from '@nestjs/microservices'
|
||||||
import type {
|
import type { OtpRequestedEvent } from '@teacinema/contracts'
|
||||||
EmailChangedEvent,
|
|
||||||
OtpRequestedEvent,
|
|
||||||
PhoneChangedEvent
|
|
||||||
} from '@teacinema/contracts'
|
|
||||||
|
|
||||||
import { RmqService } from '../../infra/rmq/rmq.service'
|
import { RmqService } from '../../infra/rmq/rmq.service'
|
||||||
|
|
||||||
@ -12,8 +8,6 @@ import { NotificationsService } from './notifications.service'
|
|||||||
|
|
||||||
@Controller()
|
@Controller()
|
||||||
export class NotificationsController {
|
export class NotificationsController {
|
||||||
private readonly logger = new Logger(NotificationsController.name)
|
|
||||||
|
|
||||||
constructor(
|
constructor(
|
||||||
private readonly notificationsService: NotificationsService,
|
private readonly notificationsService: NotificationsService,
|
||||||
private readonly rmqService: RmqService
|
private readonly rmqService: RmqService
|
||||||
@ -28,35 +22,7 @@ export class NotificationsController {
|
|||||||
await this.notificationsService.sendOtp(data)
|
await this.notificationsService.sendOtp(data)
|
||||||
this.rmqService.ack(ctx)
|
this.rmqService.ack(ctx)
|
||||||
} catch (e) {
|
} catch (e) {
|
||||||
this.logger.error(`OTP processing error ${e.message ?? e}}`)
|
console.log('OTP processing error', e.message ?? e)
|
||||||
this.rmqService.nack(ctx)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
@EventPattern('account.phone.changed')
|
|
||||||
public async phoneChangedRequested(
|
|
||||||
@Payload() data: PhoneChangedEvent,
|
|
||||||
@Ctx() ctx: RmqContext
|
|
||||||
) {
|
|
||||||
try {
|
|
||||||
await this.notificationsService.phoneChanged(data)
|
|
||||||
this.rmqService.ack(ctx)
|
|
||||||
} catch (e) {
|
|
||||||
this.logger.error(`Phone changing processing error ${e.message ?? e}}`)
|
|
||||||
this.rmqService.nack(ctx)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
@EventPattern('account.email.changed')
|
|
||||||
public async emaileChangedRequested(
|
|
||||||
@Payload() data: EmailChangedEvent,
|
|
||||||
@Ctx() ctx: RmqContext
|
|
||||||
) {
|
|
||||||
try {
|
|
||||||
await this.notificationsService.emailChanged(data)
|
|
||||||
this.rmqService.ack(ctx)
|
|
||||||
} catch (e) {
|
|
||||||
this.logger.error(`Email changing processing error ${e.message ?? e}}`)
|
|
||||||
this.rmqService.nack(ctx)
|
this.rmqService.nack(ctx)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@ -1,22 +1,13 @@
|
|||||||
import { Module } from '@nestjs/common'
|
import { Module } from '@nestjs/common'
|
||||||
import { ConfigService } from '@nestjs/config'
|
|
||||||
|
|
||||||
import { getExolveConfig } from '../../config/factories'
|
|
||||||
import { MailModule } from '../../infra/mail/mail.module'
|
import { MailModule } from '../../infra/mail/mail.module'
|
||||||
import { RmqService } from '../../infra/rmq/rmq.service'
|
import { RmqService } from '../../infra/rmq/rmq.service'
|
||||||
import { SmsModule } from '../../infra/sms/sms.module'
|
|
||||||
|
|
||||||
import { NotificationsController } from './notifications.controller'
|
import { NotificationsController } from './notifications.controller'
|
||||||
import { NotificationsService } from './notifications.service'
|
import { NotificationsService } from './notifications.service'
|
||||||
|
|
||||||
@Module({
|
@Module({
|
||||||
imports: [
|
imports: [MailModule],
|
||||||
MailModule,
|
|
||||||
SmsModule.registerAsync({
|
|
||||||
useFactory: getExolveConfig,
|
|
||||||
inject: [ConfigService]
|
|
||||||
})
|
|
||||||
],
|
|
||||||
controllers: [NotificationsController],
|
controllers: [NotificationsController],
|
||||||
providers: [NotificationsService, RmqService]
|
providers: [NotificationsService, RmqService]
|
||||||
})
|
})
|
||||||
|
|||||||
@ -1,51 +1,20 @@
|
|||||||
import { Injectable, Logger } from '@nestjs/common'
|
import { Injectable } from '@nestjs/common'
|
||||||
import {
|
import { OtpRequestedEvent } from '@teacinema/contracts'
|
||||||
EmailChangedEvent,
|
|
||||||
OtpRequestedEvent,
|
|
||||||
PhoneChangedEvent
|
|
||||||
} from '@teacinema/contracts'
|
|
||||||
|
|
||||||
import { MailService } from '../../infra/mail/mail.service'
|
import { MailService } from '../../infra/mail/mail.service'
|
||||||
import { SmsService } from '../../infra/sms/sms.service'
|
|
||||||
|
|
||||||
@Injectable()
|
@Injectable()
|
||||||
export class NotificationsService {
|
export class NotificationsService {
|
||||||
private readonly logger = new Logger(NotificationsService.name)
|
constructor(private readonly mailService: MailService) {}
|
||||||
|
|
||||||
constructor(
|
|
||||||
private readonly mailService: MailService,
|
|
||||||
private readonly smsService: SmsService
|
|
||||||
) {}
|
|
||||||
|
|
||||||
public async sendOtp(data: OtpRequestedEvent) {
|
public async sendOtp(data: OtpRequestedEvent) {
|
||||||
const { identifier, type, code } = data
|
const { identifier, type, code } = data
|
||||||
|
|
||||||
this.logger.log(`Send OTP to ${identifier} via ${type}`)
|
if (type === 'email') {
|
||||||
|
console.log('SEND EMAIL', data)
|
||||||
switch (type) {
|
await this.mailService.sendOtp(identifier, code)
|
||||||
case 'email':
|
} else {
|
||||||
await this.mailService.sendOtp(identifier, code)
|
console.log('SMS')
|
||||||
break
|
|
||||||
case 'phone':
|
|
||||||
await this.smsService.sendOtp(identifier, code)
|
|
||||||
break
|
|
||||||
default:
|
|
||||||
this.logger.error(`Unknown notification type: ${type}`)
|
|
||||||
throw new Error(`Unknown type: ${type}`)
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
public async phoneChanged(data: PhoneChangedEvent) {
|
|
||||||
const { phone, code } = data
|
|
||||||
|
|
||||||
this.logger.log(`Change phone to ${phone} via phone`)
|
|
||||||
return await this.smsService.sendPhoneChanged(phone, code)
|
|
||||||
}
|
|
||||||
|
|
||||||
public async emailChanged(data: EmailChangedEvent) {
|
|
||||||
const { email, code } = data
|
|
||||||
|
|
||||||
this.logger.log(`Change email to ${email} via email`)
|
|
||||||
return await this.mailService.sendEmailChange(email, code)
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|||||||
30
yarn.lock
30
yarn.lock
@ -1025,11 +1025,6 @@
|
|||||||
preview-email "^3.0.19"
|
preview-email "^3.0.19"
|
||||||
pug "^3.0.2"
|
pug "^3.0.2"
|
||||||
|
|
||||||
"@nestjs/axios@^4.0.1":
|
|
||||||
version "4.0.1"
|
|
||||||
resolved "https://registry.yarnpkg.com/@nestjs/axios/-/axios-4.0.1.tgz#7ff73f47727b67dc04410ac6e9b3329401ebbb65"
|
|
||||||
integrity sha512-68pFJgu+/AZbWkGu65Z3r55bTsCPlgyKaV4BSG8yUAD72q1PPuyVRgUwFv6BxdnibTUHlyxm06FmYWNC+bjN7A==
|
|
||||||
|
|
||||||
"@nestjs/cli@^11.0.0":
|
"@nestjs/cli@^11.0.0":
|
||||||
version "11.0.16"
|
version "11.0.16"
|
||||||
resolved "https://registry.yarnpkg.com/@nestjs/cli/-/cli-11.0.16.tgz#bd92b679638b5b93d0db6b9252d089412dc4e9f4"
|
resolved "https://registry.yarnpkg.com/@nestjs/cli/-/cli-11.0.16.tgz#bd92b679638b5b93d0db6b9252d089412dc4e9f4"
|
||||||
@ -1190,9 +1185,9 @@
|
|||||||
"@sinonjs/commons" "^3.0.1"
|
"@sinonjs/commons" "^3.0.1"
|
||||||
|
|
||||||
"@teacinema/contracts@^1.1.0":
|
"@teacinema/contracts@^1.1.0":
|
||||||
version "1.1.1"
|
version "1.1.0"
|
||||||
resolved "https://git.ksv741.keenetic.pro/api/packages/teacinema/npm/%40teacinema%2Fcontracts/-/1.1.1/contracts-1.1.1.tgz#69fc54e0e096cad5ecc5d8bb654d982b6c70996b"
|
resolved "https://git.ksv741.keenetic.pro/api/packages/teacinema/npm/%40teacinema%2Fcontracts/-/1.1.0/contracts-1.1.0.tgz#0d2f03f283dc9ee048eeb770152127f2037b7133"
|
||||||
integrity sha512-yK3Th430sWumcnm2/jyEx8dtaA59hmCvCAnul+q/JbW+C2s/AyhBEBoxToDVxx/KKTDixDePJ3O3EnNnIHGgRw==
|
integrity sha512-x90R4R96AgrUDzOjUT7ePtc7phG3Gx61sI8/CZ1hETF3Ok/TWTHblxX/CIZVcOormBrxEh1FfU39P8/nfno91g==
|
||||||
dependencies:
|
dependencies:
|
||||||
"@nestjs/microservices" "^11.1.12"
|
"@nestjs/microservices" "^11.1.12"
|
||||||
protoc "33.4.0"
|
protoc "33.4.0"
|
||||||
@ -2045,15 +2040,6 @@ asynckit@^0.4.0:
|
|||||||
resolved "https://registry.yarnpkg.com/asynckit/-/asynckit-0.4.0.tgz#c79ed97f7f34cb8f2ba1bc9790bcc366474b4b79"
|
resolved "https://registry.yarnpkg.com/asynckit/-/asynckit-0.4.0.tgz#c79ed97f7f34cb8f2ba1bc9790bcc366474b4b79"
|
||||||
integrity sha512-Oei9OH4tRh0YqU3GxhX79dM/mwVgvbZJaSNaRk+bshkj0S5cfHcgYakreBjrHwatXKbz+IoIdYLxrKim2MjW0Q==
|
integrity sha512-Oei9OH4tRh0YqU3GxhX79dM/mwVgvbZJaSNaRk+bshkj0S5cfHcgYakreBjrHwatXKbz+IoIdYLxrKim2MjW0Q==
|
||||||
|
|
||||||
axios@^1.13.5:
|
|
||||||
version "1.13.5"
|
|
||||||
resolved "https://registry.yarnpkg.com/axios/-/axios-1.13.5.tgz#5e464688fa127e11a660a2c49441c009f6567a43"
|
|
||||||
integrity sha512-cz4ur7Vb0xS4/KUN0tPWe44eqxrIu31me+fbang3ijiNscE129POzipJJA6zniq2C/Z6sJCjMimjS8Lc/GAs8Q==
|
|
||||||
dependencies:
|
|
||||||
follow-redirects "^1.15.11"
|
|
||||||
form-data "^4.0.5"
|
|
||||||
proxy-from-env "^1.1.0"
|
|
||||||
|
|
||||||
babel-jest@30.2.0:
|
babel-jest@30.2.0:
|
||||||
version "30.2.0"
|
version "30.2.0"
|
||||||
resolved "https://registry.yarnpkg.com/babel-jest/-/babel-jest-30.2.0.tgz#fd44a1ec9552be35ead881f7381faa7d8f3b95ac"
|
resolved "https://registry.yarnpkg.com/babel-jest/-/babel-jest-30.2.0.tgz#fd44a1ec9552be35ead881f7381faa7d8f3b95ac"
|
||||||
@ -3339,11 +3325,6 @@ flatted@^3.2.9:
|
|||||||
resolved "https://registry.yarnpkg.com/flatted/-/flatted-3.3.3.tgz#67c8fad95454a7c7abebf74bb78ee74a44023358"
|
resolved "https://registry.yarnpkg.com/flatted/-/flatted-3.3.3.tgz#67c8fad95454a7c7abebf74bb78ee74a44023358"
|
||||||
integrity sha512-GX+ysw4PBCz0PzosHDepZGANEuFCMLrnRTiEy9McGjmkCQYwRq4A/X786G/fjM/+OjsWSU1ZrY5qyARZmO/uwg==
|
integrity sha512-GX+ysw4PBCz0PzosHDepZGANEuFCMLrnRTiEy9McGjmkCQYwRq4A/X786G/fjM/+OjsWSU1ZrY5qyARZmO/uwg==
|
||||||
|
|
||||||
follow-redirects@^1.15.11:
|
|
||||||
version "1.15.11"
|
|
||||||
resolved "https://registry.yarnpkg.com/follow-redirects/-/follow-redirects-1.15.11.tgz#777d73d72a92f8ec4d2e410eb47352a56b8e8340"
|
|
||||||
integrity sha512-deG2P0JfjrTxl50XGCDyfI97ZGVCxIpfKYmfyrQ54n5FO/0gfIES8C/Psl6kWVDolizcaaxZJnTS0QSMxvnsBQ==
|
|
||||||
|
|
||||||
foreground-child@^3.1.0:
|
foreground-child@^3.1.0:
|
||||||
version "3.3.1"
|
version "3.3.1"
|
||||||
resolved "https://registry.yarnpkg.com/foreground-child/-/foreground-child-3.3.1.tgz#32e8e9ed1b68a3497befb9ac2b6adf92a638576f"
|
resolved "https://registry.yarnpkg.com/foreground-child/-/foreground-child-3.3.1.tgz#32e8e9ed1b68a3497befb9ac2b6adf92a638576f"
|
||||||
@ -5526,11 +5507,6 @@ proxy-addr@^2.0.7:
|
|||||||
forwarded "0.2.0"
|
forwarded "0.2.0"
|
||||||
ipaddr.js "1.9.1"
|
ipaddr.js "1.9.1"
|
||||||
|
|
||||||
proxy-from-env@^1.1.0:
|
|
||||||
version "1.1.0"
|
|
||||||
resolved "https://registry.yarnpkg.com/proxy-from-env/-/proxy-from-env-1.1.0.tgz#e102f16ca355424865755d2c9e8ea4f24d58c3e2"
|
|
||||||
integrity sha512-D+zkORCbA9f1tdWRK0RaCR3GPv50cMxcrz4X8k5LTSUD1Dkw47mKJEZQNunItRTkWwgtaUSo1RVFRIG9ZXiFYg==
|
|
||||||
|
|
||||||
pug-attrs@^3.0.0:
|
pug-attrs@^3.0.0:
|
||||||
version "3.0.0"
|
version "3.0.0"
|
||||||
resolved "https://registry.yarnpkg.com/pug-attrs/-/pug-attrs-3.0.0.tgz#b10451e0348165e31fad1cc23ebddd9dc7347c41"
|
resolved "https://registry.yarnpkg.com/pug-attrs/-/pug-attrs-3.0.0.tgz#b10451e0348165e31fad1cc23ebddd9dc7347c41"
|
||||||
|
|||||||
Loading…
x
Reference in New Issue
Block a user