mirror of
https://github.com/itflow-org/itflow
synced 2026-08-05 07:07:14 +00:00
Cron Fix
This commit is contained in:
165
includes/cron_jobs.php
Normal file
165
includes/cron_jobs.php
Normal file
@@ -0,0 +1,165 @@
|
||||
<?php
|
||||
|
||||
/*
|
||||
* ITFlow - The cron job registry
|
||||
*
|
||||
* The list of jobs cron/cron.php knows how to run, and the schedule each one ships with.
|
||||
* Adding a job is a script in cron/ and an entry here - the crontab never changes.
|
||||
*
|
||||
* Schedules here are DEFAULTS. They seed the job's row in the cron_jobs table the first
|
||||
* time the dispatcher sees it, and from then on the row is what runs: Settings > Cron
|
||||
* writes to it. Changing a default in this file therefore only affects installs that have
|
||||
* not met the job yet.
|
||||
*
|
||||
* This file is the only thing that decides which scripts can be run. The database holds
|
||||
* when and whether, never what - a row naming a script that is not listed here is ignored,
|
||||
* so nothing that reaches the database can point the dispatcher at an arbitrary file.
|
||||
*/
|
||||
|
||||
function cronJobRegistry(): array
|
||||
{
|
||||
return [
|
||||
[
|
||||
'name' => 'mail_queue',
|
||||
'label' => 'Mail Queue',
|
||||
'script' => 'mail_queue.php',
|
||||
'description' => 'Sends everything ITFlow has queued - invoices, quotes, ticket replies, notifications.',
|
||||
'schedule' => 'Interval',
|
||||
'interval_minutes' => 1,
|
||||
],
|
||||
[
|
||||
'name' => 'ticket_email_parser',
|
||||
'label' => 'Ticket Email Parser',
|
||||
'script' => 'ticket_email_parser.php',
|
||||
'description' => 'Reads the support mailbox and turns incoming mail into tickets and replies.',
|
||||
'schedule' => 'Interval',
|
||||
'interval_minutes' => 1,
|
||||
],
|
||||
[
|
||||
'name' => 'ticket_sla',
|
||||
'label' => 'Ticket SLA Monitor',
|
||||
'script' => 'ticket_sla.php',
|
||||
'description' => 'Moves tickets through their SLA warning and breach stages and sends the alerts.',
|
||||
'schedule' => 'Interval',
|
||||
'interval_minutes' => 1,
|
||||
],
|
||||
[
|
||||
'name' => 'domain_refresher',
|
||||
'label' => 'Domain Refresher',
|
||||
'script' => 'domain_refresher.php',
|
||||
'description' => 'Refreshes WHOIS and DNS for the domain that was checked longest ago. One domain per run.',
|
||||
'schedule' => 'Interval',
|
||||
'interval_minutes' => 5,
|
||||
],
|
||||
[
|
||||
'name' => 'nightly_tasks',
|
||||
'label' => 'Nightly Tasks',
|
||||
'script' => 'nightly_tasks.php',
|
||||
'description' => 'The daily run: recurring invoices and tickets, overdue reminders, autopay, late fees, clean-up, update check.',
|
||||
'schedule' => 'Daily',
|
||||
'daily_at' => '03:00',
|
||||
],
|
||||
[
|
||||
'name' => 'certificate_refresher',
|
||||
'label' => 'Certificate Refresher',
|
||||
'script' => 'certificate_refresher.php',
|
||||
'description' => 'Re-reads the expiry date and issuer of every SSL certificate on file.',
|
||||
'schedule' => 'Daily',
|
||||
'daily_at' => '03:30',
|
||||
],
|
||||
];
|
||||
}
|
||||
|
||||
/*
|
||||
* The registry keyed by job name, for looking up what a cron_jobs row belongs to.
|
||||
*/
|
||||
function cronJobRegistryByName(): array
|
||||
{
|
||||
$jobs = [];
|
||||
|
||||
foreach (cronJobRegistry() as $job) {
|
||||
$jobs[$job['name']] = $job;
|
||||
}
|
||||
|
||||
return $jobs;
|
||||
}
|
||||
|
||||
/*
|
||||
* How a schedule reads in the admin UI: "Every minute", "Every 5 minutes", "Daily at 03:00".
|
||||
*/
|
||||
function cronJobScheduleDescription(string $schedule, int $interval_minutes, ?string $daily_at): string
|
||||
{
|
||||
if ($schedule === 'Daily') {
|
||||
return 'Daily at ' . substr((string)$daily_at, 0, 5);
|
||||
}
|
||||
|
||||
if ($interval_minutes === 1) {
|
||||
return 'Every minute';
|
||||
}
|
||||
|
||||
if ($interval_minutes === 60) {
|
||||
return 'Hourly';
|
||||
}
|
||||
|
||||
return "Every $interval_minutes minutes";
|
||||
}
|
||||
|
||||
/*
|
||||
* When a job is next expected to run, or null when it is disabled or nothing is scheduled.
|
||||
* Interval jobs that are already overdue read as due now rather than as a time in the past.
|
||||
*/
|
||||
function cronJobNextRun(array $row): ?string
|
||||
{
|
||||
if (empty($row['cron_job_enabled'])) {
|
||||
return null;
|
||||
}
|
||||
|
||||
$last_run = $row['cron_job_last_run_at'];
|
||||
|
||||
if ($row['cron_job_schedule'] === 'Daily') {
|
||||
$today = date('Y-m-d') . ' ' . substr((string)$row['cron_job_daily_at'], 0, 5) . ':00';
|
||||
|
||||
if ($last_run === null || $last_run < $today) {
|
||||
return $today <= date('Y-m-d H:i:s') ? date('Y-m-d H:i:s') : $today;
|
||||
}
|
||||
|
||||
return date('Y-m-d H:i:s', strtotime($today) + 86400);
|
||||
}
|
||||
|
||||
if ($last_run === null) {
|
||||
return date('Y-m-d H:i:s');
|
||||
}
|
||||
|
||||
$next = strtotime($last_run) + (max(1, intval($row['cron_job_interval_minutes'])) * 60);
|
||||
|
||||
return date('Y-m-d H:i:s', max($next, time()));
|
||||
}
|
||||
|
||||
/*
|
||||
* "4 minutes ago" / "in 2 minutes" for the admin page. Anything older than a day reads as a
|
||||
* date, because "27,000 minutes ago" tells nobody anything.
|
||||
*/
|
||||
function cronJobTimeAgo(?string $datetime): string
|
||||
{
|
||||
if (empty($datetime)) {
|
||||
return 'Never';
|
||||
}
|
||||
|
||||
$seconds = time() - strtotime($datetime);
|
||||
$ahead = $seconds < 0;
|
||||
$seconds = abs($seconds);
|
||||
|
||||
if ($seconds < 60) {
|
||||
$text = 'less than a minute';
|
||||
} elseif ($seconds < 3600) {
|
||||
$minutes = floor($seconds / 60);
|
||||
$text = $minutes . ' minute' . ($minutes == 1 ? '' : 's');
|
||||
} elseif ($seconds < 86400) {
|
||||
$hours = floor($seconds / 3600);
|
||||
$text = $hours . ' hour' . ($hours == 1 ? '' : 's');
|
||||
} else {
|
||||
return date('M j, g:i A', strtotime($datetime));
|
||||
}
|
||||
|
||||
return $ahead ? "in $text" : "$text ago";
|
||||
}
|
||||
Reference in New Issue
Block a user