Explorar o código

added Cron.txt to gitignore file
added Cron settings to main settings page
added createCronFile function
added php and js checkCronFile function
added default config item for autoUpdateCronEnabled
added default config item for autoUpdateCronSchedule
added cron-file case option to settingsOption function
added test/cron api endpoint
added Auto-update Organizr to cron jobs (FR#104)
added createCronFile to track if cron setup
fixed logger using wrong function warn->warning

CauseFX %!s(int64=4) %!d(string=hai) anos
pai
achega
b90fcd4b1f

+ 1 - 0
.gitignore

@@ -72,6 +72,7 @@ test.php
 users.db
 speedtest.db
 chatpack.db
+Cron.txt
 Docker.txt
 Github.txt
 Demo.txt

+ 18 - 0
api/classes/organizr.class.php

@@ -1867,6 +1867,12 @@ class Organizr
 				$this->settingsOption('select', 'logLiveUpdateRefresh', ['label' => 'Live Update Refresh', 'options' => $this->timeOptions()]),
 				$this->settingsOption('select', 'logPageSize', ['label' => 'Log Page Size', 'options' => [['name' => '10 Items', 'value' => '10'], ['name' => '25 Items', 'value' => '25'], ['name' => '50 Items', 'value' => '50'], ['name' => '100 Items', 'value' => '100']]]),
 			],
+			'Cron' => [
+				$this->settingsOption('cron-file'),
+				$this->settingsOption('blank'),
+				$this->settingsOption('enable', 'autoUpdateCronEnabled', ['label' => 'Auto-Update Organizr']),
+				$this->settingsOption('cron', 'autoUpdateCronSchedule'),
+			],
 			'Login' => [
 				$this->settingsOption('password', 'registrationPassword', ['label' => 'Registration Password', 'help' => 'Sets the password for the Registration form on the login screen']),
 				$this->settingsOption('switch', 'hideRegistration', ['label' => 'Hide Registration', 'help' => 'Enable this to hide the Registration button on the login screen']),
@@ -5819,6 +5825,18 @@ class Organizr
 		}
 	}
 	
+	public function createCronFile()
+	{
+		$file = $this->root . DIRECTORY_SEPARATOR . 'Cron.txt';
+		file_put_contents($file, time());
+	}
+	
+	public function checkCronFile()
+	{
+		$file = $this->root . DIRECTORY_SEPARATOR . 'Cron.txt';
+		return file_exists($file) && time() - 120 < filemtime($file);
+	}
+	
 	public function plexJoinAPI($array)
 	{
 		$username = ($array['username']) ?? null;

+ 3 - 1
api/config/default.php

@@ -618,5 +618,7 @@ return [
 	'logPageSize' => '50',
 	'includeDatabaseQueriesInDebug' => false,
 	'externalPluginMarketplaceRepos' => '',
-	'checkForPluginUpdate' => true
+	'checkForPluginUpdate' => true,
+	'autoUpdateCronEnabled' => false,
+	'autoUpdateCronSchedule' => '@weekly'
 ];

+ 46 - 0
api/functions/option-functions.php

@@ -100,6 +100,52 @@ trait OptionsFunction
 					'placeholder' => '* * * * *'
 				];
 				break;
