feat(server): supplement team email remind (#9483)

fix PD-2047 AF-1996
This commit is contained in:
darkskygit
2025-01-21 05:18:02 +00:00
parent 42428fbf99
commit 1116a1d74e
13 changed files with 345 additions and 17 deletions
@@ -17,6 +17,77 @@ export class SubscriptionCronJobs {
private readonly event: EventEmitter
) {}
private getDateRange(after: number, base: number | Date = Date.now()) {
const start = new Date(base);
start.setDate(start.getDate() + after);
start.setHours(0, 0, 0, 0);
const end = new Date(start);
end.setHours(23, 59, 59, 999);
return { start, end };
}
// TODO(@darkskygit): enable this after the cluster event system is ready
// @Cron(CronExpression.EVERY_DAY_AT_MIDNIGHT /* everyday at 12am */)
async notifyExpiredWorkspace() {
const { start: after30DayStart, end: after30DayEnd } =
this.getDateRange(30);
const { start: todayStart, end: todayEnd } = this.getDateRange(0);
const { start: before150DaysStart, end: before150DaysEnd } =
this.getDateRange(-150);
const { start: before180DaysStart, end: before180DaysEnd } =
this.getDateRange(-180);
const subscriptions = await this.db.subscription.findMany({
where: {
plan: SubscriptionPlan.Team,
OR: [
{
// subscription will cancel after 30 days
status: 'active',
canceledAt: { not: null },
end: { gte: after30DayStart, lte: after30DayEnd },
},
{
// subscription will cancel today
status: 'active',
canceledAt: { not: null },
end: { gte: todayStart, lte: todayEnd },
},
{
// subscription has been canceled for 150 days
// workspace becomes delete after 180 days
status: 'canceled',
canceledAt: { gte: before150DaysStart, lte: before150DaysEnd },
},
{
// subscription has been canceled for 180 days
// workspace becomes delete after 180 days
status: 'canceled',
canceledAt: { gte: before180DaysStart, lte: before180DaysEnd },
},
],
},
});
for (const subscription of subscriptions) {
const end = subscription.end;
if (!end) {
// should not reach here
continue;
}
this.event.emit('workspace.subscription.notify', {
workspaceId: subscription.targetId,
expirationDate: end,
deletionDate:
subscription.status === 'canceled'
? this.getDateRange(180, end).end
: undefined,
});
}
}
@Cron(CronExpression.EVERY_HOUR)
async cleanExpiredOnetimeSubscriptions() {
const subscriptions = await this.db.subscription.findMany({