chore(paths): move jobs into src

This commit is contained in:
Sam Chau
2025-10-21 08:24:50 +10:30
parent 7e8068f1fb
commit 97ad75f238
10 changed files with 141 additions and 105 deletions

View File

@@ -0,0 +1,75 @@
import { config } from '$config';
import { logSettingsQueries } from '$db/queries/logSettings.ts';
import { logger } from '$logger';
import { cleanupLogs } from '../lib/cleanupLogs.ts';
import type { JobDefinition, JobResult } from '../types.ts';
/**
* Cleanup old log files job
* Deletes daily log files (YYYY-MM-DD.log) older than the configured retention period
*/
export const cleanupLogsJob: JobDefinition = {
name: 'cleanup_logs',
description: 'Delete log files according to retention policy',
schedule: 'daily',
handler: async (): Promise<JobResult> => {
try {
// Get log settings
const settings = logSettingsQueries.get();
if (!settings) {
return {
success: false,
error: 'Log settings not found'
};
}
const retentionDays = settings.retention_days;
const logsDir = config.paths.logs;
// Calculate cutoff date for logging
const cutoffDate = new Date();
cutoffDate.setDate(cutoffDate.getDate() - retentionDays);
const cutoffDateStr = cutoffDate.toISOString().split('T')[0];
await logger.info(`Cleaning up logs older than ${retentionDays} days`, {
source: 'CleanupLogsJob',
meta: { cutoffDate: cutoffDateStr }
});
// Run cleanup
const result = await cleanupLogs(logsDir, retentionDays);
// Log individual deletions
// Note: We don't have access to which specific files were deleted anymore
// If we need that, we can modify cleanupLogs to return the list
// Log errors
for (const { file, error } of result.errors) {
await logger.error(`Failed to process log file: ${file}`, {
source: 'CleanupLogsJob',
meta: { file, error }
});
}
const message = `Cleanup completed: deleted ${result.deletedCount} file(s), ${result.errorCount} error(s)`;
if (result.errorCount > 0 && result.deletedCount === 0) {
return {
success: false,
error: message
};
}
return {
success: true,
output: message
};
} catch (error) {
return {
success: false,
error: error instanceof Error ? error.message : String(error)
};
}
}
};

View File

@@ -0,0 +1,66 @@
/**
* Core cleanup logic for log files
* Separated from job definition to avoid database/config dependencies for testing
*/
export interface CleanupLogsResult {
deletedCount: number;
errorCount: number;
errors: Array<{ file: string; error: unknown }>;
}
/**
* Core cleanup logic - deletes log files older than retention period
* Pure function that only depends on Deno APIs
*
* @param logsDir Directory containing log files
* @param retentionDays Number of days to retain logs
* @returns Cleanup result with counts
*/
export async function cleanupLogs(
logsDir: string,
retentionDays: number,
): Promise<CleanupLogsResult> {
// Calculate cutoff date (YYYY-MM-DD format)
const cutoffDate = new Date();
cutoffDate.setDate(cutoffDate.getDate() - retentionDays);
const cutoffDateStr = cutoffDate.toISOString().split("T")[0]; // YYYY-MM-DD
let deletedCount = 0;
let errorCount = 0;
const errors: Array<{ file: string; error: unknown }> = [];
// Regex to match daily log files: YYYY-MM-DD.log
const dateLogPattern = /^(\d{4}-\d{2}-\d{2})\.log$/;
try {
for await (const entry of Deno.readDir(logsDir)) {
if (!entry.isFile) continue;
// Only process log files matching YYYY-MM-DD.log pattern
const match = entry.name.match(dateLogPattern);
if (!match) continue;
const logDate = match[1]; // Extract YYYY-MM-DD from filename
const filePath = `${logsDir}/${entry.name}`;
try {
// Compare date strings directly (YYYY-MM-DD format sorts correctly)
if (logDate < cutoffDateStr) {
await Deno.remove(filePath);
deletedCount++;
}
} catch (error) {
errorCount++;
errors.push({ file: entry.name, error });
}
}
} catch (error) {
// If we can't read the directory at all, that's a critical error
throw new Error(
`Failed to read logs directory: ${error instanceof Error ? error.message : String(error)}`,
);
}
return { deletedCount, errorCount, errors };
}

View File

@@ -1,105 +0,0 @@
import { config } from '$config';
import { logSettingsQueries } from '$db/queries/logSettings.ts';
import { logger } from '$logger';
import type { JobDefinition, JobResult } from '../types.ts';
/**
* Cleanup old log files job
* Deletes daily log files (YYYY-MM-DD.log) older than the configured retention period
*/
export const cleanupLogsJob: JobDefinition = {
name: 'cleanup_logs',
description: 'Delete log files according to retention policy',
schedule: 'daily',
handler: async (): Promise<JobResult> => {
try {
// Get log settings
const settings = logSettingsQueries.get();
if (!settings) {
return {
success: false,
error: 'Log settings not found'
};
}
const retentionDays = settings.retention_days;
const logsDir = config.paths.logs;
// Calculate cutoff date (YYYY-MM-DD format)
const cutoffDate = new Date();
cutoffDate.setDate(cutoffDate.getDate() - retentionDays);
const cutoffDateStr = cutoffDate.toISOString().split('T')[0]; // YYYY-MM-DD
await logger.info(`Cleaning up logs older than ${retentionDays} days`, {
source: 'CleanupLogsJob',
meta: { cutoffDate: cutoffDateStr }
});
// Read all files in logs directory
let deletedCount = 0;
let errorCount = 0;
// Regex to match daily log files: YYYY-MM-DD.log
const dateLogPattern = /^(\d{4}-\d{2}-\d{2})\.log$/;
try {
for await (const entry of Deno.readDir(logsDir)) {
if (!entry.isFile) continue;
// Only process log files matching YYYY-MM-DD.log pattern
const match = entry.name.match(dateLogPattern);
if (!match) continue;
const logDate = match[1]; // Extract YYYY-MM-DD from filename
const filePath = `${logsDir}/${entry.name}`;
try {
// Compare date strings directly (YYYY-MM-DD format sorts correctly)
if (logDate < cutoffDateStr) {
await Deno.remove(filePath);
deletedCount++;
await logger.info(`Deleted old log file: ${entry.name}`, {
source: 'CleanupLogsJob',
meta: { file: entry.name, logDate }
});
}
} catch (error) {
errorCount++;
await logger.error(`Failed to process log file: ${entry.name}`, {
source: 'CleanupLogsJob',
meta: { file: entry.name, error }
});
}
}
} catch (error) {
return {
success: false,
error: `Failed to read logs directory: ${
error instanceof Error ? error.message : String(error)
}`
};
}
const message = `Cleanup completed: deleted ${deletedCount} file(s), ${errorCount} error(s)`;
if (errorCount > 0 && deletedCount === 0) {
return {
success: false,
error: message
};
}
return {
success: true,
output: message
};
} catch (error) {
return {
success: false,
error: error instanceof Error ? error.message : String(error)
};
}
}
};