Newer
Older
smart-home-server / server / SHServ / Controllers / CronController.php
<?php

namespace SHServ\Controllers;

use \SHServ\Models\Devices;
use \SHServ\Tools\DeviceScanner;
use \SHServ\Middleware\ControlScripts;
use \SHServ\Models\Scripts;

class CronController extends \SHServ\Middleware\Controller {
	protected function ensure_localhost_only() {
		$remote_addr = $_SERVER['REMOTE_ADDR'] ?? '';
		if(!in_array($remote_addr, ['127.0.0.1', '::1'])) {
			http_response_code(403);
			header('Content-Type: application/json');
			echo json_encode([
				"status" => false,
				"error_alias" => "forbidden",
				"msg" => "Forbidden: local access only"
			]);
			exit;
		}
	}

	protected function createDeviceScanner(): DeviceScanner {
		return new DeviceScanner();
	}

	protected function run_script_cli(String $alias): int {
		$output = [];
		$returnCode = 0;
		$consolePath = __DIR__ . "/../../console.php";
		exec("php " . escapeshellarg($consolePath) . " run-regular-script " . escapeshellarg($alias) . " 2>&1", $output, $returnCode);
		if($returnCode !== 0) {
			\Fury\Kernel\Logging::ins() -> set(
				"CronController@run_regular_cron_scripts",
				"Regular script {$alias} exited with code {$returnCode}",
				implode("\n", $output)
			);
		}
		return $returnCode;
	}

	public function run_regular_cron_scripts() {
		$this -> ensure_localhost_only();
		$scripts_model = new Scripts();
		$regular_scripts = ControlScripts::get_regular_scripts();

		foreach($regular_scripts as $alias => $script) {
			if(!$scripts_model -> script_state("regular", $alias)) {
				continue;
			}

			$this -> run_script_cli($alias);
		}
	}

	public function status_update_scanning() {
		$this -> ensure_localhost_only();

		$threshold = FCONF["device_offline_threshold"] ?? 300;
		$now = time();

		$devices_model = new Devices();
		$active_devices = $devices_model -> get_device_list();

		// 1. Помечаем оффлайн устройства с устаревшим last_contact
		foreach($active_devices as $device) {
			$last_contact_ts = strtotime($device -> last_contact);
			if($last_contact_ts === false) {
				continue;
			}

			$seconds_since_contact = $now - $last_contact_ts;

			if($seconds_since_contact > $threshold) {
				if($device -> connection_status != "lost") {
					$device -> connection_status = "lost";
					$device -> update();
				}
			}
		}

		// 2. Сканируем сеть — восстанавливаем lost и обновляем IP при роуминге
		$device_scanner = $this -> createDeviceScanner();
		$found_devices = $device_scanner -> scan_range(FCONF["device_ip_range"][0], FCONF["device_ip_range"][1]);

		$found_by_hard_id = [];
		foreach($found_devices as $found) {
			$found_by_hard_id[$found["device_id"]] = $found;
		}

		foreach($active_devices as $device) {
			if(!isset($found_by_hard_id[$device -> device_hard_id])) {
				continue;
			}

			$found = $found_by_hard_id[$device -> device_hard_id];
			$is_changed = false;

			if($device -> device_ip != $found["ip_address"]) {
				$device -> device_ip = $found["ip_address"];
				$is_changed = true;
			}

			if($device -> connection_status != "active") {
				$device -> connection_status = "active";
				$is_changed = true;
			}

			if($is_changed) {
				$device -> update();
			}

			// HTTP-ответ от устройства = контакт
			$device -> touch_last_contact();
		}
	}
}