mirror of
https://github.com/fccview/cronmaster.git
synced 2025-12-23 22:18:20 -05:00
continue refactor, this is looking good!
This commit is contained in:
17
app/_consts/commands.ts
Normal file
17
app/_consts/commands.ts
Normal file
@@ -0,0 +1,17 @@
|
||||
export const WRITE_CRONTAB = (content: string, user: string) => `echo '${content}' | crontab -u ${user} -`;
|
||||
|
||||
export const READ_CRONTAB = (user: string) => `crontab -l -u ${user} 2>/dev/null || echo ""`;
|
||||
|
||||
export const READ_CRON_FILE = () => 'crontab -l 2>/dev/null || echo ""'
|
||||
|
||||
export const WRITE_CRON_FILE = (content: string) => `echo "${content}" | crontab -`;
|
||||
|
||||
export const WRITE_HOST_CRONTAB = (base64Content: string, user: string) => `echo '${base64Content}' | base64 -d | crontab -u ${user} -`;
|
||||
|
||||
export const ID_U = (username: string) => `id -u ${username}`;
|
||||
|
||||
export const ID_G = (username: string) => `id -g ${username}`;
|
||||
|
||||
export const MAKE_SCRIPT_EXECUTABLE = (scriptPath: string) => `chmod +x "${scriptPath}"`;
|
||||
|
||||
export const RUN_SCRIPT = (scriptPath: string) => `bash "${scriptPath}"`;
|
||||
@@ -273,13 +273,6 @@ export const runCronJob = async (
|
||||
command = NSENTER_RUN_JOB(executionUser, escapedCommand);
|
||||
} else {
|
||||
command = job.command;
|
||||
|
||||
const appUser = process.env.USER || "unknown";
|
||||
if (job.user !== appUser) {
|
||||
console.warn(
|
||||
`[Native Mode] Running job ${job.id} as current user (${appUser}) instead of target user (${job.user}).`
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
const { stdout, stderr } = await execAsync(command, {
|
||||
|
||||
@@ -6,10 +6,11 @@ import { join } from "path";
|
||||
import { existsSync } from "fs";
|
||||
import { exec } from "child_process";
|
||||
import { promisify } from "util";
|
||||
import { normalizeLineEndings } from "@/app/_utils/scripts";
|
||||
import { getHostScriptPath, getScriptPath, normalizeLineEndings } from "@/app/_utils/scripts";
|
||||
import { SCRIPTS_DIR } from "@/app/_consts/file";
|
||||
import { loadAllScripts, Script } from "@/app/_utils/scriptScanner";
|
||||
import { isDocker } from "@/app/_server/actions/global";
|
||||
import { MAKE_SCRIPT_EXECUTABLE, RUN_SCRIPT } from "@/app/_consts/commands";
|
||||
|
||||
const execAsync = promisify(exec);
|
||||
|
||||
@@ -51,24 +52,20 @@ const ensureHostScriptsDirectory = async () => {
|
||||
};
|
||||
|
||||
const saveScriptFile = async (filename: string, content: string) => {
|
||||
const docker = await isDocker();
|
||||
const scriptsDir = docker ? "/app/scripts" : join(process.cwd(), SCRIPTS_DIR);
|
||||
await ensureScriptsDirectory();
|
||||
|
||||
const scriptPath = join(scriptsDir, filename);
|
||||
const scriptPath = getScriptPath(filename);
|
||||
await writeFile(scriptPath, content, "utf8");
|
||||
|
||||
try {
|
||||
await execAsync(`chmod +x "${scriptPath}"`);
|
||||
await execAsync(MAKE_SCRIPT_EXECUTABLE(scriptPath));
|
||||
} catch (error) {
|
||||
console.error(`Failed to set execute permissions on ${scriptPath}:`, error);
|
||||
}
|
||||
};
|
||||
|
||||
const deleteScriptFile = async (filename: string) => {
|
||||
const docker = await isDocker();
|
||||
const scriptsDir = docker ? "/app/scripts" : join(process.cwd(), SCRIPTS_DIR);
|
||||
const scriptPath = join(scriptsDir, filename);
|
||||
const scriptPath = getScriptPath(filename);
|
||||
if (existsSync(scriptPath)) {
|
||||
await unlink(scriptPath);
|
||||
}
|
||||
@@ -238,10 +235,7 @@ export const cloneScript = async (
|
||||
|
||||
export const getScriptContent = async (filename: string): Promise<string> => {
|
||||
try {
|
||||
const docker = await isDocker();
|
||||
const scriptPath = docker
|
||||
? join("/app/scripts", filename)
|
||||
: join(process.cwd(), "scripts", filename);
|
||||
const scriptPath = getScriptPath(filename);
|
||||
|
||||
if (existsSync(scriptPath)) {
|
||||
const content = await readFile(scriptPath, "utf8");
|
||||
@@ -278,10 +272,7 @@ export const executeScript = async (
|
||||
}> => {
|
||||
try {
|
||||
await ensureHostScriptsDirectory();
|
||||
const docker = await isDocker();
|
||||
const hostScriptPath = docker
|
||||
? join("/app/scripts", filename)
|
||||
: join(process.cwd(), "scripts", filename);
|
||||
const hostScriptPath = getHostScriptPath(filename);
|
||||
|
||||
if (!existsSync(hostScriptPath)) {
|
||||
return {
|
||||
@@ -291,7 +282,7 @@ export const executeScript = async (
|
||||
};
|
||||
}
|
||||
|
||||
const { stdout, stderr } = await execAsync(`bash "${hostScriptPath}"`, {
|
||||
const { stdout, stderr } = await execAsync(RUN_SCRIPT(hostScriptPath), {
|
||||
timeout: 30000,
|
||||
});
|
||||
|
||||
|
||||
@@ -4,6 +4,7 @@ import { exec } from "child_process";
|
||||
import { promisify } from "util";
|
||||
import { readHostCrontab, writeHostCrontab } from "@/app/_utils/system/hostCrontab";
|
||||
import { isDocker } from "@/app/_server/actions/global";
|
||||
import { READ_CRON_FILE, WRITE_CRON_FILE } from "@/app/_consts/commands";
|
||||
|
||||
const execAsync = promisify(exec);
|
||||
|
||||
@@ -32,7 +33,7 @@ export const readCronFiles = async (): Promise<string> => {
|
||||
|
||||
if (!docker) {
|
||||
try {
|
||||
const { stdout } = await execAsync('crontab -l 2>/dev/null || echo ""');
|
||||
const { stdout } = await execAsync(READ_CRON_FILE());
|
||||
return stdout;
|
||||
} catch (error) {
|
||||
console.error("Error reading crontab:", error);
|
||||
@@ -48,7 +49,7 @@ export const writeCronFiles = async (content: string): Promise<boolean> => {
|
||||
|
||||
if (!docker) {
|
||||
try {
|
||||
await execAsync('echo "' + content + '" | crontab -');
|
||||
await execAsync(WRITE_CRON_FILE(content));
|
||||
return true;
|
||||
} catch (error) {
|
||||
console.error("Error writing crontab:", error);
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
import { promises as fs } from "fs";
|
||||
import path from "path";
|
||||
import { isDocker } from "../_server/actions/global";
|
||||
import { SCRIPTS_DIR } from "../_consts/file";
|
||||
|
||||
export interface Script {
|
||||
id: string;
|
||||
@@ -68,10 +68,7 @@ const scanScriptsDirectory = async (dirPath: string): Promise<Script[]> => {
|
||||
}
|
||||
|
||||
export const loadAllScripts = async (): Promise<Script[]> => {
|
||||
const docker = await isDocker();
|
||||
const scriptsDir = docker
|
||||
? "/app/scripts"
|
||||
: path.join(process.cwd(), "scripts");
|
||||
const scriptsDir = path.join(process.cwd(), SCRIPTS_DIR);
|
||||
return await scanScriptsDirectory(scriptsDir);
|
||||
}
|
||||
|
||||
|
||||
@@ -7,6 +7,7 @@ import {
|
||||
import { parseJobsFromLines, deleteJobInLines, updateJobInLines, pauseJobInLines, resumeJobInLines } from "@/app/_utils/cron/line-manipulation";
|
||||
import { cleanCrontabContent, readCronFiles, writeCronFiles } from "@/app/_utils/cron/files-manipulation";
|
||||
import { isDocker } from "@/app/_server/actions/global";
|
||||
import { READ_CRONTAB, WRITE_CRONTAB } from "@/app/_consts/commands";
|
||||
|
||||
const execAsync = promisify(exec);
|
||||
|
||||
@@ -20,24 +21,28 @@ export interface CronJob {
|
||||
}
|
||||
|
||||
const readUserCrontab = async (user: string): Promise<string> => {
|
||||
if (await isDocker()) {
|
||||
const docker = await isDocker();
|
||||
|
||||
if (docker) {
|
||||
const userCrontabs = await readAllHostCrontabs();
|
||||
const targetUserCrontab = userCrontabs.find((uc) => uc.user === user);
|
||||
return targetUserCrontab?.content || "";
|
||||
} else {
|
||||
const { stdout } = await execAsync(
|
||||
`crontab -l -u ${user} 2>/dev/null || echo ""`
|
||||
READ_CRONTAB(user)
|
||||
);
|
||||
return stdout;
|
||||
}
|
||||
};
|
||||
|
||||
const writeUserCrontab = async (user: string, content: string): Promise<boolean> => {
|
||||
if (await isDocker()) {
|
||||
const docker = await isDocker();
|
||||
|
||||
if (docker) {
|
||||
return await writeHostCrontabForUser(user, content);
|
||||
} else {
|
||||
try {
|
||||
await execAsync(`echo '${content}' | crontab -u ${user} -`);
|
||||
await execAsync(WRITE_CRONTAB(content, user));
|
||||
return true;
|
||||
} catch (error) {
|
||||
console.error(`Error writing crontab for user ${user}:`, error);
|
||||
@@ -47,7 +52,9 @@ const writeUserCrontab = async (user: string, content: string): Promise<boolean>
|
||||
};
|
||||
|
||||
const getAllUsers = async (): Promise<{ user: string; content: string }[]> => {
|
||||
if (await isDocker()) {
|
||||
const docker = await isDocker();
|
||||
|
||||
if (docker) {
|
||||
return await readAllHostCrontabs();
|
||||
} else {
|
||||
const { getAllTargetUsers } = await import("@/app/_utils/system/hostCrontab");
|
||||
@@ -56,9 +63,7 @@ const getAllUsers = async (): Promise<{ user: string; content: string }[]> => {
|
||||
|
||||
for (const user of users) {
|
||||
try {
|
||||
const { stdout } = await execAsync(
|
||||
`crontab -l -u ${user} 2>/dev/null || echo ""`
|
||||
);
|
||||
const { stdout } = await execAsync(READ_CRONTAB(user));
|
||||
results.push({ user, content: stdout });
|
||||
} catch (error) {
|
||||
console.error(`Error reading crontab for user ${user}:`, error);
|
||||
|
||||
@@ -1,3 +1,4 @@
|
||||
import { ID_G, ID_U, READ_CRONTAB, WRITE_HOST_CRONTAB } from "@/app/_consts/commands";
|
||||
import { NSENTER_HOST_CRONTAB } from "@/app/_consts/nsenter";
|
||||
import { exec } from "child_process";
|
||||
import { promisify } from "util";
|
||||
@@ -82,9 +83,7 @@ export const getAllTargetUsers = async (): Promise<string[]> => {
|
||||
export const readHostCrontab = async (): Promise<string> => {
|
||||
try {
|
||||
const user = await getTargetUser();
|
||||
return await execHostCrontab(
|
||||
`crontab -l -u ${user} 2>/dev/null || echo ""`
|
||||
);
|
||||
return await execHostCrontab(READ_CRONTAB(user));
|
||||
} catch (error) {
|
||||
console.error("Error reading host crontab:", error);
|
||||
return "";
|
||||
@@ -100,9 +99,7 @@ export const readAllHostCrontabs = async (): Promise<
|
||||
|
||||
for (const user of users) {
|
||||
try {
|
||||
const content = await execHostCrontab(
|
||||
`crontab -l -u ${user} 2>/dev/null || echo ""`
|
||||
);
|
||||
const content = await execHostCrontab(READ_CRONTAB(user));
|
||||
results.push({ user, content });
|
||||
} catch (error) {
|
||||
console.warn(`Error reading crontab for user ${user}:`, error);
|
||||
@@ -126,9 +123,7 @@ export const writeHostCrontab = async (content: string): Promise<boolean> => {
|
||||
}
|
||||
|
||||
const base64Content = Buffer.from(finalContent).toString("base64");
|
||||
await execHostCrontab(
|
||||
`echo '${base64Content}' | base64 -d | crontab -u ${user} -`
|
||||
);
|
||||
await execHostCrontab(WRITE_HOST_CRONTAB(base64Content, user));
|
||||
return true;
|
||||
} catch (error) {
|
||||
console.error("Error writing host crontab:", error);
|
||||
@@ -147,9 +142,7 @@ export const writeHostCrontabForUser = async (
|
||||
}
|
||||
|
||||
const base64Content = Buffer.from(finalContent).toString("base64");
|
||||
await execHostCrontab(
|
||||
`echo '${base64Content}' | base64 -d | crontab -u ${user} -`
|
||||
);
|
||||
await execHostCrontab(WRITE_HOST_CRONTAB(base64Content, user));
|
||||
return true;
|
||||
} catch (error) {
|
||||
console.error(`Error writing host crontab for user ${user}:`, error);
|
||||
@@ -159,8 +152,8 @@ export const writeHostCrontabForUser = async (
|
||||
|
||||
export async function getUserInfo(username: string): Promise<UserInfo | null> {
|
||||
try {
|
||||
const uidResult = await execHostCrontab(`id -u ${username}`);
|
||||
const gidResult = await execHostCrontab(`id -g ${username}`);
|
||||
const uidResult = await execHostCrontab(ID_U(username));
|
||||
const gidResult = await execHostCrontab(ID_G(username));
|
||||
|
||||
const uid = parseInt(uidResult.trim());
|
||||
const gid = parseInt(gidResult.trim());
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
# @id: demo-script
|
||||
# @title: Hi, this is a demo scripttttt
|
||||
# @title: Hi, this is a demo script
|
||||
# @description: This script logs a "hello world" to teach you how scripts work.
|
||||
|
||||
#!/bin/bash
|
||||
|
||||
Reference in New Issue
Block a user