From a213a38b3c565e3d3b936c1fc1189e1c20d8922f Mon Sep 17 00:00:00 2001 From: Anonymous Contributor Date: Sun, 10 Sep 2023 04:41:21 +0200 Subject: [PATCH] Add simple API and starter for cron jobs --- app/Mcp.php | 3 ++- app/MigrationManager.php | 6 ++++- app/api/CronStarter.php | 44 ++++++++++++++++++++++++++++++++++ app/cron/CronJob.php | 51 ++++++++++++++++++++++++++++++++++++++++ app/cron/Frequency.php | 14 +++++++++++ cron.php | 24 ------------------- 6 files changed, 116 insertions(+), 26 deletions(-) create mode 100644 app/api/CronStarter.php create mode 100644 app/cron/CronJob.php create mode 100644 app/cron/Frequency.php delete mode 100644 cron.php diff --git a/app/Mcp.php b/app/Mcp.php index 4c8fd5b..f0e5f78 100644 --- a/app/Mcp.php +++ b/app/Mcp.php @@ -77,7 +77,8 @@ class Mcp implements ConnectionProvider public function config($key): string|array|int { - return $this->config[strtolower($key)]; + $realKey = strtolower($key); + return isset($this->config[$realKey]) ? $this->config[$realKey] : array(); } public function csrfField(): string diff --git a/app/MigrationManager.php b/app/MigrationManager.php index a01d563..dc126a2 100644 --- a/app/MigrationManager.php +++ b/app/MigrationManager.php @@ -14,6 +14,7 @@ class MigrationManager 'CREATE TABLE IF NOT EXISTS `mcp_invites` (`InviteCode` CHAR(64) NOT NULL, PRIMARY KEY (`InviteCode`)) ENGINE InnoDB', 'CREATE TABLE IF NOT EXISTS `mcp_offlineim_send` (`id` int(6) NOT NULL DEFAULT 0) ENGINE=InnoDB DEFAULT CHARSET=utf8 COLLATE=utf8_unicode_ci', 'CREATE TABLE IF NOT EXISTS `mcp_regions_info` (`regionID` CHAR(36) NOT NULL COLLATE utf8_unicode_ci, `RegionVersion` VARCHAR(128) NOT NULL DEFAULT "" COLLATE utf8_unicode_ci, `ProcMem` INT(11) NOT NULL, `Prims` INT(11) NOT NULL, `SimFPS` INT(11) NOT NULL, `PhyFPS` INT(11) NOT NULL, `OfflineTimer` INT(11) NOT NULL DEFAULT 0, PRIMARY KEY (`regionID`) USING BTREE) COLLATE=utf8_unicode_ci ENGINE=InnoDB', + 'CREATE TABLE IF NOT EXISTS `mcp_cron_runs` (`Name` VARCHAR(50) NOT NULL, `LastRun` INT(11) UNSIGNED NOT NULL, PRIMARY KEY(`Name`)) ENGINE InnoDB', 'CREATE TRIGGER IF NOT EXISTS del_id_trig AFTER DELETE ON UserAccounts FOR EACH ROW DELETE FROM mcp_user_identities WHERE mcp_user_identities.PrincipalID = OLD.PrincipalID OR mcp_user_identities.IdentityID = OLD.PrincipalID', 'CREATE TRIGGER IF NOT EXISTS del_pwres_trig AFTER DELETE ON UserAccounts FOR EACH ROW DELETE FROM mcp_password_reset WHERE mcp_password_reset.PrincipalID = OLD.PrincipalID' ]; @@ -25,10 +26,13 @@ class MigrationManager 'ALTER TABLE mcp_regions_info MODIFY COLUMN regionID CHAR(36), MODIFY COLUMN ProcMem INT(11) UNSIGNED NOT NULL, MODIFY COLUMN Prims INT(11) UNSIGNED NOT NULL, MODIFY COLUMN SimFPS FLOAT NOT NULL, MODIFY COLUMN PhyFPS FLOAT NOT NULL, MODIFY COLUMN OfflineTimer BIGINT UNSIGNED NOT NULL DEFAULT 0', 'CREATE TRIGGER IF NOT EXISTS del_id_trig AFTER DELETE ON UserAccounts FOR EACH ROW DELETE FROM mcp_user_identities WHERE mcp_user_identities.PrincipalID = OLD.PrincipalID OR mcp_user_identities.IdentityID = OLD.PrincipalID', 'CREATE TRIGGER IF NOT EXISTS del_pwres_trig AFTER DELETE ON UserAccounts FOR EACH ROW DELETE FROM mcp_password_reset WHERE mcp_password_reset.PrincipalID = OLD.PrincipalID' + ], + 2 => [ + 'CREATE TABLE IF NOT EXISTS `mcp_cron_runs` (`Name` VARCHAR(50) NOT NULL, `LastRun` INT(11) UNSIGNED NOT NULL, PRIMARY KEY(`Name`)) ENGINE InnoDB' ] ]; - private const MIGRATE_VERSION_CURRENT = 2; + private const MIGRATE_VERSION_CURRENT = 3; private int $migrateVersion; private string $migrateVersionFile; diff --git a/app/api/CronStarter.php b/app/api/CronStarter.php new file mode 100644 index 0000000..ca58eaf --- /dev/null +++ b/app/api/CronStarter.php @@ -0,0 +1,44 @@ +app->config('cron-restriction') == 'key' && !(isset($_GET['key']) && hash_equals($this->app->config('cron-key'), $_GET['key']))) { + http_response_code(403); + return; + } + + $cronJobs = array_merge($this->app->config('cronjobs'), $this::CRONJOBS_INTERNAL); + + $cronStatement = $this->app->db()->prepare('SELECT Name,LastRun FROM mcp_cron_runs'); + $cronStatement->execute(); + + $jobRuns = array(); + while ($row = $cronStatement->fetch()) { + $jobRuns[$row['Name']] = $row['LastRun']; + } + + $cronUpdateStatement = $this->app->db()->prepare('REPLACE INTO mcp_cron_runs(Name,LastRun) VALUES (?,?)'); + foreach ($cronJobs as $jobName) { + $jobClass = "Mcp\\Cron\\".$jobName; + if (in_array($jobName, $cronJobs)) { + $job = (new $jobClass($this->app)); + $now = time(); + $nextRun = $job->getNextRun(isset($jobRuns[$jobName]) ? $jobRuns[$jobName] : $now - 60); + if ($now >= $nextRun && $job->run()) { + error_log($now.' <= '.$nextRun); + $cronUpdateStatement->execute([$jobName, time()]); + } + } + } + } +} diff --git a/app/cron/CronJob.php b/app/cron/CronJob.php new file mode 100644 index 0000000..82d63ae --- /dev/null +++ b/app/cron/CronJob.php @@ -0,0 +1,51 @@ +app = $app; + $this->freq = $freq; + } + + public function getNextRun(int $lastRun) + { + $prevDate = getdate($lastRun); + $res = new DateTime('@'.$lastRun); + switch($this->freq) { + case Frequency::EACH_MINUTE: + $res->add(DateInterval::createFromDateString('1 minute')); + break; + case Frequency::HOURLY: + $res->add(DateInterval::createFromDateString('1 hour')); + break; + case Frequency::DAILY: + $res->add(DateInterval::createFromDateString('1 day')); + break; + case Frequency::WEEKLY: + $res->add(DateInterval::createFromDateString('1 week')); + break; + case Frequency::MONTHLY: + $res->setDate($prevDate['year'] + ($prevDate['mon'] == 12 ? 1 : 0), $prevDate['mon'] == 12 ? 1 : $prevDate['mon'] + 1, 1); + break; + case Frequency::YEARLY: + $res->setDate($prevDate['year'] + 1, 1, 1); + break; + default: break; + } + + return $res->getTimestamp(); + } + + abstract public function run(): bool; +} diff --git a/app/cron/Frequency.php b/app/cron/Frequency.php new file mode 100644 index 0000000..cdfe9e0 --- /dev/null +++ b/app/cron/Frequency.php @@ -0,0 +1,14 @@ +