Implement backend API and database services in Docker setup
- Added a new `api` service for the NestJS backend, including health checks and dependencies on PostgreSQL, Redis, and MinIO. - Introduced PostgreSQL and Redis services with health checks and configurations for data persistence. - Added MinIO for S3-compatible object storage and a one-shot service to initialize required buckets. - Updated the Nginx configuration to proxy requests to the new backend API and MinIO storage. - Enhanced the Dockerfile to support the new API environment variables and configurations. - Updated the `package.json` and `package-lock.json` to include new dependencies for QR code generation and other utilities. Co-authored-by: Cursor <cursoragent@cursor.com>
This commit is contained in:
84
backend/src/subscriptions/subscription-email.service.ts
Normal file
84
backend/src/subscriptions/subscription-email.service.ts
Normal file
@@ -0,0 +1,84 @@
|
||||
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<Subscription>,
|
||||
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.`,
|
||||
);
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user