Fixes HTML

main
Yasen Pramatarov 2025-01-29 10:44:54 +02:00
parent 676e145349
commit 50b89f92ea
5 changed files with 432 additions and 431 deletions

View File

@ -1,94 +1,94 @@
<!-- agents live data -->
<div class="container-fluid mt-2">
<div class="row mb-4">
<div class="col-12 mb-4">
<h2 class="mb-0">Jilo Agents status</h2>
<small>manage and monitor agents on platform <?= htmlspecialchars($platform_id) ?> (<?= htmlspecialchars($platformDetails[0]['name']) ?>).</small>
</div>
</div>
<!-- hosts and their agents -->
<div class="row">
<?php foreach ($agentsByHost as $hostId => $hostData): ?>
<div class="col-12 mb-4">
<div class="card">
<div class="card-header bg-light">
<h5 class="mb-0">
<i class="fas fa-network-wired me-2 text-secondary"></i>
Host: <?= htmlspecialchars($hostData['host_name']) ?>
<a href="<?= htmlspecialchars($app_root) ?>?page=settings#platform-<?= htmlspecialchars($platform_id) ?>host-<?= htmlspecialchars($hostId) ?>" class="text-decoration-none">
<i class="fas fa-edit ms-2"></i>
</a>
</h5>
<!-- agents live data -->
<div class="container-fluid mt-2">
<div class="row mb-4">
<div class="col-12 mb-4">
<h2 class="mb-0">Jilo Agents status</h2>
<small>manage and monitor agents on platform <strong><?= htmlspecialchars($platformDetails[0]['name']) ?></strong></small>
</div>
</div>
<div class="card-body">
<?php if (empty($hostData['agents'])): ?>
<p class="text-muted">No agents on this host.</p>
<?php else: ?>
<?php foreach ($hostData['agents'] as $agent): ?>
<div class="agent-item mb-4 pb-3 border-bottom">
<div class="d-flex align-items-center mb-2">
<div class="flex-grow-1">
<i class="fas fa-robot me-2 text-secondary"></i>
<strong>Agent ID:</strong> <?= htmlspecialchars($agent['id']) ?> |
<strong>Type:</strong> <?= htmlspecialchars($agent['agent_type_id']) ?> (<?= htmlspecialchars($agent['agent_description']) ?>) |
<strong>Endpoint:</strong> <?= htmlspecialchars($agent['url']) ?><?= htmlspecialchars($agent['agent_endpoint']) ?>
<a href="<?= htmlspecialchars($app_root) ?>?page=settings#platform-<?= htmlspecialchars($platform_id) ?>agent-<?= htmlspecialchars($agent['id']) ?>" class="text-decoration-none">
<!-- hosts and their agents -->
<div class="row">
<?php foreach ($agentsByHost as $hostId => $hostData): ?>
<div class="col-12 mb-4">
<div class="card">
<div class="card-header bg-light">
<h5 class="mb-0">
<i class="fas fa-network-wired me-2 text-secondary"></i>
Host: <?= htmlspecialchars($hostData['host_name']) ?>
<a href="<?= htmlspecialchars($app_root) ?>?page=settings#platform-<?= htmlspecialchars($platform_id) ?>host-<?= htmlspecialchars($hostId) ?>" class="text-decoration-none">
<i class="fas fa-edit ms-2"></i>
</a>
</div>
</h5>
</div>
<div class="card-body">
<?php if (empty($hostData['agents'])): ?>
<p class="text-muted">No agents on this host.</p>
<?php else: ?>
<?php foreach ($hostData['agents'] as $agent): ?>
<div class="agent-item mb-4 pb-3 border-bottom">
<div class="d-flex align-items-center mb-2">
<div class="flex-grow-1">
<i class="fas fa-robot me-2 text-secondary"></i>
<strong>Agent ID:</strong> <?= htmlspecialchars($agent['id']) ?> |
<strong>Type:</strong> <?= htmlspecialchars($agent['agent_type_id']) ?> (<?= htmlspecialchars($agent['agent_description']) ?>) |
<strong>Endpoint:</strong> <?= htmlspecialchars($agent['url']) ?><?= htmlspecialchars($agent['agent_endpoint']) ?>
<a href="<?= htmlspecialchars($app_root) ?>?page=settings#platform-<?= htmlspecialchars($platform_id) ?>agent-<?= htmlspecialchars($agent['id']) ?>" class="text-decoration-none">
<i class="fas fa-edit ms-2"></i>
</a>
</div>
</div>
<div class="btn-group" role="group">
<button id="agent<?= htmlspecialchars($agent['id']) ?>-status"
class="btn btn-primary"
data-toggle="tooltip"
data-trigger="hover"
data-placement="bottom"
title="Get the agent status"
onclick="fetchData('<?= htmlspecialchars($agent['id']) ?>', '<?= htmlspecialchars($agent['url']) ?>', '/status', '<?= htmlspecialchars($agentTokens[$agent['id']]) ?>', true)">
Get Status
</button>
<button id="agent<?= htmlspecialchars($agent['id']) ?>-fetch"
class="btn btn-primary"
data-toggle="tooltip"
data-trigger="hover"
data-placement="bottom"
title="Get data from the agent"
onclick="fetchData('<?= htmlspecialchars($agent['id']) ?>', '<?= htmlspecialchars($agent['url']) ?>', '<?= htmlspecialchars($agent['agent_endpoint']) ?>', '<?= htmlspecialchars($agentTokens[$agent['id']]) ?>', <?= isset($_SESSION["agent{$agent['id']}_cache"]) ? 'true' : 'false' ?>)">
Fetch Data
</button>
<button id="agent<?= htmlspecialchars($agent['id']) ?>-cache"
<?= !isset($_SESSION["agent{$agent['id']}_cache"]) ? 'style="display:none;" ' : '' ?>
class="btn btn-secondary"
data-toggle="tooltip"
data-trigger="hover"
data-placement="bottom"
title="Load cache"
onclick="loadCache('<?= htmlspecialchars($agent['id']) ?>')">
Load Cache
</button>
<button id="agent<?= htmlspecialchars($agent['id']) ?>-clear"
<?= !isset($_SESSION["agent{$agent['id']}_cache"]) ? 'style="display:none;" ' : '' ?>
class="btn btn-danger"
data-toggle="tooltip"
data-trigger="hover"
data-placement="bottom"
title="Clear cache"
onclick="clearCache('<?= htmlspecialchars($agent['id']) ?>')">
Clear Cache
</button>
<div class="btn-group" role="group">
<button id="agent<?= htmlspecialchars($agent['id']) ?>-status"
class="btn btn-primary"
data-toggle="tooltip"
data-trigger="hover"
data-placement="bottom"
title="Get the agent status"
onclick="fetchData('<?= htmlspecialchars($agent['id']) ?>', '<?= htmlspecialchars($agent['url']) ?>', '/status', '<?= htmlspecialchars($agentTokens[$agent['id']]) ?>', true)">
Get Status
</button>
<button id="agent<?= htmlspecialchars($agent['id']) ?>-fetch"
class="btn btn-primary"
data-toggle="tooltip"
data-trigger="hover"
data-placement="bottom"
title="Get data from the agent"
onclick="fetchData('<?= htmlspecialchars($agent['id']) ?>', '<?= htmlspecialchars($agent['url']) ?>', '<?= htmlspecialchars($agent['agent_endpoint']) ?>', '<?= htmlspecialchars($agentTokens[$agent['id']]) ?>', <?= isset($_SESSION["agent{$agent['id']}_cache"]) ? 'true' : 'false' ?>)">
Fetch Data
</button>
<button id="agent<?= htmlspecialchars($agent['id']) ?>-cache"
<?= !isset($_SESSION["agent{$agent['id']}_cache"]) ? 'style="display:none;" ' : '' ?>
class="btn btn-secondary"
data-toggle="tooltip"
data-trigger="hover"
data-placement="bottom"
title="Load cache"
onclick="loadCache('<?= htmlspecialchars($agent['id']) ?>')">
Load Cache
</button>
<button id="agent<?= htmlspecialchars($agent['id']) ?>-clear"
<?= !isset($_SESSION["agent{$agent['id']}_cache"]) ? 'style="display:none;" ' : '' ?>
class="btn btn-danger"
data-toggle="tooltip"
data-trigger="hover"
data-placement="bottom"
title="Clear cache"
onclick="clearCache('<?= htmlspecialchars($agent['id']) ?>')">
Clear Cache
</button>
</div>
<span id="cacheInfo<?= htmlspecialchars($agent['id']) ?>" class="ms-2 <?= isset($_SESSION["agent{$agent['id']}_cache"]) ? '' : 'd-none' ?>"></span>
<pre class="results mt-3" id="result<?= htmlspecialchars($agent['id']) ?>">Click a button to display data from the agent.</pre>
</div>
<?php endforeach; ?>
<?php endif; ?>
</div>
<span id="cacheInfo<?= htmlspecialchars($agent['id']) ?>" class="ms-2 <?= isset($_SESSION["agent{$agent['id']}_cache"]) ? '' : 'd-none' ?>"></span>
<pre class="results mt-3" id="result<?= htmlspecialchars($agent['id']) ?>">Click a button to display data from the agent.</pre>
</div>
<?php endforeach; ?>
<?php endif; ?>
</div>
<?php endforeach; ?>
</div>
</div>
</div>
<?php endforeach; ?>
</div>
</div>
<!-- agents live data -->
<!-- agents live data -->

