feat: add cron job management functionality with status retrieval and action handling

This commit is contained in:
2026-01-12 12:22:00 +01:00
parent a8db92731f
commit e35f9b3e7b
3 changed files with 395 additions and 0 deletions

View File

@@ -0,0 +1,191 @@
import { NextResponse } from "next/server";
import { withAdminAuth } from "@/lib/middleware/auth";
import { exec } from "child_process";
import { promisify } from "util";
import fs from "fs";
import path from "path";
const execAsync = promisify(exec);
// Check if we're running in a Linux/Docker environment
const isLinux = process.platform === "linux";
async function getCronStatus() {
if (!isLinux) {
return {
available: false,
running: false,
jobs: [],
message: "Cron is only available in Linux/Docker environment",
lastBackup: getLastBackupInfo(),
lastReminder: getLastReminderInfo()
};
}
try {
// Check if cron daemon is running
let cronRunning = false;
try {
await execAsync("pgrep -x cron || pgrep -x crond");
cronRunning = true;
} catch {
cronRunning = false;
}
// Get current crontab
let jobs = [];
try {
const { stdout } = await execAsync("crontab -l 2>/dev/null");
jobs = stdout.trim().split("\n").filter(line => line && !line.startsWith("#"));
} catch {
jobs = [];
}
return {
available: true,
running: cronRunning,
jobs: jobs,
jobCount: jobs.length,
lastBackup: getLastBackupInfo(),
lastReminder: getLastReminderInfo()
};
} catch (error) {
return {
available: false,
running: false,
jobs: [],
error: error.message,
lastBackup: getLastBackupInfo(),
lastReminder: getLastReminderInfo()
};
}
}
function getLastBackupInfo() {
try {
const backupDir = path.join(process.cwd(), "backups");
if (!fs.existsSync(backupDir)) {
return { exists: false, message: "No backups directory" };
}
const files = fs.readdirSync(backupDir)
.filter(f => f.startsWith("backup-") && f.endsWith(".sqlite"))
.map(f => ({
name: f,
path: path.join(backupDir, f),
mtime: fs.statSync(path.join(backupDir, f)).mtime
}))
.sort((a, b) => b.mtime - a.mtime);
if (files.length === 0) {
return { exists: false, message: "No backups found" };
}
const latest = files[0];
return {
exists: true,
filename: latest.name,
date: latest.mtime.toISOString(),
count: files.length
};
} catch (error) {
return { exists: false, error: error.message };
}
}
function getLastReminderInfo() {
try {
const logPath = path.join(process.cwd(), "data", "reminders.log");
if (!fs.existsSync(logPath)) {
return { exists: false, message: "No reminders log" };
}
const stats = fs.statSync(logPath);
return {
exists: true,
lastModified: stats.mtime.toISOString()
};
} catch (error) {
return { exists: false, error: error.message };
}
}
async function getHandler() {
const status = await getCronStatus();
return NextResponse.json(status);
}
async function postHandler(request) {
const { action } = await request.json();
if (!isLinux) {
return NextResponse.json({
success: false,
message: "Cron operations are only available in Linux/Docker environment"
}, { status: 400 });
}
try {
if (action === "restart") {
// Run the setup-cron.sh script
const scriptPath = path.join(process.cwd(), "setup-cron.sh");
if (!fs.existsSync(scriptPath)) {
return NextResponse.json({
success: false,
message: "setup-cron.sh script not found"
}, { status: 500 });
}
// Make sure script is executable
await execAsync(`chmod +x ${scriptPath}`);
// Run the script
const { stdout, stderr } = await execAsync(`bash ${scriptPath}`);
// Get updated status
const status = await getCronStatus();
return NextResponse.json({
success: true,
message: "Cron jobs restarted successfully",
output: stdout,
status
});
} else if (action === "run-backup") {
// Manually trigger backup
const backupScript = path.join(process.cwd(), "backup-db.mjs");
const { stdout } = await execAsync(`cd ${process.cwd()} && node ${backupScript}`);
return NextResponse.json({
success: true,
message: "Backup completed",
output: stdout
});
} else if (action === "run-reminders") {
// Manually trigger reminders
const reminderScript = path.join(process.cwd(), "send-due-date-reminders.mjs");
const { stdout } = await execAsync(`cd ${process.cwd()} && node ${reminderScript}`);
return NextResponse.json({
success: true,
message: "Reminders sent",
output: stdout
});
} else {
return NextResponse.json({
success: false,
message: "Unknown action"
}, { status: 400 });
}
} catch (error) {
return NextResponse.json({
success: false,
message: error.message,
stderr: error.stderr
}, { status: 500 });
}
}
export const GET = withAdminAuth(getHandler);
export const POST = withAdminAuth(postHandler);