mirror of
https://github.com/fccview/cronmaster.git
synced 2025-12-23 22:18:20 -05:00
Merge pull request #43 from Navino16/feature/extra-groups-ids
Very happy with these changes, thank you for the help!
This commit is contained in:
1
.gitignore
vendored
1
.gitignore
vendored
@@ -11,5 +11,6 @@ node_modules
|
||||
.vscode
|
||||
.DS_Store
|
||||
.cursorignore
|
||||
.idea
|
||||
tsconfig.tsbuildinfo
|
||||
docker-compose.test.yml
|
||||
34
README.md
34
README.md
@@ -2,18 +2,6 @@
|
||||
<img src="public/heading.png" width="400px">
|
||||
</p>
|
||||
|
||||
# ATTENTION BREAKING UPDATE!!
|
||||
|
||||
> The latest `main` branch has completely changed the way this app used to run.
|
||||
> The main reason being trying to address some security concerns and make the whole application work
|
||||
> across multiple platform without too much trouble.
|
||||
>
|
||||
> If you came here due to this change trying to figure out why your app stopped working you have two options:
|
||||
>
|
||||
> 1 - Update your `docker-compose.yml` with the new one provided within this readme (or just copy [docker-compose.yml](docker-compose.yml))
|
||||
>
|
||||
> 2 - Keep your `docker-compose.yml` file as it is and use the legacy tag in the image `image: ghcr.io/fccview/cronmaster:legacy`. However bear in mind this will not be supported going forward, any issue regarding the legacy tag will be ignored and I will only support the main branch. Feel free to fork that specific branch in case you want to work on it yourself :)
|
||||
|
||||
## Features
|
||||
|
||||
- **Modern UI**: Beautiful, responsive interface with dark/light mode.
|
||||
@@ -23,6 +11,23 @@
|
||||
- **Docker Support**: Runs entirely from a Docker container.
|
||||
- **Easy Setup**: Quick presets for common cron schedules.
|
||||
|
||||
<br />
|
||||
|
||||
---
|
||||
|
||||
<p align="center">
|
||||
<a href="http://discord.gg/invite/mMuk2WzVZu">
|
||||
<img width="40" src="public/repo-images/discord_icon.webp">
|
||||
</a>
|
||||
<br />
|
||||
<i>Join the discord server for more info</i>
|
||||
<br />
|
||||
</p>
|
||||
|
||||
---
|
||||
|
||||
<br />
|
||||
|
||||
## Before we start
|
||||
|
||||
Hey there! 👋 Just a friendly heads-up: I'm a big believer in open source and love sharing my work with the community. Everything you find in my GitHub repos is and always will be 100% free. If someone tries to sell you a "premium" version of any of my projects while claiming to be me, please know that this is not legitimate. 🚫
|
||||
@@ -166,6 +171,8 @@ HOST_PROJECT_DIR=/home/<your_user_here>/homelab/cronmaster
|
||||
- `HOST_PROJECT_DIR` is required in order for the scripts created within the app to run properly
|
||||
- The `DOCKER=true` environment variable enables direct file access mode for crontab operations. This is REQUIRED when running the application in docker mode.
|
||||
|
||||
**Important Note on Root Commands**: When running commands as `root` within Cronmaster, ensure that these commands also function correctly as `root` on your host machine. If a command works as `root` on your host but fails within Cronmaster, please open an issue with detailed information.
|
||||
|
||||
## Usage
|
||||
|
||||
### Viewing System Information
|
||||
@@ -257,6 +264,9 @@ I would like to thank the following members for raising issues and help test/deb
|
||||
<td align="center" valign="top" width="20%">
|
||||
<a href="https://github.com/cerede2000"><img width="100" height="100" src="https://avatars.githubusercontent.com/u/38144752?v=4&size=100"><br />cerede2000</a>
|
||||
</td>
|
||||
<td align="center" valign="top" width="20%">
|
||||
<a href="https://github.com/Navino16"><img width="100" height="100" src="https://avatars.githubusercontent.com/u/22234867?v=4&size=100"><br />Navino16</a>
|
||||
</td>
|
||||
</tr>
|
||||
</tbody>
|
||||
</table>
|
||||
|
||||
@@ -265,12 +265,10 @@ export const runCronJob = async (
|
||||
|
||||
if (isDocker) {
|
||||
const userInfo = await getUserInfo(job.user);
|
||||
const executionUser = userInfo ? userInfo.username : "root";
|
||||
const escapedCommand = job.command.replace(/'/g, "'\\''");
|
||||
|
||||
if (userInfo && userInfo.username !== "root") {
|
||||
command = `nsenter -t 1 -m -u -i -n -p --setuid=${userInfo.uid} --setgid=${userInfo.gid} sh -c "${job.command}"`;
|
||||
} else {
|
||||
command = `nsenter -t 1 -m -u -i -n -p sh -c "${job.command}"`;
|
||||
}
|
||||
command = `nsenter -t 1 -m -u -i -n -p su - ${executionUser} -c '${escapedCommand}'`;
|
||||
}
|
||||
|
||||
const { stdout, stderr } = await execAsync(command, {
|
||||
|
||||
@@ -21,7 +21,7 @@ const sanitizeScriptName = (name: string): string => {
|
||||
.replace(/-+/g, "-")
|
||||
.replace(/^-|-$/g, "")
|
||||
.substring(0, 50);
|
||||
}
|
||||
};
|
||||
|
||||
const generateUniqueFilename = async (baseName: string): Promise<string> => {
|
||||
const scripts = await loadAllScripts();
|
||||
@@ -34,14 +34,14 @@ const generateUniqueFilename = async (baseName: string): Promise<string> => {
|
||||
}
|
||||
|
||||
return filename;
|
||||
}
|
||||
};
|
||||
|
||||
const ensureScriptsDirectory = async () => {
|
||||
const scriptsDir = await SCRIPTS_DIR();
|
||||
if (!existsSync(scriptsDir)) {
|
||||
await mkdir(scriptsDir, { recursive: true });
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
const ensureHostScriptsDirectory = async () => {
|
||||
const hostProjectDir = process.env.HOST_PROJECT_DIR || process.cwd();
|
||||
@@ -50,7 +50,7 @@ const ensureHostScriptsDirectory = async () => {
|
||||
if (!existsSync(hostScriptsDir)) {
|
||||
await mkdir(hostScriptsDir, { recursive: true });
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
const saveScriptFile = async (filename: string, content: string) => {
|
||||
const isDocker = process.env.DOCKER === "true";
|
||||
@@ -59,7 +59,13 @@ const saveScriptFile = async (filename: string, content: string) => {
|
||||
|
||||
const scriptPath = join(scriptsDir, filename);
|
||||
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 isDocker = process.env.DOCKER === "true";
|
||||
@@ -68,11 +74,11 @@ const deleteScriptFile = async (filename: string) => {
|
||||
if (existsSync(scriptPath)) {
|
||||
await unlink(scriptPath);
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
export const fetchScripts = async (): Promise<Script[]> => {
|
||||
return await loadAllScripts();
|
||||
}
|
||||
};
|
||||
|
||||
export const createScript = async (
|
||||
formData: FormData
|
||||
@@ -120,7 +126,7 @@ export const createScript = async (
|
||||
console.error("Error creating script:", error);
|
||||
return { success: false, message: "Error creating script" };
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
export const updateScript = async (
|
||||
formData: FormData
|
||||
@@ -159,7 +165,7 @@ export const updateScript = async (
|
||||
console.error("Error updating script:", error);
|
||||
return { success: false, message: "Error updating script" };
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
export const deleteScript = async (
|
||||
id: string
|
||||
@@ -180,7 +186,7 @@ export const deleteScript = async (
|
||||
console.error("Error deleting script:", error);
|
||||
return { success: false, message: "Error deleting script" };
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
export const cloneScript = async (
|
||||
id: string,
|
||||
@@ -230,7 +236,7 @@ export const cloneScript = async (
|
||||
console.error("Error cloning script:", error);
|
||||
return { success: false, message: "Error cloning script" };
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
export const getScriptContent = async (filename: string): Promise<string> => {
|
||||
try {
|
||||
@@ -263,9 +269,11 @@ export const getScriptContent = async (filename: string): Promise<string> => {
|
||||
console.error("Error reading script content:", error);
|
||||
return "";
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
export const executeScript = async (filename: string): Promise<{
|
||||
export const executeScript = async (
|
||||
filename: string
|
||||
): Promise<{
|
||||
success: boolean;
|
||||
output: string;
|
||||
error: string;
|
||||
@@ -301,4 +309,4 @@ export const executeScript = async (filename: string): Promise<{
|
||||
error: error.message || "Unknown error",
|
||||
};
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
BIN
public/repo-images/discord_icon.webp
Normal file
BIN
public/repo-images/discord_icon.webp
Normal file
Binary file not shown.
|
After Width: | Height: | Size: 4.0 KiB |
Reference in New Issue
Block a user