Added multiple backend support, better monitor config & more improvements

This commit is contained in:
Filip Znachor 2024-01-22 12:00:28 +01:00
parent f4fbe1413f
commit 5e9e0d0297
11 changed files with 131 additions and 55 deletions

View file

@ -1,16 +1,55 @@
<?php
$config = [
"uptime_kuma_url" => "http://uptime-kuma.local:3001",
"pages" => [
"public",
"second"
],
"monitor_options" => [
4 => ["rich" => true]
],
"enable_twig_cache" => true,
"default_language" => "en",
"timezone" => "Etc/UTC"
];
/**
* Uptime Kuma backends
* - URL addresses of backends
*/
"backends" => [
"http://uptime-kuma1.local:3001", // KUMA_ID = 0
"http://uptime-kuma2.local:3001" // KUMA_ID = 1
],
/**
* Status pages
* - all available pages and their backends
* - when passing an array, other backends are used as failovers
*/
"pages" => [
"public" => 0, // first defined page is visible on the home page
"second" => [0, 1] // other pages are displayed on their own subpages based on their slug (eg. /second)
],
/**
* Monitor options
* - the key must be in one of formats below (MON_ID = monitor ID, KUMA_ID = backend ID, PAGE = page)
* - MON_ID
* - KUMA_ID:MON_ID
* - PAGE/MON_ID
* - PAGE/KUMA_ID:MON_ID
* - available options are listed below
* - heartbeats: boolean
*/
"monitor_options" => [
"4" => ["heartbeats" => true],
"second/1" => ["heartbeats" => true],
"public/0:2" => ["heartbeats" => true]
],
/**
* Enable twig caching for better performance
*/
"enable_twig_cache" => true,
/**
* Default language code
*/
"default_language" => "en",
/**
* Timezone of displayed date
*/
"timezone" => "Etc/UTC"
];

23
controller/Config.php Normal file
View file

@ -0,0 +1,23 @@
<?php namespace UptimeStatus;
use Exception;
class Config {
private static array $config;
public static function set(array $config): void {
self::$config = $config;
}
public static function get(string $key, mixed $default = null): mixed {
if (array_key_exists($key, self::$config)) {
return self::$config[$key];
}
if ($default !== null) {
return $default;
}
throw new Exception("Missing config key '$key'!");
}
}

View file

