Compare commits

..

2 Commits

Author SHA1 Message Date
Yasen Pramatarov 1c710bef35 Updates changelog 2024-09-16 17:10:14 +03:00
Yasen Pramatarov f6362bfdc1 Adds initial support for logs 2024-09-16 17:09:37 +03:00
8 changed files with 200 additions and 0 deletions

View File

@ -10,6 +10,7 @@ All notable changes to this project will be documented in this file.
- Added support for managing Jilo Agents - Added support for managing Jilo Agents
- Added more fields and avatar image to user profile - Added more fields and avatar image to user profile
- Added pagination for the longer listings - Added pagination for the longer listings
- Added initial support for application logs
### Changed ### Changed
- Jitsi platforms config moved from file to SQLite database - Jitsi platforms config moved from file to SQLite database

View File

@ -0,0 +1,52 @@
<?php
class Log {
private $db;
public function __construct($database) {
$this->db = $database->getConnection();
}
// insert log event
public function insertLog($user_id, $message, $scope='user') {
try {
$sql = 'INSERT INTO logs
(user_id, scope, message)
VALUES
(:user_id, :scope, :message)';
$query = $this->db->prepare($sql);
$query->execute([
':user_id' => $user_id,
':scope' => $scope,
':message' => $message,
]);
return true;
} catch (Exception $e) {
return $e->getMessage();
}
}
// read logs
public function readLog($user_id, $scope='user') {
$sql = 'SELECT * FROM logs';
if ($scope === 'user') {
$sql .= ' WHERE user_id = :user_id';
$query = $this->db->prepare($sql);
$query->execute([
':user_id' => $user_id,
]);
}
if ($scope === 'system') {
$query = $this->db->prepare($sql);
$query->execute();
}
return $query->fetchAll(PDO::FETCH_ASSOC);
}
}
?>

View File

@ -43,12 +43,16 @@ try {
// redirect to index // redirect to index
$_SESSION['notice'] = "Login successful"; $_SESSION['notice'] = "Login successful";
$user_id = $userObject->getUserId($username)[0]['id'];
$logObject->insertLog($user_id, "User \"$username\" logged in.", 'user');
header('Location: index.php'); header('Location: index.php');
exit(); exit();
// login failed // login failed
} else { } else {
$_SESSION['error'] = "Login failed."; $_SESSION['error'] = "Login failed.";
$user_id = $userObject->getUserId($username)[0]['id'];
$logObject->insertLog($user_id, "Failed login attempt for user \"$username\".", 'user');
header('Location: index.php'); header('Location: index.php');
exit(); exit();
} }

48
app/pages/logs.php 100644
View File

@ -0,0 +1,48 @@
<?php
//
// logs listings
//
// specify time range
include '../app/helpers/time_range.php';
// prepare the result
$search = $logObject->readLog($user_id, 'user');
if (!empty($search)) {
$logs = array();
$logs['records'] = array();
foreach ($search as $item) {
extract($item);
$log_record = array(
// assign title to the field in the array record
'user ID' => $user_id,
'time' => $time,
'log message' => $message
);
// populate the result array
array_push($logs['records'], $log_record);
}
}
// prepare the widget
$widget['full'] = false;
$widget['collapsible'] = false;
$widget['name'] = 'Logs';
$username = $userObject->getUserDetails($user_id)[0]['username'];
$widget['title'] = "Log events for user \"$username\"";
$widget['filter'] = true;
if (!empty($conferences['records'])) {
$widget['full'] = true;
$widget['table_headers'] = array_keys($logs['records'][0]);
$widget['table_records'] = $logs['records'];
}
$widget['pagination'] = true;
// display the widget
include '../app/templates/logs-list.php';
?>

View File

