Files
dockhand/lib/server/scheduler/tasks/system-cleanup.ts
Jarek Krochmalski 62e3c6439e Initial commit
2025-12-28 21:16:03 +01:00

203 lines
5.7 KiB
TypeScript

/**
* System Cleanup Tasks
*
* Handles system cleanup jobs (schedule executions, container events).
*/
import type { ScheduleTrigger } from '../../db';
import {
getScheduleRetentionDays,
cleanupOldExecutions,
getEventRetentionDays,
getScheduleCleanupEnabled,
getEventCleanupEnabled,
createScheduleExecution,
updateScheduleExecution,
appendScheduleExecutionLog
} from '../../db';
// System job IDs
export const SYSTEM_SCHEDULE_CLEANUP_ID = 1;
export const SYSTEM_EVENT_CLEANUP_ID = 2;
export const SYSTEM_VOLUME_HELPER_CLEANUP_ID = 3;
/**
* Execute schedule execution cleanup job.
*/
export async function runScheduleCleanupJob(triggeredBy: ScheduleTrigger = 'cron'): Promise<void> {
// Check if cleanup is enabled (skip check if manually triggered)
if (triggeredBy === 'cron') {
const enabled = await getScheduleCleanupEnabled();
if (!enabled) {
return; // Skip execution if disabled
}
}
const startTime = Date.now();
// Create execution record
const execution = await createScheduleExecution({
scheduleType: 'system_cleanup',
scheduleId: SYSTEM_SCHEDULE_CLEANUP_ID,
environmentId: null,
entityName: 'Schedule execution cleanup',
triggeredBy,
status: 'running'
});
await updateScheduleExecution(execution.id, {
startedAt: new Date().toISOString()
});
const log = async (message: string) => {
console.log(`[Schedule Cleanup] ${message}`);
await appendScheduleExecutionLog(execution.id, `[${new Date().toISOString()}] ${message}`);
};
try {
const retentionDays = await getScheduleRetentionDays();
await log(`Starting cleanup with ${retentionDays} day retention`);
await cleanupOldExecutions(retentionDays);
await log('Cleanup completed successfully');
await updateScheduleExecution(execution.id, {
status: 'success',
completedAt: new Date().toISOString(),
duration: Date.now() - startTime,
details: { retentionDays }
});
} catch (error: any) {
await log(`Error: ${error.message}`);
await updateScheduleExecution(execution.id, {
status: 'failed',
completedAt: new Date().toISOString(),
duration: Date.now() - startTime,
errorMessage: error.message
});
}
}
/**
* Execute event cleanup job.
*/
export async function runEventCleanupJob(triggeredBy: ScheduleTrigger = 'cron'): Promise<void> {
// Check if cleanup is enabled (skip check if manually triggered)
if (triggeredBy === 'cron') {
const enabled = await getEventCleanupEnabled();
if (!enabled) {
return; // Skip execution if disabled
}
}
const startTime = Date.now();
// Create execution record
const execution = await createScheduleExecution({
scheduleType: 'system_cleanup',
scheduleId: SYSTEM_EVENT_CLEANUP_ID,
environmentId: null,
entityName: 'Container event cleanup',
triggeredBy,
status: 'running'
});
await updateScheduleExecution(execution.id, {
startedAt: new Date().toISOString()
});
const log = async (message: string) => {
console.log(`[Event Cleanup] ${message}`);
await appendScheduleExecutionLog(execution.id, `[${new Date().toISOString()}] ${message}`);
};
try {
const { deleteOldContainerEvents } = await import('../../db');
const retentionDays = await getEventRetentionDays();
await log(`Starting cleanup of events older than ${retentionDays} days`);
const deleted = await deleteOldContainerEvents(retentionDays);
await log(`Removed ${deleted} old container events`);
await updateScheduleExecution(execution.id, {
status: 'success',
completedAt: new Date().toISOString(),
duration: Date.now() - startTime,
details: { deletedCount: deleted, retentionDays }
});
} catch (error: any) {
await log(`Error: ${error.message}`);
await updateScheduleExecution(execution.id, {
status: 'failed',
completedAt: new Date().toISOString(),
duration: Date.now() - startTime,
errorMessage: error.message
});
}
}
/**
* Execute volume helper cleanup job.
* Cleans up stale dockhand-browse-* containers used for volume browsing.
* @param triggeredBy - What triggered this execution
* @param cleanupFns - Optional cleanup functions (passed from scheduler to avoid dynamic import issues)
*/
export async function runVolumeHelperCleanupJob(
triggeredBy: ScheduleTrigger = 'cron',
cleanupFns?: {
cleanupStaleVolumeHelpers: () => Promise<void>;
cleanupExpiredVolumeHelpers: () => Promise<void>;
}
): Promise<void> {
const startTime = Date.now();
// Create execution record
const execution = await createScheduleExecution({
scheduleType: 'system_cleanup',
scheduleId: SYSTEM_VOLUME_HELPER_CLEANUP_ID,
environmentId: null,
entityName: 'Volume helper cleanup',
triggeredBy,
status: 'running'
});
await updateScheduleExecution(execution.id, {
startedAt: new Date().toISOString()
});
const log = async (message: string) => {
console.log(`[Volume Helper Cleanup] ${message}`);
await appendScheduleExecutionLog(execution.id, `[${new Date().toISOString()}] ${message}`);
};
try {
await log('Starting cleanup of stale and expired volume helper containers');
if (cleanupFns) {
// Use provided functions (from scheduler static imports)
await cleanupFns.cleanupStaleVolumeHelpers();
await cleanupFns.cleanupExpiredVolumeHelpers();
} else {
// Fallback to dynamic import (may not work in production)
const { runVolumeHelperCleanup } = await import('../../db');
await runVolumeHelperCleanup();
}
await log('Cleanup completed successfully');
await updateScheduleExecution(execution.id, {
status: 'success',
completedAt: new Date().toISOString(),
duration: Date.now() - startTime
});
} catch (error: any) {
await log(`Error: ${error.message}`);
await updateScheduleExecution(execution.id, {
status: 'failed',
completedAt: new Date().toISOString(),
duration: Date.now() - startTime,
errorMessage: error.message
});
}
}