add delete account cron
This commit is contained in:
parent
706387b9ad
commit
04b4761aee
@ -31,6 +31,7 @@
|
|||||||
"@nestjs/graphql": "^13.1.0",
|
"@nestjs/graphql": "^13.1.0",
|
||||||
"@nestjs/mapped-types": "*",
|
"@nestjs/mapped-types": "*",
|
||||||
"@nestjs/platform-express": "^11.0.1",
|
"@nestjs/platform-express": "^11.0.1",
|
||||||
|
"@nestjs/schedule": "^6.0.0",
|
||||||
"@prisma/client": "^6.10.1",
|
"@prisma/client": "^6.10.1",
|
||||||
"@react-email/components": "^0.1.1",
|
"@react-email/components": "^0.1.1",
|
||||||
"@react-email/html": "^0.0.11",
|
"@react-email/html": "^0.0.11",
|
||||||
|
|||||||
@ -1,10 +1,11 @@
|
|||||||
import { getGraphQLConfig } from '@/src/core/config/graphql.config'
|
import { getGraphQLConfig } from '@/src/core/config/graphql.config'
|
||||||
import { AccountModule } from '@/src/module/auth/account/account.module'
|
import { AccountModule } from '@/src/module/auth/account/account.module'
|
||||||
import { DeactivateModule } from '@/src/module/auth/deactivate/deactivate.module';
|
import { DeactivateModule } from '@/src/module/auth/deactivate/deactivate.module'
|
||||||
import { PasswordRecoveryModule } from '@/src/module/auth/password-recovery/password-recovery.module'
|
import { PasswordRecoveryModule } from '@/src/module/auth/password-recovery/password-recovery.module'
|
||||||
import { SessionModule } from '@/src/module/auth/session/session.module'
|
import { SessionModule } from '@/src/module/auth/session/session.module'
|
||||||
import { TotpModule } from '@/src/module/auth/totp/totp.module'
|
import { TotpModule } from '@/src/module/auth/totp/totp.module'
|
||||||
import { VerificationModule } from '@/src/module/auth/verification/verification.module'
|
import { VerificationModule } from '@/src/module/auth/verification/verification.module'
|
||||||
|
import { CronModule } from '@/src/module/cron/cron.module'
|
||||||
import { MailModule } from '@/src/module/libs/mail/mail.module'
|
import { MailModule } from '@/src/module/libs/mail/mail.module'
|
||||||
import { IS_DEV } from '@/src/shared/util/is-dev.util'
|
import { IS_DEV } from '@/src/shared/util/is-dev.util'
|
||||||
import { ApolloDriver } from '@nestjs/apollo'
|
import { ApolloDriver } from '@nestjs/apollo'
|
||||||
@ -29,6 +30,7 @@ import { RedisModule } from './redis/redis.module'
|
|||||||
PrismaModule,
|
PrismaModule,
|
||||||
RedisModule,
|
RedisModule,
|
||||||
MailModule,
|
MailModule,
|
||||||
|
CronModule,
|
||||||
AccountModule,
|
AccountModule,
|
||||||
SessionModule,
|
SessionModule,
|
||||||
VerificationModule,
|
VerificationModule,
|
||||||
|
|||||||
9
backend/src/module/cron/cron.module.ts
Normal file
9
backend/src/module/cron/cron.module.ts
Normal file
@ -0,0 +1,9 @@
|
|||||||
|
import { Module } from '@nestjs/common'
|
||||||
|
import { ScheduleModule } from '@nestjs/schedule'
|
||||||
|
import { CronService } from './cron.service'
|
||||||
|
|
||||||
|
@Module({
|
||||||
|
imports: [ScheduleModule.forRoot()],
|
||||||
|
providers: [CronService],
|
||||||
|
})
|
||||||
|
export class CronModule {}
|
||||||
42
backend/src/module/cron/cron.service.ts
Normal file
42
backend/src/module/cron/cron.service.ts
Normal file
@ -0,0 +1,42 @@
|
|||||||
|
import { PrismaService } from '@/src/core/prisma/prisma.service'
|
||||||
|
import { MailService } from '@/src/module/libs/mail/mail.service'
|
||||||
|
import { Injectable } from '@nestjs/common'
|
||||||
|
import { Cron, CronExpression } from '@nestjs/schedule'
|
||||||
|
|
||||||
|
@Injectable()
|
||||||
|
export class CronService {
|
||||||
|
constructor(
|
||||||
|
private readonly prismaService: PrismaService,
|
||||||
|
private readonly mailService: MailService,
|
||||||
|
) {
|
||||||
|
}
|
||||||
|
|
||||||
|
@Cron(CronExpression.EVERY_DAY_AT_NOON)
|
||||||
|
private async deleteDeactivatedAccounts() {
|
||||||
|
const sevenDayAgo = new Date()
|
||||||
|
sevenDayAgo.setDate(sevenDayAgo.getDay() - 7)
|
||||||
|
|
||||||
|
const deactivatedAccounts = await this.prismaService.user.findMany({
|
||||||
|
where: {
|
||||||
|
isDeactivated: true,
|
||||||
|
deactivatedAt: {
|
||||||
|
lte: sevenDayAgo,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
})
|
||||||
|
|
||||||
|
for (const user of deactivatedAccounts) {
|
||||||
|
console.log('Deactivate user', user.name, user.email)
|
||||||
|
await this.mailService.sendAccountDeletion(user.email)
|
||||||
|
}
|
||||||
|
|
||||||
|
await this.prismaService.user.deleteMany({
|
||||||
|
where: {
|
||||||
|
isDeactivated: true,
|
||||||
|
deactivatedAt: {
|
||||||
|
lte: sevenDayAgo,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
})
|
||||||
|
}
|
||||||
|
}
|
||||||
@ -1,3 +1,4 @@
|
|||||||
|
import { AccountDeletionTemplate } from '@/src/module/libs/mail/templates/account-deletion.template'
|
||||||
import { DeactivateTemplate } from '@/src/module/libs/mail/templates/deactivate.template'
|
import { DeactivateTemplate } from '@/src/module/libs/mail/templates/deactivate.template'
|
||||||
import PasswordRecoveryTemplate from '@/src/module/libs/mail/templates/password-recovery.template'
|
import PasswordRecoveryTemplate from '@/src/module/libs/mail/templates/password-recovery.template'
|
||||||
import { SessionInfo } from '@/src/shared/types/session-metadata.types'
|
import { SessionInfo } from '@/src/shared/types/session-metadata.types'
|
||||||
@ -35,6 +36,13 @@ export class MailService {
|
|||||||
void this.sendMail(email, 'Деактивация аккаунта', html)
|
void this.sendMail(email, 'Деактивация аккаунта', html)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public async sendAccountDeletion(email: string) {
|
||||||
|
const domain = this.configService.getOrThrow<string>('ALLOWED_ORIGIN')
|
||||||
|
const html = await render(AccountDeletionTemplate({ domain }))
|
||||||
|
|
||||||
|
void this.sendMail(email, 'Удаление аккаунта', html)
|
||||||
|
}
|
||||||
|
|
||||||
private sendMail(email: string, subject: string, html: string) {
|
private sendMail(email: string, subject: string, html: string) {
|
||||||
return this.mailerService.sendMail({
|
return this.mailerService.sendMail({
|
||||||
to: email,
|
to: email,
|
||||||
|
|||||||
@ -0,0 +1,60 @@
|
|||||||
|
import {
|
||||||
|
Body,
|
||||||
|
Head,
|
||||||
|
Heading,
|
||||||
|
Html,
|
||||||
|
Link,
|
||||||
|
Preview,
|
||||||
|
Section,
|
||||||
|
Tailwind,
|
||||||
|
Text,
|
||||||
|
} from '@react-email/components'
|
||||||
|
import * as React from 'react'
|
||||||
|
|
||||||
|
interface AccountDeletionTemplateProps {
|
||||||
|
domain: string
|
||||||
|
}
|
||||||
|
|
||||||
|
export function AccountDeletionTemplate({ domain }: AccountDeletionTemplateProps) {
|
||||||
|
const registerLink = `${domain}/account/create`
|
||||||
|
|
||||||
|
return (
|
||||||
|
<Html>
|
||||||
|
<Head />
|
||||||
|
<Preview>Аккаунт удалён</Preview>
|
||||||
|
<Tailwind>
|
||||||
|
<Body className="max-w-2xl mx-auto p-6 bg-slate-50">
|
||||||
|
<Section className="text-center">
|
||||||
|
<Heading className="text-3xl text-black font-bold">
|
||||||
|
Ваш аккаунт был полностью удалён
|
||||||
|
</Heading>
|
||||||
|
<Text className="text-base text-black mt-2">
|
||||||
|
Ваш аккаунт был полностью стерт из базы данных TeaStream. Все ваши данные и информация были удалены безвозвратно.
|
||||||
|
</Text>
|
||||||
|
</Section>
|
||||||
|
|
||||||
|
<Section className="bg-white text-black text-center rounded-lg shadow-md p-6 mb-4">
|
||||||
|
<Text>
|
||||||
|
Вы больше не будете получать уведомления в Telegram и на почту.
|
||||||
|
</Text>
|
||||||
|
<Text>
|
||||||
|
Если вы захотите вернуться на платформу, вы можете зарегистрироваться по следующей ссылке:
|
||||||
|
</Text>
|
||||||
|
<Link
|
||||||
|
href={registerLink}
|
||||||
|
className="inline-flex justify-center items-center rounded-md mt-2 text-sm font-medium text-white bg-[#18B9AE] px-5 py-2 rounded-full"
|
||||||
|
>
|
||||||
|
Зарегистрироваться на Teastream
|
||||||
|
</Link>
|
||||||
|
</Section>
|
||||||
|
|
||||||
|
<Section className="text-center text-black">
|
||||||
|
<Text>
|
||||||
|
Спасибо, что были с нами! Мы всегда будем рады видеть вас на платформе.
|
||||||
|
</Text>
|
||||||
|
</Section>
|
||||||
|
</Body>
|
||||||
|
</Tailwind>
|
||||||
|
</Html>
|
||||||
|
)
|
||||||
|
}
|
||||||
@ -1408,6 +1408,13 @@
|
|||||||
path-to-regexp "8.2.0"
|
path-to-regexp "8.2.0"
|
||||||
tslib "2.8.1"
|
tslib "2.8.1"
|
||||||
|
|
||||||
|
"@nestjs/schedule@^6.0.0":
|
||||||
|
version "6.0.0"
|
||||||
|
resolved "https://registry.yarnpkg.com/@nestjs/schedule/-/schedule-6.0.0.tgz#c8069d1f19742e418a617af76311ab853cb48f0a"
|
||||||
|
integrity sha512-aQySMw6tw2nhitELXd3EiRacQRgzUKD9mFcUZVOJ7jPLqIBvXOyvRWLsK9SdurGA+jjziAlMef7iB5ZEFFoQpw==
|
||||||
|
dependencies:
|
||||||
|
cron "4.3.0"
|
||||||
|
|
||||||
"@nestjs/schematics@^11.0.0", "@nestjs/schematics@^11.0.1":
|
"@nestjs/schematics@^11.0.0", "@nestjs/schematics@^11.0.1":
|
||||||
version "11.0.5"
|
version "11.0.5"
|
||||||
resolved "https://registry.yarnpkg.com/@nestjs/schematics/-/schematics-11.0.5.tgz#cee2fb26f3273fb3874398aad3006517e6b802f9"
|
resolved "https://registry.yarnpkg.com/@nestjs/schematics/-/schematics-11.0.5.tgz#cee2fb26f3273fb3874398aad3006517e6b802f9"
|
||||||
@ -2096,6 +2103,11 @@
|
|||||||
resolved "https://registry.yarnpkg.com/@types/long/-/long-4.0.2.tgz#b74129719fc8d11c01868010082d483b7545591a"
|
resolved "https://registry.yarnpkg.com/@types/long/-/long-4.0.2.tgz#b74129719fc8d11c01868010082d483b7545591a"
|
||||||
integrity sha512-MqTGEo5bj5t157U6fA/BiDynNkn0YknVdh48CMPkTSpFTVmvao5UQmm7uEF6xBEo7qIMAlY/JSleYaE6VOdpaA==
|
integrity sha512-MqTGEo5bj5t157U6fA/BiDynNkn0YknVdh48CMPkTSpFTVmvao5UQmm7uEF6xBEo7qIMAlY/JSleYaE6VOdpaA==
|
||||||
|
|
||||||
|
"@types/luxon@~3.6.0":
|
||||||
|
version "3.6.2"
|
||||||
|
resolved "https://registry.yarnpkg.com/@types/luxon/-/luxon-3.6.2.tgz#be6536931801f437eafcb9c0f6d6781f72308041"
|
||||||
|
integrity sha512-R/BdP7OxEMc44l2Ex5lSXHoIXTB2JLNa3y2QISIbr58U/YcsffyQrYW//hZSdrfxrjRZj3GcUoxMPGdO8gSYuw==
|
||||||
|
|
||||||
"@types/methods@^1.1.4":
|
"@types/methods@^1.1.4":
|
||||||
version "1.1.4"
|
version "1.1.4"
|
||||||
resolved "https://registry.yarnpkg.com/@types/methods/-/methods-1.1.4.tgz#d3b7ac30ac47c91054ea951ce9eed07b1051e547"
|
resolved "https://registry.yarnpkg.com/@types/methods/-/methods-1.1.4.tgz#d3b7ac30ac47c91054ea951ce9eed07b1051e547"
|
||||||
@ -3479,6 +3491,14 @@ create-require@^1.1.0:
|
|||||||
resolved "https://registry.yarnpkg.com/create-require/-/create-require-1.1.1.tgz#c1d7e8f1e5f6cfc9ff65f9cd352d37348756c333"
|
resolved "https://registry.yarnpkg.com/create-require/-/create-require-1.1.1.tgz#c1d7e8f1e5f6cfc9ff65f9cd352d37348756c333"
|
||||||
integrity sha512-dcKFX3jn0MpIaXjisoRvexIJVEKzaq7z2rZKxf+MSr9TkdmHmsU4m2lcLojrj/FHl8mk5VxMmYA+ftRkP/3oKQ==
|
integrity sha512-dcKFX3jn0MpIaXjisoRvexIJVEKzaq7z2rZKxf+MSr9TkdmHmsU4m2lcLojrj/FHl8mk5VxMmYA+ftRkP/3oKQ==
|
||||||
|
|
||||||
|
cron@4.3.0:
|
||||||
|
version "4.3.0"
|
||||||
|
resolved "https://registry.yarnpkg.com/cron/-/cron-4.3.0.tgz#c5a62872f74f72294cf1cadef34c72ad8d8f50b5"
|
||||||
|
integrity sha512-ciiYNLfSlF9MrDqnbMdRWFiA6oizSF7kA1osPP9lRzNu0Uu+AWog1UKy7SkckiDY2irrNjeO6qLyKnXC8oxmrw==
|
||||||
|
dependencies:
|
||||||
|
"@types/luxon" "~3.6.0"
|
||||||
|
luxon "~3.6.0"
|
||||||
|
|
||||||
cross-inspect@1.0.1:
|
cross-inspect@1.0.1:
|
||||||
version "1.0.1"
|
version "1.0.1"
|
||||||
resolved "https://registry.yarnpkg.com/cross-inspect/-/cross-inspect-1.0.1.tgz#15f6f65e4ca963cf4cc1a2b5fef18f6ca328712b"
|
resolved "https://registry.yarnpkg.com/cross-inspect/-/cross-inspect-1.0.1.tgz#15f6f65e4ca963cf4cc1a2b5fef18f6ca328712b"
|
||||||
@ -5861,6 +5881,11 @@ lru-cache@^7.10.1, lru-cache@^7.14.1:
|
|||||||
resolved "https://registry.yarnpkg.com/lru-cache/-/lru-cache-7.18.3.tgz#f793896e0fd0e954a59dfdd82f0773808df6aa89"
|
resolved "https://registry.yarnpkg.com/lru-cache/-/lru-cache-7.18.3.tgz#f793896e0fd0e954a59dfdd82f0773808df6aa89"
|
||||||
integrity sha512-jumlc0BIUrS3qJGgIkWZsyfAM7NCWiBcCDhnd+3NNM5KbBmLTgHVfWBcg6W+rLUsIpzpERPsvwUP7CckAQSOoA==
|
integrity sha512-jumlc0BIUrS3qJGgIkWZsyfAM7NCWiBcCDhnd+3NNM5KbBmLTgHVfWBcg6W+rLUsIpzpERPsvwUP7CckAQSOoA==
|
||||||
|
|
||||||
|
luxon@~3.6.0:
|
||||||
|
version "3.6.1"
|
||||||
|
resolved "https://registry.yarnpkg.com/luxon/-/luxon-3.6.1.tgz#d283ffc4c0076cb0db7885ec6da1c49ba97e47b0"
|
||||||
|
integrity sha512-tJLxrKJhO2ukZ5z0gyjY1zPh3Rh88Ej9P7jNrZiHMUXHae1yvI2imgOZtL1TO8TW6biMMKfTtAOoEJANgtWBMQ==
|
||||||
|
|
||||||
magic-string@0.30.17:
|
magic-string@0.30.17:
|
||||||
version "0.30.17"
|
version "0.30.17"
|
||||||
resolved "https://registry.yarnpkg.com/magic-string/-/magic-string-0.30.17.tgz#450a449673d2460e5bbcfba9a61916a1714c7453"
|
resolved "https://registry.yarnpkg.com/magic-string/-/magic-string-0.30.17.tgz#450a449673d2460e5bbcfba9a61916a1714c7453"
|
||||||
|
|||||||
Loading…
x
Reference in New Issue
Block a user