fix: ensure scheduler cron and timezone fallback
This commit is contained in:
@@ -1602,7 +1602,13 @@ class Backups extends Page implements HasActions, HasForms, HasTable
|
||||
{
|
||||
static $timezone = null;
|
||||
if ($timezone === null) {
|
||||
$timezone = trim(shell_exec('cat /etc/timezone 2>/dev/null') ?? '') ?: 'UTC';
|
||||
$timezone = trim(shell_exec('cat /etc/timezone 2>/dev/null') ?? '');
|
||||
if ($timezone === '') {
|
||||
$timezone = trim(shell_exec('timedatectl show -p Timezone --value 2>/dev/null') ?? '');
|
||||
}
|
||||
if ($timezone === '') {
|
||||
$timezone = 'UTC';
|
||||
}
|
||||
}
|
||||
|
||||
return $timezone;
|
||||
|
||||
@@ -82,11 +82,11 @@ class BackupSchedule extends Model
|
||||
*/
|
||||
public function shouldRun(): bool
|
||||
{
|
||||
if (!$this->is_active) {
|
||||
if (! $this->is_active) {
|
||||
return false;
|
||||
}
|
||||
|
||||
if (!$this->next_run_at) {
|
||||
if (! $this->next_run_at) {
|
||||
return true;
|
||||
}
|
||||
|
||||
@@ -98,11 +98,13 @@ class BackupSchedule extends Model
|
||||
*/
|
||||
public function calculateNextRun(): Carbon
|
||||
{
|
||||
$timezone = $this->getSystemTimezone();
|
||||
$now = Carbon::now($timezone);
|
||||
$time = explode(':', $this->time);
|
||||
$hour = (int) ($time[0] ?? 2);
|
||||
$minute = (int) ($time[1] ?? 0);
|
||||
|
||||
$next = Carbon::now()->setTime($hour, $minute, 0);
|
||||
$next = $now->copy()->setTime($hour, $minute, 0);
|
||||
|
||||
// If time already passed today, start from tomorrow
|
||||
if ($next->isPast()) {
|
||||
@@ -111,7 +113,7 @@ class BackupSchedule extends Model
|
||||
|
||||
switch ($this->frequency) {
|
||||
case 'hourly':
|
||||
$next = Carbon::now()->addHour()->startOfHour();
|
||||
$next = $now->copy()->addHour()->startOfHour();
|
||||
break;
|
||||
|
||||
case 'daily':
|
||||
@@ -135,9 +137,10 @@ class BackupSchedule extends Model
|
||||
break;
|
||||
}
|
||||
|
||||
$this->next_run_at = $next;
|
||||
$nextUtc = $next->copy()->setTimezone('UTC');
|
||||
$this->attributes['next_run_at'] = $nextUtc->format($this->getDateFormat());
|
||||
|
||||
return $next;
|
||||
return $nextUtc;
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -147,9 +150,9 @@ class BackupSchedule extends Model
|
||||
{
|
||||
$base = match ($this->frequency) {
|
||||
'hourly' => 'Every hour',
|
||||
'daily' => 'Daily at ' . $this->time,
|
||||
'weekly' => 'Weekly on ' . $this->getDayName() . ' at ' . $this->time,
|
||||
'monthly' => 'Monthly on day ' . ($this->day_of_month ?? 1) . ' at ' . $this->time,
|
||||
'daily' => 'Daily at '.$this->time,
|
||||
'weekly' => 'Weekly on '.$this->getDayName().' at '.$this->time,
|
||||
'monthly' => 'Monthly on day '.($this->day_of_month ?? 1).' at '.$this->time,
|
||||
default => ucfirst($this->frequency),
|
||||
};
|
||||
|
||||
@@ -162,9 +165,26 @@ class BackupSchedule extends Model
|
||||
protected function getDayName(): string
|
||||
{
|
||||
$days = ['Sunday', 'Monday', 'Tuesday', 'Wednesday', 'Thursday', 'Friday', 'Saturday'];
|
||||
|
||||
return $days[$this->day_of_week ?? 0];
|
||||
}
|
||||
|
||||
protected function getSystemTimezone(): string
|
||||
{
|
||||
static $timezone = null;
|
||||
if ($timezone === null) {
|
||||
$timezone = trim((string) @file_get_contents('/etc/timezone'));
|
||||
if ($timezone === '') {
|
||||
$timezone = trim((string) @shell_exec('timedatectl show -p Timezone --value 2>/dev/null'));
|
||||
}
|
||||
if ($timezone === '') {
|
||||
$timezone = 'UTC';
|
||||
}
|
||||
}
|
||||
|
||||
return $timezone;
|
||||
}
|
||||
|
||||
/**
|
||||
* Scope for active schedules.
|
||||
*/
|
||||
|
||||
@@ -16,7 +16,7 @@ SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)"
|
||||
if [[ -f "$SCRIPT_DIR/VERSION" ]]; then
|
||||
JABALI_VERSION="$(sed -n 's/^VERSION=//p' "$SCRIPT_DIR/VERSION")"
|
||||
fi
|
||||
JABALI_VERSION="${JABALI_VERSION:-0.9-rc50}"
|
||||
JABALI_VERSION="${JABALI_VERSION:-0.9-rc51}"
|
||||
|
||||
# Colors
|
||||
RED='\033[0;31m'
|
||||
@@ -3019,6 +3019,12 @@ setup_scheduler_cron() {
|
||||
mkdir -p "$JABALI_DIR/storage/logs"
|
||||
chown -R www-data:www-data "$JABALI_DIR/storage/logs"
|
||||
|
||||
# Ensure cron service is enabled and running
|
||||
if command -v systemctl >/dev/null 2>&1; then
|
||||
systemctl enable cron >/dev/null 2>&1 || true
|
||||
systemctl start cron >/dev/null 2>&1 || true
|
||||
fi
|
||||
|
||||
# Add cron job for Laravel scheduler as www-data (runs every minute)
|
||||
CRON_LINE="* * * * * cd $JABALI_DIR && php artisan schedule:run >> /dev/null 2>&1"
|
||||
|
||||
|
||||
42
tests/Unit/BackupScheduleNextRunTest.php
Normal file
42
tests/Unit/BackupScheduleNextRunTest.php
Normal file
@@ -0,0 +1,42 @@
|
||||
<?php
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
namespace Tests\Unit;
|
||||
|
||||
use App\Models\BackupSchedule;
|
||||
use Carbon\Carbon;
|
||||
use Tests\TestCase;
|
||||
|
||||
class BackupScheduleNextRunTest extends TestCase
|
||||
{
|
||||
public function test_calculate_next_run_uses_system_timezone_and_stores_utc(): void
|
||||
{
|
||||
$timezone = trim((string) @file_get_contents('/etc/timezone'));
|
||||
if ($timezone === '') {
|
||||
$timezone = trim((string) @shell_exec('timedatectl show -p Timezone --value 2>/dev/null'));
|
||||
}
|
||||
if ($timezone === '') {
|
||||
$timezone = 'UTC';
|
||||
}
|
||||
$now = Carbon::create(2026, 2, 2, 1, 0, 0, $timezone);
|
||||
Carbon::setTestNow($now);
|
||||
|
||||
$schedule = new BackupSchedule([
|
||||
'frequency' => 'daily',
|
||||
'time' => '02:00',
|
||||
'is_active' => true,
|
||||
]);
|
||||
|
||||
$nextRun = $schedule->calculateNextRun();
|
||||
|
||||
$expectedLocal = $now->copy()->setTime(2, 0, 0);
|
||||
if ($expectedLocal->isPast()) {
|
||||
$expectedLocal->addDay();
|
||||
}
|
||||
|
||||
$expectedUtc = $expectedLocal->copy()->setTimezone('UTC');
|
||||
|
||||
$this->assertSame($expectedUtc->timestamp, $nextRun->timestamp);
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user