mirror of
https://github.com/Dokploy/dokploy.git
synced 2026-10-19 23:21:36 +00:00
d87229ccd3
The nightly access-log-cleanup job hardcoded "dokploy-traefik" as the container name when sending SIGUSR1. In Docker Swarm mode Traefik runs as a service task named "dokploy-traefik.1.<task-id>", so `docker exec dokploy-traefik` fails every night with "No such container". The log file is rotated (inode changes) but Traefik never reopens it, leaving the on-disk access.log frozen while real logs go to a deleted file handle. Resolve the running container id dynamically with `docker ps --filter`, matching the pattern already used elsewhere in the codebase, so it works for both standalone and swarm deployments. Skip gracefully if no running container is found. Closes #4620
94 lines
2.5 KiB
TypeScript
94 lines
2.5 KiB
TypeScript
import fs from "node:fs";
|
|
import path from "node:path";
|
|
import { paths } from "@dokploy/server/constants";
|
|
import {
|
|
getWebServerSettings,
|
|
updateWebServerSettings,
|
|
} from "@dokploy/server/services/web-server-settings";
|
|
import { scheduledJobs, scheduleJob } from "node-schedule";
|
|
import { execAsync } from "../process/execAsync";
|
|
|
|
const LOG_CLEANUP_JOB_NAME = "access-log-cleanup";
|
|
|
|
export const startLogCleanup = async (
|
|
cronExpression = "0 0 * * *",
|
|
): Promise<boolean> => {
|
|
try {
|
|
const existingJob = scheduledJobs[LOG_CLEANUP_JOB_NAME];
|
|
if (existingJob) {
|
|
existingJob.cancel();
|
|
}
|
|
|
|
scheduleJob(LOG_CLEANUP_JOB_NAME, cronExpression, async () => {
|
|
try {
|
|
const { DYNAMIC_TRAEFIK_PATH } = paths();
|
|
const accessLogPath = path.join(DYNAMIC_TRAEFIK_PATH, "access.log");
|
|
|
|
if (!fs.existsSync(accessLogPath)) {
|
|
console.error("Access log file does not exist");
|
|
return;
|
|
}
|
|
|
|
await execAsync(
|
|
`tail -n 1000 ${accessLogPath} > ${accessLogPath}.tmp && mv ${accessLogPath}.tmp ${accessLogPath}`,
|
|
);
|
|
|
|
// Traefik can run as a standalone container ("dokploy-traefik") or a
|
|
// swarm service task ("dokploy-traefik.1.<task-id>"), so resolve the
|
|
// running container id dynamically instead of assuming the name.
|
|
const { stdout: containerId } = await execAsync(
|
|
'docker ps -q --filter "name=dokploy-traefik" --filter "status=running" | head -n 1',
|
|
);
|
|
const traefikContainerId = containerId.trim();
|
|
if (!traefikContainerId) {
|
|
console.error("Traefik container not found, skipping log reopen");
|
|
return;
|
|
}
|
|
await execAsync(`docker exec ${traefikContainerId} kill -USR1 1`);
|
|
} catch (error) {
|
|
console.error("Error during log cleanup:", error);
|
|
}
|
|
});
|
|
|
|
await updateWebServerSettings({
|
|
logCleanupCron: cronExpression,
|
|
});
|
|
|
|
return true;
|
|
} catch (error) {
|
|
console.error("Error starting log cleanup:", error);
|
|
return false;
|
|
}
|
|
};
|
|
|
|
export const stopLogCleanup = async (): Promise<boolean> => {
|
|
try {
|
|
const existingJob = scheduledJobs[LOG_CLEANUP_JOB_NAME];
|
|
if (existingJob) {
|
|
existingJob.cancel();
|
|
}
|
|
|
|
// Update database
|
|
await updateWebServerSettings({
|
|
logCleanupCron: null,
|
|
});
|
|
|
|
return true;
|
|
} catch (error) {
|
|
console.error("Error stopping log cleanup:", error);
|
|
return false;
|
|
}
|
|
};
|
|
|
|
export const getLogCleanupStatus = async (): Promise<{
|
|
enabled: boolean;
|
|
cronExpression: string | null;
|
|
}> => {
|
|
const settings = await getWebServerSettings();
|
|
const cronExpression = settings?.logCleanupCron ?? null;
|
|
return {
|
|
enabled: cronExpression !== null,
|
|
cronExpression,
|
|
};
|
|
};
|