import { Injectable, Logger } from '@nestjs/common'; import { InjectRepository } from '@nestjs/typeorm'; import { Subscription } from './entities/subscription.entity'; import { Brackets, Repository } from 'typeorm'; import { MailService } from 'src/mail/mail.service'; import { Cron, CronExpression } from '@nestjs/schedule'; import * as dayjs from 'dayjs'; import { getBenefits, getPurchaseUrl, getSubsctriptionName, } from './helpers/subscription.helper'; @Injectable() export class SubscriptionsEmailService { constructor( @InjectRepository(Subscription) private readonly _subscriptionsRepository: Repository, private readonly _mailService: MailService, ) {} @Cron(CronExpression.EVERY_DAY_AT_11AM) async handleExpiringReminderCron() { Logger.log('Looking for subscriptions expiring soon...'); const now = dayjs(); const in24Hours = now.add(24, 'hour').toDate(); const last24Hours = now.subtract(24, 'hour').toDate(); // Query for subscriptions expiring in the next 24 hours // and that haven't had a reminder sent in the last 24 hours const expiring = await this._subscriptionsRepository .createQueryBuilder('subscription') // using withDeleted to include soft-deleted users data then exclude them later // otherwise, the query would not join the user table correctly .withDeleted() .leftJoinAndSelect('subscription.user', 'user') .where('subscription.periodEnd BETWEEN :now AND :in24Hours', { now, in24Hours, }) .andWhere('subscription.status = :status', { status: 'cancelled' }) .andWhere( new Brackets((qb) => { qb.where('subscription.expirationReminderSentAt IS NULL').orWhere( 'subscription.expirationReminderSentAt < :last24Hours', { last24Hours }, ); }), ) .andWhere('user.deletedAt IS NULL') .getMany(); if (expiring.length === 0) { Logger.log('No subscriptions expiring soon.'); return; } // Sending reminder emails const renewSubscriptionTemplateId = 'd-681385c5f24c412e95ad70bc6410511d'; const recipients = expiring.map((subscription) => ({ email: subscription.user.email, data: { subscriptionAddonUrl: getPurchaseUrl(subscription.type), subscriptionName: getSubsctriptionName(subscription.type), benefits: getBenefits(subscription.type), }, })); await this._mailService.sendBatch(renewSubscriptionTemplateId, recipients); // Setting the reminder sent date const ids = expiring.map((s) => s.id); await this._subscriptionsRepository .createQueryBuilder() .update() .set({ expirationReminderSentAt: new Date() }) .whereInIds(ids) .execute(); Logger.log( `Sent reminder emails for ${expiring.length} expiring subscriptions.`, ); } }