update
This commit is contained in:
parent
4e72174670
commit
e5fd11edbd
11 changed files with 3171 additions and 0 deletions
2017
Parsedown.php
Normal file
2017
Parsedown.php
Normal file
File diff suppressed because it is too large
Load diff
3
configuration.php
Normal file
3
configuration.php
Normal file
|
@ -0,0 +1,3 @@
|
||||||
|
<?php
|
||||||
|
|
||||||
|
define('PATH', substr(__FILE__, 0, strrpos(__FILE__, '/')));
|
1
incidents/message.md
Normal file
1
incidents/message.md
Normal file
|
@ -0,0 +1 @@
|
||||||
|
There are no active incidents.
|
222
index.php
Normal file
222
index.php
Normal file
|
@ -0,0 +1,222 @@
|
||||||
|
<?php
|
||||||
|
// Start session for CSRF token management
|
||||||
|
session_start();
|
||||||
|
|
||||||
|
// CSRF Protection Functions
|
||||||
|
function generate_csrf_token() {
|
||||||
|
if (empty($_SESSION['csrf_token'])) {
|
||||||
|
$_SESSION['csrf_token'] = bin2hex(random_bytes(32));
|
||||||
|
}
|
||||||
|
return $_SESSION['csrf_token'];
|
||||||
|
}
|
||||||
|
|
||||||
|
function verify_csrf_token($token) {
|
||||||
|
// Check if token exists in session and matches submitted token
|
||||||
|
if (!isset($_SESSION['csrf_token']) ||
|
||||||
|
!isset($token) ||
|
||||||
|
!hash_equals($_SESSION['csrf_token'], $token)) {
|
||||||
|
|
||||||
|
// Log potential CSRF attempt
|
||||||
|
error_log('Potential CSRF attack detected');
|
||||||
|
|
||||||
|
// Destroy session and prevent further execution
|
||||||
|
session_destroy();
|
||||||
|
die('CSRF token validation failed');
|
||||||
|
}
|
||||||
|
|
||||||
|
// Regenerate token after successful validation for additional security
|
||||||
|
unset($_SESSION['csrf_token']);
|
||||||
|
generate_csrf_token();
|
||||||
|
}
|
||||||
|
|
||||||
|
// Configuration and include path handling
|
||||||
|
include('configuration.php');
|
||||||
|
include(PATH.'/Parsedown.php');
|
||||||
|
|
||||||
|
// Mandatory file and directory checks with CSRF protection
|
||||||
|
$requiredChecks = [
|
||||||
|
PATH.'/monitors.json' => '<h1>Missing monitors.json</h1>',
|
||||||
|
PATH.'/monitors' => '<h1>Missing monitors directory</h1>',
|
||||||
|
PATH.'/incidents' => '<h1>Missing incidents directory</h1>'
|
||||||
|
];
|
||||||
|
|
||||||
|
foreach ($requiredChecks as $path => $errorMessage) {
|
||||||
|
if (!file_exists($path)) {
|
||||||
|
die($errorMessage . '<p>See project documentation for more information.</p>');
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Check monitors directory writability
|
||||||
|
if (!is_writable(PATH.'/monitors')) {
|
||||||
|
die('<h1>Monitors directory is not writable</h1>');
|
||||||
|
}
|
||||||
|
?><!DOCTYPE html>
|
||||||
|
<html lang="en">
|
||||||
|
<head>
|
||||||
|
<title>HHF Website Monitor</title>
|
||||||
|
<meta charset="utf-8">
|
||||||
|
<meta name="theme-color" content="#212529">
|
||||||
|
<meta name="viewport" content="width=device-width, initial-scale=1">
|
||||||
|
<link href="style.css" rel="stylesheet">
|
||||||
|
<script src="https://cdn.jsdelivr.net/npm/chart.js@3.8.2/dist/chart.min.js" crossorigin="anonymous"></script>
|
||||||
|
<meta name="csrf-token" content="<?php echo generate_csrf_token(); ?>">
|
||||||
|
</head>
|
||||||
|
<body>
|
||||||
|
<main>
|
||||||
|
<h1>HHF Website Monitor</h1>
|
||||||
|
|
||||||
|
<?php
|
||||||
|
|
||||||
|
$incidents = array();
|
||||||
|
foreach(glob(PATH.'/incidents/*.md') as $incident_file) {
|
||||||
|
$incident_filename = str_replace(PATH.'/incidents/', '', $incident_file);
|
||||||
|
if(substr($incident_filename, 0, 6) == 'alert_') $incidents[$incident_filename] = 'alert';
|
||||||
|
else if(substr($incident_filename, 0, 7) == 'notice_') $incidents[$incident_filename] = 'notice';
|
||||||
|
else $incidents['post_'.$incident_filename] = 'post';
|
||||||
|
}
|
||||||
|
|
||||||
|
ksort($incidents);
|
||||||
|
foreach($incidents as $incident_filename => $class) {
|
||||||
|
if(substr($incident_filename, 0, 5) == 'post_') $incident_filename = substr($incident_filename, 5);
|
||||||
|
$Parsedown = new Parsedown();
|
||||||
|
echo '<div class="incident '.$class.'">';
|
||||||
|
echo $Parsedown->text(file_get_contents(PATH.'/incidents/'.$incident_filename));
|
||||||
|
echo '</div>';
|
||||||
|
}
|
||||||
|
|
||||||
|
$monitors = json_decode(file_get_contents(PATH.'/monitors.json'));
|
||||||
|
|
||||||
|
$i = 0;
|
||||||
|
|
||||||
|
foreach($monitors as $monitor => $url) {
|
||||||
|
|
||||||
|
$log = json_decode(file_get_contents(PATH.'/monitors/'.$monitor), TRUE);
|
||||||
|
$last = $log[array_key_last($log)];
|
||||||
|
|
||||||
|
if(is_numeric($last['response'])) {
|
||||||
|
$last = ' <span class="status">HTTP/1.1 '.$last['response'].'</span>';
|
||||||
|
$class = 'good';
|
||||||
|
$graph_color = '#51cf66';
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
$last = ' <span class="status">HTTP/1.1 '.$last['response'].'</span>';
|
||||||
|
$class = 'bad';
|
||||||
|
$graph_color = '#fa5252';
|
||||||
|
}
|
||||||
|
|
||||||
|
echo '<div class="item"><h2><span class="'.$class.'">⬤</span> '.$monitor.$last.'</h2>';
|
||||||
|
|
||||||
|
if(file_exists(PATH.'/updates/'.$monitor.'.md')) {
|
||||||
|
$Parsedown = new Parsedown();
|
||||||
|
echo $Parsedown->text(file_get_contents(PATH.'/updates/'.$monitor.'.md'));
|
||||||
|
}
|
||||||
|
|
||||||
|
$labels = array();
|
||||||
|
$data = array();
|
||||||
|
|
||||||
|
$const = 'ctx'.$i;
|
||||||
|
$const_chart = $const.'_'.$const;
|
||||||
|
|
||||||
|
echo '<div class="bloops">';
|
||||||
|
|
||||||
|
// do we need bloop padding?
|
||||||
|
if(count($log) < 60) {
|
||||||
|
$diff = 59 - count($log);
|
||||||
|
|
||||||
|
for ($x = 0; $x <= $diff; $x++) {
|
||||||
|
echo '<span class="bloop" title="Not monitored"></span>';
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
foreach($log as $arr) {
|
||||||
|
|
||||||
|
$labels[] = "'".date("H:i", $arr['timestamp'])."'";
|
||||||
|
|
||||||
|
$data[] = @$arr['time'];
|
||||||
|
|
||||||
|
if(@$arr['time'] > 0) {
|
||||||
|
echo '<span class="bloop good" data-status="Up" data-time="'.date("H:i", $arr['timestamp']).'" data-response="'.$arr['response'].'" data-ms="'.$arr['time'].'" data-status="Up at '.date("H:i", $arr['timestamp']).'" title="Up at '.date("H:i", $arr['timestamp']).' ('.$arr['time'].' ms)"></span>';
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
echo '<span class="bloop bad"></span>';
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
$min = min($data);
|
||||||
|
$max = max($data);
|
||||||
|
|
||||||
|
$diff = $max - $min;
|
||||||
|
|
||||||
|
$max += ($diff / 3);
|
||||||
|
$min -= ($diff / 3);
|
||||||
|
|
||||||
|
if($min < 0) $min = 0;
|
||||||
|
|
||||||
|
echo '</div>';
|
||||||
|
|
||||||
|
$chart_id = str_replace('.', '-', $monitor);
|
||||||
|
|
||||||
|
$labels = implode(', ', $labels);
|
||||||
|
$data = implode(', ', $data);
|
||||||
|
|
||||||
|
$out = <<<EOD
|
||||||
|
<canvas id="$chart_id" width="300" height="100"></canvas>
|
||||||
|
<script>
|
||||||
|
const $const = document.getElementById('$chart_id').getContext('2d');
|
||||||
|
const $const_chart = new Chart($const, {
|
||||||
|
type: 'line',
|
||||||
|
data: {
|
||||||
|
labels: [$labels],
|
||||||
|
datasets: [{
|
||||||
|
label: 'response time',
|
||||||
|
data: [$data],
|
||||||
|
backgroundColor: '$graph_color',
|
||||||
|
borderColor: '$graph_color',
|
||||||
|
borderWidth: 2,
|
||||||
|
tension: .4
|
||||||
|
}]
|
||||||
|
},
|
||||||
|
options: {
|
||||||
|
scales: {
|
||||||
|
y: {
|
||||||
|
beginAtZero: false,
|
||||||
|
min: $min,
|
||||||
|
max: $max,
|
||||||
|
grid: { display: false, drawBorder: false }
|
||||||
|
},
|
||||||
|
x: {
|
||||||
|
grid: { display: false, drawBorder: false }
|
||||||
|
}
|
||||||
|
},
|
||||||
|
plugins: {
|
||||||
|
legend: {
|
||||||
|
display: false
|
||||||
|
},
|
||||||
|
tooltip: {
|
||||||
|
callbacks: {
|
||||||
|
label: function(context) {
|
||||||
|
return context.parsed.y + ' ms';
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
});
|
||||||
|
</script>
|
||||||
|
EOD;
|
||||||
|
echo $out;
|
||||||
|
echo '</div>';
|
||||||
|
$i++;
|
||||||
|
}
|
||||||
|
|
||||||
|
?>
|
||||||
|
|
||||||
|
<footer>
|
||||||
|
|
||||||
|
<p>Website Monitor is an open source project. <a href="https://git.hhf.technology/hhf/HHFWebsiteMonitor.git">Download it on Forgejo</a>.</p>
|
||||||
|
|
||||||
|
</footer>
|
||||||
|
|
||||||
|
</main>
|
||||||
|
</body>
|
||||||
|
</html>
|
171
monitor.php
Normal file
171
monitor.php
Normal file
|
@ -0,0 +1,171 @@
|
||||||
|
<?php
|
||||||
|
/**
|
||||||
|
* Secure Website Monitoring Script
|
||||||
|
*
|
||||||
|
* Monitors websites and logs their response times and status codes
|
||||||
|
* Recommended to run via cron job
|
||||||
|
*/
|
||||||
|
|
||||||
|
// Strict error reporting
|
||||||
|
error_reporting(E_ALL);
|
||||||
|
ini_set('display_errors', 0);
|
||||||
|
ini_set('log_errors', 1);
|
||||||
|
|
||||||
|
// Validate and include configuration
|
||||||
|
$configPath = __DIR__ . '/configuration.php';
|
||||||
|
if (!file_exists($configPath)) {
|
||||||
|
error_log('Configuration file not found');
|
||||||
|
exit(1);
|
||||||
|
}
|
||||||
|
require_once $configPath;
|
||||||
|
|
||||||
|
// Validate PATH constant
|
||||||
|
if (!defined('PATH') || !is_dir(PATH)) {
|
||||||
|
error_log('Invalid monitoring directory');
|
||||||
|
exit(1);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Secure monitors file path
|
||||||
|
$monitorsFile = PATH . '/monitors.json';
|
||||||
|
if (!file_exists($monitorsFile)) {
|
||||||
|
error_log('Monitors configuration file not found');
|
||||||
|
exit(1);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Read and validate monitors
|
||||||
|
$monitorsJson = file_get_contents($monitorsFile);
|
||||||
|
$monitors = json_decode($monitorsJson, true);
|
||||||
|
|
||||||
|
if (json_last_error() !== JSON_ERROR_NONE || !is_array($monitors)) {
|
||||||
|
error_log('Invalid monitors JSON');
|
||||||
|
exit(1);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Maximum number of historical records to keep
|
||||||
|
const MAX_HISTORY_RECORDS = 60;
|
||||||
|
|
||||||
|
// Maximum execution time
|
||||||
|
set_time_limit(30);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Validate and sanitize URL
|
||||||
|
*
|
||||||
|
* @param string $url URL to validate
|
||||||
|
* @return string|false Sanitized URL or false if invalid
|
||||||
|
*/
|
||||||
|
function validateUrl($url) {
|
||||||
|
// Trim and validate URL
|
||||||
|
$url = trim($url);
|
||||||
|
|
||||||
|
// Check if URL is valid
|
||||||
|
if (!filter_var($url, FILTER_VALIDATE_URL)) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Additional URL schemes validation (optional)
|
||||||
|
$allowedSchemes = ['http', 'https'];
|
||||||
|
$urlParts = parse_url($url);
|
||||||
|
|
||||||
|
if (!isset($urlParts['scheme']) || !in_array($urlParts['scheme'], $allowedSchemes)) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
return $url;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Safely fetch website response
|
||||||
|
*
|
||||||
|
* @param string $url URL to monitor
|
||||||
|
* @return array Response data
|
||||||
|
*/
|
||||||
|
function fetchWebsiteResponse($url) {
|
||||||
|
$response_data = [
|
||||||
|
'timestamp' => time(),
|
||||||
|
'error' => null,
|
||||||
|
'time' => null,
|
||||||
|
'response' => null
|
||||||
|
];
|
||||||
|
|
||||||
|
// Validate URL
|
||||||
|
$sanitizedUrl = validateUrl($url);
|
||||||
|
if (!$sanitizedUrl) {
|
||||||
|
$response_data['error'] = 'Invalid URL';
|
||||||
|
return $response_data;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Initialize cURL with safe options
|
||||||
|
$curl = curl_init();
|
||||||
|
curl_setopt_array($curl, [
|
||||||
|
CURLOPT_URL => $sanitizedUrl,
|
||||||
|
CURLOPT_RETURNTRANSFER => true,
|
||||||
|
CURLOPT_HEADER => false,
|
||||||
|
CURLOPT_FOLLOWLOCATION => true,
|
||||||
|
CURLOPT_MAXREDIRS => 3,
|
||||||
|
CURLOPT_TIMEOUT => 10,
|
||||||
|
CURLOPT_CONNECTTIMEOUT => 5,
|
||||||
|
CURLOPT_SSL_VERIFYPEER => true,
|
||||||
|
CURLOPT_SSL_VERIFYHOST => 2
|
||||||
|
]);
|
||||||
|
|
||||||
|
// Execute request
|
||||||
|
$response = curl_exec($curl);
|
||||||
|
|
||||||
|
// Check for cURL errors
|
||||||
|
if ($response === false) {
|
||||||
|
$response_data['error'] = curl_error($curl);
|
||||||
|
} else {
|
||||||
|
// Get connection info
|
||||||
|
$info = curl_getinfo($curl);
|
||||||
|
$response_data['time'] = round($info['total_time_us'] / 1000, 2);
|
||||||
|
$response_data['response'] = $info['http_code'];
|
||||||
|
}
|
||||||
|
|
||||||
|
// Close cURL resource
|
||||||
|
curl_close($curl);
|
||||||
|
|
||||||
|
return $response_data;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Safely write monitor data
|
||||||
|
*
|
||||||
|
* @param string $name Monitor name
|
||||||
|
* @param array $newData New response data
|
||||||
|
*/
|
||||||
|
function writeMonitorData($name, $newData) {
|
||||||
|
// Validate monitor name (prevent path traversal)
|
||||||
|
if (!preg_match('/^[a-zA-Z0-9_-]+$/', $name)) {
|
||||||
|
error_log("Invalid monitor name: $name");
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
$monitorFile = PATH . '/monitors/' . $name;
|
||||||
|
|
||||||
|
// Read existing data
|
||||||
|
$data = [];
|
||||||
|
if (file_exists($monitorFile)) {
|
||||||
|
$fileContent = file_get_contents($monitorFile);
|
||||||
|
$data = json_decode($fileContent, true) ?: [];
|
||||||
|
}
|
||||||
|
|
||||||
|
// Merge and limit historical data
|
||||||
|
$data[] = $newData;
|
||||||
|
$data = array_slice($data, -MAX_HISTORY_RECORDS);
|
||||||
|
|
||||||
|
// Securely write file
|
||||||
|
$jsonData = json_encode($data, JSON_PRETTY_PRINT);
|
||||||
|
if (file_put_contents($monitorFile, $jsonData) === false) {
|
||||||
|
error_log("Failed to write monitor data for: $name");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Main monitoring loop
|
||||||
|
foreach ($monitors as $name => $url) {
|
||||||
|
try {
|
||||||
|
$responseData = fetchWebsiteResponse($url);
|
||||||
|
writeMonitorData($name, $responseData);
|
||||||
|
} catch (Exception $e) {
|
||||||
|
error_log("Monitoring error for $name: " . $e->getMessage());
|
||||||
|
}
|
||||||
|
}
|
4
monitors.json
Normal file
4
monitors.json
Normal file
|
@ -0,0 +1,4 @@
|
||||||
|
{
|
||||||
|
"forum.hhf.technology": "https:\/\/forum.hhf.technology",
|
||||||
|
"git.hhf.technology": "https:\/\/git.hhf.technology"
|
||||||
|
}
|
302
monitors/forum.hhf.technology
Normal file
302
monitors/forum.hhf.technology
Normal file
|
@ -0,0 +1,302 @@
|
||||||
|
[
|
||||||
|
{
|
||||||
|
"timestamp": 1671749821,
|
||||||
|
"time": 4.192,
|
||||||
|
"response": 200
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"timestamp": 1671749881,
|
||||||
|
"time": 6.308,
|
||||||
|
"response": 200
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"timestamp": 1671749941,
|
||||||
|
"time": 4.439,
|
||||||
|
"response": 200
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"timestamp": 1671750002,
|
||||||
|
"time": 4.309,
|
||||||
|
"response": 200
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"timestamp": 1671750061,
|
||||||
|
"time": 4.195,
|
||||||
|
"response": 200
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"timestamp": 1671750121,
|
||||||
|
"time": 4.228,
|
||||||
|
"response": 200
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"timestamp": 1671750181,
|
||||||
|
"time": 4.366,
|
||||||
|
"response": 200
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"timestamp": 1671750241,
|
||||||
|
"time": 4.25,
|
||||||
|
"response": 200
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"timestamp": 1671750301,
|
||||||
|
"time": 4.322,
|
||||||
|
"response": 200
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"timestamp": 1671750361,
|
||||||
|
"time": 4.349,
|
||||||
|
"response": 200
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"timestamp": 1671750421,
|
||||||
|
"time": 4.231,
|
||||||
|
"response": 200
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"timestamp": 1671750481,
|
||||||
|
"time": 5.795,
|
||||||
|
"response": 200
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"timestamp": 1671750541,
|
||||||
|
"time": 4.355,
|
||||||
|
"response": 200
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"timestamp": 1671750601,
|
||||||
|
"time": 4.224,
|
||||||
|
"response": 200
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"timestamp": 1671750661,
|
||||||
|
"time": 4.213,
|
||||||
|
"response": 200
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"timestamp": 1671750721,
|
||||||
|
"time": 4.274,
|
||||||
|
"response": 200
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"timestamp": 1671750781,
|
||||||
|
"time": 4.258,
|
||||||
|
"response": 200
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"timestamp": 1671750841,
|
||||||
|
"time": 4.24,
|
||||||
|
"response": 200
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"timestamp": 1671750901,
|
||||||
|
"time": 4.22,
|
||||||
|
"response": 200
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"timestamp": 1671750961,
|
||||||
|
"time": 4.23,
|
||||||
|
"response": 200
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"timestamp": 1671751021,
|
||||||
|
"time": 4.24,
|
||||||
|
"response": 200
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"timestamp": 1671751081,
|
||||||
|
"time": 4.225,
|
||||||
|
"response": 200
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"timestamp": 1671751141,
|
||||||
|
"time": 4.199,
|
||||||
|
"response": 200
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"timestamp": 1671751201,
|
||||||
|
"time": 4.263,
|
||||||
|
"response": 200
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"timestamp": 1671751261,
|
||||||
|
"time": 4.195,
|
||||||
|
"response": 200
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"timestamp": 1671751321,
|
||||||
|
"time": 4.291,
|
||||||
|
"response": 200
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"timestamp": 1671751381,
|
||||||
|
"time": 4.14,
|
||||||
|
"response": 200
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"timestamp": 1671751441,
|
||||||
|
"time": 4.214,
|
||||||
|
"response": 200
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"timestamp": 1671751501,
|
||||||
|
"time": 4.306,
|
||||||
|
"response": 200
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"timestamp": 1671751561,
|
||||||
|
"time": 4.233,
|
||||||
|
"response": 200
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"timestamp": 1671751621,
|
||||||
|
"time": 4.39,
|
||||||
|
"response": 200
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"timestamp": 1671751681,
|
||||||
|
"time": 4.415,
|
||||||
|
"response": 200
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"timestamp": 1671751741,
|
||||||
|
"time": 4.291,
|
||||||
|
"response": 200
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"timestamp": 1671751801,
|
||||||
|
"time": 4.284,
|
||||||
|
"response": 200
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"timestamp": 1671751861,
|
||||||
|
"time": 4.364,
|
||||||
|
"response": 200
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"timestamp": 1671751921,
|
||||||
|
"time": 4.24,
|
||||||
|
"response": 200
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"timestamp": 1671751981,
|
||||||
|
"time": 4.269,
|
||||||
|
"response": 200
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"timestamp": 1671752041,
|
||||||
|
"time": 4.326,
|
||||||
|
"response": 200
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"timestamp": 1671752101,
|
||||||
|
"time": 4.256,
|
||||||
|
"response": 200
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"timestamp": 1671752161,
|
||||||
|
"time": 4.227,
|
||||||
|
"response": 200
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"timestamp": 1671752221,
|
||||||
|
"time": 4.248,
|
||||||
|
"response": 200
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"timestamp": 1671752281,
|
||||||
|
"time": 4.349,
|
||||||
|
"response": 200
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"timestamp": 1671752341,
|
||||||
|
"time": 4.274,
|
||||||
|
"response": 200
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"timestamp": 1671752401,
|
||||||
|
"time": 4.27,
|
||||||
|
"response": 200
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"timestamp": 1671752461,
|
||||||
|
"time": 4.497,
|
||||||
|
"response": 200
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"timestamp": 1671752521,
|
||||||
|
"time": 4.136,
|
||||||
|
"response": 200
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"timestamp": 1671752581,
|
||||||
|
"time": 4.397,
|
||||||
|
"response": 200
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"timestamp": 1671752641,
|
||||||
|
"time": 4.224,
|
||||||
|
"response": 200
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"timestamp": 1671752701,
|
||||||
|
"time": 4.241,
|
||||||
|
"response": 200
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"timestamp": 1671752761,
|
||||||
|
"time": 4.248,
|
||||||
|
"response": 200
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"timestamp": 1671752821,
|
||||||
|
"time": 4.517,
|
||||||
|
"response": 200
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"timestamp": 1671752881,
|
||||||
|
"time": 4.381,
|
||||||
|
"response": 200
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"timestamp": 1671752941,
|
||||||
|
"time": 4.371,
|
||||||
|
"response": 200
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"timestamp": 1671753001,
|
||||||
|
"time": 4.626,
|
||||||
|
"response": 200
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"timestamp": 1671753061,
|
||||||
|
"time": 6.07,
|
||||||
|
"response": 200
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"timestamp": 1671753121,
|
||||||
|
"time": 4.455,
|
||||||
|
"response": 200
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"timestamp": 1671753181,
|
||||||
|
"time": 4.373,
|
||||||
|
"response": 200
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"timestamp": 1671753241,
|
||||||
|
"time": 4.184,
|
||||||
|
"response": 200
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"timestamp": 1671753301,
|
||||||
|
"time": 4.685,
|
||||||
|
"response": 200
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"timestamp": 1671753361,
|
||||||
|
"time": 7.862,
|
||||||
|
"response": 200
|
||||||
|
}
|
||||||
|
]
|
302
monitors/git.hhf.technology
Normal file
302
monitors/git.hhf.technology
Normal file
|
@ -0,0 +1,302 @@
|
||||||
|
[
|
||||||
|
{
|
||||||
|
"timestamp": 1671749821,
|
||||||
|
"time": 225.4,
|
||||||
|
"response": 200
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"timestamp": 1671749881,
|
||||||
|
"time": 215.243,
|
||||||
|
"response": 200
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"timestamp": 1671749941,
|
||||||
|
"time": 232.023,
|
||||||
|
"response": 200
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"timestamp": 1671750002,
|
||||||
|
"time": 219.797,
|
||||||
|
"response": 200
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"timestamp": 1671750061,
|
||||||
|
"time": 228.624,
|
||||||
|
"response": 200
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"timestamp": 1671750121,
|
||||||
|
"time": 226.033,
|
||||||
|
"response": 200
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"timestamp": 1671750181,
|
||||||
|
"time": 224.71,
|
||||||
|
"response": 200
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"timestamp": 1671750241,
|
||||||
|
"time": 239.184,
|
||||||
|
"response": 200
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"timestamp": 1671750301,
|
||||||
|
"time": 223.836,
|
||||||
|
"response": 200
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"timestamp": 1671750361,
|
||||||
|
"time": 224.607,
|
||||||
|
"response": 200
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"timestamp": 1671750421,
|
||||||
|
"time": 220.908,
|
||||||
|
"response": 200
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"timestamp": 1671750481,
|
||||||
|
"time": 229.849,
|
||||||
|
"response": 200
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"timestamp": 1671750541,
|
||||||
|
"time": 238.873,
|
||||||
|
"response": 200
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"timestamp": 1671750601,
|
||||||
|
"time": 224.467,
|
||||||
|
"response": 200
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"timestamp": 1671750661,
|
||||||
|
"time": 215.798,
|
||||||
|
"response": 200
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"timestamp": 1671750721,
|
||||||
|
"time": 225.466,
|
||||||
|
"response": 200
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"timestamp": 1671750781,
|
||||||
|
"time": 209.352,
|
||||||
|
"response": 200
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"timestamp": 1671750841,
|
||||||
|
"time": 225.638,
|
||||||
|
"response": 200
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"timestamp": 1671750901,
|
||||||
|
"time": 228.026,
|
||||||
|
"response": 200
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"timestamp": 1671750961,
|
||||||
|
"time": 215.496,
|
||||||
|
"response": 200
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"timestamp": 1671751021,
|
||||||
|
"time": 238.666,
|
||||||
|
"response": 200
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"timestamp": 1671751081,
|
||||||
|
"time": 218.908,
|
||||||
|
"response": 200
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"timestamp": 1671751141,
|
||||||
|
"time": 216.086,
|
||||||
|
"response": 200
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"timestamp": 1671751201,
|
||||||
|
"time": 225.955,
|
||||||
|
"response": 200
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"timestamp": 1671751261,
|
||||||
|
"time": 224.078,
|
||||||
|
"response": 200
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"timestamp": 1671751321,
|
||||||
|
"time": 223.235,
|
||||||
|
"response": 200
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"timestamp": 1671751381,
|
||||||
|
"time": 226.129,
|
||||||
|
"response": 200
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"timestamp": 1671751441,
|
||||||
|
"time": 228.306,
|
||||||
|
"response": 200
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"timestamp": 1671751501,
|
||||||
|
"time": 212.357,
|
||||||
|
"response": 200
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"timestamp": 1671751561,
|
||||||
|
"time": 239.809,
|
||||||
|
"response": 200
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"timestamp": 1671751621,
|
||||||
|
"time": 237.583,
|
||||||
|
"response": 200
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"timestamp": 1671751681,
|
||||||
|
"time": 224.485,
|
||||||
|
"response": 200
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"timestamp": 1671751741,
|
||||||
|
"time": 235.199,
|
||||||
|
"response": 200
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"timestamp": 1671751801,
|
||||||
|
"time": 239.203,
|
||||||
|
"response": 200
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"timestamp": 1671751861,
|
||||||
|
"time": 239.08,
|
||||||
|
"response": 200
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"timestamp": 1671751921,
|
||||||
|
"time": 221.216,
|
||||||
|
"response": 200
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"timestamp": 1671751981,
|
||||||
|
"time": 220.381,
|
||||||
|
"response": 200
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"timestamp": 1671752041,
|
||||||
|
"time": 219.535,
|
||||||
|
"response": 200
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"timestamp": 1671752101,
|
||||||
|
"time": 218.568,
|
||||||
|
"response": 200
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"timestamp": 1671752161,
|
||||||
|
"time": 225.099,
|
||||||
|
"response": 200
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"timestamp": 1671752221,
|
||||||
|
"time": 233.544,
|
||||||
|
"response": 200
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"timestamp": 1671752281,
|
||||||
|
"time": 231.748,
|
||||||
|
"response": 200
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"timestamp": 1671752341,
|
||||||
|
"time": 219.454,
|
||||||
|
"response": 200
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"timestamp": 1671752401,
|
||||||
|
"time": 236.159,
|
||||||
|
"response": 200
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"timestamp": 1671752462,
|
||||||
|
"time": 220.944,
|
||||||
|
"response": 200
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"timestamp": 1671752521,
|
||||||
|
"time": 209.714,
|
||||||
|
"response": 200
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"timestamp": 1671752581,
|
||||||
|
"time": 240.137,
|
||||||
|
"response": 200
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"timestamp": 1671752641,
|
||||||
|
"time": 239.22,
|
||||||
|
"response": 200
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"timestamp": 1671752701,
|
||||||
|
"time": 238.772,
|
||||||
|
"response": 200
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"timestamp": 1671752761,
|
||||||
|
"time": 223.05,
|
||||||
|
"response": 200
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"timestamp": 1671752821,
|
||||||
|
"time": 223.467,
|
||||||
|
"response": 200
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"timestamp": 1671752881,
|
||||||
|
"time": 230.855,
|
||||||
|
"response": 200
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"timestamp": 1671752941,
|
||||||
|
"time": 223.755,
|
||||||
|
"response": 200
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"timestamp": 1671753001,
|
||||||
|
"time": 218.684,
|
||||||
|
"response": 200
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"timestamp": 1671753061,
|
||||||
|
"time": 223.834,
|
||||||
|
"response": 200
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"timestamp": 1671753121,
|
||||||
|
"time": 219.454,
|
||||||
|
"response": 200
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"timestamp": 1671753181,
|
||||||
|
"time": 215.982,
|
||||||
|
"response": 200
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"timestamp": 1671753241,
|
||||||
|
"time": 225.577,
|
||||||
|
"response": 200
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"timestamp": 1671753301,
|
||||||
|
"time": 227.389,
|
||||||
|
"response": 200
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"timestamp": 1671753361,
|
||||||
|
"time": 225.435,
|
||||||
|
"response": 200
|
||||||
|
}
|
||||||
|
]
|
147
style.css
Normal file
147
style.css
Normal file
|
@ -0,0 +1,147 @@
|
||||||
|
@import url('https://rsms.me/inter/inter.css');
|
||||||
|
@import url('https://cdnjs.cloudflare.com/ajax/libs/font-awesome/6.2.1/css/fontawesome.min.css');
|
||||||
|
|
||||||
|
* {
|
||||||
|
text-rendering: optimizeLegibility;
|
||||||
|
}
|
||||||
|
|
||||||
|
body {
|
||||||
|
font-family: 'Inter', sans-serif;
|
||||||
|
background:#212529;
|
||||||
|
color: #f8f9fa;
|
||||||
|
font-size: 1.3em;
|
||||||
|
}
|
||||||
|
|
||||||
|
h2 {
|
||||||
|
margin: 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
.incident {
|
||||||
|
background: #000;
|
||||||
|
padding: .1em 1em;
|
||||||
|
margin: 0 0 1em 0;
|
||||||
|
border-radius: .25em;
|
||||||
|
}
|
||||||
|
|
||||||
|
.post {
|
||||||
|
background: #495057;
|
||||||
|
color: #fff;
|
||||||
|
}
|
||||||
|
|
||||||
|
.notice {
|
||||||
|
background: #ffd43b;
|
||||||
|
color: #000;
|
||||||
|
}
|
||||||
|
|
||||||
|
.alert {
|
||||||
|
background: #c92a2a;
|
||||||
|
color: #fff;
|
||||||
|
}
|
||||||
|
|
||||||
|
.item p, .item p a {
|
||||||
|
margin: 0;
|
||||||
|
color: #aaa;
|
||||||
|
font-size: 95%;
|
||||||
|
}
|
||||||
|
|
||||||
|
p {
|
||||||
|
line-height: 150%;
|
||||||
|
}
|
||||||
|
|
||||||
|
canvas:hover {
|
||||||
|
cursor: crosshair;
|
||||||
|
}
|
||||||
|
|
||||||
|
main {
|
||||||
|
max-width: 40em;
|
||||||
|
margin: auto;
|
||||||
|
padding: 1em;
|
||||||
|
}
|
||||||
|
|
||||||
|
footer {
|
||||||
|
text-align: center;
|
||||||
|
margin: 0 0 3em 0;
|
||||||
|
color: #888;
|
||||||
|
font-size: 90%;
|
||||||
|
}
|
||||||
|
|
||||||
|
a:link, a:visited, a:hover, a:active {
|
||||||
|
color: #f8f9fa;
|
||||||
|
}
|
||||||
|
|
||||||
|
#status {
|
||||||
|
background: #343a40;
|
||||||
|
border-radius: .25em;
|
||||||
|
padding: .1em 1em;
|
||||||
|
}
|
||||||
|
|
||||||
|
#notice {
|
||||||
|
background: #ffec99;
|
||||||
|
color: #000;
|
||||||
|
border-radius: .25em;
|
||||||
|
padding: .1em 1em;
|
||||||
|
margin-top: 1em;
|
||||||
|
}
|
||||||
|
|
||||||
|
.status {
|
||||||
|
color: #868e96;
|
||||||
|
font-weight: normal;
|
||||||
|
font-size: 65%;
|
||||||
|
font-family: monospace;
|
||||||
|
padding-left: .7em;
|
||||||
|
}
|
||||||
|
|
||||||
|
.bloops {
|
||||||
|
margin: .5em 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
.bloop {
|
||||||
|
background: #555;
|
||||||
|
width: calc((100% / 60) - 2px);
|
||||||
|
height: 30px;
|
||||||
|
border-radius: 5px;
|
||||||
|
margin-right: 2px;
|
||||||
|
display: inline-block;
|
||||||
|
}
|
||||||
|
|
||||||
|
.bloop:hover {
|
||||||
|
opacity: 0.5;
|
||||||
|
cursor: help;
|
||||||
|
}
|
||||||
|
|
||||||
|
.bloops p {
|
||||||
|
background: #495057;
|
||||||
|
padding: .5em;
|
||||||
|
border-radius: .25em;
|
||||||
|
}
|
||||||
|
|
||||||
|
.bloop.good { background: #51cf66; }
|
||||||
|
.bloop.bad { background: #fa5252; }
|
||||||
|
.bloop.meh { background: #ffd43b; }
|
||||||
|
|
||||||
|
.item {
|
||||||
|
margin: 3em 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
.good { color: #51cf66; }
|
||||||
|
.bad { color: #fa5252; }
|
||||||
|
.meh { color: #ffd43b; }
|
||||||
|
|
||||||
|
.bloops {
|
||||||
|
position: relative;
|
||||||
|
}
|
||||||
|
|
||||||
|
.bloop_info {
|
||||||
|
position: absolute;
|
||||||
|
top: 2em; left: 0em;
|
||||||
|
background: #fff;
|
||||||
|
padding: 1em;
|
||||||
|
opacity: 1;
|
||||||
|
z-index: 100;
|
||||||
|
}
|
||||||
|
|
||||||
|
@media (max-width: 500px) {
|
||||||
|
body {
|
||||||
|
font-size: 1em;
|
||||||
|
}
|
||||||
|
}
|
1
updates/forum.hhf.technology.md
Normal file
1
updates/forum.hhf.technology.md
Normal file
|
@ -0,0 +1 @@
|
||||||
|
hhf.technology, your go-to community for all things technology!.
|
1
updates/git.hhf.technology.md
Normal file
1
updates/git.hhf.technology.md
Normal file
|
@ -0,0 +1 @@
|
||||||
|
HHF Technology Repository: A painless, self-hosted Git service.
|
Loading…
Reference in a new issue