View File

@ -1,10 +1,10 @@
<!-- "jitsi components events" -->
<!-- jitsi components events -->
<div class="container-fluid mt-2">
<div class="row mb-4">
<div class="col-md-6 mb-5">
<h2>Jitsi components events</h2>
<small>Log events related to Jitsi Meet components like Jicofo, Videobridge, Jigasi, etc.</small>
<h2 class="mb-0">Jitsi components events</h2>
<small>log events related to Jitsi Meet components like Jicofo, Videobridge, Jigasi, etc.</small>
</div>
<div class="row mb-4">
@ -94,4 +94,4 @@
</div>
</div>
</div>
<!-- "/jitsi components events" -->
<!-- /jitsi components events -->

View File

@ -1,126 +1,128 @@
<!-- help -->
<div class="container-fluid mt-2">
<div class="row mb-4">
<div class="col-12 mb-4">
<h2>Help</h2>
</div>
</div>
<div class="card shadow-sm">
<div class="card-body p-4">
<!-- Introduction -->
<div class="mb-5">
<p class="lead">
<a href="https://lindeas.com/jilo" class="text-decoration-none">Jilo</a> is a software tools suite developed by
<a href="https://lindeas.com" class="text-decoration-none">Lindeas Ltd.</a> designed to help in maintaining a Jitsi Meet platform.
</p>
<p>It consists of several parts meant to run together, although some of them can be used separately.</p>
</div>
<!-- Components -->
<div class="row g-4">
<!-- Jilo CLI -->
<div class="col-12">
<div class="card border bg-light">
<div class="card-header bg-light d-flex align-items-center">
<i class="fas fa-terminal me-2 text-secondary"></i>
<h5 class="card-title mb-0">JILO</h5>
<!-- help -->
<div class="container-fluid mt-2">
<div class="row mb-4">
<div class="col-12 mb-4">
<h2 class="mb-0">Help</h2>
<small>about this program</small>
</div>
<div class="card-body">
<p class="card-text">
This is the command-line tool for extracting information about important events from the Jitsi Meet log files,
storing them in a database and searching through that database.
</p>
<p class="card-text">
Jilo is written in Bash and has very minimal external dependencies. That means that you can run it on almost
any Linux system with jitsi log files.
</p>
<div class="mt-3">
<p class="mb-2">It can either:</p>
<ul class="list-unstyled ps-4">
<li><i class="fas fa-check text-success me-2"></i>Show the data directly in the terminal</li>
<li><i class="fas fa-check text-success me-2"></i>Provide it to an instance of "Jilo Web" for displaying statistics</li>
<li><i class="fas fa-check text-success me-2"></i>Send the data output to a "Jilo Agent"</li>
</ul>
</div>
<div class="card shadow-sm">
<div class="card-body p-4">
<!-- Introduction -->
<div class="mb-5">
<p class="lead">
<a href="https://lindeas.com/jilo" class="text-decoration-none">Jilo</a> is a software tools suite developed by
<a href="https://lindeas.com" class="text-decoration-none">Lindeas Ltd.</a> designed to help in maintaining a Jitsi Meet platform.
</p>
<p>It consists of several parts meant to run together, although some of them can be used separately.</p>
</div>
<!-- Components -->
<div class="row g-4">
<!-- Jilo CLI -->
<div class="col-12">
<div class="card border bg-light">
<div class="card-header bg-light d-flex align-items-center">
<i class="fas fa-terminal me-2 text-secondary"></i>
<h5 class="card-title mb-0">JILO</h5>
</div>
<div class="card-body">
<p class="card-text">
This is the command-line tool for extracting information about important events from the Jitsi Meet log files,
storing them in a database and searching through that database.
</p>
<p class="card-text">
Jilo is written in Bash and has very minimal external dependencies. That means that you can run it on almost
any Linux system with jitsi log files.
</p>
<div class="mt-3">
<p class="mb-2">It can either:</p>
<ul class="list-unstyled ps-4">
<li><i class="fas fa-check text-success me-2"></i>Show the data directly in the terminal</li>
<li><i class="fas fa-check text-success me-2"></i>Provide it to an instance of "Jilo Web" for displaying statistics</li>
<li><i class="fas fa-check text-success me-2"></i>Send the data output to a "Jilo Agent"</li>
</ul>
</div>
</div>
</div>
</div>
<!-- Jilo Agent -->
<div class="col-12">
<div class="card border bg-light">
<div class="card-header bg-light d-flex align-items-center">
<i class="fas fa-robot me-2 text-secondary"></i>
<h5 class="card-title mb-0">Jilo Agent</h5>
</div>
<div class="card-body">
<p class="card-text">
The Jilo Agent is a small program, written in Go. It runs on remote servers in the Jitsi Meet platform,
and provides info about the operation of the different components of the platform.
</p>
<div class="mt-3">
<h6 class="fw-bold mb-2">Key Features:</h6>
<ul class="list-unstyled ps-4">
<li><i class="fas fa-shield-alt text-primary me-2"></i>Secured with JWT tokens authentication</li>
<li><i class="fas fa-lock text-primary me-2"></i>HTTPS support</li>
<li><i class="fas fa-network-wired text-primary me-2"></i>Single port for all services</li>
<li><i class="fas fa-box text-primary me-2"></i>No external dependencies</li>
</ul>
</div>
</div>
</div>
</div>
<!-- Jilo Web -->
<div class="col-12">
<div class="card border bg-light">
<div class="card-header bg-light d-flex align-items-center">
<i class="fas fa-globe me-2 text-secondary"></i>
<h5 class="card-title mb-0">Jilo Web</h5>
</div>
<div class="card-body">
<p class="card-text">
Jilo Web is the web app that combines all the information received from Jilo and Jilo Agents and shows
statistics and graphs of the usage, the events and the issues.
</p>
<p class="card-text">
It is a multi-user web tool with user levels and access rights integrated into it, making it suitable
for the different levels in an enterprise.
</p>
<div class="alert alert-info mt-3 mb-0">
<i class="fas fa-info-circle me-2"></i>
The current website you are looking at is running a Jilo Web instance.
</div>
</div>
</div>
</div>
<!-- Jilo Server -->
<div class="col-12">
<div class="card border bg-light">
<div class="card-header bg-light d-flex align-items-center">
<i class="fas fa-server me-2 text-secondary"></i>
<h5 class="card-title mb-0">Jilo Server</h5>
</div>
<div class="card-body">
<p class="card-text">
Jilo Server is a server component written in Go, meant to work alongside Jilo Web. It is responsible for
all automated tasks - health checks, periodic retrieval of data from the remote Jilo Agents, etc.
</p>
<p class="card-text">
It generally works on the same machine as the web interface Jilo Web and shares its database, although
if needed it could be deployed on a separate machine.
</p>
<div class="alert alert-warning mt-3 mb-0">
<i class="fas fa-exclamation-triangle me-2"></i>
Jilo Web checks for the Jilo Server availability and displays a warning if there is a problem with the server.
</div>
</div>
</div>
</div>
</div>
</div>
</div>
</div>
<!-- Jilo Agent -->
<div class="col-12">
<div class="card border bg-light">
<div class="card-header bg-light d-flex align-items-center">
<i class="fas fa-robot me-2 text-secondary"></i>
<h5 class="card-title mb-0">Jilo Agent</h5>
</div>
<div class="card-body">
<p class="card-text">
The Jilo Agent is a small program, written in Go. It runs on remote servers in the Jitsi Meet platform,
and provides info about the operation of the different components of the platform.
</p>
<div class="mt-3">
<h6 class="fw-bold mb-2">Key Features:</h6>
<ul class="list-unstyled ps-4">
<li><i class="fas fa-shield-alt text-primary me-2"></i>Secured with JWT tokens authentication</li>
<li><i class="fas fa-lock text-primary me-2"></i>HTTPS support</li>
<li><i class="fas fa-network-wired text-primary me-2"></i>Single port for all services</li>
<li><i class="fas fa-box text-primary me-2"></i>No external dependencies</li>
</ul>
</div>
</div>
</div>
</div>
<!-- Jilo Web -->
<div class="col-12">
<div class="card border bg-light">
<div class="card-header bg-light d-flex align-items-center">
<i class="fas fa-globe me-2 text-secondary"></i>
<h5 class="card-title mb-0">Jilo Web</h5>
</div>
<div class="card-body">
<p class="card-text">
Jilo Web is the web app that combines all the information received from Jilo and Jilo Agents and shows
statistics and graphs of the usage, the events and the issues.
</p>
<p class="card-text">
It's a multi-user web tool with user levels and access rights integrated into it, making it suitable
for the different levels in an enterprise.
</p>
<div class="alert alert-info mt-3 mb-0">
<i class="fas fa-info-circle me-2"></i>
The current website you are looking at is running a Jilo Web instance.
</div>
</div>
</div>
</div>
<!-- Jilo Server -->
<div class="col-12">
<div class="card border bg-light">
<div class="card-header bg-light d-flex align-items-center">
<i class="fas fa-server me-2 text-secondary"></i>
<h5 class="card-title mb-0">Jilo Server</h5>
</div>
<div class="card-body">
<p class="card-text">
Jilo Server is a server component written in Go, meant to work alongside Jilo Web. It is responsible for
all automated tasks - health checks, periodic retrieval of data from the remote Jilo Agents, etc.
</p>
<p class="card-text">
It generally works on the same machine as the web interface Jilo Web and shares its database, although
if needed it could be deployed on a separate machine.
</p>
<div class="alert alert-warning mt-3 mb-0">
<i class="fas fa-exclamation-triangle me-2"></i>
Jilo Web checks for the Jilo Server availability and displays a warning if there is a problem with the server.
</div>
</div>
</div>
</div>
</div>
</div>
</div>
</div>
<!-- /help -->
<!-- /help -->

