Skip to content

Commit 08d3715

Browse files
committed
update readme, clean up code, add contributions
1 parent 888297c commit 08d3715

7 files changed

Lines changed: 31 additions & 46 deletions

File tree

Dockerfile

Lines changed: 0 additions & 17 deletions
Original file line numberDiff line numberDiff line change
@@ -1,18 +1,15 @@
11
FROM node:20-slim AS base
22

3-
# Install system utilities for system information and nsenter for host crontab access
43
RUN apt-get update && apt-get install -y \
54
pciutils \
65
curl \
76
iputils-ping \
87
util-linux \
98
&& rm -rf /var/lib/apt/lists/*
109

11-
# Install dependencies only when needed
1210
FROM base AS deps
1311
WORKDIR /app
1412

15-
# Install dependencies based on the preferred package manager
1613
COPY package.json yarn.lock* package-lock.json* pnpm-lock.yaml* ./
1714
RUN \
1815
if [ -f yarn.lock ]; then yarn --frozen-lockfile; \
@@ -21,20 +18,15 @@ RUN \
2118
else echo "Lockfile not found." && exit 1; \
2219
fi
2320

24-
# Rebuild the source code only when needed
2521
FROM base AS builder
2622
WORKDIR /app
2723
COPY --from=deps /app/node_modules ./node_modules
2824
COPY . .
2925

30-
# Next.js collects completely anonymous telemetry data about general usage.
31-
# Learn more here: https://nextjs.org/telemetry
32-
# Uncomment the following line in case you want to disable telemetry during the build.
3326
ENV NEXT_TELEMETRY_DISABLED=1
3427

3528
RUN yarn build
3629

37-
# Production image, copy all the files and run next
3830
FROM base AS runner
3931
WORKDIR /app
4032

@@ -44,29 +36,20 @@ ENV NEXT_TELEMETRY_DISABLED=1
4436
RUN groupadd --system --gid 1001 nodejs
4537
RUN useradd --system --uid 1001 nextjs
4638

47-
# Create directories for mounted volumes with proper permissions
4839
RUN mkdir -p /app/scripts /app/data /app/snippets && \
4940
chown -R nextjs:nodejs /app/scripts /app/data /app/snippets
5041

51-
# Copy public directory
5242
COPY --from=builder /app/public ./public
5343

54-
# Copy the entire .next directory
5544
COPY --from=builder --chown=nextjs:nodejs /app/.next ./.next
5645

57-
# Copy app directory for builtin snippets and other app files
5846
COPY --from=builder --chown=nextjs:nodejs /app/app ./app
5947

60-
# Copy package.json and yarn.lock for yarn start
6148
COPY --from=builder /app/package.json ./package.json
6249
COPY --from=builder /app/yarn.lock ./yarn.lock
6350

64-
# Copy node_modules for production dependencies
6551
COPY --from=deps --chown=nextjs:nodejs /app/node_modules ./node_modules
6652

67-
# Don't set default user - let docker-compose decide
68-
# USER nextjs
69-
7053
EXPOSE 3000
7154

7255
ENV PORT=3000

README.md

Lines changed: 27 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -49,7 +49,7 @@ If you find my projects helpful and want to fuel my late-night coding sessions w
4949
```bash
5050
services:
5151
cronjob-manager:
52-
build: .
52+
image: ghcr.io/fccview/cronmaster:main
5353
container_name: cronmaster-test
5454
user: "root"
5555
ports:
@@ -212,6 +212,32 @@ The application uses standard cron format: `* * * * *`
212212
4. Add tests if applicable
213213
5. Submit a pull request
214214

215+
## Community shouts
216+
217+
I would like to thank the following members for raising issues and help test/debug them!
218+
219+
<table>
220+
<tbody>
221+
<tr>
222+
<td align="center" valign="top" width="20%">
223+
<a href="https://github.com/hermannx5"><img width="100" height="100" alt="hermannx5" src="https://avatars.githubusercontent.com/u/46320338?v=4&s=100"><br/>hermannx5</a>
224+
</td>
225+
<td align="center" valign="top" width="20%">
226+
<a href="https://github.com/edersong"><img width="100" height="100" src="https://avatars.githubusercontent.com/u/64137913?v=4&s=100"><br />edersong</a>
227+
</td>
228+
<td align="center" valign="top" width="20%">
229+
<a href="https://github.com/corasaniti"><img width="100" height="100" src="https://avatars.githubusercontent.com/u/5001932?u=2e8bc25b74eb11f7675d38c8e312374794a7b6e0&v=4&s=100"><br />corasaniti</a>
230+
</td>
231+
<td align="center" valign="top" width="20%">
232+
<a href="https://github.com/abhisheknair"><img width="100" height="100" src="https://avatars.githubusercontent.com/u/5221047?u=313beaabbb4a8e82fe07a2523076b4dafdc0bfec&v=4&s=100"><br />abhisheknair</a>
233+
</td>
234+
<td align="center" valign="top" width="20%">
235+
<a href="https://github.com/mariushosting"><img width="100" height="100" src="https://avatars.githubusercontent.com/u/37554361?u=9007d0600680ac2b267bde2d8c19b05c06285a34&v=4&s=100"><br />mariushosting</a>
236+
</td>
237+
</tr>
238+
</tbody>
239+
</table>
240+
215241
## License
216242

217243
This project is licensed under the MIT License.

app/_utils/system/cron.ts

Lines changed: 0 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -24,7 +24,6 @@ async function readCronFiles(): Promise<string> {
2424
}
2525
}
2626

27-
// Use the new host crontab utility for Docker
2827
return await readHostCrontab();
2928
}
3029

@@ -41,7 +40,6 @@ async function writeCronFiles(content: string): Promise<boolean> {
4140
}
4241
}
4342

44-
// Use the new host crontab utility for Docker
4543
return await writeHostCrontab(content);
4644
}
4745

@@ -115,12 +113,10 @@ export async function addCronJob(
115113
? `# ${comment}\n${schedule} ${command}`
116114
: `${schedule} ${command}`;
117115

118-
// Handle empty crontab vs existing content properly
119116
let newCron;
120117
if (cronContent.trim() === "") {
121118
newCron = newEntry;
122119
} else {
123-
// Ensure existing content ends with newline before adding new entry
124120
const existingContent = cronContent.endsWith('\n') ? cronContent : cronContent + '\n';
125121
newCron = existingContent + newEntry;
126122
}

app/_utils/system/hostCrontab.ts

Lines changed: 1 addition & 18 deletions
Original file line numberDiff line numberDiff line change
@@ -3,14 +3,8 @@ import { promisify } from "util";
33

44
const execAsync = promisify(exec);
55

6-
/**
7-
* Execute crontab command on the host system using nsenter to host namespaces
8-
* This allows the Docker container to manage the host's crontab
9-
*/
106
async function execHostCrontab(command: string): Promise<string> {
117
try {
12-
// Find the host's init process and use nsenter to execute commands in host context
13-
// We use PID 1 which should be the host's init process due to pid: "host" in docker-compose
148
const { stdout } = await execAsync(
159
`nsenter -t 1 -m -u -i -n -p sh -c "${command}"`
1610
);
@@ -21,24 +15,17 @@ async function execHostCrontab(command: string): Promise<string> {
2115
}
2216
}
2317

24-
// Get the target user for crontab operations by detecting the host user dynamically
2518
async function getTargetUser(): Promise<string> {
2619
try {
27-
// If explicitly set via environment variable, use that
2820
if (process.env.HOST_CRONTAB_USER) {
2921
return process.env.HOST_CRONTAB_USER;
3022
}
3123

32-
// Auto-detect the user by finding the owner of the docker socket
33-
// This will typically be the user who started docker compose
3424
const { stdout } = await execAsync('stat -c "%U" /var/run/docker.sock');
3525
const dockerSocketOwner = stdout.trim();
3626

37-
// If docker socket is owned by root, try to find the actual user
38-
// by looking at process tree or mounted directories
3927
if (dockerSocketOwner === 'root') {
4028
try {
41-
// Try to detect from the mounted project directory ownership
4229
const projectDir = process.env.NEXT_PUBLIC_HOST_PROJECT_DIR;
4330
if (projectDir) {
4431
const dirOwner = await execHostCrontab(`stat -c "%U" "${projectDir}"`);
@@ -48,7 +35,6 @@ async function getTargetUser(): Promise<string> {
4835
console.warn("Could not detect user from project directory:", error);
4936
}
5037

51-
// Fall back to looking for non-root users with home directories
5238
try {
5339
const users = await execHostCrontab('getent passwd | grep ":/home/" | head -1 | cut -d: -f1');
5440
const firstUser = users.trim();
@@ -59,14 +45,13 @@ async function getTargetUser(): Promise<string> {
5945
console.warn("Could not detect user from passwd:", error);
6046
}
6147

62-
// Last resort - return root
6348
return 'root';
6449
}
6550

6651
return dockerSocketOwner;
6752
} catch (error) {
6853
console.error("Error detecting target user:", error);
69-
return 'root'; // Safe fallback
54+
return 'root';
7055
}
7156
}
7257

@@ -83,13 +68,11 @@ export async function readHostCrontab(): Promise<string> {
8368
export async function writeHostCrontab(content: string): Promise<boolean> {
8469
try {
8570
const user = await getTargetUser();
86-
// Ensure content ends with a newline (required by crontab)
8771
let finalContent = content;
8872
if (!finalContent.endsWith('\n')) {
8973
finalContent += '\n';
9074
}
9175

92-
// Use base64 encoding to avoid all shell escaping issues
9376
const base64Content = Buffer.from(finalContent).toString('base64');
9477
await execHostCrontab(`echo '${base64Content}' | base64 -d | crontab -u ${user} -`);
9578
return true;

app/api/system-stats/route.ts

Lines changed: 0 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -43,7 +43,6 @@ export async function GET(request: NextRequest) {
4343
}
4444
};
4545

46-
// Calculate memory usage properly - use active memory, not just used
4746
const actualUsed = memInfo.active || memInfo.used;
4847
const actualFree = memInfo.available || memInfo.free;
4948
const memUsage = ((actualUsed / memInfo.total) * 100);
@@ -88,7 +87,6 @@ export async function GET(request: NextRequest) {
8887
? `${Math.round(((mainInterface.rx_sec || 0) + (mainInterface.tx_sec || 0)) / 1024 / 1024)} Mbps`
8988
: "Unknown";
9089

91-
// Get network latency via ping
9290
let latency = 0;
9391
try {
9492
const { exec } = require('child_process');

docker-compose.yml

Lines changed: 2 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
services:
22
cronjob-manager:
3-
build: .
3+
image: ghcr.io/fccview/cronmaster:main
44
container_name: cronmaster-test
55
user: "root"
66
ports:
@@ -10,8 +10,7 @@ services:
1010
- NODE_ENV=production
1111
- DOCKER=true
1212
- NEXT_PUBLIC_CLOCK_UPDATE_INTERVAL=30000
13-
#- NEXT_PUBLIC_HOST_PROJECT_DIR=/path/to/cronmaster/directory
14-
- NEXT_PUBLIC_HOST_PROJECT_DIR=/home/fccview/www/projects/cronjob
13+
- NEXT_PUBLIC_HOST_PROJECT_DIR=/path/to/cronmaster/directory
1514
# If docker struggles to find your crontab user, update this variable with it.
1615
# Obviously replace fccview with your user - find it with: ls -asl /var/spool/cron/crontabs/
1716
# - HOST_CRONTAB_USER=fccview

next.config.js

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
/** @type {import('next').NextConfig} */
22
const nextConfig = {
3-
// Removed standalone output for traditional Next.js deployment
3+
44
}
55

66
module.exports = nextConfig

0 commit comments

Comments
 (0)