diff --git a/app/_consts/commands.ts b/app/_consts/commands.ts new file mode 100644 index 0000000..dfa5770 --- /dev/null +++ b/app/_consts/commands.ts @@ -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}"`; \ No newline at end of file diff --git a/app/_server/actions/cronjobs/index.ts b/app/_server/actions/cronjobs/index.ts index 0b4d661..9123737 100644 --- a/app/_server/actions/cronjobs/index.ts +++ b/app/_server/actions/cronjobs/index.ts @@ -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, { diff --git a/app/_server/actions/scripts/index.ts b/app/_server/actions/scripts/index.ts index e5c2dee..f955d33 100644 --- a/app/_server/actions/scripts/index.ts +++ b/app/_server/actions/scripts/index.ts @@ -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 => { 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, }); diff --git a/app/_utils/cron/files-manipulation.ts b/app/_utils/cron/files-manipulation.ts index f74c7b2..e4d3f36 100644 --- a/app/_utils/cron/files-manipulation.ts +++ b/app/_utils/cron/files-manipulation.ts @@ -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 => { 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 => { if (!docker) { try { - await execAsync('echo "' + content + '" | crontab -'); + await execAsync(WRITE_CRON_FILE(content)); return true; } catch (error) { console.error("Error writing crontab:", error); diff --git a/app/_utils/scriptScanner.ts b/app/_utils/scriptScanner.ts index 30d7d8a..ac62ca1 100644 --- a/app/_utils/scriptScanner.ts +++ b/app/_utils/scriptScanner.ts @@ -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 => { } export const loadAllScripts = async (): Promise => { - 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); } diff --git a/app/_utils/system/cron.ts b/app/_utils/system/cron.ts index cd6c578..75b8117 100644 --- a/app/_utils/system/cron.ts +++ b/app/_utils/system/cron.ts @@ -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 => { - 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 => { - 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 }; 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); diff --git a/app/_utils/system/hostCrontab.ts b/app/_utils/system/hostCrontab.ts index 2b5cae4..7ed5b7d 100644 --- a/app/_utils/system/hostCrontab.ts +++ b/app/_utils/system/hostCrontab.ts @@ -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 => { export const readHostCrontab = async (): Promise => { 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 => { } 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 { 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()); diff --git a/scripts/demo-script.sh b/scripts/demo-script.sh index d11ca78..f063b51 100755 --- a/scripts/demo-script.sh +++ b/scripts/demo-script.sh @@ -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