View File

@ -49,13 +49,13 @@ $timeNow = new DateTime('now', new DateTimeZone($userTimezone));
<i class="fas fa-list" data-toggle="tooltip" data-placement="right" data-offset="30.0" title="latest data"></i>latest data
</li>
</a>
<a href="<?= htmlspecialchars($app_root) ?>?platform=<?= htmlspecialchars($platform_id) ?>&page=data&item=configjs">
<li class="list-group-item<?php if ($page === 'data' && $item === 'configjs') echo ' list-group-item-secondary'; else echo ' list-group-item-action'; ?>">
<a href="<?= htmlspecialchars($app_root) ?>?platform=<?= htmlspecialchars($platform_id) ?>&page=livejs&item=config.js">
<li class="list-group-item<?php if ($page === 'livejs' && $item === 'config.js') echo ' list-group-item-secondary'; else echo ' list-group-item-action'; ?>">
<i class="fas fa-tv" data-toggle="tooltip" data-placement="right" data-offset="30.0" title="config.js"></i>config.js
</li>
</a>
<a href="<?= htmlspecialchars($app_root) ?>?platform=<?= htmlspecialchars($platform_id) ?>&page=data&item=interfaceconfigjs">
<li class="list-group-item<?php if ($page === 'data' && $item === 'interfaceconfigjs') echo ' list-group-item-secondary'; else echo ' list-group-item-action'; ?>">
<a href="<?= htmlspecialchars($app_root) ?>?platform=<?= htmlspecialchars($platform_id) ?>&page=livejs&item=interface_config.js">
<li class="list-group-item<?php if ($page === 'livejs' && $item === 'interface_config.js') echo ' list-group-item-secondary'; else echo ' list-group-item-action'; ?>">
<i class="fas fa-th" data-toggle="tooltip" data-placement="right" data-offset="30.0" title="interface_config.js"></i>interface_config.js
</li>
</a>

