mirror of
https://github.com/9technologygroup/patchmon.net.git
synced 2025-10-23 16:13:57 +00:00
Merge pull request #98 from 9technologygroup/dev
Fixed Crontab timing Expression
This commit is contained in:
@@ -84,7 +84,7 @@ apt install curl -y
|
||||
|
||||
#### Script
|
||||
```bash
|
||||
curl -fsSL -o setup.sh https://raw.githubusercontent.com/9technologygroup/patchmon.net/main/setup.sh && chmod +x setup.sh && bash setup.sh
|
||||
curl -fsSL -o setup.sh https://raw.githubusercontent.com/9technologygroup/patchmon.net/refs/heads/main/setup.sh && chmod +x setup.sh && bash setup.sh
|
||||
```
|
||||
|
||||
#### Minimum specs for building : #####
|
||||
|
@@ -1093,16 +1093,33 @@ update_crontab() {
|
||||
local response=$(curl -ks -H "X-API-ID: $API_ID" -H "X-API-KEY: $API_KEY" -X GET "$PATCHMON_SERVER/api/$API_VERSION/settings/update-interval")
|
||||
if [[ $? -eq 0 ]]; then
|
||||
local update_interval=$(echo "$response" | grep -o '"updateInterval":[0-9]*' | cut -d':' -f2)
|
||||
# Fallback if not found
|
||||
if [[ -z "$update_interval" ]]; then
|
||||
update_interval=60
|
||||
fi
|
||||
# Normalize interval: 5-59 valid, otherwise snap to hour presets
|
||||
if [[ $update_interval -lt 5 ]]; then
|
||||
update_interval=5
|
||||
elif [[ $update_interval -gt 1440 ]]; then
|
||||
update_interval=1440
|
||||
fi
|
||||
if [[ -n "$update_interval" ]]; then
|
||||
# Generate the expected crontab entry
|
||||
local expected_crontab=""
|
||||
if [[ $update_interval -lt 60 ]]; then
|
||||
# Every N minutes (5-59)
|
||||
expected_crontab="*/$update_interval * * * * /usr/local/bin/patchmon-agent.sh update >/dev/null 2>&1"
|
||||
else
|
||||
# Hour-based schedules
|
||||
if [[ $update_interval -eq 60 ]]; then
|
||||
# Hourly updates starting at current minute
|
||||
# Hourly updates starting at current minute to spread load
|
||||
local current_minute=$(date +%M)
|
||||
expected_crontab="$current_minute * * * * /usr/local/bin/patchmon-agent.sh update >/dev/null 2>&1"
|
||||
else
|
||||
# Custom interval updates
|
||||
expected_crontab="*/$update_interval * * * * /usr/local/bin/patchmon-agent.sh update >/dev/null 2>&1"
|
||||
# For 120, 180, 360, 720, 1440 -> every H hours at minute 0
|
||||
local hours=$((update_interval / 60))
|
||||
expected_crontab="0 */$hours * * * /usr/local/bin/patchmon-agent.sh update >/dev/null 2>&1"
|
||||
fi
|
||||
fi
|
||||
|
||||
# Get current crontab (without patchmon entries)
|
||||
|
@@ -207,20 +207,32 @@ setup_crontab() {
|
||||
local update_interval="$1"
|
||||
local patchmon_pattern="/usr/local/bin/patchmon-agent.sh update"
|
||||
|
||||
# Normalize interval: min 5, max 1440
|
||||
if [[ -z "$update_interval" ]]; then update_interval=60; fi
|
||||
if [[ "$update_interval" -lt 5 ]]; then update_interval=5; fi
|
||||
if [[ "$update_interval" -gt 1440 ]]; then update_interval=1440; fi
|
||||
|
||||
# Get current crontab, remove any existing patchmon entries
|
||||
local current_cron=$(crontab -l 2>/dev/null | grep -v "$patchmon_pattern" || true)
|
||||
|
||||
# Determine new cron entry
|
||||
local new_entry
|
||||
if [[ "$update_interval" -lt 60 ]]; then
|
||||
# Every N minutes (5-59)
|
||||
new_entry="*/$update_interval * * * * $patchmon_pattern >/dev/null 2>&1"
|
||||
info "📋 Configuring updates every $update_interval minutes"
|
||||
else
|
||||
if [[ "$update_interval" -eq 60 ]]; then
|
||||
# Hourly updates - use a random minute to spread load
|
||||
# Hourly updates - use current minute to spread load
|
||||
local current_minute=$(date +%M)
|
||||
new_entry="$current_minute * * * * $patchmon_pattern >/dev/null 2>&1"
|
||||
info "📋 Configuring hourly updates at minute $current_minute"
|
||||
else
|
||||
# Custom interval updates
|
||||
new_entry="*/$update_interval * * * * $patchmon_pattern >/dev/null 2>&1"
|
||||
info "📋 Configuring updates every $update_interval minutes"
|
||||
# For 120, 180, 360, 720, 1440 -> every H hours at minute 0
|
||||
local hours=$((update_interval / 60))
|
||||
new_entry="0 */$hours * * * $patchmon_pattern >/dev/null 2>&1"
|
||||
info "📋 Configuring updates every $hours hour(s)"
|
||||
fi
|
||||
fi
|
||||
|
||||
# Combine existing cron (without patchmon entries) + new entry
|
||||
|
@@ -105,6 +105,45 @@ async function triggerCrontabUpdates() {
|
||||
}
|
||||
}
|
||||
|
||||
// Helpers
|
||||
function normalizeUpdateInterval(minutes) {
|
||||
let m = parseInt(minutes, 10);
|
||||
if (Number.isNaN(m)) return 60;
|
||||
if (m < 5) m = 5;
|
||||
if (m > 1440) m = 1440;
|
||||
if (m < 60) {
|
||||
// Clamp to 5-59, step 5
|
||||
const snapped = Math.round(m / 5) * 5;
|
||||
return Math.min(59, Math.max(5, snapped));
|
||||
}
|
||||
// Allowed hour-based presets
|
||||
const allowed = [60, 120, 180, 360, 720, 1440];
|
||||
let nearest = allowed[0];
|
||||
let bestDiff = Math.abs(m - nearest);
|
||||
for (const a of allowed) {
|
||||
const d = Math.abs(m - a);
|
||||
if (d < bestDiff) {
|
||||
bestDiff = d;
|
||||
nearest = a;
|
||||
}
|
||||
}
|
||||
return nearest;
|
||||
}
|
||||
|
||||
function buildCronExpression(minutes) {
|
||||
const m = normalizeUpdateInterval(minutes);
|
||||
if (m < 60) {
|
||||
return `*/${m} * * * *`;
|
||||
}
|
||||
if (m === 60) {
|
||||
// Hourly at current minute is chosen by agent; default 0 here
|
||||
return `0 * * * *`;
|
||||
}
|
||||
const hours = Math.floor(m / 60);
|
||||
// Every N hours at minute 0
|
||||
return `0 */${hours} * * *`;
|
||||
}
|
||||
|
||||
// Get current settings
|
||||
router.get("/", authenticateToken, requireManageSettings, async (_req, res) => {
|
||||
try {
|
||||
@@ -191,11 +230,13 @@ router.put(
|
||||
const oldUpdateInterval = currentSettings.update_interval;
|
||||
|
||||
// Update settings using the service
|
||||
const normalizedInterval = normalizeUpdateInterval(updateInterval || 60);
|
||||
|
||||
const updatedSettings = await updateSettings(currentSettings.id, {
|
||||
server_protocol: serverProtocol,
|
||||
server_host: serverHost,
|
||||
server_port: serverPort,
|
||||
update_interval: updateInterval || 60,
|
||||
update_interval: normalizedInterval,
|
||||
auto_update: autoUpdate || false,
|
||||
signup_enabled: signupEnabled || false,
|
||||
default_user_role:
|
||||
@@ -211,9 +252,9 @@ router.put(
|
||||
console.log("Settings updated successfully:", updatedSettings);
|
||||
|
||||
// If update interval changed, trigger crontab updates on all hosts with auto-update enabled
|
||||
if (oldUpdateInterval !== (updateInterval || 60)) {
|
||||
if (oldUpdateInterval !== normalizedInterval) {
|
||||
console.log(
|
||||
`Update interval changed from ${oldUpdateInterval} to ${updateInterval || 60} minutes. Triggering crontab updates...`,
|
||||
`Update interval changed from ${oldUpdateInterval} to ${normalizedInterval} minutes. Triggering crontab updates...`,
|
||||
);
|
||||
await triggerCrontabUpdates();
|
||||
}
|
||||
@@ -262,9 +303,10 @@ router.get("/update-interval", async (req, res) => {
|
||||
}
|
||||
|
||||
const settings = await getSettings();
|
||||
const interval = normalizeUpdateInterval(settings.update_interval || 60);
|
||||
res.json({
|
||||
updateInterval: settings.update_interval,
|
||||
cronExpression: `*/${settings.update_interval} * * * *`, // Generate cron expression
|
||||
updateInterval: interval,
|
||||
cronExpression: buildCronExpression(interval),
|
||||
});
|
||||
} catch (error) {
|
||||
console.error("Update interval fetch error:", error);
|
||||
|
@@ -272,9 +272,37 @@ const Settings = () => {
|
||||
}
|
||||
};
|
||||
|
||||
// Normalize update interval to safe presets
|
||||
const normalizeInterval = (minutes) => {
|
||||
let m = parseInt(minutes, 10);
|
||||
if (Number.isNaN(m)) return 60;
|
||||
if (m < 5) m = 5;
|
||||
if (m > 1440) m = 1440;
|
||||
// If less than 60 minutes, keep within 5-59 and step of 5
|
||||
if (m < 60) {
|
||||
return Math.min(59, Math.max(5, Math.round(m / 5) * 5));
|
||||
}
|
||||
// 60 or more: only allow exact hour multiples (60, 120, 180, 360, 720, 1440)
|
||||
const allowed = [60, 120, 180, 360, 720, 1440];
|
||||
// Snap to nearest allowed value
|
||||
let nearest = allowed[0];
|
||||
let bestDiff = Math.abs(m - nearest);
|
||||
for (const a of allowed) {
|
||||
const d = Math.abs(m - a);
|
||||
if (d < bestDiff) {
|
||||
bestDiff = d;
|
||||
nearest = a;
|
||||
}
|
||||
}
|
||||
return nearest;
|
||||
};
|
||||
|
||||
const handleInputChange = (field, value) => {
|
||||
setFormData((prev) => {
|
||||
const newData = { ...prev, [field]: value };
|
||||
const newData = {
|
||||
...prev,
|
||||
[field]: field === "updateInterval" ? normalizeInterval(value) : value,
|
||||
};
|
||||
return newData;
|
||||
});
|
||||
setIsDirty(true);
|
||||
@@ -563,7 +591,8 @@ const Settings = () => {
|
||||
|
||||
{/* Quick presets */}
|
||||
<div className="mt-3 flex flex-wrap items-center gap-2">
|
||||
{[15, 30, 60, 120, 360, 720, 1440].map((m) => (
|
||||
{[5, 10, 15, 30, 45, 60, 120, 180, 360, 720, 1440].map(
|
||||
(m) => (
|
||||
<button
|
||||
key={m}
|
||||
type="button"
|
||||
@@ -577,7 +606,8 @@ const Settings = () => {
|
||||
>
|
||||
{m % 60 === 0 ? `${m / 60}h` : `${m}m`}
|
||||
</button>
|
||||
))}
|
||||
),
|
||||
)}
|
||||
</div>
|
||||
|
||||
{/* Range slider */}
|
||||
@@ -588,12 +618,13 @@ const Settings = () => {
|
||||
max="1440"
|
||||
step="5"
|
||||
value={formData.updateInterval}
|
||||
onChange={(e) =>
|
||||
onChange={(e) => {
|
||||
const raw = parseInt(e.target.value, 10);
|
||||
handleInputChange(
|
||||
"updateInterval",
|
||||
parseInt(e.target.value, 10),
|
||||
)
|
||||
}
|
||||
normalizeInterval(raw),
|
||||
);
|
||||
}}
|
||||
className="w-full accent-primary-600"
|
||||
aria-label="Update interval slider"
|
||||
/>
|
||||
|
Reference in New Issue
Block a user