@ -0,0 +1,26 @@
<!-- Logs filter -->
<div class="card w-auto bg-light border-light card-body text-right" style="text-align: right;">
<form method="POST" id="filter_form" action="?page=logs">
<label for="from_time">from</label>
<input type="date" id="from_time" name="from_time"<?php if (isset($_REQUEST['from_time'])) echo " value=\"" . $_REQUEST['from_time'] . "\"" ?> />
<label for="until_time">until</label>
<input type="date" id="until_time" name="until_time"<?php if (isset($_REQUEST['until_time'])) echo " value=\"" . $_REQUEST['until_time'] . "\"" ?> />
<input type="text" name="id" placeholder="user ID"<?php if (isset($_REQUEST['id'])) echo " value=\"" . $_REQUEST['id'] . "\"" ?> />
<input type="text" name="message" placeholder="message"<?php if (isset($_REQUEST['message'])) echo " value=\"" . $_REQUEST['message'] . "\"" ?> />
<input type="button" onclick="clearFilter()" value="clear" />
<input type="submit" value="search" />
</form>
<script>
function clearFilter() {
document.getElementById("filter_form").reset();
const filterFields = document.querySelectorAll("#filter_form input");
filterFields.forEach(input => {
if (input.type === 'text' ||input.type === 'date') {
input.value = '';
}
});
}
</script>
</div>
<!-- /Logs filter -->

View File

@ -0,0 +1,57 @@
<div class="row">
<?php if ($widget['collapsible'] === true) { ?>
<a style="text-decoration: none;" data-toggle="collapse" href="#collapse<?= $widget['name'] ?>" role="button" aria-expanded="true" aria-controls="collapse<?= $widget['name'] ?>">
<div class="card w-auto bg-light card-body" style="flex-direction: row;"><?= $widget['title'] ?></div>
<?php } else { ?>
<div class="card w-auto bg-light border-light card-body" style="flex-direction: row;"><?= $widget['title'] ?></div>
<?php } ?>
<?php if ($widget['filter'] === true) {
include '../app/templates/logs-filter.php'; } ?>
<?php if ($widget['collapsible'] === true) { ?>
</a>
<?php } ?>
</div>
<!-- widget "<?= $widget['name']; ?>" -->
<div class="collapse show" id="collapse<?= $widget['name'] ?>">
<?php if ($time_range_specified) { ?>
<p class="m-3">time period: <strong><?= $from_time ?> - <?= $until_time ?></strong></p>
<?php } ?>
<div class="mb-5">
<?php if ($widget['full'] === true) { ?>
<table class="table table-striped table-hover table-bordered">
<thead class="thead-dark">
<tr>
<?php foreach ($widget['table_headers'] as $header) { ?>
<th scope="col"><?= htmlspecialchars($header) ?></th>
<?php } ?>
</tr>
</thead>
<tbody>
<?php foreach ($widget['table_records'] as $row) { ?>
<tr>
<?php
foreach ($row as $key => $column) {
if ($key === 'user ID' && isset($user_id) && $user_id === $column) { ?>
<td><strong><?= htmlspecialchars($column ?? '') ?></strong></td>
<?php } else { ?>
<td><?= htmlspecialchars($column ?? '') ?></td>
<?php }
} ?>
</tr>
<?php } ?>
</tbody>
</table>
<?php
if ($widget['pagination'] && $item_count > $items_per_page) {
$url = "$app_root?platform=$platform_id&page=$page";
include '../app/helpers/pagination.php';
}
?>
<?php } else { ?>
<p class="m-3">No matching records found.</p>
<?php } ?>
</div>
</div>
<!-- /widget "<?= $widget['name']; ?>" -->

View File

@ -44,3 +44,11 @@ CREATE TABLE jilo_agent_types (
description TEXT, description TEXT,
endponts TEXT endponts TEXT
); );
CREATE TABLE logs (
id INTEGER PRIMARY KEY AUTOINCREMENT,
user_id INTEGET NOT NULL,
time TEXT DEFAULT (DATETIME('now')),
scope TEXT NOT NULL,
message TEXT NOT NULL,
FOREIGN KEY (user_id) REFERENCES users(id)
);

View File

@ -109,6 +109,10 @@ require '../app/classes/database.php';
require '../app/helpers/database.php'; require '../app/helpers/database.php';
$dbWeb = connectDB($config); $dbWeb = connectDB($config);
// start logging
require '../app/classes/log.php';
$logObject = new Log($dbWeb);
// get platforms details // get platforms details
require '../app/classes/platform.php'; require '../app/classes/platform.php';
$platformObject = new Platform($dbWeb); $platformObject = new Platform($dbWeb);