View File

@ -1,223 +1,222 @@
<!-- security settings -->
<div class="container-fluid mt-2">
<div class="row mb-4">
<div class="col">
<h2>Security settings</h2>
<ul class="nav nav-tabs mt-5">
<?php if ($userObject->hasRight($user_id, 'superuser') || $userObject->hasRight($user_id, 'edit whitelist')) { ?>
<li class="nav-item">
<a class="nav-link <?= $section === 'whitelist' ? 'active' : '' ?>" href="?page=security&section=whitelist">IP whitelist</a>
</li>
<?php } ?>
<?php if ($userObject->hasRight($user_id, 'superuser') || $userObject->hasRight($user_id, 'edit blacklist')) { ?>
<li class="nav-item">
<a class="nav-link <?= $section === 'blacklist' ? 'active' : '' ?>" href="?page=security&section=blacklist">IP blacklist</a>
</li>
<?php } ?>
<?php if ($userObject->hasRight($user_id, 'superuser') || $userObject->hasRight($user_id, 'edit ratelimiting')) { ?>
<li class="nav-item">
<a class="nav-link <?= $section === 'ratelimit' ? 'active' : '' ?>" href="?page=security&section=ratelimit">Rate limiting</a>
</li>
<?php } ?>
</ul>
</div>
</div>
<?php if ($section === 'whitelist' && ($userObject->hasRight($user_id, 'superuser') || $userObject->hasRight($user_id, 'edit whitelist'))) { ?>
<!-- whitelist section -->
<div class="row mb-4">
<div class="col">
<div class="card">
<div class="card-header">
<h3>IP whitelist</h3>
IP addresses and networks that will always bypass the ratelimiting login checks.
</div>
<div class="card-body">
<form method="POST" class="mb-4">
<input type="hidden" name="action" value="add_whitelist">
<div class="row g-3">
<div class="col-md-4">
<input type="text" class="form-control" name="ip_address" placeholder="IP address or CIDR" required>
</div>
<div class="col-md-4">
<input type="text" class="form-control" name="description" placeholder="Description">
</div>
<div class="col-md-2">
<div class="form-check">
<input type="checkbox" class="form-check-input" name="is_network" id="is_network_white">
<label class="form-check-label" for="is_network_white">is network</label>
</div>
</div>
<div class="col-md-2">
<button type="submit" class="btn btn-primary">Add to whitelist</button>
</div>
<!-- security settings -->
<div class="container-fluid mt-2">
<div class="row mb-4">
<div class="col">
<h2 class="mb-0">Security settings</h2>
<small>network restrictions to control flooding and brute force attacks</small>
<ul class="nav nav-tabs mt-5">
<?php if ($userObject->hasRight($user_id, 'superuser') || $userObject->hasRight($user_id, 'edit whitelist')) { ?>
<li class="nav-item">
<a class="nav-link <?= $section === 'whitelist' ? 'active' : '' ?>" href="?page=security&section=whitelist">IP whitelist</a>
</li>
<?php } ?>
<?php if ($userObject->hasRight($user_id, 'superuser') || $userObject->hasRight($user_id, 'edit blacklist')) { ?>
<li class="nav-item">
<a class="nav-link <?= $section === 'blacklist' ? 'active' : '' ?>" href="?page=security&section=blacklist">IP blacklist</a>
</li>
<?php } ?>
<?php if ($userObject->hasRight($user_id, 'superuser') || $userObject->hasRight($user_id, 'edit ratelimiting')) { ?>
<li class="nav-item">
<a class="nav-link <?= $section === 'ratelimit' ? 'active' : '' ?>" href="?page=security&section=ratelimit">Rate limiting</a>
</li>
<?php } ?>
</ul>
</div>
</form>
<table class="table">
<thead>
<tr>
<th>IP address</th>
<th>Network</th>
<th>Description</th>
<th>Added by</th>
<th>Added on</th>
<th>Actions</th>
</tr>
</thead>
<tbody>
<?php foreach ($whitelisted as $ip) { ?>
<tr>
<td><?= htmlspecialchars($ip['ip_address']) ?></td>
<td><?= $ip['is_network'] ? 'Yes' : 'No' ?></td>
<td><?= htmlspecialchars($ip['description']) ?></td>
<td><?= htmlspecialchars($ip['created_by']) ?></td>
<td><?= htmlspecialchars($ip['created_at']) ?></td>
<td>
<form method="POST" style="display: inline;">
<input type="hidden" name="action" value="remove_whitelist">
<input type="hidden" name="ip_address" value="<?= htmlspecialchars($ip['ip_address']) ?>">
<button type="submit" class="btn btn-sm btn-danger" onclick="return confirm('Are you sure you want to remove this IP from whitelist?')">Remove</button>
</form>
</td>
</tr>
<?php } ?>
</tbody>
</table>
</div>
</div>
</div>
</div>
<?php } ?>
<?php if ($section === 'blacklist' && ($userObject->hasRight($user_id, 'superuser') || $userObject->hasRight($user_id, 'edit blacklist'))) { ?>
<!-- blacklist section -->
<div class="row mb-4">
<div class="col">
<div class="card">
<div class="card-header">
<h3>IP blacklist</h3>
IP addresses and networks that will always get blocked at login.
</div>
<div class="card-body">
<form method="POST" class="mb-4">
<input type="hidden" name="action" value="add_blacklist">
<div class="row g-3">
<div class="col-md-3">
<input type="text" class="form-control" name="ip_address" placeholder="IP address or CIDR" required>
</div>
<div class="col-md-3">
<input type="text" class="form-control" name="reason" placeholder="Reason">
</div>
<div class="col-md-2">
<input type="number" class="form-control" name="expiry_hours" placeholder="Expiry (hours)">
</div>
<div class="col-md-2">
<div class="form-check">
<input type="checkbox" class="form-check-input" name="is_network" id="is_network_black">
<label class="form-check-label" for="is_network_black">is network</label>
</div>
</div>
<div class="col-md-2">
<button type="submit" class="btn btn-primary">Add to blacklist</button>
</div>
</div>
</form>
<table class="table">
<thead>
<tr>
<th>IP address</th>
<th>Network</th>
<th>Reason</th>
<th>Added by</th>
<th>Added on</th>
<th>Expires</th>
<th>Actions</th>
</tr>
</thead>
<tbody>
<?php foreach ($blacklisted as $ip) { ?>
<tr>
<td><?= htmlspecialchars($ip['ip_address']) ?></td>
<td><?= $ip['is_network'] ? 'Yes' : 'No' ?></td>
<td><?= htmlspecialchars($ip['reason']) ?></td>
<td><?= htmlspecialchars($ip['created_by']) ?></td>
<td><?= htmlspecialchars($ip['created_at']) ?></td>
<td><?= $ip['expiry_time'] ? htmlspecialchars($ip['expiry_time']) : 'Never' ?></td>
<td>
<form method="POST" style="display: inline;">
<input type="hidden" name="action" value="remove_blacklist">
<input type="hidden" name="ip_address" value="<?= htmlspecialchars($ip['ip_address']) ?>">
<button type="submit" class="btn btn-sm btn-danger" onclick="return confirm('Are you sure you want to remove this IP from blacklist?')">Remove</button>
</form>
</td>
</tr>
<?php } ?>
</tbody>
</table>
</div>
</div>
</div>
</div>
<?php } ?>
<?php if ($section === 'ratelimit' && ($userObject->hasRight($user_id, 'superuser') || $userObject->hasRight($user_id, 'edit ratelimiting'))) { ?>
<!-- rate limiting section -->
<div class="row mb-4">
<div class="col">
<div class="card">
<div class="card-header">
<h3>Rate limiting settings</h3>
Rate limiting settings control how many failed login attempts are allowed before blocking an IP address.
</div>
<div class="card-body">
<div class="alert alert-info">
<h4>Current settings</h4>
<ul>
<li>Maximum login attempts: <?= $rateLimiter->maxAttempts ?></li>
<li>Time window: <?= $rateLimiter->decayMinutes ?> minutes</li>
<li>Auto-blacklist threshold: <?= $rateLimiter->autoBlacklistThreshold ?> attempts</li>
<li>Auto-blacklist duration: <?= $rateLimiter->autoBlacklistDuration ?> hours</li>
</ul>
<p class="mb-0">
<small>Note: These settings can be modified in the RateLimiter class configuration.</small>
</p>
</div>
<h4>Recent failed login attempts</h4>
<table class="table">
<thead>
<tr>
<th>IP sddress</th>
<th>Username</th>
<th>Attempted at</th>
</tr>
</thead>
<tbody>
<?php
$stmt = $rateLimiter->db->prepare("
SELECT ip_address, username, attempted_at
FROM {$rateLimiter->ratelimitTable}
ORDER BY attempted_at DESC
LIMIT 10
");
$stmt->execute();
$attempts = $stmt->fetchAll(PDO::FETCH_ASSOC);
foreach ($attempts as $attempt) {
?>
<tr>
<td><?= htmlspecialchars($attempt['ip_address']) ?></td>
<td><?= htmlspecialchars($attempt['username']) ?></td>
<td><?= htmlspecialchars($attempt['attempted_at']) ?></td>
</tr>
<?php } ?>
</tbody>
</table>
<?php if ($section === 'whitelist' && ($userObject->hasRight($user_id, 'superuser') || $userObject->hasRight($user_id, 'edit whitelist'))) { ?>
<!-- whitelist section -->
<div class="row mb-4">
<div class="col">
<div class="card">
<div class="card-header">
<h3>IP whitelist</h3>
IP addresses and networks that will always bypass the ratelimiting login checks.
</div>
<div class="card-body">
<form method="POST" class="mb-4">
<input type="hidden" name="action" value="add_whitelist">
<div class="row g-3">
<div class="col-md-4">
<input type="text" class="form-control" name="ip_address" placeholder="IP address or CIDR" required>
</div>
<div class="col-md-4">
<input type="text" class="form-control" name="description" placeholder="Description">
</div>
<div class="col-md-2">
<div class="form-check">
<input type="checkbox" class="form-check-input" name="is_network" id="is_network_white">
<label class="form-check-label" for="is_network_white">is network</label>
</div>
</div>
<div class="col-md-2">
<button type="submit" class="btn btn-primary">Add to whitelist</button>
</div>
</div>
</form>
<table class="table">
<thead>
<tr>
<th>IP address</th>
<th>Network</th>
<th>Description</th>
<th>Added by</th>
<th>Added on</th>
<th>Actions</th>
</tr>
</thead>
<tbody>
<?php foreach ($whitelisted as $ip) { ?>
<tr>
<td><?= htmlspecialchars($ip['ip_address']) ?></td>
<td><?= $ip['is_network'] ? 'Yes' : 'No' ?></td>
<td><?= htmlspecialchars($ip['description']) ?></td>
<td><?= htmlspecialchars($ip['created_by']) ?></td>
<td><?= htmlspecialchars($ip['created_at']) ?></td>
<td>
<form method="POST" style="display: inline;">
<input type="hidden" name="action" value="remove_whitelist">
<input type="hidden" name="ip_address" value="<?= htmlspecialchars($ip['ip_address']) ?>">
<button type="submit" class="btn btn-sm btn-danger" onclick="return confirm('Are you sure you want to remove this IP from whitelist?')">Remove</button>
</form>
</td>
</tr>
<?php } ?>
</tbody>
</table>
</div>
</div>
</div>
</div>
<?php } ?>
<?php if ($section === 'blacklist' && ($userObject->hasRight($user_id, 'superuser') || $userObject->hasRight($user_id, 'edit blacklist'))) { ?>
<!-- blacklist section -->
<div class="row mb-4">
<div class="col">
<div class="card">
<div class="card-header">
<h3>IP blacklist</h3>
IP addresses and networks that will always get blocked at login.
</div>
<div class="card-body">
<form method="POST" class="mb-4">
<input type="hidden" name="action" value="add_blacklist">
<div class="row g-3">
<div class="col-md-3">
<input type="text" class="form-control" name="ip_address" placeholder="IP address or CIDR" required>
</div>
<div class="col-md-3">
<input type="text" class="form-control" name="reason" placeholder="Reason">
</div>
<div class="col-md-2">
<input type="number" class="form-control" name="expiry_hours" placeholder="Expiry (hours)">
</div>
<div class="col-md-2">
<div class="form-check">
<input type="checkbox" class="form-check-input" name="is_network" id="is_network_black">
<label class="form-check-label" for="is_network_black">is network</label>
</div>
</div>
<div class="col-md-2">
<button type="submit" class="btn btn-primary">Add to blacklist</button>
</div>
</div>
</form>
<table class="table">
<thead>
<tr>
<th>IP address</th>
<th>Network</th>
<th>Reason</th>
<th>Added by</th>
<th>Added on</th>
<th>Expires</th>
<th>Actions</th>
</tr>
</thead>
<tbody>
<?php foreach ($blacklisted as $ip) { ?>
<tr>
<td><?= htmlspecialchars($ip['ip_address']) ?></td>
<td><?= $ip['is_network'] ? 'Yes' : 'No' ?></td>
<td><?= htmlspecialchars($ip['reason']) ?></td>
<td><?= htmlspecialchars($ip['created_by']) ?></td>
<td><?= htmlspecialchars($ip['created_at']) ?></td>
<td><?= $ip['expiry_time'] ? htmlspecialchars($ip['expiry_time']) : 'Never' ?></td>
<td>
<form method="POST" style="display: inline;">
<input type="hidden" name="action" value="remove_blacklist">
<input type="hidden" name="ip_address" value="<?= htmlspecialchars($ip['ip_address']) ?>">
<button type="submit" class="btn btn-sm btn-danger" onclick="return confirm('Are you sure you want to remove this IP from blacklist?')">Remove</button>
</form>
</td>
</tr>
<?php } ?>
</tbody>
</table>
</div>
</div>
</div>
</div>
<?php } ?>
<?php if ($section === 'ratelimit' && ($userObject->hasRight($user_id, 'superuser') || $userObject->hasRight($user_id, 'edit ratelimiting'))) { ?>
<!-- rate limiting section -->
<div class="row mb-4">
<div class="col">
<div class="card">
<div class="card-header">
<h3>Rate limiting settings</h3>
Rate limiting settings control how many failed login attempts are allowed before blocking an IP address.
</div>
<div class="card-body">
<div class="alert alert-info">
<h4>Current settings</h4>
<ul>
<li>Maximum login attempts: <?= $rateLimiter->maxAttempts ?></li>
<li>Time window: <?= $rateLimiter->decayMinutes ?> minutes</li>
<li>Auto-blacklist threshold: <?= $rateLimiter->autoBlacklistThreshold ?> attempts</li>
<li>Auto-blacklist duration: <?= $rateLimiter->autoBlacklistDuration ?> hours</li>
</ul>
<p class="mb-0">
<small>Note: These settings can be modified in the RateLimiter class configuration.</small>
</p>
</div>
<h4>Recent failed login attempts</h4>
<table class="table">
<thead>
<tr>
<th>IP sddress</th>
<th>Username</th>
<th>Attempted at</th>
</tr>
</thead>
<tbody>
<?php $stmt = $rateLimiter->db->prepare("
SELECT ip_address, username, attempted_at
FROM {$rateLimiter->ratelimitTable}
ORDER BY attempted_at DESC
LIMIT 10
");
$stmt->execute();
$attempts = $stmt->fetchAll(PDO::FETCH_ASSOC);
foreach ($attempts as $attempt) { ?>
<tr>
<td><?= htmlspecialchars($attempt['ip_address']) ?></td>
<td><?= htmlspecialchars($attempt['username']) ?></td>
<td><?= htmlspecialchars($attempt['attempted_at']) ?></td>
</tr>
<?php } ?>
</tbody>
</table>
</div>
</div>
</div>
</div>
<?php } ?>
</div>
</div>
</div>
</div>
<?php } ?>
</div>
<!-- /security settings -->
<!-- /security settings -->