+			case 'cronfile':
+				$path = $this->root . DIRECTORY_SEPARATOR . 'cron.php';
+				$server = $this->serverIP();
+				$installInstruction = ($this->docker) ?
+					'<p lang="en">No action needed.  Organizr\'s docker image comes with the Cron job built-in</p>' :
+					'<p lang="en">Setup a Cron job so it\'s call will originate from either the server\'s IP address or a local IP address.  Please use the following information to set up the Cron Job correctly.</p>
+					<h5>Cron Information</h5>
+					<ul class="list-icons">
+						<li><i class="fa fa-caret-right text-info"></i> <b lang="en">Schedule</b> <small>* * * * *</small></li>
+						<li><i class="fa fa-caret-right text-info"></i> <b lang="en">File Path</b> <small>' . $path . '</small></li>
+					</ul>
+					<h5>Command Examples</h5>
+					<ul class="list-icons">
+						<li><i class="ti-angle-right"></i> * * * * * /path/to/php ' . $path . '</li>
+						<li><i class="ti-angle-right"></i> * * * * * curl -XGET -sL  "http://' . $server . '/cron.php"</li>
+					</ul>
+					';
+				$settingMerge = [
+					'type' => 'html',
+					'override' => 12,
+					'label' => '',
+					'html' => '
+						<div class="row">
+							<div class="col-lg-12">
+								<div class="panel panel-info">
+									<div class="panel-heading">
+										<span lang="en">Cron Instructions</span>
+									</div>
+									<div class="panel-wrapper collapse in" aria-expanded="true">
+										<div class="panel-body">
+											<h3 lang="en">Instructions for your install type</h3>
+											<span>' . $installInstruction . '</span>
+											<button onclick="checkCronFile();" class="btn btn-outline btn-info btn-lg btn-block" lang="en">Check Cron Status</button>
+											<div class="m-t-15 hidden cron-results-container">
+												<div class="well">
+													<pre class="cron-results"></pre>
+												</div>
+											</div>
+										</div>
+									</div>
+								</div>
+							</div>
+						</div>
+						'
+				];
+				break;
 			case 'username':
 				$settingMerge = [
 					'type' => 'input',

+ 27 - 0
api/v2/routes/connectionTester.php

@@ -597,4 +597,31 @@ $app->post('/test/cron', function ($request, $response, $args) {
 	return $response
 		->withHeader('Content-Type', 'application/json;charset=UTF-8')
 		->withStatus($GLOBALS['responseCode']);
+});
+$app->get('/test/cron', function ($request, $response, $args) {
+	/**
+	 * @OA\Get(
+	 *     security={{ "api_key":{} }},
+	 *     tags={"test connection"},
+	 *     path="/api/v2/test/cron",
+	 *     summary="Test if cron is setup correctly",
+	 *     @OA\Response(response="200",description="Success",@OA\JsonContent(ref="#/components/schemas/success-message")),
+	 *     @OA\Response(response="401",description="Unauthorized",@OA\JsonContent(ref="#/components/schemas/unauthorized-message")),
+	 *     @OA\Response(response="422",description="Error",@OA\JsonContent(ref="#/components/schemas/error-message")),
+	 *     @OA\Response(response="500",description="Error",@OA\JsonContent(ref="#/components/schemas/error-message")),
+	 * )
+	 */
+	$Organizr = ($request->getAttribute('Organizr')) ?? new Organizr();
+	if ($Organizr->qualifyRequest(1, true)) {
+		$file = $Organizr->checkCronFile();
+		if ($file) {
+			$Organizr->setResponse(200, 'Cron file is setup');
+		} else {
+			$Organizr->setResponse(500, 'Cron file is not setup correctly');
+		}
+	}
+	$response->getBody()->write(jsonE($GLOBALS['api']));
+	return $response
+		->withHeader('Content-Type', 'application/json;charset=UTF-8')
+		->withStatus($GLOBALS['responseCode']);
 });

+ 25 - 1
cron.php

@@ -10,6 +10,28 @@ if ($Organizr->isLocalOrServer() && $Organizr->hasDB()) {
 	// Clear any pre-existing jobs if any
 	$scheduler->clearJobs();
 	$Organizr->logger->debug('Cron process starting');
+	// Auto-update Cron
+	if ($Organizr->config['autoUpdateCronEnabled'] && $Organizr->config['autoUpdateCronSchedule']) {
+		try {
+			$schedule = new Cron\CronExpression($Organizr->config['autoUpdateCronSchedule']);
+			$Organizr->logger->debug('Cron schedule has passed validation', ['schedule' => $Organizr->config['autoUpdateCronSchedule']]);
+			$scheduler->call(
+				function ($Organizr) {
+					$Organizr->logger->debug('Running cron job', ['function' => 'Auto-update']);
+					return $Organizr->updateOrganizr();
+				})
+				->then(function ($output) use ($Organizr) {
+					$Organizr->logger->debug('Completed cron job', [
+						'output' => $output,
+					]);
+				})
+				->at($Organizr->config['autoUpdateCronSchedule']);
+		} catch (InvalidArgumentException $e) {
+			$Organizr->logger->warning('Cron schedule has failed validation', ['schedule' => $Organizr->config['autoUpdateCronSchedule']]);
+			$Organizr->logger->error($e);
+		}
+	}
+	// End Auto-update Cron
 	// Add plugin cron
 	$Organizr->logger->debug('Checking if any plugins have cron jobs');
 	foreach ($GLOBALS['cron'] as $cronJob) {
@@ -24,7 +46,7 @@ if ($Organizr->isLocalOrServer() && $Organizr->hasDB()) {
 						$schedule = new Cron\CronExpression($Organizr->config[$cronJob['schedule']]);
 						$Organizr->logger->debug('Cron schedule has passed validation', ['schedule' => $Organizr->config[$cronJob['schedule']]]);
 					} catch (InvalidArgumentException $e) {
-						$Organizr->logger->warn('Cron schedule has failed validation', ['schedule' => $Organizr->config[$cronJob['schedule']]]);
+						$Organizr->logger->warning('Cron schedule has failed validation', ['schedule' => $Organizr->config[$cronJob['schedule']]]);
 						$Organizr->logger->error($e);
 						break;
 					}
@@ -83,6 +105,8 @@ if ($Organizr->isLocalOrServer() && $Organizr->hasDB()) {
 	if (!empty($scheduler->getFailedJobs())) {
 		$Organizr->logger->warning('Cron jobs have failed', ['jobs' => $scheduler->getFailedJobs()]);
 	}
+	// End Run and set file with time
+	$Organizr->createCronFile();
 } else {
 	if ($Organizr->hasDB()) {
 		$Organizr->logger->warning('Unauthorized user tried to access cron file');

+ 14 - 0
js/functions.js

@@ -1164,6 +1164,20 @@ function buildFormItem(item){
 			return '<span class="text-danger">BuildFormItem Class not setup...';
 	}
 }
+function checkCronFile(){
+	$('.cron-results-container').removeClass('hidden');
+	organizrAPI2('GET','api/v2/test/cron').success(function(data) {
+		try {
+			$('.cron-results').text('Cron file is setup correctly');
+		}catch(e) {
+			$('.cron-results').text('Unknown error');
+			organizrCatchError(e,data);
+		}
+	}).fail(function(xhr) {
+		$('.cron-results').text('Cron file is not setup or is setup incorrectly');
+		OrganizrApiError(xhr);
+	});
+}
 function buildPluginsItem(array, type = 'enabled'){
 	var activePlugins = '';
 	var inactivePlugins = '';