mirror of
https://github.com/fccview/cronmaster.git
synced 2026-01-04 11:59:07 -05:00
Compare commits
4 Commits
bugfix/fix
...
feature/ex
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
d21bed64fe | ||
|
|
8329c0d030 | ||
|
|
6e34474993 | ||
|
|
65ac81d97c |
1
.gitignore
vendored
1
.gitignore
vendored
@@ -11,5 +11,6 @@ node_modules
|
|||||||
.vscode
|
.vscode
|
||||||
.DS_Store
|
.DS_Store
|
||||||
.cursorignore
|
.cursorignore
|
||||||
|
.idea
|
||||||
tsconfig.tsbuildinfo
|
tsconfig.tsbuildinfo
|
||||||
docker-compose.test.yml
|
docker-compose.test.yml
|
||||||
@@ -72,6 +72,12 @@ services:
|
|||||||
# replace root with your user - find it with: ls -asl /var/spool/cron/crontabs/
|
# replace root with your user - find it with: ls -asl /var/spool/cron/crontabs/
|
||||||
# For multiple users, use comma-separated values: HOST_CRONTAB_USER=root,user1,user2
|
# For multiple users, use comma-separated values: HOST_CRONTAB_USER=root,user1,user2
|
||||||
- HOST_CRONTAB_USER=root
|
- HOST_CRONTAB_USER=root
|
||||||
|
|
||||||
|
# --- !! IMPORTANT !!DOCKER EXEC USER
|
||||||
|
# If you do not specify this user to be a valid user on your system,
|
||||||
|
# any cronjob containing a docker command will fail. IDEALLY you should not be running
|
||||||
|
# docker commands as root, so this is only a fallback. ONLY ONE USER IS ALLOWED.
|
||||||
|
- DOCKER_EXEC_USER=fccview
|
||||||
volumes:
|
volumes:
|
||||||
# --- MOUNT DOCKER SOCKET
|
# --- MOUNT DOCKER SOCKET
|
||||||
# Mount Docker socket to execute commands on host
|
# Mount Docker socket to execute commands on host
|
||||||
@@ -147,6 +153,7 @@ The following environment variables can be configured:
|
|||||||
| `DOCKER` | `false` | ONLY set this to true if you are runnign the app via docker, in the docker-compose.yml file |
|
| `DOCKER` | `false` | ONLY set this to true if you are runnign the app via docker, in the docker-compose.yml file |
|
||||||
| `HOST_CRONTAB_USER` | `root` | Comma separated list of users that run cronjobs on your host machine |
|
| `HOST_CRONTAB_USER` | `root` | Comma separated list of users that run cronjobs on your host machine |
|
||||||
| `AUTH_PASSWORD` | `N/A` | If you set a password the application will be password protected with basic next-auth |
|
| `AUTH_PASSWORD` | `N/A` | If you set a password the application will be password protected with basic next-auth |
|
||||||
|
| `DOCKER_EXEC_USER` | `N/A` | If you don't set this user you won't be able to run docker commands as root |
|
||||||
|
|
||||||
**Example**: To change the clock update interval to 60 seconds:
|
**Example**: To change the clock update interval to 60 seconds:
|
||||||
|
|
||||||
|
|||||||
@@ -265,12 +265,19 @@ export const runCronJob = async (
|
|||||||
|
|
||||||
if (isDocker) {
|
if (isDocker) {
|
||||||
const userInfo = await getUserInfo(job.user);
|
const userInfo = await getUserInfo(job.user);
|
||||||
|
const dockerExecUser = process.env.DOCKER_EXEC_USER;
|
||||||
|
|
||||||
if (userInfo && userInfo.username !== "root") {
|
let executionUser = userInfo ? userInfo.username : "root";
|
||||||
command = `nsenter -t 1 -m -u -i -n -p --setuid=${userInfo.uid} --setgid=${userInfo.gid} sh -c "${job.command}"`;
|
|
||||||
} else {
|
if (dockerExecUser && executionUser === "root") {
|
||||||
command = `nsenter -t 1 -m -u -i -n -p sh -c "${job.command}"`;
|
console.log(
|
||||||
|
`Overriding root execution. Running command as user: ${dockerExecUser}`
|
||||||
|
);
|
||||||
|
executionUser = dockerExecUser;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
const escapedCommand = job.command.replace(/'/g, "'\\''");
|
||||||
|
command = `nsenter -t 1 -m -u -i -n -p su - ${executionUser} -c '${escapedCommand}'`;
|
||||||
}
|
}
|
||||||
|
|
||||||
const { stdout, stderr } = await execAsync(command, {
|
const { stdout, stderr } = await execAsync(command, {
|
||||||
|
|||||||
@@ -21,7 +21,7 @@ const sanitizeScriptName = (name: string): string => {
|
|||||||
.replace(/-+/g, "-")
|
.replace(/-+/g, "-")
|
||||||
.replace(/^-|-$/g, "")
|
.replace(/^-|-$/g, "")
|
||||||
.substring(0, 50);
|
.substring(0, 50);
|
||||||
}
|
};
|
||||||
|
|
||||||
const generateUniqueFilename = async (baseName: string): Promise<string> => {
|
const generateUniqueFilename = async (baseName: string): Promise<string> => {
|
||||||
const scripts = await loadAllScripts();
|
const scripts = await loadAllScripts();
|
||||||
@@ -34,14 +34,14 @@ const generateUniqueFilename = async (baseName: string): Promise<string> => {
|
|||||||
}
|
}
|
||||||
|
|
||||||
return filename;
|
return filename;
|
||||||
}
|
};
|
||||||
|
|
||||||
const ensureScriptsDirectory = async () => {
|
const ensureScriptsDirectory = async () => {
|
||||||
const scriptsDir = await SCRIPTS_DIR();
|
const scriptsDir = await SCRIPTS_DIR();
|
||||||
if (!existsSync(scriptsDir)) {
|
if (!existsSync(scriptsDir)) {
|
||||||
await mkdir(scriptsDir, { recursive: true });
|
await mkdir(scriptsDir, { recursive: true });
|
||||||
}
|
}
|
||||||
}
|
};
|
||||||
|
|
||||||
const ensureHostScriptsDirectory = async () => {
|
const ensureHostScriptsDirectory = async () => {
|
||||||
const hostProjectDir = process.env.HOST_PROJECT_DIR || process.cwd();
|
const hostProjectDir = process.env.HOST_PROJECT_DIR || process.cwd();
|
||||||
@@ -50,7 +50,7 @@ const ensureHostScriptsDirectory = async () => {
|
|||||||
if (!existsSync(hostScriptsDir)) {
|
if (!existsSync(hostScriptsDir)) {
|
||||||
await mkdir(hostScriptsDir, { recursive: true });
|
await mkdir(hostScriptsDir, { recursive: true });
|
||||||
}
|
}
|
||||||
}
|
};
|
||||||
|
|
||||||
const saveScriptFile = async (filename: string, content: string) => {
|
const saveScriptFile = async (filename: string, content: string) => {
|
||||||
const isDocker = process.env.DOCKER === "true";
|
const isDocker = process.env.DOCKER === "true";
|
||||||
@@ -59,7 +59,13 @@ const saveScriptFile = async (filename: string, content: string) => {
|
|||||||
|
|
||||||
const scriptPath = join(scriptsDir, filename);
|
const scriptPath = join(scriptsDir, filename);
|
||||||
await writeFile(scriptPath, content, "utf8");
|
await writeFile(scriptPath, content, "utf8");
|
||||||
}
|
|
||||||
|
try {
|
||||||
|
await execAsync(`chmod +x "${scriptPath}"`);
|
||||||
|
} catch (error) {
|
||||||
|
console.error(`Failed to set execute permissions on ${scriptPath}:`, error);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
const deleteScriptFile = async (filename: string) => {
|
const deleteScriptFile = async (filename: string) => {
|
||||||
const isDocker = process.env.DOCKER === "true";
|
const isDocker = process.env.DOCKER === "true";
|
||||||
@@ -68,11 +74,11 @@ const deleteScriptFile = async (filename: string) => {
|
|||||||
if (existsSync(scriptPath)) {
|
if (existsSync(scriptPath)) {
|
||||||
await unlink(scriptPath);
|
await unlink(scriptPath);
|
||||||
}
|
}
|
||||||
}
|
};
|
||||||
|
|
||||||
export const fetchScripts = async (): Promise<Script[]> => {
|
export const fetchScripts = async (): Promise<Script[]> => {
|
||||||
return await loadAllScripts();
|
return await loadAllScripts();
|
||||||
}
|
};
|
||||||
|
|
||||||
export const createScript = async (
|
export const createScript = async (
|
||||||
formData: FormData
|
formData: FormData
|
||||||
@@ -120,7 +126,7 @@ export const createScript = async (
|
|||||||
console.error("Error creating script:", error);
|
console.error("Error creating script:", error);
|
||||||
return { success: false, message: "Error creating script" };
|
return { success: false, message: "Error creating script" };
|
||||||
}
|
}
|
||||||
}
|
};
|
||||||
|
|
||||||
export const updateScript = async (
|
export const updateScript = async (
|
||||||
formData: FormData
|
formData: FormData
|
||||||
@@ -159,7 +165,7 @@ export const updateScript = async (
|
|||||||
console.error("Error updating script:", error);
|
console.error("Error updating script:", error);
|
||||||
return { success: false, message: "Error updating script" };
|
return { success: false, message: "Error updating script" };
|
||||||
}
|
}
|
||||||
}
|
};
|
||||||
|
|
||||||
export const deleteScript = async (
|
export const deleteScript = async (
|
||||||
id: string
|
id: string
|
||||||
@@ -180,7 +186,7 @@ export const deleteScript = async (
|
|||||||
console.error("Error deleting script:", error);
|
console.error("Error deleting script:", error);
|
||||||
return { success: false, message: "Error deleting script" };
|
return { success: false, message: "Error deleting script" };
|
||||||
}
|
}
|
||||||
}
|
};
|
||||||
|
|
||||||
export const cloneScript = async (
|
export const cloneScript = async (
|
||||||
id: string,
|
id: string,
|
||||||
@@ -230,7 +236,7 @@ export const cloneScript = async (
|
|||||||
console.error("Error cloning script:", error);
|
console.error("Error cloning script:", error);
|
||||||
return { success: false, message: "Error cloning script" };
|
return { success: false, message: "Error cloning script" };
|
||||||
}
|
}
|
||||||
}
|
};
|
||||||
|
|
||||||
export const getScriptContent = async (filename: string): Promise<string> => {
|
export const getScriptContent = async (filename: string): Promise<string> => {
|
||||||
try {
|
try {
|
||||||
@@ -263,9 +269,11 @@ export const getScriptContent = async (filename: string): Promise<string> => {
|
|||||||
console.error("Error reading script content:", error);
|
console.error("Error reading script content:", error);
|
||||||
return "";
|
return "";
|
||||||
}
|
}
|
||||||
}
|
};
|
||||||
|
|
||||||
export const executeScript = async (filename: string): Promise<{
|
export const executeScript = async (
|
||||||
|
filename: string
|
||||||
|
): Promise<{
|
||||||
success: boolean;
|
success: boolean;
|
||||||
output: string;
|
output: string;
|
||||||
error: string;
|
error: string;
|
||||||
@@ -301,4 +309,4 @@ export const executeScript = async (filename: string): Promise<{
|
|||||||
error: error.message || "Unknown error",
|
error: error.message || "Unknown error",
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
}
|
};
|
||||||
|
|||||||
@@ -23,6 +23,12 @@ services:
|
|||||||
# replace root with your user - find it with: ls -asl /var/spool/cron/crontabs/
|
# replace root with your user - find it with: ls -asl /var/spool/cron/crontabs/
|
||||||
# For multiple users, use comma-separated values: HOST_CRONTAB_USER=root,user1,user2
|
# For multiple users, use comma-separated values: HOST_CRONTAB_USER=root,user1,user2
|
||||||
- HOST_CRONTAB_USER=root
|
- HOST_CRONTAB_USER=root
|
||||||
|
|
||||||
|
# --- !! IMPORTANT !!DOCKER EXEC USER
|
||||||
|
# If you do not specify this user to be a valid user on your system,
|
||||||
|
# any cronjob containing a docker command will fail. IDEALLY you should not be running
|
||||||
|
# docker commands as root, so this is only a fallback. ONLY ONE USER IS ALLOWED.
|
||||||
|
- DOCKER_EXEC_USER=fccview
|
||||||
volumes:
|
volumes:
|
||||||
# --- MOUNT DOCKER SOCKET
|
# --- MOUNT DOCKER SOCKET
|
||||||
# Mount Docker socket to execute commands on host
|
# Mount Docker socket to execute commands on host
|
||||||
|
|||||||
Reference in New Issue
Block a user