files: Add statistics page
This commit is contained in:
@@ -1,21 +1,44 @@
|
||||
<?php
|
||||
require "credentials.inc";
|
||||
|
||||
function http_401_unauthorized(): void {
|
||||
function http_401_unauthorized(?string $mode = null): void {
|
||||
header('Status: 401');
|
||||
header('WWW-Authenticate: Basic realm="Elwig"');
|
||||
header('Content-Type: text/plain; charset=UTF-8');
|
||||
header('Content-Length: 17');
|
||||
exit("401 Unauthorized\n");
|
||||
if ($mode === 'text' || $mode === 'ascii') {
|
||||
header('Content-Type: text/plain; charset=UTF-8');
|
||||
header('Content-Length: 17');
|
||||
echo "401 Unauthorized\n";
|
||||
} else {
|
||||
header('Content-Length: 0');
|
||||
}
|
||||
exit;
|
||||
}
|
||||
|
||||
function authenticate(): void {
|
||||
function http_403_forbidden(?string $mode = null): void {
|
||||
header('Status: 403');
|
||||
header('WWW-Authenticate: Basic realm="Elwig"');
|
||||
if ($mode === 'text' || $mode === 'ascii') {
|
||||
header('Content-Type: text/plain; charset=UTF-8');
|
||||
header('Content-Length: 14');
|
||||
echo "403 Forbidden\n";
|
||||
} else {
|
||||
header('Content-Length: 0');
|
||||
}
|
||||
exit;
|
||||
}
|
||||
|
||||
function authenticate(array $scope, ?string $mode = null): void {
|
||||
global $CREDENTIALS;
|
||||
if (!array_key_exists('PHP_AUTH_USER', $_SERVER) ||
|
||||
!array_key_exists('PHP_AUTH_PW', $_SERVER) ||
|
||||
!array_key_exists($_SERVER['PHP_AUTH_USER'], $CREDENTIALS) ||
|
||||
$_SERVER['PHP_AUTH_PW'] !== $CREDENTIALS[$_SERVER['PHP_AUTH_USER']])
|
||||
!array_key_exists($_SERVER['PHP_AUTH_USER'], $CREDENTIALS))
|
||||
{
|
||||
http_401_unauthorized();
|
||||
http_401_unauthorized($mode);
|
||||
}
|
||||
$cred = $CREDENTIALS[$_SERVER['PHP_AUTH_USER']];
|
||||
if ($_SERVER['PHP_AUTH_PW'] !== $cred[1]) {
|
||||
http_401_unauthorized($mode);
|
||||
} else if (!in_array($cred[0], $scope)) {
|
||||
http_403_forbidden($mode);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -5,5 +5,5 @@ global $CREDENTIALS;
|
||||
$GITEA_TOKEN = 'token';
|
||||
|
||||
$CREDENTIALS = [
|
||||
'username' => 'password',
|
||||
'username' => ['scope', 'password'],
|
||||
];
|
||||
|
||||
7
www/.php/devices.sample.inc
Normal file
7
www/.php/devices.sample.inc
Normal file
@@ -0,0 +1,7 @@
|
||||
<?php
|
||||
global $DEVICES;
|
||||
$DEVICES = [
|
||||
'TOKEN' => [
|
||||
'DESKTOP-0123456' => 'Office PC',
|
||||
],
|
||||
];
|
||||
@@ -4,7 +4,7 @@ require "../.php/credentials.inc";
|
||||
global $GITEA_TOKEN;
|
||||
|
||||
if (!isset($_SERVER['PHP_AUTH_USER']) || !isset($_SERVER['PHP_AUTH_PW']) || $_SERVER['PHP_AUTH_USER'] !== 'elwig' || $_SERVER['PHP_AUTH_PW'] !== 'ganzGeheim123!') {
|
||||
http_401_unauthorized();
|
||||
http_401_unauthorized('text');
|
||||
}
|
||||
|
||||
$repo = "winzer/elwig-misc.git";
|
||||
|
||||
@@ -5,7 +5,7 @@ require "../.php/auth.inc";
|
||||
$ua = $_SERVER['HTTP_USER_AGENT'] ?? null;;
|
||||
|
||||
if ($_SERVER['REQUEST_METHOD'] === 'PUT') {
|
||||
authenticate();
|
||||
authenticate(['PUT'], 'text');
|
||||
|
||||
header('Content-Type: text/plain; charset=UTF-8');
|
||||
|
||||
@@ -51,26 +51,6 @@ if ($_SERVER['REQUEST_METHOD'] === 'PUT') {
|
||||
exit("405 Method Not Allowed\n");
|
||||
}
|
||||
|
||||
if ($_SERVER['PATH_INFO'] === '/stat') {
|
||||
if (!isset($_SERVER['PHP_AUTH_USER']) || !isset($_SERVER['PHP_AUTH_PW']) || $_SERVER['PHP_AUTH_USER'] !== 'elwig' || $_SERVER['PHP_AUTH_PW'] !== 'ganzGeheim123!') {
|
||||
header('Status: 401');
|
||||
header('WWW-Authenticate: Basic realm="Elwig"');
|
||||
header('Content-Length: 0');
|
||||
exit;
|
||||
}
|
||||
|
||||
header("Content-Type: text/html; charset=UTF-8");
|
||||
echo "<!DOCTYPE html><html lang='en'><head><title>Activity Statistics - Elwig</title><style>table{border-collapse:collapse;}th,td{border:1px solid black;padding:0.25em 0.5em;}</style></head><body><table>\n";
|
||||
echo "<tr><th>Timestamp</th><th>IP Address</th><th>Method</th><th>URI</th><th>Format</th><th>User Agent</th></tr>\n";
|
||||
passthru(<<<EOF
|
||||
cat .log.csv \
|
||||
| awk -F'|' -vOFS='|' '{\$1 = strftime("%F %T", $1); print $0}' \
|
||||
| sed 's:|:</td><td>:g;s:^:<tr><td>:g;s:$:</td></tr>:g'
|
||||
EOF);
|
||||
echo "</table></body></html>\n";
|
||||
exit;
|
||||
}
|
||||
|
||||
global $getProd;
|
||||
global $getVers;
|
||||
$getProd = null;
|
||||
@@ -94,7 +74,7 @@ $filesRaw = scandir('.');
|
||||
usort($filesRaw, 'version_compare');
|
||||
$files = [];
|
||||
foreach ($filesRaw as $file) {
|
||||
if (str_starts_with($file, ".") || str_ends_with($file, ".php")) continue;
|
||||
if (str_starts_with($file, ".") || str_ends_with($file, ".php") || !str_contains($file, ".")) continue;
|
||||
$files[$file] = [filesize($file), filemtime($file), filectime($file)];
|
||||
}
|
||||
|
||||
@@ -157,7 +137,7 @@ if ($format === 'json') {
|
||||
foreach ($entities as $name => [$prod, $vers, $url, $size, $mtime, $ctime, $mod, $cre]) {
|
||||
echo "$name\t" . number_format($size / 1024 / 1024, 1) . " MB\n";
|
||||
}
|
||||
} else if ($format === 'html') {
|
||||
} else {
|
||||
if (isset($getProd) && isset($getVers) && sizeof($entities) === 1) {
|
||||
header('Status: 303');
|
||||
header('Location: ' . $entities[array_key_first($entities)][2]);
|
||||
|
||||
216
www/files/stat.php
Normal file
216
www/files/stat.php
Normal file
@@ -0,0 +1,216 @@
|
||||
<?php
|
||||
require "../.php/format.inc";
|
||||
require "../.php/auth.inc";
|
||||
include "../.php/devices.inc";
|
||||
|
||||
authenticate(['stat']);
|
||||
|
||||
$osVersions = [
|
||||
'Microsoft Windows NT 10.0.10240.0' => 'Windows 10 (1507)',
|
||||
'Microsoft Windows NT 10.0.10586.0' => 'Windows 10 (1511)',
|
||||
'Microsoft Windows NT 10.0.14393.0' => 'Windows 10 (1607)',
|
||||
'Microsoft Windows NT 10.0.15063.0' => 'Windows 10 (1703)',
|
||||
'Microsoft Windows NT 10.0.16299.0' => 'Windows 10 (1709)',
|
||||
'Microsoft Windows NT 10.0.17134.0' => 'Windows 10 (1803)',
|
||||
'Microsoft Windows NT 10.0.17763.0' => 'Windows 10 (1809)',
|
||||
'Microsoft Windows NT 10.0.18362.0' => 'Windows 10 (1903)',
|
||||
'Microsoft Windows NT 10.0.18363.0' => 'Windows 10 (1909)',
|
||||
'Microsoft Windows NT 10.0.19041.0' => 'Windows 10 (2004)',
|
||||
'Microsoft Windows NT 10.0.19042.0' => 'Windows 10 (20H2)',
|
||||
'Microsoft Windows NT 10.0.19043.0' => 'Windows 10 (21H1)',
|
||||
'Microsoft Windows NT 10.0.19044.0' => 'Windows 10 (21H2)',
|
||||
'Microsoft Windows NT 10.0.19045.0' => 'Windows 10 (22H2)',
|
||||
'Microsoft Windows NT 10.0.22000.0' => 'Windows 11 (21H2)',
|
||||
'Microsoft Windows NT 10.0.22621.0' => 'Windows 11 (22H2)',
|
||||
'Microsoft Windows NT 10.0.22631.0' => 'Windows 11 (23H2)',
|
||||
'Microsoft Windows NT 10.0.26100.0' => 'Windows 11 (24H2)',
|
||||
'Microsoft Windows NT 10.0.26200.0' => 'Windows 11 (25H2)',
|
||||
];
|
||||
|
||||
$DEVICES ??= [];
|
||||
|
||||
if ($_SERVER['PATH_INFO'] === '/log') {
|
||||
header("Content-Type: text/html; charset=UTF-8");
|
||||
echo "<!DOCTYPE html><html lang='en'><head><title>Table - Activity Statistics - Elwig</title><style>table{border-collapse:collapse;}th,td{border:1px solid black;padding:0.25em 0.5em;}</style></head><body><table>\n";
|
||||
echo "<tr><th>Timestamp</th><th>IP Address</th><th>Method</th><th>URI</th><th>Format</th><th>User Agent</th></tr>\n";
|
||||
if ($_SERVER['QUERY_STRING'] === 'full') {
|
||||
passthru(<<<EOF
|
||||
cat .log.csv \
|
||||
| awk -F'|' -vOFS='|' '{\$1 = strftime("%F %T", $1); print $0}' \
|
||||
| sed 's:|:</td><td>:g;s:^:<tr><td>:g;s:$:</td></tr>:g'
|
||||
EOF);
|
||||
} else {
|
||||
passthru(<<<EOF
|
||||
cat .log.csv | grep -E '\|json\|$|\|Elwig/' | grep -v "PICARD|ENTERPRISE|GEORGIOU" \
|
||||
| awk -F'|' -vOFS='|' '{\$1 = strftime("%F %T", $1); print $0}' \
|
||||
| sed 's:|:</td><td>:g;s:^:<tr><td>:g;s:$:</td></tr>:g'
|
||||
EOF);
|
||||
}
|
||||
echo "</table></body></html>\n";
|
||||
exit;
|
||||
} else if ($_SERVER['PATH_INFO'] === '') {
|
||||
$format = get_fmt();
|
||||
$stat = [];
|
||||
if (($file = fopen('.log.csv', 'r'))) {
|
||||
while (($line = fgets($file)) !== false) {
|
||||
$line = explode('|', $line);
|
||||
if (sizeof($line) < 6) continue;
|
||||
if (!str_starts_with($line[5], 'Elwig/')) continue;
|
||||
if (str_contains($line[5], "PICARD") || str_contains($line[5], "ENTERPRISE") || str_contains($line[5], "GEORGIOU")) continue;
|
||||
$timestamp = intval($line[0]);
|
||||
$version = substr(explode(' ', $line[5], 2)[0], 6);
|
||||
$comment = preg_match("/\((.*?), *(.*?), *(.*?), *(.*?)\)/", $line[5], $m);
|
||||
$token = $m[1];
|
||||
$branch = $m[2];
|
||||
$device = $m[3];
|
||||
$win = $m[4];
|
||||
if (!isset($stat[$token])) $stat[$token] = [];
|
||||
if (!isset($stat[$token][$device])) $stat[$token][$device] = [];
|
||||
$addr = $line[1];
|
||||
if (str_contains($addr, ':')) {
|
||||
$list = explode(':', $addr);
|
||||
$addr = implode(':', array_splice($list, 0, 4)) . '::';
|
||||
}
|
||||
$stat[$token][$device][$timestamp] = [$version, $branch, $win, $addr];
|
||||
}
|
||||
fclose($file);
|
||||
}
|
||||
|
||||
if ($format === 'json') {
|
||||
header('Content-Type: application/json; charset=UTF-8');
|
||||
echo "{\"clients\":[\n";
|
||||
$first1 = true;
|
||||
foreach ($stat as $token => $devices) {
|
||||
if (!$first1) echo ",\n";
|
||||
echo " {\"token\": \"$token\", \"devices\": [\n";
|
||||
$first2 = true;
|
||||
foreach ($devices as $device => $data) {
|
||||
if (!$first2) echo ",\n";
|
||||
echo " {";
|
||||
$timestamp = array_key_last($data);
|
||||
$ref = $timestamp;
|
||||
foreach (array_reverse(array_keys($data)) as $t) {
|
||||
if ($t + 90 * 60 > $ref) {
|
||||
$ref = $t;
|
||||
} else {
|
||||
break;
|
||||
}
|
||||
}
|
||||
$addr = array_reverse(array_unique(array_map(function ($d) {
|
||||
return "\"$d[3]\"";
|
||||
}, array_values($data))));
|
||||
$d = $data[$timestamp];
|
||||
$name = $DEVICES[$token][$device] ?? $device;
|
||||
$active = $timestamp + 90 * 60 > time();
|
||||
$activeStr = $active ? 'true' : 'false';
|
||||
$from = date('c', $ref);
|
||||
$to = date('c', $timestamp);
|
||||
$hours = intval((($active ? time() : $timestamp) - $ref) / 60 / 60 + 0.4);
|
||||
$win = isset($osVersions[$d[2]]) ? '"' . $osVersions[$d[2]] . '"' : "null";
|
||||
echo "\"branch\": \"$d[1]\", \"description\": \"$name\", \"name\": \"$device\", \"isActive\": $activeStr, " .
|
||||
"\"lastActivity\": {\"start\": \"$from\", \"end\": \"$to\", \"hours\": $hours}, " .
|
||||
"\"elwigVersion\": \"$d[0]\", \"osVersion\": $win, \"osVersionFull\": \"$d[2]\", " .
|
||||
"\"addresses\": [" . implode(",", $addr) . "]";
|
||||
echo "}";
|
||||
$first2 = false;
|
||||
}
|
||||
echo "\n ]}";
|
||||
$first1 = false;
|
||||
}
|
||||
echo "\n]}\n";
|
||||
} else if ($format === 'text') {
|
||||
header('Content-Type: text/plain; charset=UTF-8');
|
||||
echo "Client Branch Device Device Name Last Activity Elwig Version OS Version IP Addresses\n";
|
||||
foreach ($stat as $token => $devices) {
|
||||
foreach ($devices as $device => $data) {
|
||||
echo "$token ";
|
||||
$timestamp = array_key_last($data);
|
||||
$ref = $timestamp;
|
||||
foreach (array_reverse(array_keys($data)) as $t) {
|
||||
if ($t + 90 * 60 > $ref) {
|
||||
$ref = $t;
|
||||
} else {
|
||||
break;
|
||||
}
|
||||
}
|
||||
$addr = array_reverse(array_unique(array_map(function ($d) {
|
||||
return $d[3];
|
||||
}, array_values($data))));
|
||||
$d = $data[$timestamp];
|
||||
$name = $DEVICES[$token][$device] ?? $device;
|
||||
echo mb_str_pad($d[1], 16) . " " . mb_str_pad($name, 20) . " " . mb_str_pad($device, 20) . " ";
|
||||
if ($timestamp + 90 * 60 > time()) {
|
||||
echo mb_str_pad(date('d.m.Y, H:i', $timestamp) . " (since " . intval((time() - $ref) / 60 / 60 + 0.4) . "h)", 28);
|
||||
} else {
|
||||
echo mb_str_pad(date('d.m.Y, H:i', $timestamp) . " (for " . intval(($timestamp - $ref) / 60 / 60 + 0.4) . "h)", 28);
|
||||
}
|
||||
echo " " . mb_str_pad($d[0], 13) . " ";
|
||||
if (isset($osVersions[$d[2]])) {
|
||||
echo mb_str_pad($osVersions[$d[2]], 33) . " ";
|
||||
} else {
|
||||
echo mb_str_pad($d[2], 33) . " ";
|
||||
}
|
||||
echo implode(", ", array_slice($addr, 0, 5));
|
||||
echo "\n";
|
||||
}
|
||||
}
|
||||
} else {
|
||||
header('Content-Type: text/html; charset=UTF-8');
|
||||
echo "<!DOCTYPE html>\n<html lang='en'>\n";
|
||||
echo "<head><title>Activity Statistics - Elwig</title><style>table{border-collapse:collapse;}th,td{border:1px solid black;padding:0.25em 0.5em;}</style></head>\n";
|
||||
echo "<body>\n";
|
||||
echo "<h1>Activity Statistics</h1>\n";
|
||||
echo "<table>\n";
|
||||
echo "<tr><th>Client</th><th>Branch</th><th>Device</th><th>Device Name</th><th>Last Activity</th><th>Elwig Version</th><th>OS Version</th><th>IP Addresses</th></tr>\n";
|
||||
foreach ($stat as $token => $devices) {
|
||||
$first = true;
|
||||
foreach ($devices as $device => $data) {
|
||||
echo "<tr>";
|
||||
if ($first) {
|
||||
$n = sizeof($devices);
|
||||
echo "<td rowspan='$n'>$token</td>";
|
||||
$first = false;
|
||||
}
|
||||
$timestamp = array_key_last($data);
|
||||
$ref = $timestamp;
|
||||
foreach (array_reverse(array_keys($data)) as $t) {
|
||||
if ($t + 90 * 60 > $ref) {
|
||||
$ref = $t;
|
||||
} else {
|
||||
break;
|
||||
}
|
||||
}
|
||||
$addr = array_reverse(array_unique(array_map(function ($d) {
|
||||
return $d[3];
|
||||
}, array_values($data))));
|
||||
$d = $data[$timestamp];
|
||||
$name = $DEVICES[$token][$device] ?? $device;
|
||||
echo "<td>$d[1]</td>";
|
||||
if ($timestamp + 90 * 60 > time()) {
|
||||
echo "<td><b>$name</b></td>";
|
||||
echo "<td><b>$device</b></td>";
|
||||
echo "<td style='color:darkgreen'>" . date('d.m.Y, H:i', $timestamp) . " (since " . intval((time() - $ref) / 60 / 60 + 0.4) . "h)</td>";
|
||||
} else {
|
||||
echo "<td>$name</td>";
|
||||
echo "<td>$device</td>";
|
||||
echo "<td>" . date('d.m.Y, H:i', $timestamp) . " (for " . intval(($timestamp - $ref) / 60 / 60 + 0.4) . "h)</td>";
|
||||
}
|
||||
echo "<td>$d[0]</td>";
|
||||
if (isset($osVersions[$d[2]])) {
|
||||
echo "<td>" . $osVersions[$d[2]] . "</td>";
|
||||
} else {
|
||||
echo "<td>$d[2]</td>";
|
||||
}
|
||||
echo "<td>" . implode("<br>", array_slice($addr, 0, 5)) . "</td>";
|
||||
echo "</tr>\n";
|
||||
}
|
||||
}
|
||||
echo "</table>\n";
|
||||
echo "<p><a href='stat/log'>Log</a> <a href='stat/log?full'>Full Log</a></p>\n";
|
||||
echo "</body>\n</html>\n";
|
||||
}
|
||||
} else {
|
||||
header('Status: 404');
|
||||
header('Content-Length: 0');
|
||||
exit();
|
||||
}
|
||||
Reference in New Issue
Block a user