Skip to content

Commit 6186e35

Browse files
committed
chore: update changelog and fix backup handling
- Updated CHANGELOG.md to document fixes for cron job next-run calculation and backup exclusion handling. - Enhanced ServerSchedule.php to ensure accurate next-run time calculations for cron jobs, accommodating monthly and yearly expressions. - Improved BackupIgnoreHelper.php documentation to clarify pattern formats. - Updated frontend components to handle backup ignore patterns more effectively and reflect loading states in the UI. - Closes #187 - Closes #185 - Closes #186 - Closes #184
1 parent bdd143a commit 6186e35

10 files changed

Lines changed: 202 additions & 145 deletions

File tree

CHANGELOG.md

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -25,6 +25,8 @@
2525
- Fixed the issue with unlimited backup limit. by @nayskutzu
2626
- Issues related to port allocation were fixed. by @nayskutzu
2727
- Manual and scheduled server backups now honor custom backup names and exclude lists (patterns are sent to Wings in the correct format). by @nayskutzu
28+
- Fixed the issue with the next-run calculation for cron jobs. by @nayskutzu
29+
- Backups ignored the exclude list and backup names. by @nayskutzu
2830

2931
## v1.3.7.4 STABLE
3032

backend/app/Chat/ServerSchedule.php

Lines changed: 29 additions & 17 deletions
Original file line numberDiff line numberDiff line change
@@ -572,19 +572,22 @@ public static function calculateNextRunTime(string $dayOfWeek, string $month, st
572572
if (class_exists(CrontabSchedule::class)) {
573573
try {
574574
$schedule = new CrontabSchedule($expression);
575-
if (method_exists($schedule, 'getNextRunDate')) {
576-
$base = self::resolveBaseDateTime($referenceTime, $tz);
577-
$nextRun = $schedule->getNextRunDate($base, 0, false);
578-
$now = new \DateTime('now', $tz);
579-
580-
// Ensure next run is strictly in the future to avoid schedules running instantly
581-
if ($nextRun <= $now) {
582-
$nextRun->modify('+1 second');
583-
$nextRun = $schedule->getNextRunDate($nextRun, 0, false);
584-
}
575+
$base = self::resolveBaseDateTime($referenceTime, $tz);
576+
$now = new \DateTime('now', $tz);
577+
578+
$nextRun = clone $base;
579+
$nextRun->modify('+1 minute');
580+
581+
// cron/cron exposes valid() rather than getNextRunDate(); scan forward
582+
// far enough for monthly/yearly expressions (incl. leap-day gaps).
583+
$limit = (clone $nextRun)->modify('+4 years');
585584

586-
// Persist as UTC literal so DB comparisons stay deterministic.
587-
return $nextRun->setTimezone($utc)->format('Y-m-d H:i:s');
585+
while ($nextRun <= $limit) {
586+
if ($schedule->valid($nextRun) && $nextRun > $now) {
587+
// Persist as UTC literal so DB comparisons stay deterministic.
588+
return $nextRun->setTimezone($utc)->format('Y-m-d H:i:s');
589+
}
590+
$nextRun->modify('+1 minute');
588591
}
589592
} catch (\Throwable $e) {
590593
App::getInstance(true)->getLogger()->error('Failed to calculate next run time for expression ' . $expression . ': ' . $e->getMessage());
@@ -666,6 +669,10 @@ private static function resolveBaseDateTime(?string $referenceTime, \DateTimeZon
666669

667670
/**
668671
* Fallback calculation when external cron library is unavailable.
672+
*
673+
* Searches minute-by-minute up to 4 years ahead so monthly and yearly
674+
* expressions (e.g. "0 4 1 * *" after the 1st) resolve correctly instead
675+
* of incorrectly falling back to near-immediate execution.
669676
*/
670677
private static function calculateNextRunTimeFallback(string $dayOfWeek, string $month, string $dayOfMonth, string $hour, string $minute, ?string $referenceTime = null, string $timezone = 'UTC'): string
671678
{
@@ -694,12 +701,11 @@ private static function calculateNextRunTimeFallback(string $dayOfWeek, string $
694701
$nextRun->add(new \DateInterval('PT1M'));
695702

696703
$found = false;
697-
$attempts = 0;
698-
$maxAttempts = 10080; // Prevent infinite loops (covers up to 7 days)
699-
700-
while (!$found && $attempts < $maxAttempts) {
701-
++$attempts;
704+
// 4 years covers leap-day schedules; previously 7 days caused monthly
705+
// cron expressions whose next match was >7 days away to fall back to +1 day.
706+
$limit = (clone $searchStart)->modify('+4 years');
702707

708+
while ($nextRun <= $limit) {
703709
if (self::timeMatchesCron($nextRun, $dayOfWeek, $month, $dayOfMonth, $hour, $minute)) {
704710
if ($nextRun > $currentTime) {
705711
$found = true;
@@ -711,6 +717,12 @@ private static function calculateNextRunTimeFallback(string $dayOfWeek, string $
711717
}
712718

713719
if (!$found) {
720+
// Unsatisfiable expression (e.g. 31 February); keep a deterministic
721+
// placeholder rather than scheduling immediately.
722+
App::getInstance(true)->getLogger()->warning(
723+
'No matching cron run found within 4 years for expression '
724+
. self::formatCronExpression($dayOfWeek, $month, $dayOfMonth, $hour, $minute)
725+
);
714726
$nextRun = clone $currentTime;
715727
$nextRun->add(new \DateInterval('P1D'));
716728
}

backend/app/Helpers/BackupIgnoreHelper.php

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -21,7 +21,7 @@
2121
* Normalizes backup ignore/exclude patterns between panel storage and Wings API.
2222
*
2323
* Panel stores ignored_files as a JSON array string; Wings expects a newline-separated
24-
* glob list (same format as .pteroignore).
24+
* glob list (same format as .featherpanelignore / gitignore).
2525
*/
2626
class BackupIgnoreHelper
2727
{

frontendv2/package.json

Lines changed: 6 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -40,10 +40,10 @@
4040
"@hcaptcha/react-hcaptcha": "^2.0.2",
4141
"@headlessui/react": "^2.2.10",
4242
"@monaco-editor/react": "4.8.0-rc.3",
43-
"@radix-ui/react-progress": "^1.1.13",
44-
"@radix-ui/react-slot": "^1.3.0",
45-
"@radix-ui/react-switch": "^1.3.4",
46-
"@radix-ui/react-tabs": "^1.1.18",
43+
"@radix-ui/react-progress": "^1.1.14",
44+
"@radix-ui/react-slot": "^1.3.1",
45+
"@radix-ui/react-switch": "^1.3.5",
46+
"@radix-ui/react-tabs": "^1.1.19",
4747
"@react-three/drei": "^10.7.7",
4848
"@react-three/fiber": "^9.6.1",
4949
"@simplewebauthn/browser": "13.3.0",
@@ -65,7 +65,7 @@
6565
"date-fns": "^4.4.0",
6666
"date-fns-tz": "^3.2.0",
6767
"js-yaml": "^5.2.1",
68-
"lucide-react": "^1.25.0",
68+
"lucide-react": "^1.26.0",
6969
"next": "16.2.11",
7070
"ogl": "^1.0.11",
7171
"prettier": "^3.9.6",
@@ -97,5 +97,5 @@
9797
"tailwindcss": "^4",
9898
"typescript": "^6.0.3"
9999
},
100-
"packageManager": "pnpm@11.15.1"
100+
"packageManager": "pnpm@11.16.0"
101101
}

0 commit comments

Comments
 (0)