chore: initial project import
Some checks failed
CI - Production Readiness / Verify (push) Has been cancelled
Some checks failed
CI - Production Readiness / Verify (push) Has been cancelled
This commit is contained in:
122
scripts/ops-maintenance.mjs
Normal file
122
scripts/ops-maintenance.mjs
Normal file
@ -0,0 +1,122 @@
|
||||
#!/usr/bin/env node
|
||||
|
||||
import { PrismaClient } from "@prisma/client";
|
||||
|
||||
const prisma = new PrismaClient();
|
||||
|
||||
const ONE_MINUTE_MS = 60_000;
|
||||
const ONE_DAY_MS = 24 * 60 * 60 * 1000;
|
||||
const now = new Date();
|
||||
const consumedRetentionHours = getPositiveNumber(process.env.AUTH_TOKEN_CONSUMED_RETENTION_HOURS, 24);
|
||||
const staleLockMinutes = getPositiveNumber(process.env.CAMPAIGN_RETRY_STALE_LOCK_MINUTES, 120);
|
||||
const webhookRetentionDays = getPositiveNumber(process.env.WEBHOOK_EVENT_RETENTION_DAYS, 30);
|
||||
const auditRetentionDays = getPositiveNumber(process.env.AUDIT_LOG_RETENTION_DAYS, 365);
|
||||
|
||||
function getPositiveNumber(raw, fallback) {
|
||||
const parsed = Number(raw);
|
||||
if (!Number.isFinite(parsed) || parsed <= 0) {
|
||||
return fallback;
|
||||
}
|
||||
return parsed;
|
||||
}
|
||||
|
||||
function printLine(message) {
|
||||
console.log(`[ops-maintenance] ${message}`);
|
||||
}
|
||||
|
||||
async function main() {
|
||||
try {
|
||||
const consumedCutoff = new Date(now.getTime() - consumedRetentionHours * 60 * 60 * 1000);
|
||||
const staleLockCutoff = new Date(now.getTime() - staleLockMinutes * ONE_MINUTE_MS);
|
||||
const webhookCutoff = new Date(now.getTime() - webhookRetentionDays * ONE_DAY_MS);
|
||||
const auditCutoff = new Date(now.getTime() - auditRetentionDays * ONE_DAY_MS);
|
||||
|
||||
const expiredTokens = await prisma.authToken.deleteMany({
|
||||
where: {
|
||||
expiresAt: { lte: now },
|
||||
consumedAt: null
|
||||
}
|
||||
});
|
||||
|
||||
const consumedTokens = await prisma.authToken.deleteMany({
|
||||
where: {
|
||||
consumedAt: { lte: consumedCutoff }
|
||||
}
|
||||
});
|
||||
|
||||
const recoveredLock = await prisma.backgroundJobState.updateMany({
|
||||
where: {
|
||||
lockedUntil: { lte: staleLockCutoff },
|
||||
lastRunStatus: "running"
|
||||
},
|
||||
data: {
|
||||
lockedUntil: null,
|
||||
lastRunStatus: "stale",
|
||||
lastError: "Recovered stale lock from maintenance cleanup",
|
||||
lastFailureAt: now,
|
||||
lastRunCompletedAt: now,
|
||||
consecutiveFailures: { increment: 1 }
|
||||
}
|
||||
});
|
||||
|
||||
const removedWebhookEvents = await prisma.webhookEvent.deleteMany({
|
||||
where: {
|
||||
createdAt: { lte: webhookCutoff },
|
||||
processStatus: { not: "skipped" }
|
||||
}
|
||||
});
|
||||
|
||||
const removedAuditLogs = await prisma.auditLog.deleteMany({
|
||||
where: {
|
||||
createdAt: { lte: auditCutoff }
|
||||
}
|
||||
});
|
||||
|
||||
printLine(`Expired auth tokens deleted: ${expiredTokens.count}`);
|
||||
printLine(`Consumed auth tokens deleted (older than ${consumedRetentionHours}h): ${consumedTokens.count}`);
|
||||
printLine(`Recovered stale background locks: ${recoveredLock.count}`);
|
||||
printLine(`Webhook events deleted (older than ${webhookRetentionDays}d): ${removedWebhookEvents.count}`);
|
||||
printLine(`Audit logs deleted (older than ${auditRetentionDays}d): ${removedAuditLogs.count}`);
|
||||
|
||||
const sampleTenant = await prisma.tenant.findFirst({ select: { id: true } });
|
||||
const shouldWriteAudit = Boolean(sampleTenant) && (
|
||||
expiredTokens.count > 0 ||
|
||||
consumedTokens.count > 0 ||
|
||||
recoveredLock.count > 0
|
||||
);
|
||||
|
||||
if (shouldWriteAudit) {
|
||||
const tenantId = sampleTenant.id;
|
||||
await prisma.auditLog.create({
|
||||
data: {
|
||||
tenantId,
|
||||
actorUserId: null,
|
||||
entityType: "system",
|
||||
entityId: "maintenance",
|
||||
action: "ops_maintenance_cleanup",
|
||||
metadataJson: JSON.stringify({
|
||||
expiredAuthTokens: expiredTokens.count,
|
||||
consumedAuthTokens: consumedTokens.count,
|
||||
recoveredLocks: recoveredLock.count,
|
||||
webhookEventsDeleted: removedWebhookEvents.count,
|
||||
auditLogsDeleted: removedAuditLogs.count,
|
||||
consumedRetentionHours,
|
||||
staleLockMinutes,
|
||||
webhookRetentionDays,
|
||||
auditRetentionDays
|
||||
})
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
printLine("maintenance completed");
|
||||
} catch (error) {
|
||||
const message = error instanceof Error ? error.message : String(error);
|
||||
console.error(`[ops-maintenance] failed: ${message}`);
|
||||
process.exitCode = 1;
|
||||
} finally {
|
||||
await prisma.$disconnect();
|
||||
}
|
||||
}
|
||||
|
||||
await main();
|
||||
Reference in New Issue
Block a user