add cron operation
This commit is contained in:
parent
bfe3d9573c
commit
d1c5ac7e19
@ -1,9 +1,10 @@
|
||||
import { NotificationService } from '@/src/module/notification/notification.service'
|
||||
import { Module } from '@nestjs/common'
|
||||
import { ScheduleModule } from '@nestjs/schedule'
|
||||
import { CronService } from './cron.service'
|
||||
|
||||
@Module({
|
||||
imports: [ScheduleModule.forRoot()],
|
||||
providers: [CronService],
|
||||
providers: [CronService, NotificationService],
|
||||
})
|
||||
export class CronModule {}
|
||||
|
||||
@ -2,6 +2,7 @@ import { PrismaService } from '@/src/core/prisma/prisma.service'
|
||||
import { MailService } from '@/src/module/libs/mail/mail.service'
|
||||
import { StorageService } from '@/src/module/libs/storage/storage.service'
|
||||
import { TelegramService } from '@/src/module/libs/telegram/telegram.service'
|
||||
import { NotificationService } from '@/src/module/notification/notification.service'
|
||||
import { Injectable } from '@nestjs/common'
|
||||
import { Cron, CronExpression } from '@nestjs/schedule'
|
||||
|
||||
@ -12,6 +13,7 @@ export class CronService {
|
||||
private readonly mailService: MailService,
|
||||
private readonly storageService: StorageService,
|
||||
private readonly telegramService: TelegramService,
|
||||
private readonly notificationService: NotificationService,
|
||||
) {
|
||||
}
|
||||
|
||||
@ -53,4 +55,72 @@ export class CronService {
|
||||
},
|
||||
})
|
||||
}
|
||||
|
||||
@Cron('0 0 */4 * *')
|
||||
public async notifyUsersEnableTwoFactor() {
|
||||
const users = await this.prismaService.user.findMany({
|
||||
where: { isTotpEnabled: false },
|
||||
include: { notificationSettings: true },
|
||||
})
|
||||
|
||||
for (const user of users) {
|
||||
if (!user) {
|
||||
continue
|
||||
}
|
||||
|
||||
await this.mailService.sendEnableTwoFactor(user.email)
|
||||
|
||||
if (user.notificationSettings?.siteNotifications) {
|
||||
await this.notificationService.createEnableTwoFactor(user.id)
|
||||
}
|
||||
|
||||
if (user.notificationSettings?.telegramNotifications && user.telegramId) {
|
||||
await this.telegramService.sendEnableTwoFactor(user.telegramId)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@Cron(CronExpression.EVERY_DAY_AT_1AM)
|
||||
public async verifyChannels() {
|
||||
const users = await this.prismaService.user.findMany({
|
||||
include: { notificationSettings: true },
|
||||
})
|
||||
|
||||
for (const user of users) {
|
||||
const followersCount = await this.prismaService.follow.count({
|
||||
where: { followingId: user.id },
|
||||
})
|
||||
|
||||
if (followersCount > 10 && !user.isVerified) {
|
||||
await this.prismaService.user.update({
|
||||
where: { id: user.id },
|
||||
data: { isVerified: true },
|
||||
})
|
||||
|
||||
await this.mailService.sendVerifyChannel(user.email)
|
||||
|
||||
if (user.notificationSettings?.siteNotifications) {
|
||||
await this.notificationService.createVerifyChannel(user.id)
|
||||
}
|
||||
|
||||
if (user.notificationSettings?.telegramNotifications && user.telegramId) {
|
||||
await this.telegramService.sendVerifyChannel(user.telegramId)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@Cron(CronExpression.EVERY_DAY_AT_1AM)
|
||||
public async deleteOldNotifications() {
|
||||
const sevenDaysAgo = new Date()
|
||||
sevenDaysAgo.setDate(sevenDaysAgo.getDate() - 7)
|
||||
|
||||
await this.prismaService.notification.deleteMany({
|
||||
where: {
|
||||
createdAt: {
|
||||
lte: sevenDaysAgo,
|
||||
},
|
||||
},
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
@ -1,14 +1,16 @@
|
||||
import { AccountDeletionTemplate } from '@/src/module/libs/mail/templates/account-deletion.template'
|
||||
import { DeactivateTemplate } from '@/src/module/libs/mail/templates/deactivate.template'
|
||||
import PasswordRecoveryTemplate from '@/src/module/libs/mail/templates/password-recovery.template'
|
||||
import { SessionInfo } from '@/src/shared/types/session-metadata.types'
|
||||
import { Token } from '@prisma/generated'
|
||||
import { ProcessEnv } from '@/src/shared/types/env'
|
||||
import VerificationTemplate from './templates/verification.template'
|
||||
import { MailerService } from '@nestjs-modules/mailer'
|
||||
import { Injectable } from '@nestjs/common'
|
||||
import { ConfigService } from '@nestjs/config'
|
||||
import { render } from '@react-email/components'
|
||||
import { EnableTwoFactorTemplate } from './templates'
|
||||
import { VerifyChannelTemplate } from './templates'
|
||||
import { VerificationTemplate } from './templates'
|
||||
import { PasswordRecoveryTemplate } from './templates'
|
||||
import { DeactivateTemplate } from './templates'
|
||||
import { AccountDeletionTemplate } from './templates'
|
||||
|
||||
@Injectable()
|
||||
export class MailService {
|
||||
@ -44,6 +46,19 @@ export class MailService {
|
||||
void this.sendMail(email, 'Удаление аккаунта', html)
|
||||
}
|
||||
|
||||
public async sendEnableTwoFactor(email: string) {
|
||||
const domain = this.configService.getOrThrow<string>('ALLOWED_ORIGIN')
|
||||
const html = await render(EnableTwoFactorTemplate({ domain }))
|
||||
|
||||
void this.sendMail(email, 'Обеспечьте свою безопасность', html)
|
||||
}
|
||||
|
||||
public async sendVerifyChannel(email: string) {
|
||||
const html = await render(VerifyChannelTemplate())
|
||||
|
||||
void this.sendMail(email, 'Ваш канал верифицирован', html)
|
||||
}
|
||||
|
||||
private sendMail(email: string, subject: string, html: string) {
|
||||
return this.mailerService.sendMail({
|
||||
to: email,
|
||||
|
||||
@ -0,0 +1,68 @@
|
||||
import {
|
||||
Body,
|
||||
Head,
|
||||
Heading,
|
||||
Html,
|
||||
Link,
|
||||
Preview,
|
||||
Section,
|
||||
Tailwind,
|
||||
Text,
|
||||
} from '@react-email/components'
|
||||
import * as React from 'react'
|
||||
|
||||
interface EnableTwoFactorTemplateProps {
|
||||
domain: string
|
||||
}
|
||||
|
||||
export function EnableTwoFactorTemplate({ domain }: EnableTwoFactorTemplateProps) {
|
||||
const settingsLink = `${domain}/dashboard/settings`
|
||||
|
||||
return (
|
||||
<Html>
|
||||
<Head />
|
||||
<Preview>Обеспечьте свою безопасность</Preview>
|
||||
<Tailwind>
|
||||
<Body className="max-w-2xl mx-auto p-6 bg-slate-50">
|
||||
<Section className="text-center mb-8">
|
||||
<Heading className="text-3xl text-black font-bold">
|
||||
Защитите свой аккаунт с двухфакторной аутентификацией
|
||||
</Heading>
|
||||
<Text className="text-black text-base mt-2">
|
||||
Включите двухфакторную аутентификацию, чтобы повысить безопасность вашего аккаунта.
|
||||
</Text>
|
||||
</Section>
|
||||
|
||||
<Section className="bg-white rounded-lg shadow-md p-6 text-center mb-6">
|
||||
<Heading className="text-2xl text-black font-semibold">
|
||||
Почему это важно?
|
||||
</Heading>
|
||||
<Text className="text-base text-black mt-2">
|
||||
Двухфакторная аутентификация добавляет дополнительный уровень защиты, требуя код, который известен только вам.
|
||||
</Text>
|
||||
<Link
|
||||
href={settingsLink}
|
||||
className="inline-flex justify-center items-center rounded-md text-sm font-medium text-white bg-[#18B9AE] px-5 py-2 rounded-full"
|
||||
>
|
||||
Перейти в настройки аккаунта
|
||||
</Link>
|
||||
</Section>
|
||||
|
||||
<Section className="text-center mt-8">
|
||||
<Text className="text-gray-600">
|
||||
Если у вас возникли вопросы, обращайтесь в службу поддержки по адресу
|
||||
{' '}
|
||||
<Link
|
||||
href="mailto:help@teastream.ru"
|
||||
className="text-[#18b9ae] underline"
|
||||
>
|
||||
help@teastream.ru
|
||||
</Link>
|
||||
.
|
||||
</Text>
|
||||
</Section>
|
||||
</Body>
|
||||
</Tailwind>
|
||||
</Html>
|
||||
)
|
||||
}
|
||||
6
backend/src/module/libs/mail/templates/index.ts
Normal file
6
backend/src/module/libs/mail/templates/index.ts
Normal file
@ -0,0 +1,6 @@
|
||||
export { default as VerificationTemplate } from './verification.template'
|
||||
export { AccountDeletionTemplate } from './account-deletion.template'
|
||||
export { DeactivateTemplate } from './deactivate.template'
|
||||
export { EnableTwoFactorTemplate } from './enable-two-factor.template'
|
||||
export { default as PasswordRecoveryTemplate } from './password-recovery.template'
|
||||
export { VerifyChannelTemplate } from './verify-channel.template'
|
||||
@ -0,0 +1,56 @@
|
||||
import {
|
||||
Body,
|
||||
Head,
|
||||
Heading,
|
||||
Html,
|
||||
Link,
|
||||
Preview,
|
||||
Section,
|
||||
Tailwind,
|
||||
Text,
|
||||
} from '@react-email/components'
|
||||
import * as React from 'react'
|
||||
|
||||
export function VerifyChannelTemplate() {
|
||||
return (
|
||||
<Html>
|
||||
<Head />
|
||||
<Preview>Ваш канал верифицирован</Preview>
|
||||
<Tailwind>
|
||||
<Body className="max-w-2xl mx-auto p-6 bg-slate-50">
|
||||
<Section className="text-center mb-8">
|
||||
<Heading className="text-3xl text-black font-bold">
|
||||
Поздравляем! Ваш канал верифицирован
|
||||
</Heading>
|
||||
<Text className="text-black text-base mt-2">
|
||||
Мы рады сообщить, что ваш канал теперь верифицирован, и вы получили официальный значок.
|
||||
</Text>
|
||||
</Section>
|
||||
|
||||
<Section className="bg-white rounded-lg shadow-md p-6 text-center mb-6">
|
||||
<Heading className="text-2xl text-black font-semibold">
|
||||
Что это значит?
|
||||
</Heading>
|
||||
<Text className="text-base text-black mt-2">
|
||||
Значок верификации подтверждает подлинность вашего канала и улучшает доверие зрителей.
|
||||
</Text>
|
||||
</Section>
|
||||
|
||||
<Section className="text-center mt-8">
|
||||
<Text className="text-gray-600">
|
||||
Если у вас есть вопросы, напишите нам на
|
||||
{' '}
|
||||
<Link
|
||||
href="mailto:help@teastream.ru"
|
||||
className="text-[#18b9ae] underline"
|
||||
>
|
||||
help@teastream.ru
|
||||
</Link>
|
||||
.
|
||||
</Text>
|
||||
</Section>
|
||||
</Body>
|
||||
</Tailwind>
|
||||
</Html>
|
||||
)
|
||||
}
|
||||
@ -62,6 +62,7 @@ export const MESSAGES = {
|
||||
`<b>📡 На канале ${channel.displayName} началась трансляция!</b>\n\n`
|
||||
+ `Смотрите здесь: <a href="https://teastream.ru/${channel.name}">Перейти к трансляции</a>`,
|
||||
newFollowing: (follower: User, followersCount: number) =>
|
||||
|
||||
`<b>У вас новый подписчик!</b>\n\nЭто пользователь <a href="https://teastream.ru/${follower.name}">${follower.displayName}</a>\n\nИтоговое количество подписчиков на вашем канале: ${followersCount}`,
|
||||
enableTwoFactor:
|
||||
`🔐 Обеспечьте свою безопасность!\n\n`
|
||||
|
||||
@ -144,6 +144,14 @@ export class TelegramService extends Telegraf {
|
||||
await this.telegram.sendMessage(chatId, MESSAGES.newFollowing(follower, user.followings.length), { parse_mode: 'HTML' })
|
||||
}
|
||||
|
||||
public async sendEnableTwoFactor(chatId: string) {
|
||||
return this.telegram.sendMessage(chatId, MESSAGES.enableTwoFactor, { parse_mode: 'HTML' })
|
||||
}
|
||||
|
||||
public async sendVerifyChannel(chatId: string) {
|
||||
return this.telegram.sendMessage(chatId, MESSAGES.verifyChannel, { parse_mode: 'HTML' })
|
||||
}
|
||||
|
||||
private async connectTelegram(userId: string, chatId: string) {
|
||||
return this.prismaService.user.update({
|
||||
where: { id: userId },
|
||||
|
||||
Loading…
x
Reference in New Issue
Block a user