Init
This commit is contained in:
commit
81befa917b
2
.gitignore
vendored
Normal file
2
.gitignore
vendored
Normal file
@ -0,0 +1,2 @@
|
||||
pinglerr_web/config.php
|
||||
pinglerr_daemon/config.ini
|
34
dump.sql
Normal file
34
dump.sql
Normal file
@ -0,0 +1,34 @@
|
||||
CREATE TABLE `pinglerr_pings` (
|
||||
`id` int(11) NOT NULL,
|
||||
`ping_avg` float DEFAULT NULL,
|
||||
`pkt_transmitted` int(11) DEFAULT NULL,
|
||||
`pkt_received` int(11) DEFAULT NULL,
|
||||
`scan_id` int(11) NOT NULL,
|
||||
`date_added` timestamp NOT NULL DEFAULT current_timestamp()
|
||||
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb3 COLLATE=utf8mb3_general_ci;
|
||||
|
||||
|
||||
CREATE TABLE `pinglerr_scans` (
|
||||
`id` int(11) NOT NULL,
|
||||
`start_date` timestamp NOT NULL DEFAULT current_timestamp(),
|
||||
`end_date` timestamp NOT NULL,
|
||||
`status_code` varchar(255) NOT NULL,
|
||||
`host` varchar(255) NOT NULL,
|
||||
`error_message` mediumtext DEFAULT NULL,
|
||||
`ref_uuid` varchar(255) DEFAULT NULL
|
||||
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb3 COLLATE=utf8mb3_general_ci;
|
||||
|
||||
ALTER TABLE `pinglerr_pings`
|
||||
ADD PRIMARY KEY (`id`),
|
||||
ADD KEY `scan_id` (`scan_id`);
|
||||
|
||||
ALTER TABLE `pinglerr_scans`
|
||||
ADD PRIMARY KEY (`id`),
|
||||
ADD KEY `ref_uuid` (`ref_uuid`);
|
||||
|
||||
ALTER TABLE `pinglerr_pings`
|
||||
MODIFY `id` int(11) NOT NULL AUTO_INCREMENT;
|
||||
|
||||
ALTER TABLE `pinglerr_scans`
|
||||
MODIFY `id` int(11) NOT NULL AUTO_INCREMENT;
|
||||
COMMIT;
|
156
pinglerr_daemon/Pinglerr.py
Normal file
156
pinglerr_daemon/Pinglerr.py
Normal file
@ -0,0 +1,156 @@
|
||||
import configparser
|
||||
|
||||
config = configparser.ConfigParser()
|
||||
config.read('config.ini')
|
||||
|
||||
config = {
|
||||
'endpoint' : config.get('General', 'endpoint')
|
||||
}
|
||||
|
||||
import logging
|
||||
import subprocess
|
||||
import threading
|
||||
import time
|
||||
import requests
|
||||
|
||||
# index being the scan_id
|
||||
ping_is_in_progress = {
|
||||
0: False
|
||||
}
|
||||
|
||||
# use persistent connection, this is MUCH faster
|
||||
session = requests.Session()
|
||||
|
||||
# holds the result of all pings that are waiting to be sent to the server
|
||||
completed_pings = []
|
||||
|
||||
# used to prevent race conditions
|
||||
lock = threading.Lock()
|
||||
|
||||
def main():
|
||||
global completed_pings
|
||||
global session
|
||||
global lock
|
||||
|
||||
logging.basicConfig(level=logging.INFO, format='[%(asctime)s] %(levelname)s: %(message)s', datefmt='%Y-%m-%d %H:%M:%S')
|
||||
logging.info("Started")
|
||||
|
||||
last_loop_time = 0
|
||||
time_since_last_status_update = 0.0
|
||||
scans_in_progress = []
|
||||
while True:
|
||||
last_loop_time = time.time()
|
||||
try:
|
||||
response = session.get(config['endpoint'] + '?p=daemon_poll')
|
||||
if scans_in_progress != response.json():
|
||||
prev_scan_ids = []
|
||||
for scan in scans_in_progress:
|
||||
prev_scan_ids.append(scan['id'])
|
||||
|
||||
scans_in_progress = response.json()
|
||||
|
||||
for scan in scans_in_progress:
|
||||
if scan['id'] not in prev_scan_ids:
|
||||
logging.info(f"Found job for host: {scan['host']}, ref_uuid: {scan['ref_uuid']}")
|
||||
except:
|
||||
logging.error("Failed to retrieve jobs!")
|
||||
time.sleep(5)
|
||||
continue
|
||||
|
||||
for scan in scans_in_progress:
|
||||
ping_thread = threading.Thread(
|
||||
args=[scan],
|
||||
target=ping
|
||||
)
|
||||
ping_thread.start()
|
||||
|
||||
if completed_pings:
|
||||
lock.acquire()
|
||||
session.post(config['endpoint'] + '?p=daemon_bulk_update', json=completed_pings)
|
||||
completed_pings = []
|
||||
lock.release()
|
||||
|
||||
if time.time() > time_since_last_status_update + 10:
|
||||
time_since_last_status_update = time.time()
|
||||
logging.info(f"Summary: [Active scans: {len(scans_in_progress)}] [Active threads: {threading.active_count()}]")
|
||||
|
||||
# make sure it stays around 5 seconds each loop
|
||||
time_to_sleep = last_loop_time - time.time() + 5
|
||||
if time_to_sleep < 0:
|
||||
continue
|
||||
|
||||
time.sleep(time_to_sleep)
|
||||
|
||||
def report_ping_error(scan_id, error_message, pkt_transmitted = '0', pkt_received = '0'):
|
||||
global completed_pings
|
||||
global lock
|
||||
|
||||
logging.debug('Ping error; scan_id: ' + str(scan_id) + ' Error message: ' + error_message)
|
||||
|
||||
lock.acquire()
|
||||
completed_pings.append({
|
||||
'scan_id' : str(scan_id),
|
||||
'pkt_transmitted' : pkt_transmitted,
|
||||
'pkt_received' : pkt_received,
|
||||
})
|
||||
lock.release()
|
||||
|
||||
|
||||
def ping(scan):
|
||||
global ping_is_in_progress
|
||||
global completed_pings
|
||||
global lock
|
||||
|
||||
if scan['id'] in ping_is_in_progress and ping_is_in_progress[scan['id']] == True:
|
||||
return logging.debug('Ping command is already in progress, exiting thread for host: ' + scan['host'])
|
||||
|
||||
lock.acquire()
|
||||
ping_is_in_progress[scan['id']] = True
|
||||
lock.release()
|
||||
|
||||
pkts_to_send = '10'
|
||||
ping_cmd_output = subprocess.run(
|
||||
['ping', '-c', pkts_to_send, '-i', '0.5', scan['host'], '-w', '5'],
|
||||
capture_output=True
|
||||
)
|
||||
|
||||
lock.acquire()
|
||||
ping_is_in_progress[scan['id']] = False
|
||||
lock.release()
|
||||
|
||||
if ping_cmd_output.returncode == 2:
|
||||
return report_ping_error(scan['id'],
|
||||
'Ping command exited with status code 2; most likely failed to resolve host',
|
||||
pkts_to_send
|
||||
)
|
||||
|
||||
cmd_lines = (
|
||||
ping_cmd_output.stdout.decode()
|
||||
).split('\n')
|
||||
|
||||
ping_avg = '0'
|
||||
pkt_transmitted = '0'
|
||||
pkt_received = '0'
|
||||
try:
|
||||
ping_stats = cmd_lines[-3].split()
|
||||
ping_avgs = cmd_lines[-2].split()[3].split('/')
|
||||
ping_avg = ping_avgs[1]
|
||||
pkt_transmitted = ping_stats[0]
|
||||
pkt_received = ping_stats[3]
|
||||
except:
|
||||
return report_ping_error(scan['id'],
|
||||
'Failed to parse ping output: \n' + ping_cmd_output.stdout.decode(),
|
||||
pkts_to_send
|
||||
)
|
||||
|
||||
lock.acquire()
|
||||
completed_pings.append({
|
||||
'scan_id' : str(scan['id']),
|
||||
'pkt_transmitted' : pkt_transmitted,
|
||||
'pkt_received' : pkt_received,
|
||||
'ping_avg' : ping_avg,
|
||||
})
|
||||
lock.release()
|
||||
|
||||
if __name__ == "__main__":
|
||||
main()
|
2
pinglerr_daemon/config.default.ini
Normal file
2
pinglerr_daemon/config.default.ini
Normal file
@ -0,0 +1,2 @@
|
||||
[General]
|
||||
endpoint = https://whatever.com/
|
10
pinglerr_web/config.default.php
Normal file
10
pinglerr_web/config.default.php
Normal file
@ -0,0 +1,10 @@
|
||||
<?php
|
||||
/**
|
||||
* Copy this file and rename it to config.php
|
||||
*/
|
||||
return [
|
||||
'db_host' => 'hostname.invalid',
|
||||
'db_user' => 'admin',
|
||||
'db_pass' => 'pass',
|
||||
'db_database' => 'poop',
|
||||
];
|
147
pinglerr_web/ping_stats_compressor.php
Normal file
147
pinglerr_web/ping_stats_compressor.php
Normal file
@ -0,0 +1,147 @@
|
||||
<?php
|
||||
/**
|
||||
* Since we are gathering ping statistics at a high resolution interval this builds up really
|
||||
* quickly for longer running ping jobs causing slowdowns and using lots of storage.
|
||||
*
|
||||
* This script is meant to be ran periodically; it merges the high resolution dataset into a lower
|
||||
* resolution dataset without losing too much important information.
|
||||
*/
|
||||
|
||||
$config = require 'config.php';
|
||||
|
||||
function get_mysqli(): MySQLi
|
||||
{
|
||||
static $handle;
|
||||
if (!isset($handle)) {
|
||||
$handle = new MySQLi(
|
||||
hostname: $config['db_host'],
|
||||
username: $config['db_name'],
|
||||
password: $config['db_pass'],
|
||||
database: $config['db_database'],
|
||||
);
|
||||
// make sure that the timezone is set to UTC!!!
|
||||
$handle->query("SET time_zone = '+00:00'");
|
||||
}
|
||||
return $handle;
|
||||
}
|
||||
|
||||
$scans = (function(): array
|
||||
{
|
||||
$stmt = get_mysqli()->prepare(
|
||||
"SELECT * FROM pinglerr_scans"
|
||||
);
|
||||
$stmt->execute();
|
||||
$result = $stmt->get_result();
|
||||
$assoc_array = [];
|
||||
while ($row = $result->fetch_assoc()) {
|
||||
$assoc_array[] = $row;
|
||||
}
|
||||
return $assoc_array;
|
||||
})();
|
||||
|
||||
foreach ($scans as $scan) {
|
||||
$pings = (function() use($scan): array
|
||||
{
|
||||
$stmt = get_mysqli()->prepare(
|
||||
"SELECT ping_avg, pkt_transmitted, pkt_received, date_added FROM pinglerr_pings WHERE scan_id = ? ORDER BY date_added ASC"
|
||||
);
|
||||
$stmt->bind_param("i", $scan['id']);
|
||||
$stmt->execute();
|
||||
$result = $stmt->get_result();
|
||||
$assoc_array = [];
|
||||
while ($row = $result->fetch_assoc()) {
|
||||
$assoc_array[] = $row;
|
||||
}
|
||||
return $assoc_array;
|
||||
})();
|
||||
if (empty($pings)) {
|
||||
continue;
|
||||
}
|
||||
|
||||
echo 'Processing scan_id: ' . $scan['id'] . ' rows: ' . count($pings) . "\n";
|
||||
|
||||
$limit_length = 1000;
|
||||
if (count($pings) > $limit_length) {
|
||||
$merged_pings = [];
|
||||
|
||||
$merge_gap = count($pings) / $limit_length;
|
||||
$merge_iteration = 0;
|
||||
$merge_remainder = 0;
|
||||
|
||||
$ping_avg_sum_storage = [];
|
||||
$ping_pkt_transmitted_storage = [];
|
||||
$ping_pkt_received_storage = [];
|
||||
foreach ($pings as $ping) {
|
||||
if ($merge_iteration >= $merge_gap - $merge_remainder) {
|
||||
$merge_remainder+= $merge_iteration - $merge_gap;
|
||||
|
||||
$how_many_nulls = 0;
|
||||
for ($i=0; $i < count($ping_avg_sum_storage); $i++) {
|
||||
if ($ping_avg_sum_storage[$i] === null) {
|
||||
$how_many_nulls++;
|
||||
}
|
||||
}
|
||||
if ($how_many_nulls > count($ping_avg_sum_storage) / 2) {
|
||||
$ping_avg = null;
|
||||
} else {
|
||||
for ($i=0; $i < count($ping_avg_sum_storage); $i++) {
|
||||
if ($ping_avg_sum_storage[$i] === null) {
|
||||
unset($ping_avg_sum_storage[$i]);
|
||||
}
|
||||
}
|
||||
$ping_avg = round(array_sum($ping_avg_sum_storage) / count($ping_avg_sum_storage), 3);
|
||||
}
|
||||
|
||||
$merged_pings[] = [
|
||||
'ping_avg' => $ping_avg,
|
||||
'date_added' => $ping['date_added'],
|
||||
'pkt_transmitted' => round(array_sum($ping_pkt_transmitted_storage) / count($ping_pkt_transmitted_storage), 3),
|
||||
'pkt_received' => round(array_sum($ping_pkt_received_storage) / count($ping_pkt_received_storage), 3),
|
||||
];
|
||||
$ping_pkt_transmitted_storage = [];
|
||||
$ping_pkt_received_storage = [];
|
||||
$ping_avg_sum_storage = [];
|
||||
$merge_iteration = 0;
|
||||
}
|
||||
$ping_pkt_transmitted_storage[] = $ping['pkt_transmitted'];
|
||||
$ping_pkt_received_storage[] = $ping['pkt_received'];
|
||||
$ping_avg_sum_storage[] = $ping['ping_avg'];
|
||||
$merge_iteration++;
|
||||
}
|
||||
|
||||
$pings = $merged_pings;
|
||||
} else {
|
||||
echo "Skipped\n";
|
||||
continue;
|
||||
}
|
||||
|
||||
(function() use($scan) {
|
||||
$stmt = get_mysqli()->prepare(
|
||||
"DELETE FROM pinglerr_pings WHERE scan_id = ?"
|
||||
);
|
||||
$status_code = 'DONE';
|
||||
$error_message = 'Stopped because end_date was reached';
|
||||
$stmt->bind_param("s",
|
||||
$scan['id']
|
||||
);
|
||||
$stmt->execute();
|
||||
})();
|
||||
|
||||
foreach ($pings as $ping) {
|
||||
$stmt = get_mysqli()->prepare(
|
||||
"INSERT INTO pinglerr_pings (scan_id, ping_avg, pkt_transmitted, pkt_received, date_added) VALUES (?,?,?,?,?)"
|
||||
);
|
||||
$stmt->bind_param("idiis",
|
||||
$scan['id'],
|
||||
$ping['ping_avg'],
|
||||
$ping['pkt_transmitted'],
|
||||
$ping['pkt_received'],
|
||||
$ping['date_added']
|
||||
);
|
||||
$stmt->execute();
|
||||
}
|
||||
|
||||
echo 'Done: ' . count($pings) . "\n";
|
||||
}
|
||||
|
||||
exit();
|
955
pinglerr_web/public/index.php
Normal file
955
pinglerr_web/public/index.php
Normal file
@ -0,0 +1,955 @@
|
||||
<?php
|
||||
|
||||
$config = require '../config.php';
|
||||
|
||||
function get_mysqli(): MySQLi
|
||||
{
|
||||
global $config;
|
||||
static $handle;
|
||||
if (!isset($handle)) {
|
||||
$handle = new MySQLi(
|
||||
hostname: $config['db_host'],
|
||||
username: $config['db_user'],
|
||||
password: $config['db_pass'],
|
||||
database: $config['db_database'],
|
||||
);
|
||||
// make sure that the timezone is set to UTC!!!
|
||||
$handle->query("SET time_zone = '+00:00'");
|
||||
}
|
||||
return $handle;
|
||||
}
|
||||
|
||||
function flash_message(string $msg, string $type = 'info', bool $unsafe = false): void
|
||||
{
|
||||
if (session_status() === PHP_SESSION_NONE)
|
||||
session_start();
|
||||
|
||||
$types = [
|
||||
"info",
|
||||
"success",
|
||||
"danger",
|
||||
"warning"
|
||||
];
|
||||
if (!in_array($type, $types)) {
|
||||
throw new InvalidArgumentException("Flash type: \"$type\" does not exist");
|
||||
}
|
||||
$key = 'flash_messages';
|
||||
if (!isset($_SESSION[$key]))
|
||||
{
|
||||
$_SESSION[$key] = [];
|
||||
}
|
||||
if (count($_SESSION[$key]) >= 100)
|
||||
{
|
||||
$_SESSION[$key] = [];
|
||||
throw new Exception('Too many flashed messages!');
|
||||
}
|
||||
$_SESSION[$key][] = [
|
||||
"message" => ($unsafe) ? $msg : htmlspecialchars($msg),
|
||||
"type" => $type
|
||||
];
|
||||
}
|
||||
|
||||
function get_flash_messages(): ?array
|
||||
{
|
||||
if (session_status() === PHP_SESSION_NONE)
|
||||
session_start();
|
||||
|
||||
$key = 'flash_messages';
|
||||
if (empty($_SESSION[$key])) {
|
||||
return null;
|
||||
}
|
||||
$msgs = $_SESSION[$key];
|
||||
$_SESSION[$key] = [];
|
||||
return $msgs;
|
||||
}
|
||||
|
||||
function page_header(string $title, array $toolbar_links = [])
|
||||
{
|
||||
$ref_uuid = filter_input(INPUT_GET, 'ref_uuid');
|
||||
$prefer_ref_uuid = !empty($ref_uuid);
|
||||
if ($prefer_ref_uuid) $ref_uuid = htmlspecialchars($ref_uuid);
|
||||
|
||||
$host = strtolower(filter_input(INPUT_GET, 'host'));
|
||||
if (!empty($host) && !is_valid_host($host)) {
|
||||
$host = NULL;
|
||||
}
|
||||
|
||||
?>
|
||||
<!DOCTYPE html>
|
||||
<html lang="en">
|
||||
<head>
|
||||
<meta charset="UTF-8">
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1.0">
|
||||
<title><?=$title?></title>
|
||||
<link rel="stylesheet" href="static/css/style.css">
|
||||
<link rel="icon" type="image/svg+xml" href="static/img/logo.svg">
|
||||
</head>
|
||||
<body>
|
||||
<header>
|
||||
<nav>
|
||||
<a href="?p=default<?=($prefer_ref_uuid ? '&ref_uuid=' . $ref_uuid : '') . (!empty($host) ? '&host=' . $host : '')?>">Pinglerr</a>
|
||||
</nav>
|
||||
</header>
|
||||
|
||||
<?php if (!empty($toolbar_links)): ?>
|
||||
<div class="toolbar">
|
||||
<nav>
|
||||
Handling:
|
||||
<?php foreach ($toolbar_links as $link): ?>
|
||||
<span>[ <a
|
||||
<?php if(isset($link['type']) && $link['type'] === 'danger'): ?>
|
||||
class="link-btn-danger"
|
||||
<?php else: ?>
|
||||
class="link-btn-normal"
|
||||
<?php endif; ?>
|
||||
href="<?=$link['href']?>"><?=$link['name']?></a> ]</span>
|
||||
<?php endforeach; ?>
|
||||
</nav>
|
||||
</div>
|
||||
<?php endif; ?>
|
||||
|
||||
<main>
|
||||
|
||||
<?php
|
||||
$flash_messages = get_flash_messages();
|
||||
?>
|
||||
<?php if(!empty($flash_messages)): ?>
|
||||
<?php foreach ($flash_messages as $message): ?>
|
||||
<?php if($message['type'] === 'info'): ?>
|
||||
<div class="alert alert-info"><?=$message['message']?></div>
|
||||
<?php endif; ?>
|
||||
<?php if($message['type'] === 'success'): ?>
|
||||
<div class="alert alert-success"><?=$message['message']?></div>
|
||||
<?php endif; ?>
|
||||
<?php if($message['type'] === 'danger'): ?>
|
||||
<div class="alert alert-danger"><?=$message['message']?></div>
|
||||
<?php endif; ?>
|
||||
<?php if($message['type'] === 'warning'): ?>
|
||||
<div class="alert alert-warning"><?=$message['message']?></div>
|
||||
<?php endif; ?>
|
||||
<?php endforeach ?>
|
||||
<hr>
|
||||
<?php endif; ?>
|
||||
<?php
|
||||
}
|
||||
|
||||
function page_footer()
|
||||
{
|
||||
?>
|
||||
</main>
|
||||
<script src="static/js/apexcharts.min.js"></script>
|
||||
<script src="static/js/global.js"></script>
|
||||
<footer>
|
||||
<small>Laget av William for Altidata AS</small>
|
||||
</footer>
|
||||
</body>
|
||||
</html>
|
||||
<?php
|
||||
}
|
||||
|
||||
function get_referer(): string
|
||||
{
|
||||
if (isset($_SERVER['HTTP_REFERER'])) {
|
||||
return htmlspecialchars($_SERVER['HTTP_REFERER']);
|
||||
}
|
||||
return '?p=default';
|
||||
}
|
||||
|
||||
function is_valid_host(string $host): bool
|
||||
{
|
||||
if (empty($host)) {
|
||||
return false;
|
||||
}
|
||||
if (!filter_var(
|
||||
$host, FILTER_VALIDATE_DOMAIN, FILTER_FLAG_HOSTNAME)
|
||||
) {
|
||||
return false;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
function suicide_user_bad_request(string $errormsg)
|
||||
{
|
||||
http_response_code(400);
|
||||
page_header('Oops, dette var en feil');
|
||||
?>
|
||||
<h1>Kunne ikke fortsette</h1>
|
||||
<p>Feilmelding: <?=htmlspecialchars($errormsg)?></p>
|
||||
<a href="<?=get_referer()?>">Klikk her for å gå tilbake</a>
|
||||
<?php
|
||||
page_footer();
|
||||
die();
|
||||
}
|
||||
|
||||
function pinglerr_get_scan(?string $host = null, ?string $ref_uuid = null, ?string $scan_id = null, bool $must_be_active_or_waiting = true): ?array
|
||||
{
|
||||
if ($host !== null) {
|
||||
$sql = "SELECT * FROM pinglerr_scans WHERE host = ? AND ref_uuid IS NULL";
|
||||
$param = $host;
|
||||
}
|
||||
if ($ref_uuid !== null) {
|
||||
$sql = "SELECT * FROM pinglerr_scans WHERE ref_uuid = ?";
|
||||
$param = $ref_uuid;
|
||||
}
|
||||
if ($scan_id !== null) {
|
||||
$sql = "SELECT * FROM pinglerr_scans WHERE id = ?";
|
||||
$param = $scan_id;
|
||||
}
|
||||
if ($must_be_active_or_waiting) {
|
||||
$sql = $sql . " AND status_code IN ('ACTIVE', 'WAITING')";
|
||||
}
|
||||
$stmt = get_mysqli()->prepare($sql);
|
||||
$stmt->bind_param("s", $param);
|
||||
$stmt->execute();
|
||||
$result = $stmt->get_result();
|
||||
$assoc = $result->fetch_assoc();
|
||||
return $assoc;
|
||||
}
|
||||
|
||||
function pinglerr_get_scan_history(?string $host = null, ?string $ref_uuid = null, ?int $max_rows = null): ?array
|
||||
{
|
||||
if ($host !== null) {
|
||||
$sql = "SELECT * FROM pinglerr_scans WHERE host = ? AND ref_uuid IS NULL ORDER BY start_date DESC";
|
||||
$param = $host;
|
||||
}
|
||||
if ($ref_uuid !== null) {
|
||||
$sql = "SELECT * FROM pinglerr_scans WHERE ref_uuid= ? ORDER BY start_date DESC";
|
||||
$param = $ref_uuid;
|
||||
}
|
||||
if ($max_rows !== null) {
|
||||
$sql = $sql . ' LIMIT ' . $max_rows;
|
||||
}
|
||||
$scans = (function() use ($sql, $param): ?array
|
||||
{
|
||||
$stmt = get_mysqli()->prepare($sql);
|
||||
$stmt->bind_param("s", $param);
|
||||
$stmt->execute();
|
||||
$result = $stmt->get_result();
|
||||
$assoc_array = [];
|
||||
while ($row = $result->fetch_assoc()) {
|
||||
$assoc_array[] = $row;
|
||||
}
|
||||
if (empty($assoc_array)) {
|
||||
return null;
|
||||
}
|
||||
return $assoc_array;
|
||||
})();
|
||||
return $scans;
|
||||
}
|
||||
|
||||
$route['default'] = function()
|
||||
{
|
||||
$ref_uuid = filter_input(INPUT_GET, 'ref_uuid');
|
||||
$prefer_ref_uuid = !empty($ref_uuid);
|
||||
if ($prefer_ref_uuid) $ref_uuid = htmlspecialchars($ref_uuid);
|
||||
|
||||
$host = strtolower(filter_input(INPUT_GET, 'host'));
|
||||
|
||||
|
||||
if (!empty($host) && !is_valid_host($host)) {
|
||||
suicide_user_bad_request('Ugyldig vert adresse');
|
||||
}
|
||||
|
||||
if ($prefer_ref_uuid) {
|
||||
$scan = pinglerr_get_scan(ref_uuid: $ref_uuid);
|
||||
} else {
|
||||
$scan = pinglerr_get_scan(host: $host);
|
||||
}
|
||||
|
||||
$function['show_form'] = function() use($ref_uuid, $prefer_ref_uuid, $host) {
|
||||
?>
|
||||
<h1>Pinglerr</h1>
|
||||
<p>Sjekk tilkobling status til IP-adresse eller annen tjeneste</p>
|
||||
<form method="POST" action="?p=start_scan">
|
||||
<input style="width: 100%; box-sizing: border-box; padding: .5rem;" value="<?=$host?>" autocomplete="off" placeholder="Vertsnavn eller adresse som f.eks nrk.no, 192.168.1.1" type="text" name="host">
|
||||
|
||||
<?php if($prefer_ref_uuid): ?>
|
||||
<br>
|
||||
<br>
|
||||
<label for="ref_uuid">Referanse</label>
|
||||
<input style="width: 100%; box-sizing: border-box; padding: .5rem;" value="<?=$ref_uuid?>" autocomplete="off" placeholder="Referanse" type="text" name="ref_uuid">
|
||||
<?php endif; ?>
|
||||
|
||||
<br>
|
||||
<br>
|
||||
<label for="time_limit">Hvor lenge skal denne kjøre?</label>
|
||||
<select name="time_limit" id="time_limit">
|
||||
<option value="600">10 minutter</option>
|
||||
<option value="3600">1 time</option>
|
||||
<option value="14400">4 timer</option>
|
||||
<option value="28800">8 timer</option>
|
||||
<option value="43200">12 timer</option>
|
||||
<option value="86400">24 timer</option>
|
||||
<option value="172800">2 døgn</option>
|
||||
<option value="345600">4 døgn</option>
|
||||
<option value="604800">1 uke</option>
|
||||
</select>
|
||||
<br>
|
||||
<br>
|
||||
<input style="width: 5rem; padding: .5rem;" type="submit" value="Sjekk" class="btn btn-primary btn-lg">
|
||||
</form>
|
||||
<?php
|
||||
};
|
||||
|
||||
$function['show_scan_history'] = function() use($prefer_ref_uuid, $ref_uuid, $host) {
|
||||
$show_full_history = boolval(filter_input(INPUT_GET, 'show_full_history'));
|
||||
$max_rows = 10;
|
||||
if ($show_full_history) {
|
||||
$max_rows = 10000;
|
||||
}
|
||||
|
||||
if ($prefer_ref_uuid) {
|
||||
$scans = pinglerr_get_scan_history(ref_uuid: $ref_uuid, max_rows: $max_rows);
|
||||
$current_scan = pinglerr_get_scan(ref_uuid: $ref_uuid);
|
||||
} else {
|
||||
$scans = pinglerr_get_scan_history($host, max_rows: $max_rows);
|
||||
$current_scan = pinglerr_get_scan($host);
|
||||
}
|
||||
if (empty($scans)) {
|
||||
return;
|
||||
}
|
||||
?>
|
||||
<h1>Historikk</h1>
|
||||
<p>Tidligere skanninger som er gjort på denne verten eller referanse.</p>
|
||||
|
||||
<table>
|
||||
<tr>
|
||||
<th>Start Dato</th>
|
||||
<th>Handling</th>
|
||||
</tr>
|
||||
<?php foreach($scans as $scan): ?>
|
||||
<tr>
|
||||
<td class="dateConverter"><?=$scan['start_date']?></td>
|
||||
|
||||
<td>[ <a class="link-btn-normal" href="?p=scan_details&scan_id=<?=$scan['id']?><?=($prefer_ref_uuid ? '&ref_uuid=' . $ref_uuid : '&host=' . $host)?>">Detaljer</a> ]</td>
|
||||
</tr>
|
||||
<?php endforeach; ?>
|
||||
</table>
|
||||
<?php
|
||||
};
|
||||
|
||||
$function['show_live_stats'] = function() use($scan, $ref_uuid, $prefer_ref_uuid)
|
||||
{
|
||||
?>
|
||||
<form>
|
||||
<label for="timeRangeSelector">Hent statistikk fra siste:</label>
|
||||
<select name="timeRangeSelector" id="timeRangeSelector">
|
||||
<option value="900">15 minutter</option>
|
||||
<option value="1800">30 minutter</option>
|
||||
<option value="3600">1 time</option>
|
||||
<option value="14400">4 timer</option>
|
||||
<option value="28800">8 timer</option>
|
||||
<option value="43200">12 timer</option>
|
||||
<option value="86400">24 timer</option>
|
||||
<option value="Details">Fullstendig</option>
|
||||
</select>
|
||||
</form>
|
||||
<hr>
|
||||
<h1>Sanntid statistikk</h1>
|
||||
<div class="sign-container">
|
||||
<div class="sign">
|
||||
<span class="sign-label" style="">Tilkobling</span><span class="sign-text" id="connection_status">Ukjent</span>
|
||||
</div>
|
||||
<div class="sign">
|
||||
<span class="sign-label">Vert</span><span class="sign-text"><?=$scan['host']?></span>
|
||||
</div>
|
||||
<?php if($prefer_ref_uuid): ?>
|
||||
<div class="sign">
|
||||
<span class="sign-label">Referanse</span><span class="sign-text"><?=$scan['ref_uuid']?></span>
|
||||
</div>
|
||||
<?php endif; ?>
|
||||
</div>
|
||||
|
||||
<br>
|
||||
|
||||
<div style="
|
||||
display: grid;
|
||||
grid-template-columns: repeat(3, minmax(0, 1fr));
|
||||
gap: .5rem;
|
||||
">
|
||||
<div style="padding: 0.5rem; background: #eee; border: 1px solid #ccc; text-align: center; border-radius: 0.25rem;">
|
||||
<div>Ping (siste)</div>
|
||||
<div style="font-size: 2rem;"><span id="ping">Vent</span></div>
|
||||
</div>
|
||||
<div style="padding: 0.5rem; background: #eee; border: 1px solid #ccc; text-align: center; border-radius: 0.25rem;">
|
||||
<div style="">Pakke tap</div>
|
||||
<div style="font-size: 2rem;"><span id="packet_loss">Vent</span></div>
|
||||
</div>
|
||||
<div style="padding: 0.5rem; background: #eee; border: 1px solid #ccc; text-align: center; border-radius: 0.25rem;">
|
||||
<div style="">Suksess-rate</div>
|
||||
<div style="font-size: 2rem;"><span id="uptime">Vent</span></div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<br>
|
||||
|
||||
<div style="height: 15rem">
|
||||
<div id="liveChart"></div>
|
||||
</div>
|
||||
|
||||
<script src="static/js/live_stats.js"></script>
|
||||
<?php
|
||||
};
|
||||
|
||||
if (!empty($scan)) {
|
||||
page_header('Pinglerr', toolbar_links: [
|
||||
[
|
||||
'href' => '?p=stop_scanning&scan_id=' . $scan['id'] . ($prefer_ref_uuid ? '&ref_uuid=' . $ref_uuid : '') . (!empty($host) ? '&host=' . $host : ''),
|
||||
'name' => 'Stop skanning',
|
||||
'type' => 'danger'
|
||||
]
|
||||
]);
|
||||
} else {
|
||||
page_header('Pinglerr');
|
||||
}
|
||||
?>
|
||||
<?php if(!empty($scan)): ?>
|
||||
<?=$function['show_live_stats']()?>
|
||||
<?php endif;?>
|
||||
|
||||
<?php if(empty($scan)): ?>
|
||||
<?=$function['show_form']()?>
|
||||
<?php endif;?>
|
||||
|
||||
<?php if(!empty($host) || $prefer_ref_uuid): ?>
|
||||
<?=$function['show_scan_history']();?>
|
||||
<?php endif; ?>
|
||||
<?php
|
||||
page_footer();
|
||||
};
|
||||
|
||||
$route['scan_details'] = function()
|
||||
{
|
||||
$ref_uuid = filter_input(INPUT_GET, 'ref_uuid');
|
||||
$prefer_ref_uuid = !empty($ref_uuid);
|
||||
if ($prefer_ref_uuid) $ref_uuid = htmlspecialchars($ref_uuid);
|
||||
|
||||
$scan_id = filter_input(INPUT_GET, 'scan_id');
|
||||
if (empty($scan_id)) {
|
||||
suicide_user_bad_request('scan_id kan ikke være tom!');
|
||||
}
|
||||
|
||||
$scan = pinglerr_get_scan(
|
||||
scan_id: $scan_id,
|
||||
must_be_active_or_waiting: false
|
||||
);
|
||||
if (!$scan) {
|
||||
suicide_user_bad_request('Fant ikke scan i databasen!');
|
||||
}
|
||||
if ($scan['status_code'] === 'WAITING') {
|
||||
suicide_user_bad_request('Venter på at denne skanningen skal starte');
|
||||
}
|
||||
page_header('Detaljer - Pinglerr', toolbar_links: [
|
||||
[
|
||||
'href' => get_referer(),
|
||||
'name' => 'Gå tilbake'
|
||||
]
|
||||
]);
|
||||
?>
|
||||
<h1>Fullstendig statistikk</h1>
|
||||
<div class="sign-container">
|
||||
<?php if($scan['status_code'] === 'ACTIVE'): ?>
|
||||
<span class="sign"><span class="sign-label">Status</span><span class="sign-text alert-success">Aktiv</span></span>
|
||||
<?php elseif($scan['status_code'] === 'DONE'): ?>
|
||||
<span class="sign"><span class="sign-label">Status</span><span class="sign-text alert-info">Ferdig</span></span>
|
||||
<?php endif; ?>
|
||||
<span class="sign"><span class="sign-label">Vert</span><span class="sign-text"><?=$scan['host']?></span></span>
|
||||
<span class="sign"><span class="sign-label">ID</span><span class="sign-text"><?=$scan_id?></span></span>
|
||||
</div>
|
||||
|
||||
<div id="chartElement"></div>
|
||||
<div id="brushChartElement"></div>
|
||||
|
||||
<script src="static/js/scan_history_details.js"></script>
|
||||
<?php
|
||||
page_footer();
|
||||
};
|
||||
|
||||
$route['start_scan'] = function()
|
||||
{
|
||||
$ref_uuid = filter_input(INPUT_POST, 'ref_uuid');
|
||||
$prefer_ref_uuid = !empty($ref_uuid);
|
||||
if ($prefer_ref_uuid) $ref_uuid = htmlspecialchars($ref_uuid);
|
||||
|
||||
$host = strtolower(filter_input(INPUT_POST, 'host'));
|
||||
if (!is_valid_host($host)) {
|
||||
if ($prefer_ref_uuid) {
|
||||
header('Location: ?p=default&host=' . $host . '&ref_uuid=' . $ref_uuid);
|
||||
} else {
|
||||
header('Location: ?p=default&host=' . $host);
|
||||
}
|
||||
flash_message('Kunne ikke starte ny skanning: ugyldig vert adresse!', 'danger');
|
||||
die();
|
||||
}
|
||||
|
||||
if ($prefer_ref_uuid) {
|
||||
$scan = pinglerr_get_scan(ref_uuid: $ref_uuid);
|
||||
} else {
|
||||
$scan = pinglerr_get_scan($host);
|
||||
}
|
||||
|
||||
// if scan is not active or waiting
|
||||
if ($scan === null) {
|
||||
$stmt = get_mysqli()->prepare(
|
||||
"INSERT INTO pinglerr_scans (host, ref_uuid, status_code, end_date) VALUES (?, ?, ?, NOW() + INTERVAL ? SECOND)"
|
||||
);
|
||||
$status_code = 'WAITING';
|
||||
|
||||
$time_limit_in_seconds = filter_input(INPUT_POST, 'time_limit');
|
||||
if (!is_numeric($time_limit_in_seconds))
|
||||
suicide_user_bad_request('time_limit is not numeric');
|
||||
if ($time_limit_in_seconds > 31536000)
|
||||
suicide_user_bad_request('time_limit should not be above 1 year');
|
||||
if ($time_limit_in_seconds < 0)
|
||||
suicide_user_bad_request('time_limit should not be a negative value');
|
||||
|
||||
// run forever
|
||||
if ($time_limit_in_seconds == 0)
|
||||
$time_limit_in_seconds = 315569260;
|
||||
|
||||
$stmt->bind_param("sssi", $host, $ref_uuid, $status_code, $time_limit_in_seconds);
|
||||
$stmt->execute();
|
||||
} else {
|
||||
flash_message('Det eksisterer allerede en aktiv skanning på denne verten eller referansen');
|
||||
if ($prefer_ref_uuid) {
|
||||
header('Location: ?p=default&ref_uuid=' . $ref_uuid);
|
||||
} else {
|
||||
header('Location: ?p=default&host=' . $host);
|
||||
}
|
||||
die();
|
||||
}
|
||||
if ($prefer_ref_uuid) {
|
||||
header('Location: ?p=default&ref_uuid=' . $ref_uuid);
|
||||
} else {
|
||||
header('Location: ?p=default&host=' . $host);
|
||||
}
|
||||
};
|
||||
|
||||
$route['stop_scanning'] = function()
|
||||
{
|
||||
$confirm = filter_input(INPUT_GET, 'confirm');
|
||||
$scan_id = filter_input(INPUT_GET, 'scan_id');
|
||||
|
||||
$ref_uuid = filter_input(INPUT_GET, 'ref_uuid');
|
||||
$prefer_ref_uuid = !empty($ref_uuid);
|
||||
if ($prefer_ref_uuid) $ref_uuid = htmlspecialchars($ref_uuid);
|
||||
|
||||
$host = strtolower(filter_input(INPUT_GET, 'host'));
|
||||
if (!$prefer_ref_uuid && !empty($host) && !is_valid_host($host)) {
|
||||
suicide_user_bad_request('Ugyldig vertsnavn');
|
||||
}
|
||||
|
||||
if (boolval($confirm)) {
|
||||
$scan = pinglerr_get_scan(scan_id: $scan_id);
|
||||
if (empty($scan)) {
|
||||
suicide_user_bad_request('Fant ikke noen aktiv skanning');
|
||||
}
|
||||
$stmt = get_mysqli()->prepare(
|
||||
"UPDATE pinglerr_scans SET status_code = ?, end_date = NOW(), error_message = ? WHERE status_code IN ('ACTIVE', 'WAITING') AND id = ?"
|
||||
);
|
||||
$status_code = 'DONE';
|
||||
$error_message = 'Manually stopped by user';
|
||||
$stmt->bind_param("sss",
|
||||
$status_code,
|
||||
$error_message,
|
||||
$scan['id']
|
||||
);
|
||||
$stmt->execute();
|
||||
|
||||
if ($prefer_ref_uuid) {
|
||||
header('Location: ?p=default&host=' . $host . '&ref_uuid=' . $ref_uuid);
|
||||
} else {
|
||||
header('Location: ?p=default&host=' . $host);
|
||||
}
|
||||
flash_message('Du har nå stanset skanningen', 'success');
|
||||
die();
|
||||
|
||||
} else {
|
||||
page_header('Er du sikker? - Pingler');
|
||||
?>
|
||||
<h1>Er du sikker?</h1>
|
||||
<p>Vil du virkelig stanse denne skanningen?</p>
|
||||
<span>[ <a class="link-btn-success" href="?p=stop_scanning&confirm=true&scan_id=<?=htmlspecialchars($scan_id)?><?=($prefer_ref_uuid ? '&ref_uuid=' . $ref_uuid : '') . (!empty($host) ? '&host=' . $host : '')?>">Ja det vil jeg!</a> ]</span>
|
||||
<span>[ <a class="link-btn-danger" href="<?=get_referer()?>">Nei ta meg tilbake igjen</a> ]</span>
|
||||
<?php
|
||||
page_footer();
|
||||
}
|
||||
};
|
||||
|
||||
$route['daemon_poll'] = function()
|
||||
{
|
||||
$scans = (function(): ?array {
|
||||
$stmt = get_mysqli()->prepare(
|
||||
"SELECT * FROM pinglerr_scans WHERE status_code IN ('ACTIVE', 'WAITING')"
|
||||
);
|
||||
$stmt->execute();
|
||||
$result = $stmt->get_result();
|
||||
$assoc_array = [];
|
||||
while ($row = $result->fetch_assoc()) {
|
||||
$assoc_array[] = $row;
|
||||
}
|
||||
return $assoc_array;
|
||||
})();
|
||||
header('Content-type: application/json');
|
||||
echo json_encode($scans);
|
||||
};
|
||||
|
||||
// not in use anymore, replaced with daemon_bulk_update
|
||||
$route['daemon_update'] = function()
|
||||
{
|
||||
$scan_id = filter_input(INPUT_GET, 'scan_id');
|
||||
if (empty($scan_id)) {
|
||||
suicide_user_bad_request('Mangler scan_id query parameter');
|
||||
}
|
||||
|
||||
$scan = pinglerr_get_scan(scan_id: $scan_id);
|
||||
if ($scan === null) {
|
||||
suicide_user_bad_request('Kunne ikke finne scan fra scan_id');
|
||||
}
|
||||
|
||||
// if time is up change the status to DONE and also exit the script
|
||||
// TODO: this would probably be more suitable in the route daemon_jobs or some other
|
||||
// endpoint that the daemon would periodically call
|
||||
(function() use($scan)
|
||||
{
|
||||
$current_epoch = time();
|
||||
$end_epoch = strtotime($scan['end_date']);
|
||||
if ($current_epoch > $end_epoch) {
|
||||
$stmt = get_mysqli()->prepare(
|
||||
"UPDATE pinglerr_scans SET status_code = ?, error_message = ? WHERE status_code IN ('ACTIVE', 'WAITING') AND id = ?"
|
||||
);
|
||||
$status_code = 'DONE';
|
||||
$error_message = 'Stopped because end_date was reached';
|
||||
$stmt->bind_param("sss",
|
||||
$status_code,
|
||||
$error_message,
|
||||
$scan['id']
|
||||
);
|
||||
$stmt->execute();
|
||||
die();
|
||||
}
|
||||
})();
|
||||
|
||||
// set scan status from WAITING to ACTIVE
|
||||
(function() use($scan)
|
||||
{
|
||||
$stmt = get_mysqli()->prepare(
|
||||
"UPDATE pinglerr_scans SET status_code = ? WHERE id = ?"
|
||||
);
|
||||
$status_code = 'ACTIVE';
|
||||
$stmt->bind_param("si",
|
||||
$status_code,
|
||||
$scan['id'],
|
||||
);
|
||||
$stmt->execute();
|
||||
})();
|
||||
|
||||
(function() use($scan)
|
||||
{
|
||||
$pkt_transmitted = filter_input(INPUT_GET, 'pkt_transmitted');
|
||||
$pkt_received = filter_input(INPUT_GET, 'pkt_received');
|
||||
$ping_avg = filter_input(INPUT_GET, 'ping_avg');
|
||||
|
||||
$stmt = get_mysqli()->prepare(
|
||||
"INSERT INTO pinglerr_pings (scan_id, ping_avg, pkt_transmitted, pkt_received) VALUES (?,?,?,?)"
|
||||
);
|
||||
$stmt->bind_param("idii",
|
||||
$scan['id'],
|
||||
$ping_avg,
|
||||
$pkt_transmitted,
|
||||
$pkt_received,
|
||||
);
|
||||
$stmt->execute();
|
||||
})();
|
||||
};
|
||||
|
||||
$route['daemon_bulk_update'] = function()
|
||||
{
|
||||
$bulk_pings = json_decode(file_get_contents('php://input'), true);
|
||||
|
||||
foreach ($bulk_pings as $ping) {
|
||||
$scan_id = $ping['scan_id'];
|
||||
$pkt_transmitted = $ping['pkt_transmitted'];
|
||||
$pkt_received = $ping['pkt_received'];
|
||||
if (!isset($ping['ping_avg'])) {
|
||||
$ping_avg = null;
|
||||
} else {
|
||||
$ping_avg = $ping['ping_avg'];
|
||||
}
|
||||
|
||||
$scan = pinglerr_get_scan(scan_id: $scan_id);
|
||||
if ($scan === null) {
|
||||
suicide_user_bad_request('Kunne ikke finne scan for scan_id');
|
||||
}
|
||||
|
||||
$time_is_up = (function() use($scan)
|
||||
{
|
||||
$current_epoch = time();
|
||||
$end_epoch = strtotime($scan['end_date']);
|
||||
if ($current_epoch > $end_epoch) {
|
||||
$stmt = get_mysqli()->prepare(
|
||||
"UPDATE pinglerr_scans SET status_code = ?, error_message = ? WHERE status_code IN ('ACTIVE', 'WAITING') AND id = ?"
|
||||
);
|
||||
$status_code = 'DONE';
|
||||
$error_message = 'Stopped because end_date was reached';
|
||||
$stmt->bind_param("sss",
|
||||
$status_code,
|
||||
$error_message,
|
||||
$scan['id']
|
||||
);
|
||||
$stmt->execute();
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
})();
|
||||
if ($time_is_up) {
|
||||
continue;
|
||||
}
|
||||
|
||||
// set scan status from WAITING to ACTIVE
|
||||
(function() use($scan)
|
||||
{
|
||||
$stmt = get_mysqli()->prepare(
|
||||
"UPDATE pinglerr_scans SET status_code = ? WHERE id = ?"
|
||||
);
|
||||
$status_code = 'ACTIVE';
|
||||
$stmt->bind_param("si",
|
||||
$status_code,
|
||||
$scan['id'],
|
||||
);
|
||||
$stmt->execute();
|
||||
})();
|
||||
|
||||
(function() use($scan, $pkt_transmitted, $pkt_received, $ping_avg)
|
||||
{
|
||||
$stmt = get_mysqli()->prepare(
|
||||
"INSERT INTO pinglerr_pings (scan_id, ping_avg, pkt_transmitted, pkt_received) VALUES (?,?,?,?)"
|
||||
);
|
||||
$stmt->bind_param("idii",
|
||||
$scan['id'],
|
||||
$ping_avg,
|
||||
$pkt_transmitted,
|
||||
$pkt_received,
|
||||
);
|
||||
$stmt->execute();
|
||||
})();
|
||||
|
||||
}
|
||||
};
|
||||
|
||||
$route['api_chart'] = function()
|
||||
{
|
||||
$scan_id = filter_input(INPUT_GET, 'scan_id');
|
||||
$since = intval(filter_input(INPUT_GET, 'since'));
|
||||
$limit_length = intval(filter_input(INPUT_GET, 'limit_length'));
|
||||
|
||||
$pings = (function() use($scan_id, $since): array
|
||||
{
|
||||
$stmt = get_mysqli()->prepare(
|
||||
"SELECT ping_avg, pkt_transmitted, pkt_received, date_added FROM pinglerr_pings WHERE scan_id = ? AND date_added > FROM_UNIXTIME(?) ORDER BY date_added ASC"
|
||||
);
|
||||
$stmt->bind_param("ii", $scan_id, $since);
|
||||
$stmt->execute();
|
||||
$result = $stmt->get_result();
|
||||
$assoc_array = [];
|
||||
while ($row = $result->fetch_assoc()) {
|
||||
$assoc_array[] = $row;
|
||||
}
|
||||
return $assoc_array;
|
||||
})();
|
||||
if (empty($pings)) {
|
||||
http_response_code(204);
|
||||
die();
|
||||
}
|
||||
|
||||
if ($limit_length > 0 && $limit_length < count($pings)) {
|
||||
$merged_pings = [];
|
||||
|
||||
$merge_gap = count($pings) / $limit_length;
|
||||
$merge_iteration = 0;
|
||||
$merge_remainder = 0;
|
||||
|
||||
$ping_avg_sum_storage = [];
|
||||
$ping_pkt_transmitted_storage = [];
|
||||
$ping_pkt_received_storage = [];
|
||||
foreach ($pings as $ping) {
|
||||
if ($merge_iteration >= $merge_gap - $merge_remainder) {
|
||||
$merge_remainder+= $merge_iteration - $merge_gap;
|
||||
|
||||
$how_many_nulls = 0;
|
||||
for ($i=0; $i < count($ping_avg_sum_storage); $i++) {
|
||||
if ($ping_avg_sum_storage[$i] === null) {
|
||||
$how_many_nulls++;
|
||||
}
|
||||
}
|
||||
if ($how_many_nulls > count($ping_avg_sum_storage) / 2) {
|
||||
$ping_avg = null;
|
||||
} else {
|
||||
for ($i=0; $i < count($ping_avg_sum_storage); $i++) {
|
||||
if ($ping_avg_sum_storage[$i] === null) {
|
||||
unset($ping_avg_sum_storage[$i]);
|
||||
}
|
||||
}
|
||||
$ping_avg = round(array_sum($ping_avg_sum_storage) / count($ping_avg_sum_storage), 3);
|
||||
}
|
||||
|
||||
$merged_pings[] = [
|
||||
'ping_avg' => $ping_avg,
|
||||
'date_added' => $ping['date_added'],
|
||||
'pkt_transmitted' => round(array_sum($ping_pkt_transmitted_storage) / count($ping_pkt_transmitted_storage), 3),
|
||||
'pkt_received' => round(array_sum($ping_pkt_received_storage) / count($ping_pkt_received_storage), 3),
|
||||
];
|
||||
$ping_pkt_transmitted_storage = [];
|
||||
$ping_pkt_received_storage = [];
|
||||
$ping_avg_sum_storage = [];
|
||||
$merge_iteration = 0;
|
||||
}
|
||||
$ping_pkt_transmitted_storage[] = $ping['pkt_transmitted'];
|
||||
$ping_pkt_received_storage[] = $ping['pkt_received'];
|
||||
$ping_avg_sum_storage[] = $ping['ping_avg'];
|
||||
$merge_iteration++;
|
||||
}
|
||||
|
||||
$pings = $merged_pings;
|
||||
}
|
||||
|
||||
|
||||
//echo 'Gap: ' . htmlspecialchars($merge_gap) . ' Antall: ' . count($pings) . ' Remains: ' . $merge_remainder;
|
||||
//die();
|
||||
|
||||
header('Content-type: application/json');
|
||||
echo json_encode([
|
||||
'latest' => strtotime($pings[count($pings) - 1]['date_added']),
|
||||
'pings' => $pings,
|
||||
]);
|
||||
};
|
||||
|
||||
$route['api_stats'] = function()
|
||||
{
|
||||
$ref_uuid = filter_input(INPUT_GET, 'ref_uuid');
|
||||
$prefer_ref_uuid = !empty($ref_uuid);
|
||||
if ($prefer_ref_uuid) $ref_uuid = htmlspecialchars($ref_uuid);
|
||||
|
||||
$host = strtolower(filter_input(INPUT_GET, 'host'));
|
||||
if (!$prefer_ref_uuid && !empty($host) && !is_valid_host($host)) {
|
||||
suicide_user_bad_request('Ugyldig vertsnavn');
|
||||
}
|
||||
|
||||
$scan_id = filter_input(INPUT_GET, 'scan_id');
|
||||
|
||||
if ($prefer_ref_uuid) {
|
||||
$scan = pinglerr_get_scan(ref_uuid: $ref_uuid);
|
||||
} elseif(!empty($host)) {
|
||||
$scan = pinglerr_get_scan($host);
|
||||
} elseif(!empty($scan_id)) {
|
||||
$scan = pinglerr_get_scan(
|
||||
scan_id: $scan_id,
|
||||
must_be_active_or_waiting: false
|
||||
);
|
||||
} else {
|
||||
suicide_user_bad_request('Mangler ref_uuid, host eller scan_id parameter');
|
||||
}
|
||||
if ($scan === null) {
|
||||
suicide_user_bad_request('Kunne ikke finne noen aktive skanninger for denne verten eller referanse');
|
||||
}
|
||||
|
||||
$since = intval(filter_input(INPUT_GET, 'since'));
|
||||
|
||||
$pings = (function() use($scan, $since): array
|
||||
{
|
||||
$stmt = get_mysqli()->prepare(
|
||||
"SELECT * FROM pinglerr_pings WHERE scan_id = ? AND date_added > FROM_UNIXTIME(?) ORDER BY date_added DESC"
|
||||
);
|
||||
$stmt->bind_param("is", $scan['id'], $since);
|
||||
$stmt->execute();
|
||||
$result = $stmt->get_result();
|
||||
$assoc_array = [];
|
||||
while ($row = $result->fetch_assoc()) {
|
||||
$assoc_array[] = $row;
|
||||
}
|
||||
return $assoc_array;
|
||||
})();
|
||||
if (empty($pings)) {
|
||||
http_response_code(204);
|
||||
die();
|
||||
}
|
||||
|
||||
$pkt_transmitted = (function() use($pings): int {
|
||||
$pkts = 0;
|
||||
foreach ($pings as $ping_row) {
|
||||
if ($ping_row['pkt_transmitted'] === null) {
|
||||
continue;
|
||||
}
|
||||
$pkts = $pkts + $ping_row['pkt_transmitted'];
|
||||
}
|
||||
return $pkts;
|
||||
})();
|
||||
$pkt_received = (function() use($pings): int {
|
||||
$pkts = 0;
|
||||
foreach ($pings as $ping_row) {
|
||||
if ($ping_row['pkt_received'] === null) {
|
||||
continue;
|
||||
}
|
||||
$pkts = $pkts + $ping_row['pkt_received'];
|
||||
}
|
||||
return $pkts;
|
||||
})();
|
||||
$packet_loss_percentage = (function() use($pkt_transmitted, $pkt_received): float {
|
||||
try {
|
||||
$packet_loss_percentage = $pkt_received / $pkt_transmitted * 100;
|
||||
} catch (\DivisionByZeroError $th) {
|
||||
return 100;
|
||||
}
|
||||
if ($packet_loss_percentage > 100) {
|
||||
$packet_loss_percentage = 100;
|
||||
} else {
|
||||
$packet_loss_percentage = 100 - $packet_loss_percentage;
|
||||
}
|
||||
return $packet_loss_percentage;
|
||||
})();
|
||||
// TODO: this is just packet_loss_percentage reversed! make a more proper uptime calculation
|
||||
$uptime_percentage = (function() use($packet_loss_percentage): float {
|
||||
return 100 - $packet_loss_percentage;
|
||||
})();
|
||||
// TODO: this is too simple, but it works for now. make a more proper calculation using jitter
|
||||
$connection_status = (function() use($pings, $packet_loss_percentage) {
|
||||
if ($pings[0]['ping_avg'] === null) {
|
||||
return 'OFFLINE';
|
||||
}
|
||||
if ($packet_loss_percentage >= 2) {
|
||||
return 'UNSTABLE';
|
||||
}
|
||||
return 'ONLINE';
|
||||
})();
|
||||
|
||||
header('Content-type: application/json');
|
||||
return json_encode([
|
||||
'latest' => strtotime($pings[0]['date_added']),
|
||||
'scan_id' => $scan['id'],
|
||||
'host' => $scan['host'],
|
||||
'ref_uuid' => $scan['ref_uuid'],
|
||||
'status_code' => $scan['status_code'],
|
||||
'connection_status' => $connection_status,
|
||||
'pkt_transmitted' => $pkt_transmitted,
|
||||
'pkt_received' => $pkt_received,
|
||||
'ping_latest' => round($pings[0]['ping_avg'], 0, PHP_ROUND_HALF_EVEN),
|
||||
'packet_loss_percentage' => round($packet_loss_percentage, 0, PHP_ROUND_HALF_UP),
|
||||
'uptime_percentage' => round($uptime_percentage, 0, PHP_ROUND_HALF_DOWN),
|
||||
]);
|
||||
};
|
||||
|
||||
$page = filter_input(INPUT_GET, 'p');
|
||||
if (empty($page)) {
|
||||
$page = 'default';
|
||||
}
|
||||
if (isset($route[$page])) {
|
||||
die($route[$page]());
|
||||
}
|
||||
http_response_code(404);
|
||||
page_header('Kunne ikke finne siden')
|
||||
?>
|
||||
<h1>Siden ble ikke funnet</h1>
|
||||
<a href="<?=get_referer()?>">Klikk her for å gå tilbake</a>
|
||||
<?php
|
||||
page_footer()
|
||||
?>
|
149
pinglerr_web/public/static/css/style.css
Normal file
149
pinglerr_web/public/static/css/style.css
Normal file
@ -0,0 +1,149 @@
|
||||
@media (prefers-color-scheme: dark) {
|
||||
html {
|
||||
filter: invert();
|
||||
background: #ddd !important;
|
||||
}
|
||||
header {
|
||||
filter: invert();
|
||||
}
|
||||
}
|
||||
html {
|
||||
background: #def0fe;
|
||||
}
|
||||
body {
|
||||
background: #fff;
|
||||
max-width: 50rem;
|
||||
margin: 0 auto;
|
||||
|
||||
color: #111;
|
||||
font-family: -apple-system, BlinkMacSystemFont, avenir next, avenir, segoe ui, helvetica neue, Cantarell, Ubuntu, roboto, noto, helvetica, arial, sans-serif;
|
||||
}
|
||||
header, main {
|
||||
padding: .5rem;
|
||||
}
|
||||
header {
|
||||
background: #1280c3;
|
||||
}
|
||||
header>nav>a {
|
||||
color: #000 !important;
|
||||
text-decoration: none;
|
||||
background: url(../img/logo.svg) no-repeat left center;
|
||||
background-size: 1.25rem;
|
||||
filter: invert(100%);
|
||||
padding-left: 1.5rem;
|
||||
}
|
||||
.toolbar {
|
||||
padding: .5rem;
|
||||
background: #eee;
|
||||
border-bottom: 1px solid #ccc;
|
||||
}
|
||||
.toolbar>nav>span>a {
|
||||
white-space: pre;
|
||||
}
|
||||
footer {
|
||||
padding: .5rem;
|
||||
color: #333;
|
||||
background: #eee;
|
||||
border-top: 1px solid #ccc;
|
||||
}
|
||||
table {
|
||||
display: block;
|
||||
border-collapse: collapse;
|
||||
width: 100%;
|
||||
overflow: auto;
|
||||
}
|
||||
table td, table th {
|
||||
padding: 0.5rem;
|
||||
}
|
||||
table tr:nth-child(even) {
|
||||
background-color: #eee;
|
||||
}
|
||||
table th {
|
||||
font-weight: bold;
|
||||
text-align: left;
|
||||
}
|
||||
table tr {
|
||||
border-bottom: 1px solid #ccc;
|
||||
}
|
||||
tbody {
|
||||
display: table;
|
||||
width: 100%
|
||||
}
|
||||
a, a:visited, a:hover, a:active {
|
||||
color: #1280c3;
|
||||
}
|
||||
hr {
|
||||
border: 0;
|
||||
border-bottom: 1px solid #ccc;
|
||||
}
|
||||
.sign * {
|
||||
padding: .25rem;
|
||||
border: 1px solid #ccc;
|
||||
}
|
||||
.sign-label {
|
||||
border-right: 0;
|
||||
border-radius: .25rem 0 0 .25rem;
|
||||
}
|
||||
.sign-container {
|
||||
display: flex;
|
||||
flex-wrap: wrap;
|
||||
gap: .5rem;
|
||||
row-gap: 1rem;
|
||||
}
|
||||
.sign-text {
|
||||
border-radius: 0 .25rem .25rem 0;
|
||||
color: #222;
|
||||
background-color: #e2e3e5;
|
||||
}
|
||||
.sign>.sign-label {
|
||||
background: #eee;
|
||||
}
|
||||
|
||||
.alert {
|
||||
padding: .5rem;
|
||||
border-radius: .25rem;
|
||||
border-width: 1px;
|
||||
border-style: solid;
|
||||
}
|
||||
.alert-info {
|
||||
color: #0c5460;
|
||||
background-color: #d1ecf1;
|
||||
border-color: #bee5eb;
|
||||
}
|
||||
.alert-success {
|
||||
color: #155724;
|
||||
background-color: #d4edda;
|
||||
border-color: #c3e6cb;
|
||||
}
|
||||
.alert-danger {
|
||||
color: #721c24;
|
||||
background-color: #f8d7da;
|
||||
border-color: #f5c6cb;
|
||||
}
|
||||
.alert-warning {
|
||||
color: #856404;
|
||||
background-color: #fff3cd;
|
||||
border-color: #ffeeba;
|
||||
}
|
||||
|
||||
.pill {
|
||||
padding-right: 0.5rem;
|
||||
padding-left: 0.5rem;
|
||||
border-radius: 1rem;
|
||||
border-style: solid;
|
||||
border-width: .15rem;
|
||||
}
|
||||
|
||||
.link-btn {
|
||||
font-weight: bold;
|
||||
}
|
||||
|
||||
.link-btn-normal {
|
||||
color: #1280c3 !important;
|
||||
}
|
||||
.link-btn-success {
|
||||
color: #16b93b !important;
|
||||
}
|
||||
.link-btn-danger {
|
||||
color: #c61910 !important
|
||||
}
|
13
pinglerr_web/public/static/img/logo.svg
Normal file
13
pinglerr_web/public/static/img/logo.svg
Normal file
@ -0,0 +1,13 @@
|
||||
<?xml version="1.0" encoding="iso-8859-1"?>
|
||||
<!-- Uploaded to: SVG Repo, www.svgrepo.com, Generator: SVG Repo Mixer Tools -->
|
||||
<svg fill="#000000" height="800px" width="800px" version="1.1" id="Capa_1" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink"
|
||||
viewBox="0 0 55 55" xml:space="preserve">
|
||||
<path d="M49,0c-3.309,0-6,2.691-6,6c0,1.035,0.263,2.009,0.726,2.86l-9.829,9.829C32.542,17.634,30.846,17,29,17
|
||||
s-3.542,0.634-4.898,1.688l-7.669-7.669C16.785,10.424,17,9.74,17,9c0-2.206-1.794-4-4-4S9,6.794,9,9s1.794,4,4,4
|
||||
c0.74,0,1.424-0.215,2.019-0.567l7.669,7.669C21.634,21.458,21,23.154,21,25s0.634,3.542,1.688,4.897L10.024,42.562
|
||||
C8.958,41.595,7.549,41,6,41c-3.309,0-6,2.691-6,6s2.691,6,6,6s6-2.691,6-6c0-1.035-0.263-2.009-0.726-2.86l12.829-12.829
|
||||
c1.106,0.86,2.44,1.436,3.898,1.619v10.16c-2.833,0.478-5,2.942-5,5.91c0,3.309,2.691,6,6,6s6-2.691,6-6c0-2.967-2.167-5.431-5-5.91
|
||||
v-10.16c1.458-0.183,2.792-0.759,3.898-1.619l7.669,7.669C41.215,39.576,41,40.26,41,41c0,2.206,1.794,4,4,4s4-1.794,4-4
|
||||
s-1.794-4-4-4c-0.74,0-1.424,0.215-2.019,0.567l-7.669-7.669C36.366,28.542,37,26.846,37,25s-0.634-3.542-1.688-4.897l9.665-9.665
|
||||
C46.042,11.405,47.451,12,49,12c3.309,0,6-2.691,6-6S52.309,0,49,0z"/>
|
||||
</svg>
|
After Width: | Height: | Size: 1.2 KiB |
38
pinglerr_web/public/static/js/apexcharts.min.js
vendored
Normal file
38
pinglerr_web/public/static/js/apexcharts.min.js
vendored
Normal file
File diff suppressed because one or more lines are too long
58
pinglerr_web/public/static/js/global.js
Normal file
58
pinglerr_web/public/static/js/global.js
Normal file
@ -0,0 +1,58 @@
|
||||
window.addEventListener('DOMContentLoaded', function() {
|
||||
updateDates();
|
||||
}, false);
|
||||
|
||||
function updateDates() {
|
||||
dateElements = document.querySelectorAll(".dateConverter");
|
||||
const options = {
|
||||
weekday: "long",
|
||||
year: "numeric",
|
||||
month: "long",
|
||||
day: "numeric",
|
||||
hour: "2-digit",
|
||||
minute: "2-digit"
|
||||
};
|
||||
dateElements.forEach(element => {
|
||||
dateToConvert = element.innerHTML + ' UTC';
|
||||
newDate = new Date(dateToConvert).toLocaleDateString('no-NB', options);
|
||||
timeAgo(dateToConvert);
|
||||
element.innerHTML = 'Startet ' + timeAgo(dateToConvert);
|
||||
});
|
||||
}
|
||||
|
||||
function timeAgo(dateString)
|
||||
{
|
||||
// Assume we have a date string being received from some object.
|
||||
const data = {
|
||||
formattedDate: dateString // Example date string in ISO 8601 format
|
||||
};
|
||||
|
||||
// Extract the formatted date string from the object
|
||||
const { formattedDate } = data;
|
||||
|
||||
// Convert the date string to a Date object
|
||||
const dateObject = new Date(formattedDate);
|
||||
|
||||
// Calculate the difference in seconds between the given date and the current date
|
||||
const secondsDiff = Math.round((dateObject - Date.now()) / 1000);
|
||||
|
||||
// Array representing one minute, hour, day, week, month, etc. in seconds
|
||||
const unitsInSec = [60, 3600, 86400, 86400 * 7, 86400 * 30, 86400 * 365, Infinity];
|
||||
|
||||
// Array equivalent to the above but in the string representation of the units
|
||||
const unitStrings = ["second", "minute", "hour", "day", "week", "month", "year"];
|
||||
|
||||
// Find the appropriate unit based on the seconds difference
|
||||
const unitIndex = unitsInSec.findIndex((cutoff) => cutoff > Math.abs(secondsDiff));
|
||||
|
||||
// Get the divisor to convert seconds to the appropriate unit
|
||||
const divisor = unitIndex ? unitsInSec[unitIndex - 1] : 1;
|
||||
|
||||
// Initialize Intl.RelativeTimeFormat
|
||||
const rtf = new Intl.RelativeTimeFormat("no", { numeric: "auto" });
|
||||
|
||||
// Format the relative time based on the calculated unit
|
||||
const relativeTime = rtf.format(Math.floor(secondsDiff / divisor), unitStrings[unitIndex]);
|
||||
|
||||
return(relativeTime);
|
||||
}
|
240
pinglerr_web/public/static/js/live_stats.js
Normal file
240
pinglerr_web/public/static/js/live_stats.js
Normal file
@ -0,0 +1,240 @@
|
||||
var chartOptions = {
|
||||
colors:['#008ffb', '#f08681'],
|
||||
series: [
|
||||
{
|
||||
name: 'Ping',
|
||||
data: [null]
|
||||
},
|
||||
{
|
||||
name: 'Pakke Tap',
|
||||
type: 'area',
|
||||
data: [null]
|
||||
}
|
||||
],
|
||||
chart: {
|
||||
height: '100%',
|
||||
id: 'realtime',
|
||||
type: 'area',
|
||||
animations: {
|
||||
enabled: false,
|
||||
},
|
||||
toolbar: {
|
||||
show: false
|
||||
},
|
||||
zoom: {
|
||||
enabled: false
|
||||
},
|
||||
},
|
||||
dataLabels: {
|
||||
enabled: false,
|
||||
},
|
||||
stroke: {
|
||||
curve: 'straight'
|
||||
},
|
||||
markers: {
|
||||
size: 0
|
||||
},
|
||||
xaxis: {
|
||||
type: 'datetime',
|
||||
labels: {
|
||||
datetimeUTC: false,
|
||||
},
|
||||
},
|
||||
yaxis: [
|
||||
{
|
||||
seriesName: 'Ping',
|
||||
labels: {
|
||||
formatter: function (val) {
|
||||
if (val == null) {
|
||||
return 0;
|
||||
}
|
||||
return val.toFixed(1);
|
||||
},
|
||||
}
|
||||
},
|
||||
{
|
||||
seriesName: 'Pakke Tap',
|
||||
min:0,
|
||||
max:100,
|
||||
opposite: true,
|
||||
|
||||
labels: {
|
||||
formatter: function (val) {
|
||||
if (val == null) {
|
||||
return 0 + '%';
|
||||
}
|
||||
return val.toFixed(0) + "%";
|
||||
},
|
||||
}
|
||||
}
|
||||
],
|
||||
legend: {
|
||||
show: false
|
||||
},
|
||||
};
|
||||
var timeRangeInSeconds = 0;
|
||||
var chartLatest;
|
||||
var chartHasFetchedTimeRange = false;
|
||||
var chartElement;
|
||||
var chartPingSeries = [];
|
||||
var chartPacketLossSeries = [];
|
||||
var scanId;
|
||||
|
||||
const queryParam = new Proxy(new URLSearchParams(window.location.search), {
|
||||
get: (searchParams, prop) => searchParams.get(prop),
|
||||
});
|
||||
|
||||
document.body.onload = async function() {
|
||||
chartElement = new ApexCharts(document.querySelector("#liveChart"), chartOptions);
|
||||
chartElement.render();
|
||||
|
||||
timeRangeInSeconds = timeRangeSelector[timeRangeSelector.selectedIndex].attributes.value.nodeValue;
|
||||
|
||||
await update_summary();
|
||||
update_chart();
|
||||
window.setInterval(async function() {
|
||||
update_summary();
|
||||
update_chart();
|
||||
}, 5000)
|
||||
};
|
||||
|
||||
timeRangeSelector.onchange = function(event) {
|
||||
timeRangeInSeconds = event.srcElement[event.srcElement.selectedIndex].attributes.value.nodeValue;
|
||||
|
||||
if (timeRangeInSeconds == 'Details') {
|
||||
if (queryParam.ref_uuid !== null) {
|
||||
window.location.href = './?p=scan_details&ref_uuid=' + queryParam.ref_uuid + '&scan_id=' + scanId;
|
||||
} else {
|
||||
window.location.href = './?p=scan_details&host=' + queryParam.host + '&scan_id=' + scanId;
|
||||
}
|
||||
}
|
||||
|
||||
// TODO: a very rare race condition may occur when switching too fast between time ranges can cause the chart to show wrong data
|
||||
// cancel the update interval and start it again
|
||||
|
||||
// reset chart
|
||||
chartHasFetchedTimeRange = false;
|
||||
chartPingSeries = [];
|
||||
chartPacketLossSeries = [];
|
||||
chartElement.updateSeries([
|
||||
{
|
||||
data: chartPingSeries
|
||||
},
|
||||
{
|
||||
data: chartPacketLossSeries
|
||||
}
|
||||
]);
|
||||
|
||||
update_chart();
|
||||
update_summary();
|
||||
};
|
||||
|
||||
async function update_summary() {
|
||||
if (queryParam.ref_uuid !== null) {
|
||||
var response = await fetch('./?p=api_stats&ref_uuid=' + queryParam.ref_uuid + '&since=' + (Math.floor(Date.now() / 1000) - timeRangeInSeconds));
|
||||
} else {
|
||||
var response = await fetch('./?p=api_stats&host=' + queryParam.host + '&since=' + (Math.floor(Date.now() / 1000) - timeRangeInSeconds));
|
||||
}
|
||||
|
||||
if (response.status == 400) {
|
||||
setTimeout(() => {
|
||||
window.location.reload();
|
||||
}, 1000);
|
||||
return;
|
||||
}
|
||||
|
||||
if (response.status !== 200) {
|
||||
return;
|
||||
}
|
||||
|
||||
let jsonResponse = await response.json();
|
||||
|
||||
scanId = jsonResponse.scan_id;
|
||||
|
||||
let resp_ping = jsonResponse.ping_latest;
|
||||
let resp_packet_loss = jsonResponse.packet_loss_percentage;
|
||||
let resp_uptime = jsonResponse.uptime_percentage;
|
||||
let resp_connection_status = jsonResponse.connection_status;
|
||||
|
||||
if (resp_ping == null) {
|
||||
ping.innerHTML = 'Feilet';
|
||||
} else {
|
||||
ping.innerHTML = resp_ping + ' ms';
|
||||
}
|
||||
|
||||
packet_loss.innerHTML = resp_packet_loss + ' %';
|
||||
uptime.innerHTML = resp_uptime + ' %';
|
||||
|
||||
if (resp_connection_status == 'ONLINE') {
|
||||
connection_status.className = 'sign-text alert-success'
|
||||
connection_status.innerHTML = 'Online';
|
||||
}
|
||||
if (resp_connection_status == 'UNSTABLE') {
|
||||
connection_status.className = 'sign-text alert-warning'
|
||||
connection_status.innerHTML = 'Ustabilt';
|
||||
}
|
||||
if (resp_connection_status == 'OFFLINE') {
|
||||
connection_status.className = 'sign-text alert-danger'
|
||||
connection_status.innerHTML = 'Offline';
|
||||
}
|
||||
}
|
||||
|
||||
async function update_chart()
|
||||
{
|
||||
if (!chartHasFetchedTimeRange) {
|
||||
var response = await fetch('./?p=api_chart&limit_length=1000&scan_id=' + scanId + '&since=' + (Math.floor(Date.now() / 1000) - timeRangeInSeconds));
|
||||
chartHasFetchedTimeRange = true;
|
||||
} else {
|
||||
var response = await fetch('./?p=api_chart&scan_id=' + scanId + '&since=' + chartLatest);
|
||||
}
|
||||
|
||||
if (response.status === 204) {
|
||||
return;
|
||||
}
|
||||
|
||||
let jsonResponse = await response.json();
|
||||
|
||||
chartLatest = jsonResponse.latest;
|
||||
|
||||
jsonResponse.pings.forEach(ping => {
|
||||
let newDate = new Date(ping.date_added + ' UTC');
|
||||
chartPingSeries.push({ x: newDate.toString(), y: ping.ping_avg});
|
||||
});
|
||||
|
||||
// remove old pings
|
||||
for (let i = 0; i < chartPingSeries.length; i++) {
|
||||
let pingEpoch = Math.floor((new Date(chartPingSeries[i].x)).getTime() / 1000);
|
||||
if (pingEpoch < chartLatest - timeRangeInSeconds) {
|
||||
chartPingSeries.splice(i, 1);
|
||||
}
|
||||
}
|
||||
|
||||
jsonResponse.pings.forEach(ping => {
|
||||
let newDate = new Date(ping.date_added + ' UTC');
|
||||
if (ping.pkt_transmitted != ping.pkt_received) {
|
||||
let pktLossPercentage = ping.pkt_received / ping.pkt_transmitted * 100;
|
||||
pktLossPercentage = 100 - pktLossPercentage;
|
||||
chartPacketLossSeries.push({ x: newDate.toString(), y: pktLossPercentage});
|
||||
} else {
|
||||
chartPacketLossSeries.push({ x: newDate.toString(), y: 0});
|
||||
}
|
||||
});
|
||||
|
||||
for (let i = 0; i < chartPacketLossSeries.length; i++) {
|
||||
let pingEpoch = Math.floor((new Date(chartPacketLossSeries[i].x)).getTime() / 1000);
|
||||
if (pingEpoch < chartLatest - timeRangeInSeconds) {
|
||||
chartPacketLossSeries.splice(i, 1);
|
||||
}
|
||||
}
|
||||
|
||||
chartElement.updateSeries([
|
||||
{
|
||||
name: 'Ping',
|
||||
data: chartPingSeries
|
||||
},
|
||||
{
|
||||
name: 'Pakke Tap',
|
||||
data: chartPacketLossSeries
|
||||
}
|
||||
]);
|
||||
}
|
222
pinglerr_web/public/static/js/scan_history_details.js
Normal file
222
pinglerr_web/public/static/js/scan_history_details.js
Normal file
@ -0,0 +1,222 @@
|
||||
var chartOptions = {
|
||||
colors:['#008ffb', '#f08681'],
|
||||
series: [
|
||||
{
|
||||
name: 'Ping',
|
||||
data: [null]
|
||||
},
|
||||
{
|
||||
name: 'Pakke Tap',
|
||||
type: 'area',
|
||||
data: [null]
|
||||
}
|
||||
],
|
||||
chart: {
|
||||
height: '300',
|
||||
id: 'chart',
|
||||
type: 'area',
|
||||
animations: {
|
||||
enabled: false,
|
||||
},
|
||||
toolbar: {
|
||||
show: true
|
||||
},
|
||||
zoom: {
|
||||
enabled: true
|
||||
},
|
||||
},
|
||||
dataLabels: {
|
||||
enabled: false
|
||||
},
|
||||
stroke: {
|
||||
curve: 'straight'
|
||||
},
|
||||
markers: {
|
||||
size: 0
|
||||
},
|
||||
xaxis: {
|
||||
type: 'datetime',
|
||||
labels: {
|
||||
datetimeUTC: false,
|
||||
},
|
||||
},
|
||||
yaxis: [
|
||||
{
|
||||
seriesName: 'Ping',
|
||||
labels: {
|
||||
formatter: function (val) {
|
||||
if (val == null) {
|
||||
return 0;
|
||||
}
|
||||
return val.toFixed(1);
|
||||
},
|
||||
}
|
||||
},
|
||||
{
|
||||
seriesName: 'Pakke Tap',
|
||||
min:0,
|
||||
max:100,
|
||||
opposite: true,
|
||||
|
||||
labels: {
|
||||
formatter: function (val) {
|
||||
if (val == null) {
|
||||
return 0 + '%';
|
||||
}
|
||||
return val.toFixed(0) + "%";
|
||||
},
|
||||
}
|
||||
}
|
||||
],
|
||||
legend: {
|
||||
show: false
|
||||
},
|
||||
};
|
||||
|
||||
var brushChartOptions = {
|
||||
colors:['#008ffb', '#f08681'],
|
||||
series: [
|
||||
{
|
||||
name: 'Ping',
|
||||
data: [1]
|
||||
},
|
||||
{
|
||||
name: 'Pakke Tap',
|
||||
type: 'area',
|
||||
data: [1]
|
||||
}
|
||||
],
|
||||
chart: {
|
||||
height: '100',
|
||||
id: 'brushChart',
|
||||
type: 'area',
|
||||
brush:{
|
||||
target: 'chart',
|
||||
enabled: true
|
||||
},
|
||||
animations: {
|
||||
enabled: false,
|
||||
},
|
||||
toolbar: {
|
||||
show: false,
|
||||
},
|
||||
zoom: {
|
||||
enabled: false
|
||||
},
|
||||
selection: {
|
||||
enabled: true,
|
||||
},
|
||||
},
|
||||
dataLabels: {
|
||||
enabled: false
|
||||
},
|
||||
stroke: {
|
||||
curve: 'straight'
|
||||
},
|
||||
markers: {
|
||||
size: 0
|
||||
},
|
||||
xaxis: {
|
||||
type: 'datetime',
|
||||
labels: {
|
||||
datetimeUTC: false,
|
||||
},
|
||||
},
|
||||
yaxis: [
|
||||
{
|
||||
seriesName: 'Ping',
|
||||
labels: {
|
||||
formatter: function (val) {
|
||||
if (val == null) {
|
||||
return 0;
|
||||
}
|
||||
return val.toFixed(1);
|
||||
},
|
||||
}
|
||||
},
|
||||
{
|
||||
seriesName: 'Pakke Tap',
|
||||
min:0,
|
||||
max:100,
|
||||
opposite: true,
|
||||
|
||||
labels: {
|
||||
formatter: function (val) {
|
||||
if (val == null) {
|
||||
return 0 + '%';
|
||||
}
|
||||
return val.toFixed(0) + "%"
|
||||
},
|
||||
}
|
||||
}
|
||||
],
|
||||
legend: {
|
||||
show: false
|
||||
},
|
||||
};
|
||||
|
||||
const queryParam = new Proxy(new URLSearchParams(window.location.search), {
|
||||
get: (searchParams, prop) => searchParams.get(prop),
|
||||
});
|
||||
|
||||
document.body.onload = async function() {
|
||||
chartElement = new ApexCharts(document.querySelector("#chartElement"), chartOptions);
|
||||
chartElement.render();
|
||||
|
||||
brushChartElement = new ApexCharts(document.querySelector("#brushChartElement"), brushChartOptions);
|
||||
brushChartElement.render();
|
||||
|
||||
var statsResponse = await fetch('./?p=api_stats&scan_id=' + queryParam.scan_id);
|
||||
|
||||
if (statsResponse.status != 200) {
|
||||
alert('Feil under henting av statistikk');
|
||||
}
|
||||
|
||||
let chartResponse = await fetch('./?p=api_chart&limit_length=1000&scan_id=' + queryParam.scan_id);
|
||||
|
||||
if (chartResponse.status != 200) {
|
||||
alert('Feil under henting av graf data');
|
||||
}
|
||||
|
||||
let jsonResponse = await chartResponse.json();
|
||||
let newPingSeries = [];
|
||||
|
||||
jsonResponse.pings.forEach(ping => {
|
||||
let newDate = new Date(ping.date_added + ' UTC');
|
||||
newPingSeries.push({ x: newDate.toString(), y: ping.ping_avg});
|
||||
});
|
||||
|
||||
let chartPacketLossSeries = [];
|
||||
jsonResponse.pings.forEach(ping => {
|
||||
let newDate = new Date(ping.date_added + ' UTC');
|
||||
if (ping.pkt_transmitted != ping.pkt_received) {
|
||||
let pktLossPercentage = ping.pkt_received / ping.pkt_transmitted * 100;
|
||||
pktLossPercentage = 100 - pktLossPercentage;
|
||||
chartPacketLossSeries.push({ x: newDate.toString(), y: pktLossPercentage});
|
||||
} else {
|
||||
chartPacketLossSeries.push({ x: newDate.toString(), y: 0});
|
||||
}
|
||||
});
|
||||
|
||||
chartElement.updateSeries([
|
||||
{
|
||||
name: 'Ping',
|
||||
data: newPingSeries
|
||||
},
|
||||
{
|
||||
name: 'Pakke Tap',
|
||||
data: chartPacketLossSeries
|
||||
}
|
||||
]);
|
||||
|
||||
brushChartElement.updateSeries([
|
||||
{
|
||||
name: 'Ping',
|
||||
data: newPingSeries
|
||||
},
|
||||
{
|
||||
name: 'Pakke Tap',
|
||||
data: chartPacketLossSeries
|
||||
}
|
||||
]);
|
||||
}
|
Loading…
x
Reference in New Issue
Block a user