@ -10,14 +10,14 @@ class Filters {
return 2;
});
}
public static function statusicon() {
return new \Twig\TwigFilter('statusicon', function (int $status, string $suffix = "svg") {
$icons = ["error", "success", "warning", "maintenance"];
return "/icon/{$icons[$status]}.$suffix";
});
}
public static function statuscolor() {
return new \Twig\TwigFilter('statuscolor', function (int $status) {
return ["#F87171", "#10B981", "#FFBB6D", "#9575cd"][$status];

View file

@ -2,12 +2,6 @@
class Router {
private Status $status;
public function __construct($config) {
$this->status = new Status($config);
}
public function get_path(): string {
$url = $_SERVER["REQUEST_URI"];
$parsed_url = parse_url($url);
@ -21,14 +15,15 @@ class Router {
return $path;
}
public function get_page($path) {
$pages = $this->status->cfg("pages");
foreach ($pages as $page) {
public function get_page($path): string {
$pages = Config::get("pages");
$slugs = array_keys($pages);
foreach ($slugs as $page) {
if ($path == $page) {
return $page;
}
}
return $pages[0];
return $slugs[0];
}
public function render() {
@ -36,10 +31,20 @@ class Router {
$path = $this->get_path();
$slug = $this->get_page($path);
$page = $this->status->get_page($slug);
if (!$page) die("Failed to load data!");
$this->status->display($page);
$backend_ids = Config::get("pages")[$slug];
if (!is_array($backend_ids)) $backend_ids = [$backend_ids];
$status = null;
foreach ($backend_ids as $bid) {
$status = new Status($bid, $slug);
$page = $status->get_page();
if ($page) {
$status->display();
return;
}
}
echo "Failed to load data!";
}

View file

@ -4,29 +4,29 @@ use UptimeStatus\Model\Page;
class Status {
private array $config;
readonly int $backend_id;
public function __construct(array $config) {
$this->config = $config;
readonly string $slug;
public ?Page $page = null;
public function __construct(int $backend_id, string $slug) {
$this->backend_id = $backend_id;
$this->slug = $slug;
}
public function cfg($name) {
if(array_key_exists($name, $this->config)) {
return $this->config[$name];
}
return null;
public function get_page(): ?Page {
$this->page = Page::get($this, $this->backend_id, $this->slug);
return $this->page;
}
public function get_page(string $page): ?Page {
return Page::get($this, $page);
}
public function display(): void {
public function display(Page $page) {
$data = $page->export();
if ($this->page == null) return;
$data = $this->page->export();
$twig_config = [];
if ($this->cfg("enable_twig_cache")) $twig_config["cache"] = "../cache/twig/";
if (Config::get("enable_twig_cache")) $twig_config["cache"] = "../cache/twig/";
$loader = new \Twig\Loader\FilesystemLoader("../view/");
$twig = new \Twig\Environment($loader, $twig_config);
@ -35,12 +35,12 @@ class Status {
$twig->addFilter(Filters::statusicon());
$twig->addFilter(Filters::statuscolor());
$locale = new Locale($this->cfg("default_language"));
$locale = new Locale(Config::get("default_language"));
$twig->addFilter($locale->t());
$ext = $twig->getExtension(\Twig\Extension\CoreExtension::class);
$ext->setDateFormat($locale->get("dateformat"));
$ext->setTimezone($this->cfg("timezone"));
$ext->setTimezone(Config::get("timezone"));
echo $twig->render('index.twig', $data);

View file

@ -10,6 +10,6 @@
"status.2": "Problém",
"status.3": "Údržba",
"heartbeat.ping": "Odezva: {{ping}}ms",
"dateformat": "j. m. Y G:i",
"dateformat": "j. n. Y G:i",
"footer.poweredby": "Poháněno projektem <b><a href=\"{{link}}\" target=\"_blank\">Uptime Status</a></b>"
}

View file

@ -1,5 +1,6 @@
<?php namespace UptimeStatus\Model;
use UptimeStatus\Config;
use UptimeStatus\Status;
class Monitor {
@ -25,8 +26,10 @@ class Monitor {
public static function convert(Status $s, array $oldMonitor, array $heartbeat): Monitor {
$id = $oldMonitor["id"];
$opts = $s->cfg("monitor_options") ?? [];
$opt = $opts[$id] ?? [];
$opts = Config::get("monitor_options", []);
$bid = $s->backend_id;
$page = $s->slug;
$opt = $opts["$page/$bid:$id"] ?? $opts["$page/$id"] ?? $opts["$bid:$id"] ?? $opts[$id] ?? [];
$heartbeats = [];
foreach ($heartbeat["heartbeatList"][$id] as $beat) {

View file

@ -1,5 +1,6 @@
<?php namespace UptimeStatus\Model;
use UptimeStatus\Config;
use UptimeStatus\Status;
class Page {
@ -32,12 +33,12 @@ class Page {
];
}
public static function get(Status $s, string $page): ?Page {
public static function get(Status $s, int $backend_id, string $page): ?Page {
$url = $s->cfg("uptime_kuma_url");
$backend = Config::get("backends")[$backend_id];
$urls = [
$url . "/api/status-page/" . $page,
$url . "/api/status-page/heartbeat/" . $page
$backend . "/api/status-page/" . $page,
$backend . "/api/status-page/heartbeat/" . $page
];
[$oldPage, $heartbeat] = Page::download($urls);
@ -60,6 +61,7 @@ class Page {
$ch = curl_init();
curl_setopt($ch, CURLOPT_URL, $url);
curl_setopt($ch, CURLOPT_HEADER, 0);
curl_setopt($ch, CURLOPT_TIMEOUT, 5);
curl_setopt($ch, CURLOPT_RETURNTRANSFER, true);
curl_multi_add_handle($mh, $ch);
array_push($chs, $ch);

View file

@ -1,8 +1,11 @@
<?php
use UptimeStatus\Config;
use UptimeStatus\Router;
require("../vendor/autoload.php");
require("../config.inc.php");
$router = new UptimeStatus\Router($config);
Config::set($config);
$router = new Router();
$router->render();

View file

@ -30,6 +30,7 @@ html, body {
* {
box-sizing: border-box;
text-wrap: balance;
}
:is(header, section, footer) > .inner {

View file

@ -9,7 +9,7 @@
{{ "monitor.uptime" | t({pct: (monitor.uptime * 100) | number_format(1, '.')}) | raw }}
</div>
</div>
{% if monitor.opt.rich %}
{% if monitor.opt.heartbeats %}
{{ include("./heartbeats.twig") }}
{% endif %}
</div>