Files
iddaai-be/src/tasks/limit-resetter.task.ts
T
2026-04-19 13:23:00 +03:00

179 lines
5.2 KiB
TypeScript
Executable File

import { Injectable, Logger } from "@nestjs/common";
import { Cron } from "@nestjs/schedule";
import { PrismaService } from "../database/prisma.service";
import {
FINISHED_STATE_VALUES_FOR_DB,
FINISHED_STATUS_VALUES_FOR_DB,
LIVE_STATE_VALUES_FOR_DB,
LIVE_STATUS_VALUES_FOR_DB,
} from "../common/utils/match-status.util";
import {
getDateOnlyValueForTimeZone,
getShiftedDateStringInTimeZone,
getDayBoundsForTimeZone,
} from "../common/utils/timezone.util";
import { TaskLockService } from "./task-lock.service";
@Injectable()
export class LimitResetterTask {
private readonly logger = new Logger(LimitResetterTask.name);
private readonly timeZone = "Europe/Istanbul";
constructor(
private readonly prisma: PrismaService,
private readonly taskLock: TaskLockService,
) {}
private shouldSkipInHistoricalMode(jobName: string): boolean {
if (process.env.FEEDER_MODE === "historical") {
this.logger.debug(`Skipping ${jobName} in historical feeder mode`);
return true;
}
return false;
}
/**
* Reset usage limits daily at 03:00 (Europe/Istanbul)
*/
@Cron("0 3 * * *", { timeZone: "Europe/Istanbul" })
async resetUsageLimits() {
if (this.shouldSkipInHistoricalMode("resetUsageLimits")) return;
await this.taskLock.runWithLease(
"resetUsageLimits",
30 * 60 * 1000,
async () => {
this.logger.log("Starting daily usage limit reset job...");
try {
const today = getDateOnlyValueForTimeZone(this.timeZone);
const result = await this.prisma.usageLimit.updateMany({
where: {
lastResetDate: { lt: today },
},
data: {
analysisCount: 0,
couponCount: 0,
lastResetDate: today,
},
});
if (result.count > 0) {
this.logger.log(
`Usage limits for ${result.count} users have been reset`,
);
} else {
this.logger.log("No user limits needed resetting");
}
} catch (error: any) {
this.logger.error(`Limit reset job failed: ${error.message}`);
}
},
this.logger,
);
}
/**
* Clean up old predictions (older than 30 days)
*/
@Cron("0 4 * * *", { timeZone: "Europe/Istanbul" })
async cleanupOldData() {
if (this.shouldSkipInHistoricalMode("cleanupOldData")) return;
await this.taskLock.runWithLease(
"cleanupOldData",
60 * 60 * 1000,
async () => {
this.logger.log("Starting data cleanup job...");
try {
const thirtyDaysAgo = new Date();
thirtyDaysAgo.setDate(thirtyDaysAgo.getDate() - 30);
const deletedLogs = await this.prisma.aiPredictionsLog.deleteMany({
where: {
createdAt: { lt: thirtyDaysAgo },
},
});
const yesterdayDate = getShiftedDateStringInTimeZone(
-1,
this.timeZone,
);
const { startMs: yesterdayStartMs } = getDayBoundsForTimeZone(
yesterdayDate,
this.timeZone,
);
const liveMatchCutoff = new Date(yesterdayStartMs);
const deletedLiveMatches = await this.prisma.liveMatch.deleteMany({
where: {
updatedAt: { lt: liveMatchCutoff },
OR: [
{ status: { in: FINISHED_STATUS_VALUES_FOR_DB } },
{ state: { in: FINISHED_STATE_VALUES_FOR_DB } },
{
AND: [
{ scoreHome: { not: null } },
{ scoreAway: { not: null } },
{
NOT: {
OR: [
{ status: { in: LIVE_STATUS_VALUES_FOR_DB } },
{ state: { in: LIVE_STATE_VALUES_FOR_DB } },
],
},
},
],
},
],
},
});
this.logger.log(
`Cleanup complete: ${deletedLogs.count} old logs, ${deletedLiveMatches.count} old live matches`,
);
} catch (error: any) {
this.logger.error(`Cleanup job failed: ${error.message}`);
}
},
this.logger,
);
}
/**
* Reset subscription status for expired users
*/
@Cron("0 0 * * *", { timeZone: "Europe/Istanbul" })
async checkSubscriptions() {
if (this.shouldSkipInHistoricalMode("checkSubscriptions")) return;
await this.taskLock.runWithLease(
"checkSubscriptions",
30 * 60 * 1000,
async () => {
this.logger.log("Checking expired subscriptions...");
try {
const now = new Date();
const result = await this.prisma.user.updateMany({
where: {
subscriptionStatus: "active",
subscriptionExpiresAt: { lt: now },
},
data: {
subscriptionStatus: "expired",
},
});
if (result.count > 0) {
this.logger.log(`${result.count} subscriptions marked as expired`);
}
} catch (error: any) {
this.logger.error(`Subscription check failed: ${error.message}`);
}
},
this.logger,
);
}
}