Compare commits
190 Commits
Author | SHA1 | Date |
---|---|---|
|
55ab59372e | |
|
80a3068742 | |
|
a5b2653ed4 | |
|
378ecb8a14 | |
|
0cf4795fc7 | |
|
89076d3aeb | |
|
7e67b2907b | |
|
8b1fd2e2c1 | |
|
5982d5eef9 | |
|
d3f65939cd | |
|
5986993e45 | |
|
da4a35d506 | |
|
f1c63de8c0 | |
|
a8bf994ae5 | |
|
708a50bcf8 | |
|
01b2c26580 | |
|
d5e30400d0 | |
|
bc5ae76534 | |
|
b314cdd14d | |
|
4bfae911db | |
|
608946ddee | |
|
eae2a8a47c | |
|
9c129fcf76 | |
|
84354b183d | |
|
50b74a15db | |
|
8f32a79d0e | |
|
a076c28a30 | |
|
f4c008c65f | |
|
13947e2099 | |
|
0a17b947d7 | |
|
528f4829af | |
|
ee920d8e66 | |
|
65417f7d92 | |
|
020d0ee22d | |
|
68f2353c97 | |
|
db97101113 | |
|
a55a02c209 | |
|
95fb7f06d8 | |
|
589abf2731 | |
|
45d4fbb377 | |
|
9b8f92f2eb | |
|
8d0518c7ff | |
|
d15c6d6f1f | |
|
76f4e0e3c8 | |
|
0d05d66c0f | |
|
db6dabedec | |
|
bfa467996f | |
|
2a270dac74 | |
|
667695881c | |
|
bc1089be21 | |
|
a0747cfbc8 | |
|
0f72f3bea4 | |
|
38e4b002c8 | |
|
645e98cd6a | |
|
a31939cb87 | |
|
08394be35e | |
|
c78951da60 | |
|
f549940249 | |
|
fee0616ca4 | |
|
626fc4ba2b | |
|
f7e4aeb898 | |
|
1f7d42b083 | |
|
858cc264f1 | |
|
7d21406be4 | |
|
d8dc937e48 | |
|
06f6a3dfb7 | |
|
da08ad54ca | |
|
b18cca8075 | |
|
a6b0553393 | |
|
0808f573fc | |
|
eb998a555b | |
|
7add95dd1b | |
|
09c1669812 | |
|
e57ab435fc | |
|
161f74f6bd | |
|
b94613f049 | |
|
28f9fa1007 | |
|
da75076130 | |
|
4ba0faf20b | |
|
2bd6cf89f6 | |
|
c711d6f011 | |
|
3ce20c2069 | |
|
866af8acfe | |
|
a8a85b0666 | |
|
831c119636 | |
|
3279a565aa | |
|
69ac69f41c | |
|
4f5557f6ca | |
|
06bf414f41 | |
|
4b4a9603b9 | |
|
396b449bf2 | |
|
9562a7d0bb | |
|
51282eae38 | |
|
d1fe3a7cf5 | |
|
fac6e4ea83 | |
|
c7a161963f | |
|
16826b93bf | |
|
7ed19a6e48 | |
|
77f7e14f78 | |
|
f48ed80103 | |
|
89d4450796 | |
|
6fcc6da51c | |
|
43148d3f17 | |
|
8c37fea093 | |
|
1ad19492f6 | |
|
ade26b7267 | |
|
7a1f2b841e | |
|
c5e123ea2f | |
|
96de30f3e0 | |
|
b015ef275f | |
|
a272d50174 | |
|
d32e270598 | |
|
686d2c1153 | |
|
c245bbb3be | |
|
c40dc747d3 | |
|
04e89ddca9 | |
|
f59c758fbc | |
|
1aebb7faf9 | |
|
9993f3183c | |
|
55cefffa5e | |
|
e97092c46d | |
|
4fcf1b5fec | |
|
449e65bb91 | |
|
ca7c8d0909 | |
|
a971ff9bf1 | |
|
428348bada | |
|
2e2905e014 | |
|
75370231e9 | |
|
e2fb712098 | |
|
422413f45d | |
|
8ada088ebb | |
|
83fd64cf51 | |
|
e90f4ca020 | |
|
5220ab7fcd | |
|
213c627208 | |
|
d492bff7b7 | |
|
52e5afffbc | |
|
43b6547238 | |
|
13ec66548f | |
|
a65ead94ad | |
|
c00c1845f6 | |
|
7841b13fd8 | |
|
7d845b7998 | |
|
1bc6313e98 | |
|
0120abf246 | |
|
f8bf21e3e5 | |
|
681ba504aa | |
|
37642cedaa | |
|
1733e75dd1 | |
|
e084a04305 | |
|
5d715e4aac | |
|
eea3271fe6 | |
|
ad6b2d0799 | |
|
84901a77e0 | |
|
d629e2d9d3 | |
|
b79a0ac4da | |
|
3c85c16480 | |
|
5327a82685 | |
|
d51b16619f | |
|
e1265500cf | |
|
b67a915ebe | |
|
67380b1b3d | |
|
74e9285804 | |
|
a953dd386d | |
|
82962e0449 | |
|
20c36f9d02 | |
|
15798b08a7 | |
|
ced3ac484d | |
|
5f6de8d328 | |
|
1b03a8dcd1 | |
|
29f5e89acf | |
|
7628671241 | |
|
9a983ecca6 | |
|
93a65c0836 | |
|
e9325ee57b | |
|
27c2682c3f | |
|
be8f008eb4 | |
|
9bfb79d036 | |
|
b3e3a78ed9 | |
|
2bf3a423f4 | |
|
8840efebdb | |
|
fee54aa827 | |
|
36f287e169 | |
|
9b45d5df9e | |
|
73d429647c | |
|
ed9ce2bb07 | |
|
daf41efa4c | |
|
d5bc6a3c8d | |
|
cada9dd67c | |
|
f3457d5240 |
34
CHANGELOG.md
34
CHANGELOG.md
|
@ -4,6 +4,40 @@ All notable changes to this project will be documented in this file.
|
|||
|
||||
---
|
||||
|
||||
## 0.3 - 2025-01-15
|
||||
|
||||
#### Links
|
||||
- upstream: https://code.lindeas.com/lindeas/jilo-web/compare/v0.2.1...v0.3
|
||||
- codeberg: https://codeberg.org/lindeas/jilo-web/compare/v0.2.1...v0.3
|
||||
- github: https://github.com/lindeas/jilo-web/compare/v0.2.1...v0.3
|
||||
- gitlab: https://gitlab.com/lindeas/jilo-web/-/compare/v0.2.1...v0.3
|
||||
|
||||
### Added
|
||||
- Added status page
|
||||
- Added latest data page
|
||||
- Added graphs page
|
||||
- Added Jilo agents status checks
|
||||
- Added periodic Jilo agents checks
|
||||
- Added Jilo Server check and notice on error
|
||||
- Added "jitsi platforms config" section in the sidebar
|
||||
- Added editing for platforms
|
||||
- Added editing for hosts
|
||||
- Added editing for the Jilo configuration file
|
||||
- Added phpdoc comments
|
||||
- Added rate limiting for login with blacklist and whitelist
|
||||
- Added a page for configuring the rate limiting
|
||||
|
||||
### Changed
|
||||
- Implemented a new messaging and notifications system
|
||||
- Moved all live checks pages to the "live data" sidebar section
|
||||
- Separated the config page to multiple pages
|
||||
- Moved the config pages to "jitsi platforms config" section
|
||||
|
||||
### Fixed
|
||||
- Fixed bugs in config editing pages and cleaned up the HTML
|
||||
|
||||
---
|
||||
|
||||
## 0.2.1 - 2024-10-17
|
||||
|
||||
#### Links
|
||||
|
|
|
@ -26,7 +26,7 @@ To see a demo install, go to https://work.lindeas.com/jilo-web-demo/
|
|||
|
||||
## version
|
||||
|
||||
Current version: **0.2.1** released on **2024-10-17**
|
||||
Current version: **0.3** released on **2025-01-15**
|
||||
|
||||
## license
|
||||
|
||||
|
@ -43,6 +43,7 @@ Chart.js is used in this project and is licensed under the MIT License. See lice
|
|||
- web server (deb: apache | nginx)
|
||||
- php support in the web server (deb: php-fpm | libapache2-mod-php)
|
||||
- pdo and pdo_sqlite support in php (deb: php-db, php-sqlite3) uncomment in php.ini: ;extension=pdo_sqlite
|
||||
- php-curl module
|
||||
|
||||
## installation
|
||||
|
||||
|
|
|
@ -1,13 +1,36 @@
|
|||
<?php
|
||||
|
||||
/**
|
||||
* class Agent
|
||||
*
|
||||
* Provides methods to interact with Jilo agents, including retrieving details, managing agents, generating JWT tokens,
|
||||
* and fetching data from agent APIs.
|
||||
*/
|
||||
class Agent {
|
||||
/**
|
||||
* @var PDO|null $db The database connection instance.
|
||||
*/
|
||||
private $db;
|
||||
|
||||
/**
|
||||
* Agent constructor.
|
||||
* Initializes the database connection.
|
||||
*
|
||||
* @param object $database The database object to initialize the connection.
|
||||
*/
|
||||
public function __construct($database) {
|
||||
$this->db = $database->getConnection();
|
||||
}
|
||||
|
||||
// get details of a specified agent ID (or all) in a specified platform ID
|
||||
|
||||
/**
|
||||
* Retrieves details of a specified agent ID (or all agents) in a specified platform.
|
||||
*
|
||||
* @param int $platform_id The platform ID to filter agents by.
|
||||
* @param int $agent_id The agent ID to filter by. If empty, all agents are returned.
|
||||
*
|
||||
* @return array The list of agent details.
|
||||
*/
|
||||
public function getAgentDetails($platform_id, $agent_id = '') {
|
||||
$sql = 'SELECT
|
||||
ja.id,
|
||||
|
@ -15,6 +38,7 @@ class Agent {
|
|||
ja.agent_type_id,
|
||||
ja.url,
|
||||
ja.secret_key,
|
||||
ja.check_period,
|
||||
jat.description AS agent_description,
|
||||
jat.endpoint AS agent_endpoint
|
||||
FROM
|
||||
|
@ -40,7 +64,44 @@ class Agent {
|
|||
return $query->fetchAll(PDO::FETCH_ASSOC);
|
||||
}
|
||||
|
||||
// get agent types
|
||||
|
||||
/**
|
||||
* Retrieves details of a specified agent by its agent ID.
|
||||
*
|
||||
* @param int $agent_id The agent ID to filter by.
|
||||
*
|
||||
* @return array The agent details.
|
||||
*/
|
||||
public function getAgentIDDetails($agent_id) {
|
||||
$sql = 'SELECT
|
||||
ja.id,
|
||||
ja.platform_id,
|
||||
ja.agent_type_id,
|
||||
ja.url,
|
||||
ja.secret_key,
|
||||
ja.check_period,
|
||||
jat.description AS agent_description,
|
||||
jat.endpoint AS agent_endpoint
|
||||
FROM
|
||||
jilo_agents ja
|
||||
JOIN
|
||||
jilo_agent_types jat ON ja.agent_type_id = jat.id
|
||||
WHERE
|
||||
ja.id = :agent_id';
|
||||
|
||||
$query = $this->db->prepare($sql);
|
||||
$query->bindParam(':agent_id', $agent_id);
|
||||
$query->execute();
|
||||
|
||||
return $query->fetchAll(PDO::FETCH_ASSOC);
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Retrieves all agent types.
|
||||
*
|
||||
* @return array List of all agent types.
|
||||
*/
|
||||
public function getAgentTypes() {
|
||||
$sql = 'SELECT *
|
||||
FROM jilo_agent_types
|
||||
|
@ -51,13 +112,44 @@ class Agent {
|
|||
return $query->fetchAll(PDO::FETCH_ASSOC);
|
||||
}
|
||||
|
||||
// add new agent
|
||||
|
||||
/**
|
||||
* Retrieves agent types already configured for a specific platform.
|
||||
*
|
||||
* @param int $platform_id The platform ID to filter agents by.
|
||||
*
|
||||
* @return array List of agent types configured for the platform.
|
||||
*/
|
||||
public function getPlatformAgentTypes($platform_id) {
|
||||
$sql = 'SELECT
|
||||
id,
|
||||
agent_type_id
|
||||
FROM
|
||||
jilo_agents
|
||||
WHERE
|
||||
platform_id = :platform_id';
|
||||
$query = $this->db->prepare($sql);
|
||||
$query->bindParam(':platform_id', $platform_id);
|
||||
$query->execute();
|
||||
|
||||
return $query->fetchAll(PDO::FETCH_ASSOC);
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Adds a new agent to the platform.
|
||||
*
|
||||
* @param int $platform_id The platform ID where the agent is to be added.
|
||||
* @param array $newAgent The new agent details to add.
|
||||
*
|
||||
* @return bool|string Returns true on success or an error message on failure.
|
||||
*/
|
||||
public function addAgent($platform_id, $newAgent) {
|
||||
try {
|
||||
$sql = 'INSERT INTO jilo_agents
|
||||
(platform_id, agent_type_id, url, secret_key)
|
||||
(platform_id, agent_type_id, url, secret_key, check_period)
|
||||
VALUES
|
||||
(:platform_id, :agent_type_id, :url, :secret_key)';
|
||||
(:platform_id, :agent_type_id, :url, :secret_key, :check_period)';
|
||||
|
||||
$query = $this->db->prepare($sql);
|
||||
$query->execute([
|
||||
|
@ -65,6 +157,7 @@ class Agent {
|
|||
':agent_type_id' => $newAgent['type_id'],
|
||||
':url' => $newAgent['url'],
|
||||
':secret_key' => $newAgent['secret_key'],
|
||||
':check_period' => $newAgent['check_period'],
|
||||
]);
|
||||
|
||||
return true;
|
||||
|
@ -74,13 +167,22 @@ class Agent {
|
|||
}
|
||||
}
|
||||
|
||||
// edit an existing agent
|
||||
|
||||
/**
|
||||
* Edits an existing agent's details.
|
||||
*
|
||||
* @param int $platform_id The platform ID where the agent exists.
|
||||
* @param array $updatedAgent The updated agent details.
|
||||
*
|
||||
* @return bool|string Returns true on success or an error message on failure.
|
||||
*/
|
||||
public function editAgent($platform_id, $updatedAgent) {
|
||||
try {
|
||||
$sql = 'UPDATE jilo_agents SET
|
||||
agent_type_id = :agent_type_id,
|
||||
url = :url,
|
||||
secret_key = :secret_key
|
||||
secret_key = :secret_key,
|
||||
check_period = :check_period
|
||||
WHERE
|
||||
id = :agent_id
|
||||
AND
|
||||
|
@ -91,6 +193,7 @@ class Agent {
|
|||
':agent_type_id' => $updatedAgent['agent_type_id'],
|
||||
':url' => $updatedAgent['url'],
|
||||
':secret_key' => $updatedAgent['secret_key'],
|
||||
':check_period' => $updatedAgent['check_period'],
|
||||
':agent_id' => $updatedAgent['id'],
|
||||
':platform_id' => $platform_id,
|
||||
]);
|
||||
|
@ -103,7 +206,13 @@ class Agent {
|
|||
}
|
||||
|
||||
|
||||
// delete an agent
|
||||
/**
|
||||
* Deletes an agent from the platform.
|
||||
*
|
||||
* @param int $agent_id The agent ID to delete.
|
||||
*
|
||||
* @return bool|string Returns true on success or an error message on failure.
|
||||
*/
|
||||
public function deleteAgent($agent_id) {
|
||||
try {
|
||||
$sql = 'DELETE FROM jilo_agents
|
||||
|
@ -122,7 +231,13 @@ class Agent {
|
|||
}
|
||||
|
||||
|
||||
// check for agent cache
|
||||
/**
|
||||
* Checks if the agent cache is still valid.
|
||||
*
|
||||
* @param int $agent_id The agent ID to check.
|
||||
*
|
||||
* @return bool Returns true if cache is valid, false otherwise.
|
||||
*/
|
||||
public function checkAgentCache($agent_id) {
|
||||
$agent_cache_name = 'agent' . $agent_id . '_cache';
|
||||
$agent_cache_time = 'agent' . $agent_id . '_time';
|
||||
|
@ -130,13 +245,26 @@ class Agent {
|
|||
}
|
||||
|
||||
|
||||
// method for base64 URL encoding for JWT tokens
|
||||
/**
|
||||
* Base64 URL encodes the input data. Used for encoding JWT tokens
|
||||
*
|
||||
* @param string $data The data to encode.
|
||||
*
|
||||
* @return string The base64 URL encoded string.
|
||||
*/
|
||||
private function base64UrlEncode($data) {
|
||||
return rtrim(strtr(base64_encode($data), '+/', '-_'), '=');
|
||||
}
|
||||
|
||||
|
||||
// generate a JWT token for jilo agent
|
||||
/**
|
||||
* Generates a JWT token for a Jilo agent.
|
||||
*
|
||||
* @param array $payload The payload data to include in the token.
|
||||
* @param string $secret_key The secret key used to sign the token.
|
||||
*
|
||||
* @return string The generated JWT token.
|
||||
*/
|
||||
public function generateAgentToken($payload, $secret_key) {
|
||||
|
||||
// header
|
||||
|
@ -161,61 +289,282 @@ class Agent {
|
|||
}
|
||||
|
||||
|
||||
// fetch result from jilo agent API
|
||||
/**
|
||||
* Fetches data from a Jilo agent's API, optionally forcing a refresh of the cache.
|
||||
*
|
||||
* @param int $agent_id The agent ID to fetch data for.
|
||||
* @param bool $force Whether to force-refresh the cache (default: false).
|
||||
*
|
||||
* @return string The API response, or an error message in JSON format.
|
||||
*/
|
||||
public function fetchAgent($agent_id, $force = false) {
|
||||
|
||||
// we need agent details for URL and JWT token
|
||||
$agent = $this->getAgentDetails($agent_id);
|
||||
$agentDetails = $this->getAgentIDDetails($agent_id);
|
||||
|
||||
// Safe exit in case the agent is not found
|
||||
if (empty($agentDetails)) {
|
||||
return json_encode(['error' => 'Agent not found']);
|
||||
}
|
||||
|
||||
$agent = $agentDetails[0];
|
||||
$agent_cache_name = 'agent' . $agent_id . '_cache';
|
||||
$agent_cache_time = 'agent' . $agent_id . '_time';
|
||||
|
||||
// check if the cache is still valid, unless force-refresh is requested
|
||||
if (!$force && this->checkAgentCache($agent_id)) {
|
||||
if (!$force && $this->checkAgentCache($agent_id)) {
|
||||
return $_SESSION[$agent_cache_name];
|
||||
}
|
||||
|
||||
// generate the JWT token
|
||||
$payload = [
|
||||
'agent_id' => $agent_id,
|
||||
'timestamp' => time()
|
||||
];
|
||||
$jwt = $this->generateAgentToken($payload, $agent['secret_key']);
|
||||
|
||||
// Make the API request
|
||||
$ch = curl_init();
|
||||
curl_setopt($ch, CURLOPT_URL, $agent[0]['url']);
|
||||
curl_setopt($ch, CURLOPT_URL, $agent['url'] . $agent['agent_endpoint']);
|
||||
curl_setopt($ch, CURLOPT_RETURNTRANSFER, true);
|
||||
curl_setopt($ch, CURLOPT_TIMEOUT, 10); // timeout 10 seconds
|
||||
curl_setopt($ch, CURLOPT_HTTPHEADER, [
|
||||
'Authorization: Bearer ' . $jwt,
|
||||
'Content-Type: application/json'
|
||||
]);
|
||||
|
||||
$response = curl_exec($ch);
|
||||
$curl_error = curl_error($ch);
|
||||
$curl_errno = curl_errno($ch);
|
||||
$http_code = curl_getinfo($ch, CURLINFO_HTTP_CODE);
|
||||
|
||||
curl_close($ch);
|
||||
|
||||
// general curl error
|
||||
if ($curl_error) {
|
||||
// curl error
|
||||
if ($curl_errno) {
|
||||
return json_encode(['error' => 'curl error: ' . $curl_error]);
|
||||
}
|
||||
|
||||
// response is not 200 OK
|
||||
if ($http_code !== 200) {
|
||||
return json_encode(['error' => 'HTTP error: ' . $http_code]);
|
||||
}
|
||||
|
||||
// other custom error(s)
|
||||
if (strpos($response, 'Auth header not received') !== false) {
|
||||
return json_encode(['error' => 'Auth header not received']);
|
||||
}
|
||||
|
||||
// Cache the result and the timestamp if the response is successful
|
||||
$_SESSION[$agent_cache_name] = $response;
|
||||
// We decode it so that it's pure JSON and not escaped
|
||||
$_SESSION[$agent_cache_name] = json_decode($response, true);
|
||||
$_SESSION[$agent_cache_time] = time();
|
||||
|
||||
return $response;
|
||||
}
|
||||
|
||||
|
||||
// clear agent cache
|
||||
/**
|
||||
* Clears the cached data for a specific agent.
|
||||
*
|
||||
* @param int $agent_id The agent ID for which the cache should be cleared.
|
||||
*/
|
||||
public function clearAgentCache($agent_id) {
|
||||
$_SESSION["agent{$agent_id}_cache"] = '';
|
||||
$_SESSION["agent{$agent_id}_cache_time"] = '';
|
||||
}
|
||||
|
||||
|
||||
// get latest stored jilo agents data
|
||||
public function getLatestData($platform_id, $agent_type, $metric_type) {
|
||||
// retrieves data already stored in db from another function (or the jilo-server to-be)
|
||||
/**
|
||||
* Gets a value from a nested array using dot notation
|
||||
* e.g. "bridge_selector.bridge_count" will get $array['bridge_selector']['bridge_count']
|
||||
*
|
||||
* @param array $array The array to search in
|
||||
* @param string $path The path in dot notation
|
||||
* @return mixed|null The value if found, null otherwise
|
||||
*/
|
||||
private function getNestedValue($array, $path) {
|
||||
$keys = explode('.', $path);
|
||||
$value = $array;
|
||||
|
||||
foreach ($keys as $key) {
|
||||
if (!isset($value[$key])) {
|
||||
return null;
|
||||
}
|
||||
$value = $value[$key];
|
||||
}
|
||||
|
||||
return $value;
|
||||
}
|
||||
|
||||
/**
|
||||
* Retrieves the latest stored data for a specific platform, agent type, and metric type.
|
||||
*
|
||||
* @param int $platform_id The platform ID.
|
||||
* @param string $agent_type The agent type.
|
||||
* @param string $metric_type The metric type to filter by.
|
||||
*
|
||||
* @return mixed The latest stored data.
|
||||
*/
|
||||
public function getLatestData($platform_id, $agent_type, $metric_type) {
|
||||
$sql = 'SELECT
|
||||
jac.timestamp,
|
||||
jac.response_content,
|
||||
jac.agent_id,
|
||||
jat.description
|
||||
FROM
|
||||
jilo_agent_checks jac
|
||||
JOIN
|
||||
jilo_agents ja ON jac.agent_id = ja.id
|
||||
JOIN
|
||||
jilo_agent_types jat ON ja.agent_type_id = jat.id
|
||||
WHERE
|
||||
ja.platform_id = :platform_id
|
||||
AND jat.description = :agent_type
|
||||
AND jac.status_code = 200
|
||||
ORDER BY
|
||||
jac.timestamp DESC
|
||||
LIMIT 1';
|
||||
|
||||
$query = $this->db->prepare($sql);
|
||||
$query->execute([
|
||||
':platform_id' => $platform_id,
|
||||
':agent_type' => $agent_type
|
||||
]);
|
||||
|
||||
$result = $query->fetch(PDO::FETCH_ASSOC);
|
||||
|
||||
if ($result) {
|
||||
// Parse the JSON response content
|
||||
$data = json_decode($result['response_content'], true);
|
||||
if (json_last_error() !== JSON_ERROR_NONE) {
|
||||
return null;
|
||||
}
|
||||
|
||||
// Extract the specific metric value from the response based on agent type
|
||||
if ($agent_type === 'jvb') {
|
||||
$value = $this->getNestedValue($data['jvb_api_data'], $metric_type);
|
||||
if ($value !== null) {
|
||||
return [
|
||||
'value' => $value,
|
||||
'timestamp' => $result['timestamp']
|
||||
];
|
||||
}
|
||||
|
||||
} elseif ($agent_type === 'jicofo') {
|
||||
$value = $this->getNestedValue($data['jicofo_api_data'], $metric_type);
|
||||
if ($value !== null) {
|
||||
return [
|
||||
'value' => $value,
|
||||
'timestamp' => $result['timestamp']
|
||||
];
|
||||
}
|
||||
|
||||
} elseif ($agent_type === 'jigasi') {
|
||||
$value = $this->getNestedValue($data['jigasi_api_data'], $metric_type);
|
||||
if ($value !== null) {
|
||||
return [
|
||||
'value' => $value,
|
||||
'timestamp' => $result['timestamp']
|
||||
];
|
||||
}
|
||||
|
||||
} elseif ($agent_type === 'prosody') {
|
||||
$value = $this->getNestedValue($data['prosody_api_data'], $metric_type);
|
||||
if ($value !== null) {
|
||||
return [
|
||||
'value' => $value,
|
||||
'timestamp' => $result['timestamp']
|
||||
];
|
||||
}
|
||||
|
||||
} elseif ($agent_type === 'nginx') {
|
||||
$value = $this->getNestedValue($data['nginx_api_data'], $metric_type);
|
||||
if ($value !== null) {
|
||||
return [
|
||||
'value' => $value,
|
||||
'timestamp' => $result['timestamp']
|
||||
];
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
}
|
||||
|
||||
return null;
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets historical data for a specific metric from agent checks
|
||||
*
|
||||
* @param int $platform_id The platform ID
|
||||
* @param string $agent_type The type of agent (e.g., 'jvb', 'jicofo')
|
||||
* @param string $metric_type The type of metric to retrieve
|
||||
* @param string $from_time Start time in Y-m-d format
|
||||
* @param string $until_time End time in Y-m-d format
|
||||
* @return array Array with the dataset from agent checks
|
||||
*/
|
||||
public function getHistoricalData($platform_id, $agent_type, $metric_type, $from_time, $until_time) {
|
||||
// Get data from agent checks
|
||||
$sql = 'SELECT
|
||||
DATE(jac.timestamp) as date,
|
||||
jac.response_content,
|
||||
COUNT(*) as checks_count
|
||||
FROM
|
||||
jilo_agent_checks jac
|
||||
JOIN
|
||||
jilo_agents ja ON jac.agent_id = ja.id
|
||||
JOIN
|
||||
jilo_agent_types jat ON ja.agent_type_id = jat.id
|
||||
WHERE
|
||||
ja.platform_id = :platform_id
|
||||
AND jat.description = :agent_type
|
||||
AND jac.status_code = 200
|
||||
AND DATE(jac.timestamp) BETWEEN :from_time AND :until_time
|
||||
GROUP BY
|
||||
DATE(jac.timestamp)
|
||||
ORDER BY
|
||||
DATE(jac.timestamp)';
|
||||
|
||||
$query = $this->db->prepare($sql);
|
||||
$query->execute([
|
||||
':platform_id' => $platform_id,
|
||||
':agent_type' => $agent_type,
|
||||
':from_time' => $from_time,
|
||||
':until_time' => $until_time
|
||||
]);
|
||||
|
||||
$results = $query->fetchAll(PDO::FETCH_ASSOC);
|
||||
|
||||
$data = [];
|
||||
foreach ($results as $row) {
|
||||
$json_data = json_decode($row['response_content'], true);
|
||||
if (json_last_error() === JSON_ERROR_NONE) {
|
||||
$api_data = [];
|
||||
if ($agent_type === 'jvb') {
|
||||
$api_data = $json_data['jvb_api_data'] ?? [];
|
||||
} elseif ($agent_type === 'jicofo') {
|
||||
$api_data = $json_data['jicofo_api_data'] ?? [];
|
||||
} elseif ($agent_type === 'jigasi') {
|
||||
$api_data = $json_data['jigasi_api_data'] ?? [];
|
||||
} elseif ($agent_type === 'prosody') {
|
||||
$api_data = $json_data['prosody_api_data'] ?? [];
|
||||
} elseif ($agent_type === 'nginx') {
|
||||
$api_data = $json_data['nginx_api_data'] ?? [];
|
||||
}
|
||||
|
||||
$value = $this->getNestedValue($api_data, $metric_type);
|
||||
if ($value !== null) {
|
||||
$data[] = [
|
||||
'date' => $row['date'],
|
||||
'value' => $value
|
||||
];
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return $data;
|
||||
}
|
||||
}
|
||||
|
||||
?>
|
||||
|
|
|
@ -1,14 +1,40 @@
|
|||
<?php
|
||||
|
||||
/**
|
||||
* class Component
|
||||
*
|
||||
* Provides methods to interact with Jitsi component events in the database.
|
||||
*/
|
||||
class Component {
|
||||
/**
|
||||
* @var PDO|null $db The database connection instance.
|
||||
*/
|
||||
private $db;
|
||||
|
||||
/**
|
||||
* Component constructor.
|
||||
* Initializes the database connection.
|
||||
*
|
||||
* @param object $database The database object to initialize the connection.
|
||||
*/
|
||||
public function __construct($database) {
|
||||
$this->db = $database->getConnection();
|
||||
}
|
||||
|
||||
|
||||
// list of component events
|
||||
/**
|
||||
* Retrieves Jitsi component events based on various filters.
|
||||
*
|
||||
* @param string $jitsi_component The Jitsi component name.
|
||||
* @param int $component_id The component ID.
|
||||
* @param string $event_type The type of event to filter by.
|
||||
* @param string $from_time The start date in 'YYYY-MM-DD' format.
|
||||
* @param string $until_time The end date in 'YYYY-MM-DD' format.
|
||||
* @param int $offset The offset for pagination.
|
||||
* @param int $items_per_page The number of items to retrieve per page.
|
||||
*
|
||||
* @return array The list of Jitsi component events or an empty array if no results.
|
||||
*/
|
||||
public function jitsiComponents($jitsi_component, $component_id, $event_type, $from_time, $until_time, $offset=0, $items_per_page='') {
|
||||
|
||||
// time period drill-down
|
||||
|
@ -63,7 +89,6 @@ ORDER BY
|
|||
return $query->fetchAll(PDO::FETCH_ASSOC);
|
||||
}
|
||||
|
||||
|
||||
}
|
||||
|
||||
?>
|
||||
|
|
|
@ -1,14 +1,38 @@
|
|||
<?php
|
||||
|
||||
/**
|
||||
* class Conference
|
||||
*
|
||||
* Provides methods for querying conference-related data from the database.
|
||||
*/
|
||||
class Conference {
|
||||
/**
|
||||
* @var PDO|null $db The database connection instance.
|
||||
*/
|
||||
private $db;
|
||||
|
||||
/**
|
||||
* Conference constructor.
|
||||
* Initializes the database connection.
|
||||
*
|
||||
* @param object $database The database object to initialize the connection.
|
||||
*/
|
||||
public function __construct($database) {
|
||||
$this->db = $database->getConnection();
|
||||
}
|
||||
|
||||
|
||||
// search/list specific conference ID
|
||||
/**
|
||||
* Retrieves conference data by conference ID within a specific time range.
|
||||
*
|
||||
* @param string $conference_id The conference ID.
|
||||
* @param string $from_time The start date in 'YYYY-MM-DD' format.
|
||||
* @param string $until_time The end date in 'YYYY-MM-DD' format.
|
||||
* @param int $offset The offset for pagination.
|
||||
* @param int $items_per_page The number of items to retrieve per page.
|
||||
*
|
||||
* @return array The list of conference events or an empty array if no results.
|
||||
*/
|
||||
public function conferenceById($conference_id, $from_time, $until_time, $offset=0, $items_per_page='') {
|
||||
|
||||
// time period drill-down
|
||||
|
@ -83,7 +107,17 @@ ORDER BY
|
|||
}
|
||||
|
||||
|
||||
// search/list specific conference name
|
||||
/**
|
||||
* Retrieves conference data by conference name within a specific time range.
|
||||
*
|
||||
* @param string $conference_name The conference name.
|
||||
* @param string $from_time The start date in 'YYYY-MM-DD' format.
|
||||
* @param string $until_time The end date in 'YYYY-MM-DD' format.
|
||||
* @param int $offset The offset for pagination.
|
||||
* @param int $items_per_page The number of items to retrieve per page.
|
||||
*
|
||||
* @return array The list of conference events or an empty array if no results.
|
||||
*/
|
||||
public function conferenceByName($conference_name, $from_time, $until_time, $offset=0, $items_per_page='') {
|
||||
|
||||
// time period drill-down
|
||||
|
@ -158,7 +192,16 @@ ORDER BY
|
|||
}
|
||||
|
||||
|
||||
// list of all conferences
|
||||
/**
|
||||
* Retrieves all conferences within a specific time range, formatted.
|
||||
*
|
||||
* @param string $from_time The start date in 'YYYY-MM-DD' format.
|
||||
* @param string $until_time The end date in 'YYYY-MM-DD' format.
|
||||
* @param int $offset The offset for pagination.
|
||||
* @param int $items_per_page The number of items to retrieve per page.
|
||||
*
|
||||
* @return array The list of formatted conference data or an empty array if no results.
|
||||
*/
|
||||
public function conferencesAllFormatted($from_time, $until_time, $offset=0, $items_per_page='') {
|
||||
|
||||
// time period drill-down
|
||||
|
@ -257,7 +300,15 @@ ORDER BY
|
|||
return $query->fetchAll(PDO::FETCH_ASSOC);
|
||||
}
|
||||
|
||||
// number of conferences
|
||||
|
||||
/**
|
||||
* Retrieves the number of conferences within a specific time range.
|
||||
*
|
||||
* @param string $from_time The start date in 'YYYY-MM-DD' format.
|
||||
* @param string $until_time The end date in 'YYYY-MM-DD' format.
|
||||
*
|
||||
* @return int The number of conferences found.
|
||||
*/
|
||||
public function conferenceNumber($from_time, $until_time) {
|
||||
|
||||
// time period drill-down
|
||||
|
|
|
@ -1,8 +1,64 @@
|
|||
<?php
|
||||
|
||||
/**
|
||||
* class Config
|
||||
*
|
||||
* Handles editing and fetching configuration files.
|
||||
*/
|
||||
class Config {
|
||||
|
||||
// loading the config.js
|
||||
/**
|
||||
* Edits a configuration file by updating specified options.
|
||||
*
|
||||
* @param array $updatedConfig Key-value pairs of configuration options to update.
|
||||
* @param string $config_file Path to the configuration file.
|
||||
*
|
||||
* @return mixed Returns true on success, or an error message on failure.
|
||||
*/
|
||||
public function editConfigFile($updatedConfig, $config_file) {
|
||||
// first we get a fresh config file contents as text
|
||||
$config_contents = file_get_contents($config_file);
|
||||
if (!$config_contents) {
|
||||
return "Failed to read the config file \"$config_file\".";
|
||||
}
|
||||
|
||||
// loop through the variables and updated them
|
||||
foreach ($updatedConfig as $key => $newValue) {
|
||||
// we look for 'option' => value
|
||||
// option is always in single quotes
|
||||
// value is without quotes, because it could be true/false
|
||||
$pattern = "/(['\"]{$key}['\"]\s*=>\s*)([^,]+),/";
|
||||
|
||||
// prepare the value, make booleans w/out single quotes
|
||||
if ($newValue === 'true') {
|
||||
$replacementValue = 'true';
|
||||
} elseif ($newValue === 'false') {
|
||||
$replacementValue = 'false';
|
||||
} else {
|
||||
$replacementValue = var_export($newValue, true);
|
||||
}
|
||||
|
||||
// value replacing
|
||||
$config_contents = preg_replace($pattern, "$1{$replacementValue},", $config_contents);
|
||||
}
|
||||
|
||||
// write the new config file
|
||||
if (!file_put_contents($config_file, $config_contents)) {
|
||||
return "Failed to write the config file \"$config_file\".";
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Loads the config.js file from the Jitsi server.
|
||||
*
|
||||
* @param string $jitsiUrl The base URL of the Jitsi server.
|
||||
* @param bool $raw Whether to return the full file (true) or only uncommented values (false).
|
||||
*
|
||||
* @return string The content of the config.js file or an error message.
|
||||
*/
|
||||
public function getPlatformConfigjs($jitsiUrl, $raw = false) {
|
||||
// constructing the URL
|
||||
$configjsFile = $jitsiUrl . '/config.js';
|
||||
|
@ -44,7 +100,14 @@ class Config {
|
|||
}
|
||||
|
||||
|
||||
// loading the interface_config.js
|
||||
/**
|
||||
* Loads the interface_config.js file from the Jitsi server.
|
||||
*
|
||||
* @param string $jitsiUrl The base URL of the Jitsi server.
|
||||
* @param bool $raw Whether to return the full file (true) or only uncommented values (false).
|
||||
*
|
||||
* @return string The content of the interface_config.js file or an error message.
|
||||
*/
|
||||
public function getPlatformInterfaceConfigjs($jitsiUrl, $raw = false) {
|
||||
// constructing the URL
|
||||
$interfaceConfigjsFile = $jitsiUrl . '/interface_config.js';
|
||||
|
@ -85,7 +148,6 @@ class Config {
|
|||
|
||||
}
|
||||
|
||||
|
||||
}
|
||||
|
||||
?>
|
||||
|
|
|
@ -1,10 +1,33 @@
|
|||
<?php
|
||||
|
||||
/**
|
||||
* class Database
|
||||
*
|
||||
* Manages database connections for SQLite and MySQL (or MariaDB).
|
||||
*/
|
||||
class Database {
|
||||
/**
|
||||
* @var PDO|null $pdo The database connection instance.
|
||||
*/
|
||||
private $pdo;
|
||||
|
||||
/**
|
||||
* Database constructor.
|
||||
* Initializes the database connection based on provided options.
|
||||
*
|
||||
* @param array $options An associative array with database connection options:
|
||||
* - type: The database type ('sqlite', 'mysql', or 'mariadb').
|
||||
* - dbFile: The path to the SQLite database file (required for SQLite).
|
||||
* - host: The database host (required for MySQL).
|
||||
* - port: The port for MySQL (optional, default: 3306).
|
||||
* - dbname: The name of the MySQL database (required for MySQL).
|
||||
* - user: The username for MySQL (required for MySQL).
|
||||
* - password: The password for MySQL (optional).
|
||||
*
|
||||
* @throws Exception If required extensions are not loaded or options are invalid.
|
||||
*/
|
||||
public function __construct($options) {
|
||||
// pdo needed
|
||||
// check if PDO extension is loaded
|
||||
if ( !extension_loaded('pdo') ) {
|
||||
$error = getError('PDO extension not loaded.');
|
||||
}
|
||||
|
@ -14,7 +37,7 @@ class Database {
|
|||
$error = getError('Database type is not set.');
|
||||
}
|
||||
|
||||
// database type
|
||||
// connect based on database type
|
||||
switch ($options['type']) {
|
||||
case 'sqlite':
|
||||
$this->connectSqlite($options);
|
||||
|
@ -27,6 +50,15 @@ class Database {
|
|||
}
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Establishes a connection to a SQLite database.
|
||||
*
|
||||
* @param array $options An associative array with SQLite connection options:
|
||||
* - dbFile: The path to the SQLite database file.
|
||||
*
|
||||
* @throws Exception If the SQLite PDO extension is not loaded or the database file is missing.
|
||||
*/
|
||||
private function connectSqlite($options) {
|
||||
// pdo_sqlite extension is needed
|
||||
if (!extension_loaded('pdo_sqlite')) {
|
||||
|
@ -35,7 +67,7 @@ class Database {
|
|||
|
||||
// SQLite options
|
||||
if (empty($options['dbFile']) || !file_exists($options['dbFile'])) {
|
||||
$error = getError("SQLite database file \"{$dbFile}\" not found.");
|
||||
$error = getError("SQLite database file \"{$options['dbFile']}\" not found.");
|
||||
}
|
||||
|
||||
// connect to SQLite
|
||||
|
@ -49,6 +81,19 @@ class Database {
|
|||
}
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Establishes a connection to a MySQL (or MariaDB) database.
|
||||
*
|
||||
* @param array $options An associative array with MySQL connection options:
|
||||
* - host: The database host.
|
||||
* - port: The database port (default: 3306).
|
||||
* - dbname: The name of the database.
|
||||
* - user: The database username.
|
||||
* - password: The database password (optional).
|
||||
*
|
||||
* @throws Exception If the MySQL PDO extension is not loaded or required options are missing.
|
||||
*/
|
||||
private function connectMysql($options) {
|
||||
// pdo_mysql extension is needed
|
||||
if (!extension_loaded('pdo_mysql')) {
|
||||
|
@ -66,10 +111,16 @@ class Database {
|
|||
$this->pdo = new PDO($dsn, $options['user'], $options['password'] ?? '');
|
||||
$this->pdo->setAttribute(PDO::ATTR_ERRMODE, PDO::ERRMODE_EXCEPTION);
|
||||
} catch (PDOException $e) {
|
||||
$error = getError('MySQL connection failed: ', $e->getMessage(), $config['environment']);
|
||||
$error = getError('MySQL connection failed: ', $e->getMessage());
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Retrieves the current PDO connection instance.
|
||||
*
|
||||
* @return PDO|null The PDO instance or null if no connection is established.
|
||||
*/
|
||||
public function getConnection() {
|
||||
return $this->pdo;
|
||||
}
|
||||
|
|
|
@ -0,0 +1,155 @@
|
|||
<?php
|
||||
|
||||
/**
|
||||
* class Host
|
||||
*
|
||||
* Manages the hosts in the database, providing methods to retrieve, add, edit, and delete host entries.
|
||||
*/
|
||||
class Host {
|
||||
/**
|
||||
* @var PDO|null $db The database connection instance.
|
||||
*/
|
||||
private $db;
|
||||
|
||||
/**
|
||||
* Host constructor.
|
||||
* Initializes the database connection.
|
||||
*
|
||||
* @param object $database The database object to initialize the connection.
|
||||
*/
|
||||
public function __construct($database) {
|
||||
$this->db = $database->getConnection();
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Get details of a specified host ID (or all hosts) in a specified platform ID.
|
||||
*
|
||||
* @param string $platform_id The platform ID to filter the hosts by (optional).
|
||||
* @param string $host_id The host ID to filter the details (optional).
|
||||
*
|
||||
* @return array The details of the host(s) in the form of an associative array.
|
||||
*/
|
||||
public function getHostDetails($platform_id = '', $host_id = '') {
|
||||
$sql = 'SELECT
|
||||
id,
|
||||
address,
|
||||
port,
|
||||
platform_id,
|
||||
name
|
||||
FROM
|
||||
hosts';
|
||||
|
||||
if ($platform_id !== '' && $host_id !== '') {
|
||||
$sql .= ' WHERE platform_id = :platform_id AND id = :host_id';
|
||||
} elseif ($platform_id !== '') {
|
||||
$sql .= ' WHERE platform_id = :platform_id';
|
||||
} elseif ($host_id !== '') {
|
||||
$sql .= ' WHERE id = :host_id';
|
||||
}
|
||||
|
||||
$query = $this->db->prepare($sql);
|
||||
|
||||
if ($platform_id !== '') {
|
||||
$query->bindParam(':platform_id', $platform_id);
|
||||
}
|
||||
if ($host_id !== '') {
|
||||
$query->bindParam(':host_id', $host_id);
|
||||
}
|
||||
|
||||
$query->execute();
|
||||
|
||||
return $query->fetchAll(PDO::FETCH_ASSOC);
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Add a new host to the database.
|
||||
*
|
||||
* @param array $newHost An associative array containing the details of the host to be added.
|
||||
*
|
||||
* @return bool True if the host was added successfully, otherwise false.
|
||||
*/
|
||||
public function addHost($newHost) {
|
||||
try {
|
||||
$sql = 'INSERT INTO hosts
|
||||
(address, port, platform_id, name)
|
||||
VALUES
|
||||
(:address, :port, :platform_id, :name)';
|
||||
|
||||
$query = $this->db->prepare($sql);
|
||||
$query->execute([
|
||||
':address' => $newHost['address'],
|
||||
':port' => $newHost['port'],
|
||||
':platform_id' => $newHost['platform_id'],
|
||||
':name' => $newHost['name'],
|
||||
]);
|
||||
|
||||
return true;
|
||||
|
||||
} catch (Exception $e) {
|
||||
return $e->getMessage();
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Edit an existing host in the database.
|
||||
*
|
||||
* @param string $platform_id The platform ID to which the host belongs.
|
||||
* @param array $updatedHost An associative array containing the updated details of the host.
|
||||
*
|
||||
* @return bool True if the host was updated successfully, otherwise false.
|
||||
*/
|
||||
public function editHost($platform_id, $updatedHost) {
|
||||
try {
|
||||
$sql = 'UPDATE hosts SET
|
||||
address = :address,
|
||||
port = :port,
|
||||
name = :name
|
||||
WHERE
|
||||
id = :id';
|
||||
|
||||
$query = $this->db->prepare($sql);
|
||||
$query->execute([
|
||||
':id' => $updatedHost['id'],
|
||||
':address' => $updatedHost['address'],
|
||||
':port' => $updatedHost['port'],
|
||||
':name' => $updatedHost['name'],
|
||||
]);
|
||||
|
||||
return true;
|
||||
|
||||
} catch (Exception $e) {
|
||||
return $e->getMessage();
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Delete a host from the database.
|
||||
*
|
||||
* @param int $host_id The ID of the host to be deleted.
|
||||
*
|
||||
* @return bool True if the host was deleted successfully, otherwise false.
|
||||
*/
|
||||
public function deleteHost($host_id) {
|
||||
try {
|
||||
$sql = 'DELETE FROM hosts
|
||||
WHERE
|
||||
id = :host_id';
|
||||
|
||||
$query = $this->db->prepare($sql);
|
||||
$query->bindParam(':host_id', $host_id);
|
||||
|
||||
$query->execute();
|
||||
return true;
|
||||
|
||||
} catch (Exception $e) {
|
||||
return $e->getMessage();
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
?>
|
|
@ -1,13 +1,36 @@
|
|||
<?php
|
||||
|
||||
/**
|
||||
* class Log
|
||||
*
|
||||
* Handles logging events into a database and reading log entries.
|
||||
*/
|
||||
class Log {
|
||||
/**
|
||||
* @var PDO|null $db The database connection instance.
|
||||
*/
|
||||
private $db;
|
||||
|
||||
/**
|
||||
* Logs constructor.
|
||||
* Initializes the database connection.
|
||||
*
|
||||
* @param object $database The database object to initialize the connection.
|
||||
*/
|
||||
public function __construct($database) {
|
||||
$this->db = $database->getConnection();
|
||||
}
|
||||
|
||||
// insert log event
|
||||
|
||||
/**
|
||||
* Insert a log event into the database.
|
||||
*
|
||||
* @param int $user_id The ID of the user associated with the log event.
|
||||
* @param string $message The log message to insert.
|
||||
* @param string $scope The scope of the log event (e.g., 'user', 'system'). Default is 'user'.
|
||||
*
|
||||
* @return bool|string True on success, or an error message on failure.
|
||||
*/
|
||||
public function insertLog($user_id, $message, $scope='user') {
|
||||
try {
|
||||
$sql = 'INSERT INTO logs
|
||||
|
@ -29,7 +52,17 @@ class Log {
|
|||
}
|
||||
}
|
||||
|
||||
// read logs
|
||||
|
||||
/**
|
||||
* Retrieve log entries from the database.
|
||||
*
|
||||
* @param int $user_id The ID of the user whose logs are being retrieved.
|
||||
* @param string $scope The scope of the logs ('user' or 'system').
|
||||
* @param int $offset The offset for pagination. Default is 0.
|
||||
* @param int $items_per_page The number of log entries to retrieve per page. Default is no limit.
|
||||
*
|
||||
* @return array An array of log entries.
|
||||
*/
|
||||
public function readLog($user_id, $scope, $offset=0, $items_per_page='') {
|
||||
if ($scope === 'user') {
|
||||
$sql = 'SELECT * FROM logs WHERE user_id = :user_id ORDER BY time DESC';
|
||||
|
|
|
@ -0,0 +1,178 @@
|
|||
<?php
|
||||
|
||||
class Messages {
|
||||
// Message types
|
||||
const TYPE_SUCCESS = 'success';
|
||||
const TYPE_ERROR = 'danger';
|
||||
const TYPE_INFO = 'info';
|
||||
const TYPE_WARNING = 'warning';
|
||||
|
||||
// Default message configurations
|
||||
const NOTICE = [
|
||||
'DEFAULT' => [
|
||||
'type' => self::TYPE_INFO,
|
||||
'dismissible' => true
|
||||
]
|
||||
];
|
||||
|
||||
const ERROR = [
|
||||
'DEFAULT' => [
|
||||
'type' => self::TYPE_ERROR,
|
||||
'dismissible' => false
|
||||
]
|
||||
];
|
||||
|
||||
const LOGIN = [
|
||||
'LOGIN_SUCCESS' => [
|
||||
'type' => self::TYPE_SUCCESS,
|
||||
'dismissible' => true
|
||||
],
|
||||
'LOGIN_FAILED' => [
|
||||
'type' => self::TYPE_ERROR,
|
||||
'dismissible' => false
|
||||
],
|
||||
'LOGOUT_SUCCESS' => [
|
||||
'type' => self::TYPE_SUCCESS,
|
||||
'dismissible' => true
|
||||
],
|
||||
'IP_BLACKLISTED' => [
|
||||
'type' => self::TYPE_ERROR,
|
||||
'dismissible' => false
|
||||
],
|
||||
'IP_NOT_WHITELISTED' => [
|
||||
'type' => self::TYPE_ERROR,
|
||||
'dismissible' => false
|
||||
],
|
||||
'TOO_MANY_ATTEMPTS' => [
|
||||
'type' => self::TYPE_ERROR,
|
||||
'dismissible' => false
|
||||
]
|
||||
];
|
||||
|
||||
const SECURITY = [
|
||||
'WHITELIST_ADD_SUCCESS' => [
|
||||
'type' => self::TYPE_SUCCESS,
|
||||
'dismissible' => true
|
||||
],
|
||||
'WHITELIST_ADD_ERROR' => [
|
||||
'type' => self::TYPE_ERROR,
|
||||
'dismissible' => true
|
||||
],
|
||||
'WHITELIST_REMOVE_SUCCESS' => [
|
||||
'type' => self::TYPE_SUCCESS,
|
||||
'dismissible' => true
|
||||
],
|
||||
'WHITELIST_REMOVE_ERROR' => [
|
||||
'type' => self::TYPE_ERROR,
|
||||
'dismissible' => true
|
||||
],
|
||||
'BLACKLIST_ADD_SUCCESS' => [
|
||||
'type' => self::TYPE_SUCCESS,
|
||||
'dismissible' => true
|
||||
],
|
||||
'BLACKLIST_ADD_ERROR' => [
|
||||
'type' => self::TYPE_ERROR,
|
||||
'dismissible' => true
|
||||
],
|
||||
'BLACKLIST_REMOVE_SUCCESS' => [
|
||||
'type' => self::TYPE_SUCCESS,
|
||||
'dismissible' => true
|
||||
],
|
||||
'BLACKLIST_REMOVE_ERROR' => [
|
||||
'type' => self::TYPE_ERROR,
|
||||
'dismissible' => true
|
||||
],
|
||||
'RATE_LIMIT_INFO' => [
|
||||
'type' => self::TYPE_INFO,
|
||||
'dismissible' => false
|
||||
],
|
||||
'PERMISSION_DENIED' => [
|
||||
'type' => self::TYPE_ERROR,
|
||||
'dismissible' => false
|
||||
],
|
||||
'IP_REQUIRED' => [
|
||||
'type' => self::TYPE_ERROR,
|
||||
'dismissible' => false
|
||||
]
|
||||
];
|
||||
|
||||
private static $strings = null;
|
||||
|
||||
/**
|
||||
* Get message strings
|
||||
*/
|
||||
private static function getStrings() {
|
||||
if (self::$strings === null) {
|
||||
self::$strings = require __DIR__ . '/../includes/messages-strings.php';
|
||||
}
|
||||
return self::$strings;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get message configuration by key
|
||||
*/
|
||||
public static function get($category, $key) {
|
||||
$config = constant("self::$category")[$key] ?? null;
|
||||
if (!$config) return null;
|
||||
|
||||
$strings = self::getStrings();
|
||||
$message = $strings[$category][$key] ?? '';
|
||||
|
||||
return array_merge($config, ['message' => $message]);
|
||||
}
|
||||
|
||||
/**
|
||||
* Render message HTML
|
||||
*/
|
||||
// Usage: echo Messages::render('LOGIN', 'LOGIN_SUCCESS', 'custom message [or null]', true [for dismissible; or null], true [for small; or omit]);
|
||||
public static function render($category, $key, $customMessage = null, $dismissible = null, $small = false, $sanitize = true) {
|
||||
$config = self::get($category, $key);
|
||||
if (!$config) return '';
|
||||
|
||||
$message = $customMessage ?? $config['message'];
|
||||
$isDismissible = $dismissible ?? $config['dismissible'] ?? false;
|
||||
$dismissClass = $isDismissible ? ' alert-dismissible fade show' : '';
|
||||
$dismissButton = $isDismissible ? '<button type="button" class="btn-close' . ($small ? ' btn-close-sm' : '') . '" data-bs-dismiss="alert" aria-label="Close"></button>' : '';
|
||||
$smallClass = $small ? ' alert-sm' : '';
|
||||
|
||||
return sprintf(
|
||||
'<div class="alert alert-%s%s%s" role="alert">%s%s</div>',
|
||||
$config['type'],
|
||||
$dismissClass,
|
||||
$smallClass,
|
||||
$sanitize ? htmlspecialchars($message) : $message,
|
||||
$dismissButton
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* Store message in session for display after redirect
|
||||
*/
|
||||
// Usage: Messages::flash('LOGIN', 'LOGIN_SUCCESS', 'custom message [or null]', true [for dismissible; or null], true [for small; or omit]);
|
||||
public static function flash($category, $key, $customMessage = null, $dismissible = null, $small = false) {
|
||||
if (!isset($_SESSION['flash_messages'])) {
|
||||
$_SESSION['flash_messages'] = [];
|
||||
}
|
||||
|
||||
// Get the message configuration
|
||||
$config = self::get($category, $key);
|
||||
$isDismissible = $dismissible ?? $config['dismissible'] ?? false;
|
||||
|
||||
$_SESSION['flash_messages'][] = [
|
||||
'category' => $category,
|
||||
'key' => $key,
|
||||
'custom_message' => $customMessage,
|
||||
'dismissible' => $isDismissible,
|
||||
'small' => $small
|
||||
];
|
||||
}
|
||||
|
||||
/**
|
||||
* Get and clear all flash messages
|
||||
*/
|
||||
public static function getFlash() {
|
||||
$messages = $_SESSION['flash_messages'] ?? [];
|
||||
unset($_SESSION['flash_messages']);
|
||||
return $messages;
|
||||
}
|
||||
}
|
|
@ -1,14 +1,39 @@
|
|||
<?php
|
||||
|
||||
/**
|
||||
* class Participant
|
||||
*
|
||||
* This class provides methods to retrieve information about participants and their related conference data.
|
||||
* It supports querying participant details by ID, name, or IP, as well as listing all participants and counting them within a specific time frame.
|
||||
*/
|
||||
class Participant {
|
||||
/**
|
||||
* @var PDO|null $db The database connection instance.
|
||||
*/
|
||||
private $db;
|
||||
|
||||
/**
|
||||
* Constructor
|
||||
* Initializes the database connection.
|
||||
*
|
||||
* @param object $database The database object to initialize the connection.
|
||||
*/
|
||||
public function __construct($database) {
|
||||
$this->db = $database->getConnection();
|
||||
}
|
||||
|
||||
|
||||
// search/list specific participant ID
|
||||
/**
|
||||
* Retrieve conferences by participant ID within a specified time period.
|
||||
*
|
||||
* @param string $participant_id The participant's ID (endpoint_id).
|
||||
* @param string $from_time The start date (format: 'YYYY-MM-DD'). Defaults to '0000-01-01' if empty.
|
||||
* @param string $until_time The end date (format: 'YYYY-MM-DD'). Defaults to '9999-12-31' if empty.
|
||||
* @param int $offset The offset for pagination.
|
||||
* @param int $items_per_page The number of items per page for pagination.
|
||||
*
|
||||
* @return array List of conferences involving the specified participant ID.
|
||||
*/
|
||||
public function conferenceByParticipantId($participant_id, $from_time, $until_time, $offset=0, $items_per_page='') {
|
||||
|
||||
// time period drill-down
|
||||
|
@ -83,7 +108,17 @@ ORDER BY
|
|||
}
|
||||
|
||||
|
||||
// search/list specific participant name (stats_id)
|
||||
/**
|
||||
* Retrieve conferences by participant name within a specified time period.
|
||||
*
|
||||
* @param string $participant_name The participant's name (stats_id).
|
||||
* @param string $from_time The start date (format: 'YYYY-MM-DD'). Defaults to '0000-01-01' if empty.
|
||||
* @param string $until_time The end date (format: 'YYYY-MM-DD'). Defaults to '9999-12-31' if empty.
|
||||
* @param int $offset The offset for pagination.
|
||||
* @param int $items_per_page The number of items per page for pagination.
|
||||
*
|
||||
* @return array List of conferences involving the specified participant name.
|
||||
*/
|
||||
public function conferenceByParticipantName($participant_name, $from_time, $until_time, $offset=0, $items_per_page='') {
|
||||
|
||||
// time period drill-down
|
||||
|
@ -158,7 +193,17 @@ ORDER BY
|
|||
}
|
||||
|
||||
|
||||
// search/list specific participant IP
|
||||
/**
|
||||
* Retrieve conferences by participant IP within a specified time period.
|
||||
*
|
||||
* @param string $participant_ip The participant's IP address.
|
||||
* @param string $from_time The start date (format: 'YYYY-MM-DD'). Defaults to '0000-01-01' if empty.
|
||||
* @param string $until_time The end date (format: 'YYYY-MM-DD'). Defaults to '9999-12-31' if empty.
|
||||
* @param int $offset The offset for pagination.
|
||||
* @param int $items_per_page The number of items per page for pagination.
|
||||
*
|
||||
* @return array List of conferences involving the specified participant IP.
|
||||
*/
|
||||
public function conferenceByParticipantIP($participant_ip, $from_time, $until_time, $offset=0, $items_per_page='') {
|
||||
|
||||
// time period drill-down
|
||||
|
@ -233,7 +278,16 @@ ORDER BY
|
|||
}
|
||||
|
||||
|
||||
// list of all participants
|
||||
/**
|
||||
* Retrieve a list of all participants within a specified time period.
|
||||
*
|
||||
* @param string $from_time The start date (format: 'YYYY-MM-DD'). Defaults to '0000-01-01' if empty.
|
||||
* @param string $until_time The end date (format: 'YYYY-MM-DD'). Defaults to '9999-12-31' if empty.
|
||||
* @param int $offset The offset for pagination.
|
||||
* @param int $items_per_page The number of items per page for pagination.
|
||||
*
|
||||
* @return array List of all participants.
|
||||
*/
|
||||
public function participantsAll($from_time, $until_time, $offset=0, $items_per_page='') {
|
||||
|
||||
// time period drill-down
|
||||
|
@ -274,7 +328,15 @@ ORDER BY p.id";
|
|||
return $query->fetchAll(PDO::FETCH_ASSOC);
|
||||
}
|
||||
|
||||
// number of participants
|
||||
|
||||
/**
|
||||
* Count the number of participants within a specified time period.
|
||||
*
|
||||
* @param string $from_time The start date (format: 'YYYY-MM-DD'). Defaults to '0000-01-01' if empty.
|
||||
* @param string $until_time The end date (format: 'YYYY-MM-DD'). Defaults to '9999-12-31' if empty.
|
||||
*
|
||||
* @return int The number of participants.
|
||||
*/
|
||||
public function participantNumber($from_time, $until_time) {
|
||||
|
||||
// time period drill-down
|
||||
|
@ -309,7 +371,6 @@ AND pe.event_type = 'participant joining'";
|
|||
return $query->fetchAll(PDO::FETCH_ASSOC);
|
||||
}
|
||||
|
||||
|
||||
}
|
||||
|
||||
?>
|
||||
|
|
|
@ -1,13 +1,34 @@
|
|||
<?php
|
||||
|
||||
/**
|
||||
* class Platform
|
||||
*
|
||||
* Handles platform management in the database, including retrieving, adding, editing, and deleting platforms.
|
||||
*/
|
||||
class Platform {
|
||||
/**
|
||||
* @var PDO|null $db The database connection instance.
|
||||
*/
|
||||
private $db;
|
||||
|
||||
/**
|
||||
* Platform constructor.
|
||||
* Initializes the database connection.
|
||||
*
|
||||
* @param object $database The database object to initialize the connection.
|
||||
*/
|
||||
public function __construct($database) {
|
||||
$this->db = $database->getConnection();
|
||||
}
|
||||
|
||||
// get details of a specified platform ID (or all)
|
||||
|
||||
/**
|
||||
* Retrieve details of a specific platform or all platforms.
|
||||
*
|
||||
* @param string $platform_id The ID of the platform to retrieve details for (optional).
|
||||
*
|
||||
* @return array An associative array containing platform details.
|
||||
*/
|
||||
public function getPlatformDetails($platform_id = '') {
|
||||
$sql = 'SELECT * FROM platforms';
|
||||
if ($platform_id !== '') {
|
||||
|
@ -23,7 +44,17 @@ class Platform {
|
|||
return $query->fetchAll(PDO::FETCH_ASSOC);
|
||||
}
|
||||
|
||||
// add new platform
|
||||
|
||||
/**
|
||||
* Add a new platform to the database.
|
||||
*
|
||||
* @param array $newPlatform An associative array containing the details of the new platform:
|
||||
* - `name` (string): The name of the platform.
|
||||
* - `jitsi_url` (string): The URL for the Jitsi integration.
|
||||
* - `jilo_database` (string): The database name for Jilo integration.
|
||||
*
|
||||
* @return bool|string True if the platform was added successfully, or an error message on failure.
|
||||
*/
|
||||
public function addPlatform($newPlatform) {
|
||||
try {
|
||||
$sql = 'INSERT INTO platforms
|
||||
|
@ -45,7 +76,18 @@ class Platform {
|
|||
}
|
||||
}
|
||||
|
||||
// edit an existing platform
|
||||
|
||||
/**
|
||||
* Edit an existing platform in the database.
|
||||
*
|
||||
* @param int $platform_id The ID of the platform to update.
|
||||
* @param array $updatedPlatform An associative array containing the updated platform details:
|
||||
* - `name` (string): The updated name of the platform.
|
||||
* - `jitsi_url` (string): The updated Jitsi URL.
|
||||
* - `jilo_database` (string): The updated Jilo database name.
|
||||
*
|
||||
* @return bool|string True if the platform was updated successfully, or an error message on failure.
|
||||
*/
|
||||
public function editPlatform($platform_id, $updatedPlatform) {
|
||||
try {
|
||||
$sql = 'UPDATE platforms SET
|
||||
|
@ -70,7 +112,14 @@ class Platform {
|
|||
}
|
||||
}
|
||||
|
||||
// delete a platform
|
||||
|
||||
/**
|
||||
* Delete a platform from the database.
|
||||
*
|
||||
* @param int $platform_id The ID of the platform to delete.
|
||||
*
|
||||
* @return bool|string True if the platform was deleted successfully, or an error message on failure.
|
||||
*/
|
||||
public function deletePlatform($platform_id) {
|
||||
try {
|
||||
$sql = 'DELETE FROM platforms
|
||||
|
@ -88,7 +137,6 @@ class Platform {
|
|||
}
|
||||
}
|
||||
|
||||
|
||||
}
|
||||
|
||||
?>
|
||||
|
|
|
@ -0,0 +1,457 @@
|
|||
<?php
|
||||
|
||||
class RateLimiter {
|
||||
public $db;
|
||||
private $log;
|
||||
public $maxAttempts = 5; // Maximum login attempts
|
||||
public $decayMinutes = 15; // Time window in minutes
|
||||
public $autoBlacklistThreshold = 10; // Attempts before auto-blacklist
|
||||
public $autoBlacklistDuration = 24; // Hours to blacklist for
|
||||
public $ratelimitTable = 'login_attempts';
|
||||
public $whitelistTable = 'ip_whitelist';
|
||||
public $blacklistTable = 'ip_blacklist';
|
||||
|
||||
public function __construct($database) {
|
||||
$this->db = $database->getConnection();
|
||||
$this->log = new Log($database);
|
||||
$this->createTablesIfNotExist();
|
||||
}
|
||||
|
||||
// Database preparation
|
||||
private function createTablesIfNotExist() {
|
||||
// Login attempts table
|
||||
$sql = "CREATE TABLE IF NOT EXISTS {$this->ratelimitTable} (
|
||||
id INTEGER PRIMARY KEY AUTOINCREMENT,
|
||||
ip_address TEXT NOT NULL UNIQUE,
|
||||
username TEXT NOT NULL,
|
||||
attempted_at TEXT DEFAULT (DATETIME('now'))
|
||||
)";
|
||||
$this->db->exec($sql);
|
||||
|
||||
// IP whitelist table
|
||||
$sql = "CREATE TABLE IF NOT EXISTS {$this->whitelistTable} (
|
||||
id INTEGER PRIMARY KEY AUTOINCREMENT,
|
||||
ip_address TEXT NOT NULL UNIQUE,
|
||||
is_network BOOLEAN DEFAULT 0 CHECK(is_network IN (0,1)),
|
||||
description TEXT,
|
||||
created_at TEXT DEFAULT (DATETIME('now')),
|
||||
created_by TEXT
|
||||
)";
|
||||
$this->db->exec($sql);
|
||||
|
||||
// IP blacklist table
|
||||
$sql = "CREATE TABLE IF NOT EXISTS {$this->blacklistTable} (
|
||||
id INTEGER PRIMARY KEY AUTOINCREMENT,
|
||||
ip_address TEXT NOT NULL UNIQUE,
|
||||
is_network BOOLEAN DEFAULT 0 CHECK(is_network IN (0,1)),
|
||||
reason TEXT,
|
||||
expiry_time TEXT NULL,
|
||||
created_at TEXT DEFAULT (DATETIME('now')),
|
||||
created_by TEXT
|
||||
)";
|
||||
$this->db->exec($sql);
|
||||
|
||||
// Default IPs to whitelist (local interface and private networks IPs)
|
||||
$defaultIps = [
|
||||
['127.0.0.1', false, 'localhost IPv4'],
|
||||
['::1', false, 'localhost IPv6'],
|
||||
['10.0.0.0/8', true, 'Private network (Class A)'],
|
||||
['172.16.0.0/12', true, 'Private network (Class B)'],
|
||||
['192.168.0.0/16', true, 'Private network (Class C)']
|
||||
];
|
||||
|
||||
// Insert default whitelisted IPs if they don't exist
|
||||
$stmt = $this->db->prepare("INSERT OR IGNORE INTO {$this->whitelistTable}
|
||||
(ip_address, is_network, description, created_by)
|
||||
VALUES (?, ?, ?, 'system')");
|
||||
foreach ($defaultIps as $ip) {
|
||||
$stmt->execute([$ip[0], $ip[1], $ip[2]]);
|
||||
}
|
||||
|
||||
// Insert known malicious networks
|
||||
$defaultBlacklist = [
|
||||
['0.0.0.0/8', true, 'Reserved address space - RFC 1122'],
|
||||
['100.64.0.0/10', true, 'Carrier-grade NAT space - RFC 6598'],
|
||||
['192.0.2.0/24', true, 'TEST-NET-1 Documentation space - RFC 5737'],
|
||||
['198.51.100.0/24', true, 'TEST-NET-2 Documentation space - RFC 5737'],
|
||||
['203.0.113.0/24', true, 'TEST-NET-3 Documentation space - RFC 5737']
|
||||
];
|
||||
|
||||
$stmt = $this->db->prepare("INSERT OR IGNORE INTO {$this->blacklistTable}
|
||||
(ip_address, is_network, reason, created_by)
|
||||
VALUES (?, ?, ?, 'system')");
|
||||
|
||||
foreach ($defaultBlacklist as $ip) {
|
||||
$stmt->execute([$ip[0], $ip[1], $ip[2]]);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
/**
|
||||
* Get number of recent login attempts for an IP
|
||||
*/
|
||||
public function getRecentAttempts($ip) {
|
||||
$stmt = $this->db->prepare("SELECT COUNT(*) as attempts FROM {$this->ratelimitTable}
|
||||
WHERE ip_address = ? AND attempted_at > datetime('now', '-' || :minutes || ' minutes')");
|
||||
$stmt->execute([$ip, $this->decayMinutes]);
|
||||
$result = $stmt->fetch(PDO::FETCH_ASSOC);
|
||||
return intval($result['attempts']);
|
||||
}
|
||||
|
||||
/**
|
||||
* Check if an IP is blacklisted
|
||||
*/
|
||||
public function isIpBlacklisted($ip) {
|
||||
// First check if IP is explicitly blacklisted or in a blacklisted range
|
||||
$stmt = $this->db->prepare("SELECT ip_address, is_network, expiry_time FROM {$this->blacklistTable}");
|
||||
$stmt->execute();
|
||||
|
||||
while ($row = $stmt->fetch(PDO::FETCH_ASSOC)) {
|
||||
// Skip expired entries
|
||||
if ($row['expiry_time'] !== null && strtotime($row['expiry_time']) < time()) {
|
||||
continue;
|
||||
}
|
||||
|
||||
if ($row['is_network']) {
|
||||
if ($this->ipInRange($ip, $row['ip_address'])) {
|
||||
return true;
|
||||
}
|
||||
} else {
|
||||
if ($ip === $row['ip_address']) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
/**
|
||||
* Check if an IP is whitelisted
|
||||
*/
|
||||
public function isIpWhitelisted($ip) {
|
||||
// Check exact IP match and CIDR ranges
|
||||
$stmt = $this->db->prepare("SELECT ip_address, is_network FROM {$this->whitelistTable}");
|
||||
$stmt->execute();
|
||||
|
||||
while ($row = $stmt->fetch(PDO::FETCH_ASSOC)) {
|
||||
if ($row['is_network']) {
|
||||
if ($this->ipInRange($ip, $row['ip_address'])) {
|
||||
return true;
|
||||
}
|
||||
} else {
|
||||
if ($ip === $row['ip_address']) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
private function ipInRange($ip, $cidr) {
|
||||
list($subnet, $bits) = explode('/', $cidr);
|
||||
|
||||
$ip = ip2long($ip);
|
||||
$subnet = ip2long($subnet);
|
||||
$mask = -1 << (32 - $bits);
|
||||
$subnet &= $mask;
|
||||
|
||||
return ($ip & $mask) == $subnet;
|
||||
}
|
||||
|
||||
// Add to whitelist
|
||||
public function addToWhitelist($ip, $isNetwork = false, $description = '', $createdBy = 'system', $userId = null) {
|
||||
try {
|
||||
// Check if IP is blacklisted first
|
||||
if ($this->isIpBlacklisted($ip)) {
|
||||
$message = "Cannot whitelist {$ip} - IP is currently blacklisted";
|
||||
if ($userId) {
|
||||
$this->log->insertLog($userId, "IP Whitelist: {$message}", 'system');
|
||||
Messages::flash('ERROR', 'DEFAULT', $message);
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
$stmt = $this->db->prepare("INSERT OR REPLACE INTO {$this->whitelistTable}
|
||||
(ip_address, is_network, description, created_by)
|
||||
VALUES (?, ?, ?, ?)");
|
||||
|
||||
$result = $stmt->execute([$ip, $isNetwork, $description, $createdBy]);
|
||||
|
||||
if ($result) {
|
||||
$logMessage = sprintf(
|
||||
'IP Whitelist: Added %s "%s" by %s. Description: %s',
|
||||
$isNetwork ? 'network' : 'IP',
|
||||
$ip,
|
||||
$createdBy,
|
||||
$description
|
||||
);
|
||||
$this->log->insertLog($userId ?? 0, $logMessage, 'system');
|
||||
}
|
||||
|
||||
return $result;
|
||||
|
||||
} catch (Exception $e) {
|
||||
if ($userId) {
|
||||
$this->log->insertLog($userId, "IP Whitelist: Failed to add {$ip}: " . $e->getMessage(), 'system');
|
||||
Messages::flash('ERROR', 'DEFAULT', "IP Whitelist: Failed to add {$ip}: " . $e->getMessage());
|
||||
}
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
// Remove from whitelist
|
||||
public function removeFromWhitelist($ip, $removedBy = 'system', $userId = null) {
|
||||
try {
|
||||
// Get IP details before removal for logging
|
||||
$stmt = $this->db->prepare("SELECT * FROM {$this->whitelistTable} WHERE ip_address = ?");
|
||||
$stmt->execute([$ip]);
|
||||
$ipDetails = $stmt->fetch(PDO::FETCH_ASSOC);
|
||||
|
||||
// Remove the IP
|
||||
$stmt = $this->db->prepare("DELETE FROM {$this->whitelistTable} WHERE ip_address = ?");
|
||||
|
||||
$result = $stmt->execute([$ip]);
|
||||
|
||||
if ($result && $ipDetails) {
|
||||
$logMessage = sprintf(
|
||||
'IP Whitelist: Removed %s "%s" by %s. Was added by: %s',
|
||||
$ipDetails['is_network'] ? 'network' : 'IP',
|
||||
$ip,
|
||||
$removedBy,
|
||||
$ipDetails['created_by']
|
||||
);
|
||||
$this->log->insertLog($userId ?? 0, $logMessage, 'system');
|
||||
}
|
||||
|
||||
return $result;
|
||||
|
||||
} catch (Exception $e) {
|
||||
if ($userId) {
|
||||
$this->log->insertLog($userId, "IP Whitelist: Failed to remove {$ip}: " . $e->getMessage(), 'system');
|
||||
Messages::flash('ERROR', 'DEFAULT', "IP Whitelist: Failed to remove {$ip}: " . $e->getMessage());
|
||||
}
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
public function addToBlacklist($ip, $isNetwork = false, $reason = '', $createdBy = 'system', $userId = null, $expiryHours = null) {
|
||||
try {
|
||||
// Check if IP is whitelisted first
|
||||
if ($this->isIpWhitelisted($ip)) {
|
||||
$message = "Cannot blacklist {$ip} - IP is currently whitelisted";
|
||||
if ($userId) {
|
||||
$this->log->insertLog($userId, "IP Blacklist: {$message}", 'system');
|
||||
Messages::flash('ERROR', 'DEFAULT', $message);
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
$expiryTime = $expiryHours ? date('Y-m-d H:i:s', strtotime("+{$expiryHours} hours")) : null;
|
||||
|
||||
$stmt = $this->db->prepare("INSERT OR REPLACE INTO {$this->blacklistTable}
|
||||
(ip_address, is_network, reason, expiry_time, created_by)
|
||||
VALUES (?, ?, ?, ?, ?)");
|
||||
|
||||
$result = $stmt->execute([$ip, $isNetwork, $reason, $expiryTime, $createdBy]);
|
||||
|
||||
if ($result) {
|
||||
$logMessage = sprintf(
|
||||
'IP Blacklist: Added %s "%s" by %s. Reason: %s. Expires: %s',
|
||||
$isNetwork ? 'network' : 'IP',
|
||||
$ip,
|
||||
$createdBy,
|
||||
$reason,
|
||||
$expiryTime ?? 'never'
|
||||
);
|
||||
$this->log->insertLog($userId ?? 0, $logMessage, 'system');
|
||||
}
|
||||
|
||||
return $result;
|
||||
} catch (Exception $e) {
|
||||
if ($userId) {
|
||||
$this->log->insertLog($userId, "IP Blacklist: Failed to add {$ip}: " . $e->getMessage(), 'system');
|
||||
Messages::flash('ERROR', 'DEFAULT', "IP Blacklist: Failed to add {$ip}: " . $e->getMessage());
|
||||
}
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
public function removeFromBlacklist($ip, $removedBy = 'system', $userId = null) {
|
||||
try {
|
||||
// Get IP details before removal for logging
|
||||
$stmt = $this->db->prepare("SELECT * FROM {$this->blacklistTable} WHERE ip_address = ?");
|
||||
$stmt->execute([$ip]);
|
||||
$ipDetails = $stmt->fetch(PDO::FETCH_ASSOC);
|
||||
|
||||
// Remove the IP
|
||||
$stmt = $this->db->prepare("DELETE FROM {$this->blacklistTable} WHERE ip_address = ?");
|
||||
$result = $stmt->execute([$ip]);
|
||||
|
||||
if ($result && $ipDetails) {
|
||||
$logMessage = sprintf(
|
||||
'IP Blacklist: Removed %s "%s" by %s. Was added by: %s. Reason was: %s',
|
||||
$ipDetails['is_network'] ? 'network' : 'IP',
|
||||
$ip,
|
||||
$removedBy,
|
||||
$ipDetails['created_by'],
|
||||
$ipDetails['reason']
|
||||
);
|
||||
$this->log->insertLog($userId ?? 0, $logMessage, 'system');
|
||||
}
|
||||
|
||||
return $result;
|
||||
} catch (Exception $e) {
|
||||
if ($userId) {
|
||||
$this->log->insertLog($userId, "IP Blacklist: Failed to remove {$ip}: " . $e->getMessage(), 'system');
|
||||
Messages::flash('ERROR', 'DEFAULT', "IP Blacklist: Failed to remove {$ip}: " . $e->getMessage());
|
||||
}
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
public function getWhitelistedIps() {
|
||||
$stmt = $this->db->prepare("SELECT * FROM {$this->whitelistTable} ORDER BY created_at DESC");
|
||||
$stmt->execute();
|
||||
|
||||
return $stmt->fetchAll(PDO::FETCH_ASSOC);
|
||||
}
|
||||
|
||||
public function getBlacklistedIps() {
|
||||
$stmt = $this->db->prepare("SELECT * FROM {$this->blacklistTable} ORDER BY created_at DESC");
|
||||
$stmt->execute();
|
||||
|
||||
return $stmt->fetchAll(PDO::FETCH_ASSOC);
|
||||
}
|
||||
|
||||
public function cleanupExpiredEntries() {
|
||||
try {
|
||||
// Remove expired blacklist entries
|
||||
$stmt = $this->db->prepare("DELETE FROM {$this->blacklistTable}
|
||||
WHERE expiry_time IS NOT NULL AND expiry_time < datetime('now')");
|
||||
$stmt->execute();
|
||||
|
||||
// Clean old login attempts
|
||||
$stmt = $this->db->prepare("DELETE FROM {$this->ratelimitTable}
|
||||
WHERE attempted_at < datetime('now', '-' || :minutes || ' minutes')");
|
||||
$stmt->execute([':minutes' => $this->decayMinutes]);
|
||||
|
||||
return true;
|
||||
} catch (Exception $e) {
|
||||
$this->log->insertLog(0, "Failed to cleanup expired entries: " . $e->getMessage(), 'system');
|
||||
Messages::flash('ERROR', 'DEFAULT', "Failed to cleanup expired entries: " . $e->getMessage());
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
public function isAllowed($username, $ipAddress) {
|
||||
// First check if IP is blacklisted
|
||||
if ($this->isIpBlacklisted($ipAddress)) {
|
||||
return false;
|
||||
}
|
||||
|
||||
// Then check if IP is whitelisted
|
||||
if ($this->isIpWhitelisted($ipAddress)) {
|
||||
return true;
|
||||
}
|
||||
|
||||
// Clean old attempts
|
||||
$this->clearOldAttempts();
|
||||
|
||||
// Check if we've hit the rate limit
|
||||
if ($this->tooManyAttempts($username, $ipAddress)) {
|
||||
return false;
|
||||
}
|
||||
|
||||
// Check total attempts across all usernames from this IP
|
||||
$sql = "SELECT COUNT(*) as total_attempts
|
||||
FROM {$this->ratelimitTable}
|
||||
WHERE ip_address = :ip
|
||||
AND attempted_at > datetime('now', '-' || :minutes || ' minutes')";
|
||||
$stmt = $this->db->prepare($sql);
|
||||
$stmt->execute([
|
||||
':ip' => $ipAddress,
|
||||
':minutes' => $this->decayMinutes
|
||||
]);
|
||||
$result = $stmt->fetch(PDO::FETCH_ASSOC);
|
||||
|
||||
// Check if we would hit auto-blacklist threshold
|
||||
return $result['total_attempts'] < $this->autoBlacklistThreshold;
|
||||
}
|
||||
|
||||
public function attempt($username, $ipAddress) {
|
||||
// Record this attempt
|
||||
$sql = "INSERT INTO {$this->ratelimitTable} (ip_address, username) VALUES (:ip, :username)";
|
||||
$stmt = $this->db->prepare($sql);
|
||||
$stmt->execute([
|
||||
':ip' => $ipAddress,
|
||||
':username' => $username
|
||||
]);
|
||||
|
||||
// Auto-blacklist if too many attempts
|
||||
if (!$this->isAllowed($username, $ipAddress)) {
|
||||
$this->addToBlacklist(
|
||||
$ipAddress,
|
||||
false,
|
||||
'Auto-blacklisted due to excessive login attempts',
|
||||
'system',
|
||||
null,
|
||||
$this->autoBlacklistDuration
|
||||
);
|
||||
return false;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
public function tooManyAttempts($username, $ipAddress) {
|
||||
$sql = "SELECT COUNT(*) as attempts
|
||||
FROM {$this->ratelimitTable}
|
||||
WHERE ip_address = :ip
|
||||
AND username = :username
|
||||
AND attempted_at > datetime('now', '-' || :minutes || ' minutes')";
|
||||
|
||||
$stmt = $this->db->prepare($sql);
|
||||
$stmt->execute([
|
||||
':ip' => $ipAddress,
|
||||
':username' => $username,
|
||||
':minutes' => $this->decayMinutes
|
||||
]);
|
||||
|
||||
$result = $stmt->fetch(PDO::FETCH_ASSOC);
|
||||
return $result['attempts'] >= $this->maxAttempts;
|
||||
}
|
||||
|
||||
public function clearOldAttempts() {
|
||||
$sql = "DELETE FROM {$this->ratelimitTable}
|
||||
WHERE attempted_at < datetime('now', '-' || :minutes || ' minutes')";
|
||||
|
||||
$stmt = $this->db->prepare($sql);
|
||||
$stmt->execute([
|
||||
':minutes' => $this->decayMinutes
|
||||
]);
|
||||
}
|
||||
|
||||
public function getRemainingAttempts($username, $ipAddress) {
|
||||
$sql = "SELECT COUNT(*) as attempts
|
||||
FROM {$this->ratelimitTable}
|
||||
WHERE ip_address = :ip
|
||||
AND username = :username
|
||||
AND attempted_at > datetime('now', '-' || :minutes || ' minutes')";
|
||||
|
||||
$stmt = $this->db->prepare($sql);
|
||||
$stmt->execute([
|
||||
':ip' => $ipAddress,
|
||||
':username' => $username,
|
||||
':minutes' => $this->decayMinutes
|
||||
]);
|
||||
|
||||
$result = $stmt->fetch(PDO::FETCH_ASSOC);
|
||||
return max(0, $this->maxAttempts - $result['attempts']);
|
||||
}
|
||||
|
||||
public function getDecayMinutes() {
|
||||
return $this->decayMinutes;
|
||||
}
|
||||
}
|
|
@ -1,20 +1,46 @@
|
|||
<?php
|
||||
|
||||
/**
|
||||
* class Router
|
||||
*
|
||||
* A simple Router class to manage URL-to-callback mapping and dispatch requests to the appropriate controllers and methods.
|
||||
* The class supports defining routes, matching URLs against patterns, and invoking callbacks for matched routes.
|
||||
*/
|
||||
class Router {
|
||||
|
||||
/**
|
||||
* @var array $routes Associative array of route patterns and their corresponding callbacks.
|
||||
*/
|
||||
private $routes = [];
|
||||
|
||||
public function add() {
|
||||
|
||||
/**
|
||||
* Adds a new route to the router.
|
||||
*
|
||||
* @param string $pattern The URL pattern to match (regular expression).
|
||||
* @param string $callback The callback for the route in the format "Controller@Method".
|
||||
*
|
||||
* @return void
|
||||
*/
|
||||
public function add($pattern, $callback) {
|
||||
$this->routes[$pattern] = $callback;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Dispatches a request to the appropriate route callback.
|
||||
*
|
||||
* @param string $url The URL to match against the defined routes.
|
||||
*
|
||||
* @return void Outputs the result of the invoked callback or a 404 error if no route matches.
|
||||
*/
|
||||
public function dispatch($url) {
|
||||
// remove variables from url
|
||||
// remove query string variables from url
|
||||
$url = strtok($url, '?');
|
||||
|
||||
foreach ($this->routes as $pattern => $callback) {
|
||||
// check if the URL matches the current route pattern
|
||||
if (preg_match('#^' . $pattern . '$#', $url, $matches)) {
|
||||
// move any exact match
|
||||
// remove the exact match to extrat parameters
|
||||
array_shift($matches);
|
||||
return $this->invoke($callback, $matches);
|
||||
}
|
||||
|
@ -25,11 +51,34 @@ class Router {
|
|||
echo '404 page not found';
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Invokes the callback for a matched route.
|
||||
*
|
||||
* @param string $callback The callback for the route in the format "Controller@Method".
|
||||
* @param array $params Parameters extracted from the route pattern.
|
||||
*
|
||||
* @return void Executes the specified method on the specified controller with the provided parameters.
|
||||
*
|
||||
* @throws Exception If the controller class or method does not exist.
|
||||
*/
|
||||
private function invoke($callback, $params) {
|
||||
list($controllerName, $methodName) = explode('@', $callback);
|
||||
// $controllerClass = "\\App\\Controllers\\$controllerName";
|
||||
$controllerClass = "../pages/$pageName";
|
||||
$controllerClass = "../pages/$controllerName";
|
||||
|
||||
// ensure the controller class exists
|
||||
if (!class_exists($controllerClass)) {
|
||||
throw new Exception("Controller '$controllerClass' not found.");
|
||||
}
|
||||
|
||||
$controller = new $controllerClass();
|
||||
|
||||
// ensure the method exists on the controller
|
||||
if (!method_exists($controller, $methodName)) {
|
||||
throw new Exception("Method '$methodName' not found in controller '$controllerClass'.");
|
||||
}
|
||||
|
||||
// call the controller's method with the parameters
|
||||
call_user_func_array([$controller, $methodName], $params);
|
||||
}
|
||||
|
||||
|
|
|
@ -0,0 +1,56 @@
|
|||
<?php
|
||||
|
||||
/**
|
||||
* class Server
|
||||
*
|
||||
* Handles server-related operations, including retrieving server status.
|
||||
*/
|
||||
class Server {
|
||||
/**
|
||||
* @var PDO|null $db The database connection instance.
|
||||
*/
|
||||
private $db;
|
||||
|
||||
/**
|
||||
* Server constructor.
|
||||
* Initializes the database connection.
|
||||
*
|
||||
* @param object $database The database object to initialize the connection.
|
||||
*/
|
||||
public function __construct($database) {
|
||||
$this->db = $database->getConnection();
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Checks the status of a Jilo server by sending a GET request to its health endpoint.
|
||||
*
|
||||
* @param string $host The server hostname or IP address (default: '127.0.0.1').
|
||||
* @param int $port The port on which the server is running (default: 8080).
|
||||
* @param string $endpoint The health check endpoint path (default: '/health').
|
||||
*
|
||||
* @return bool True if the server returns a 200 OK status, otherwise false.
|
||||
*/
|
||||
public function getServerStatus($host = '127.0.0.1', $port = 8080, $endpoint = '/health') {
|
||||
$url = "http://$host:$port$endpoint";
|
||||
$options = [
|
||||
'http' => [
|
||||
'method' => 'GET',
|
||||
'timeout' => 3,
|
||||
],
|
||||
];
|
||||
$context = stream_context_create($options);
|
||||
$response = @file_get_contents($url, false, $context);
|
||||
|
||||
// We check the response if it's 200 OK
|
||||
if ($response !== false && isset($http_response_header) && strpos($http_response_header[0], '200 OK') !== false) {
|
||||
return true;
|
||||
}
|
||||
|
||||
// If it's not 200 OK
|
||||
return false;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
?>
|
|
@ -1,13 +1,37 @@
|
|||
<?php
|
||||
|
||||
/**
|
||||
* class User
|
||||
*
|
||||
* Handles user-related functionalities such as registration, login, rights management, and profile updates.
|
||||
*/
|
||||
class User {
|
||||
/**
|
||||
* @var PDO|null $db The database connection instance.
|
||||
*/
|
||||
private $db;
|
||||
private $rateLimiter;
|
||||
|
||||
/**
|
||||
* User constructor.
|
||||
* Initializes the database connection.
|
||||
*
|
||||
* @param object $database The database object to initialize the connection.
|
||||
*/
|
||||
public function __construct($database) {
|
||||
$this->db = $database->getConnection();
|
||||
$this->rateLimiter = new RateLimiter($database);
|
||||
}
|
||||
|
||||
// registration
|
||||
|
||||
/**
|
||||
* Registers a new user.
|
||||
*
|
||||
* @param string $username The username of the new user.
|
||||
* @param string $password The password for the new user.
|
||||
*
|
||||
* @return bool|string True if registration is successful, error message otherwise.
|
||||
*/
|
||||
public function register($username, $password) {
|
||||
try {
|
||||
// we have two inserts, start a transaction
|
||||
|
@ -56,23 +80,53 @@ class User {
|
|||
}
|
||||
}
|
||||
|
||||
// login
|
||||
|
||||
/**
|
||||
* Logs in a user by verifying credentials.
|
||||
*
|
||||
* @param string $username The username of the user.
|
||||
* @param string $password The password of the user.
|
||||
*
|
||||
* @return bool True if login is successful, false otherwise.
|
||||
*/
|
||||
public function login($username, $password) {
|
||||
$query = $this->db->prepare("SELECT * FROM users WHERE username = :username");
|
||||
// get client IP address
|
||||
$ipAddress = $_SERVER['REMOTE_ADDR'] ?? '0.0.0.0';
|
||||
|
||||
// Record attempt
|
||||
$this->rateLimiter->attempt($username, $ipAddress);
|
||||
|
||||
// Check rate limiting first
|
||||
if (!$this->rateLimiter->isAllowed($username, $ipAddress)) {
|
||||
$remainingTime = $this->rateLimiter->getDecayMinutes();
|
||||
throw new Exception("Too many login attempts. Please try again in {$remainingTime} minutes.");
|
||||
}
|
||||
|
||||
// Then check credentials
|
||||
$query = $this->db->prepare("SELECT * FROM users WHERE username = :username");
|
||||
$query->bindParam(':username', $username);
|
||||
$query->execute();
|
||||
|
||||
$user = $query->fetch(PDO::FETCH_ASSOC);
|
||||
if ( $user && password_verify($password, $user['password'])) {
|
||||
if ($user && password_verify($password, $user['password'])) {
|
||||
$_SESSION['user_id'] = $user['id'];
|
||||
$_SESSION['username'] = $user['username'];
|
||||
return true;
|
||||
} else {
|
||||
return false;
|
||||
}
|
||||
|
||||
// Get remaining attempts AFTER this failed attempt
|
||||
$remainingAttempts = $this->rateLimiter->getRemainingAttempts($username, $ipAddress);
|
||||
throw new Exception("Invalid credentials. {$remainingAttempts} attempts remaining.");
|
||||
}
|
||||
|
||||
// get user ID from username
|
||||
|
||||
/**
|
||||
* Retrieves a user ID based on the username.
|
||||
*
|
||||
* @param string $username The username to look up.
|
||||
*
|
||||
* @return array|null User ID details or null if not found.
|
||||
*/
|
||||
// FIXME not used now?
|
||||
public function getUserId($username) {
|
||||
$sql = 'SELECT id FROM users WHERE username = :username';
|
||||
|
@ -85,7 +139,14 @@ class User {
|
|||
|
||||
}
|
||||
|
||||
// get user details
|
||||
|
||||
/**
|
||||
* Fetches user details by user ID.
|
||||
*
|
||||
* @param int $user_id The user ID.
|
||||
*
|
||||
* @return array|null User details or null if not found.
|
||||
*/
|
||||
public function getUserDetails($user_id) {
|
||||
$sql = 'SELECT
|
||||
um.*,
|
||||
|
@ -106,7 +167,15 @@ class User {
|
|||
|
||||
}
|
||||
|
||||
// add user right
|
||||
|
||||
/**
|
||||
* Grants a user a specific right.
|
||||
*
|
||||
* @param int $user_id The user ID.
|
||||
* @param int $right_id The right ID to grant.
|
||||
*
|
||||
* @return void
|
||||
*/
|
||||
public function addUserRight($user_id, $right_id) {
|
||||
$sql = 'INSERT INTO users_rights
|
||||
(user_id, right_id)
|
||||
|
@ -119,7 +188,15 @@ class User {
|
|||
]);
|
||||
}
|
||||
|
||||
// remove user right
|
||||
|
||||
/**
|
||||
* Revokes a specific right from a user.
|
||||
*
|
||||
* @param int $user_id The user ID.
|
||||
* @param int $right_id The right ID to revoke.
|
||||
*
|
||||
* @return void
|
||||
*/
|
||||
public function removeUserRight($user_id, $right_id) {
|
||||
$sql = 'DELETE FROM users_rights
|
||||
WHERE
|
||||
|
@ -133,7 +210,12 @@ class User {
|
|||
]);
|
||||
}
|
||||
|
||||
// get all rights
|
||||
|
||||
/**
|
||||
* Retrieves all rights in the system.
|
||||
*
|
||||
* @return array List of rights.
|
||||
*/
|
||||
public function getAllRights() {
|
||||
$sql = 'SELECT
|
||||
id AS right_id,
|
||||
|
@ -147,7 +229,14 @@ class User {
|
|||
|
||||
}
|
||||
|
||||
// get user rights
|
||||
|
||||
/**
|
||||
* Retrieves the rights assigned to a specific user.
|
||||
*
|
||||
* @param int $user_id The user ID.
|
||||
*
|
||||
* @return array List of user rights.
|
||||
*/
|
||||
public function getUserRights($user_id) {
|
||||
$sql = 'SELECT
|
||||
u.id AS user_id,
|
||||
|
@ -203,7 +292,15 @@ class User {
|
|||
|
||||
}
|
||||
|
||||
// check if the user has a specific right
|
||||
|
||||
/**
|
||||
* Check if the user has a specific right.
|
||||
*
|
||||
* @param int $user_id The user ID.
|
||||
* @param string $right_name The human-readable name of the user right.
|
||||
*
|
||||
* @return bool True if the user has the right, false otherwise.
|
||||
*/
|
||||
function hasRight($user_id, $right_name) {
|
||||
$userRights = $this->getUserRights($user_id);
|
||||
$userHasRight = false;
|
||||
|
@ -224,7 +321,19 @@ class User {
|
|||
|
||||
}
|
||||
|
||||
// update an existing user
|
||||
|
||||
/**
|
||||
* Updates a user's metadata in the database.
|
||||
*
|
||||
* @param int $user_id The ID of the user to update.
|
||||
* @param array $updatedUser An associative array containing updated user data:
|
||||
* - 'name' (string): The updated name of the user.
|
||||
* - 'email' (string): The updated email of the user.
|
||||
* - 'timezone' (string): The updated timezone of the user.
|
||||
* - 'bio' (string): The updated biography of the user.
|
||||
*
|
||||
* @return bool|string Returns true if the update is successful, or an error message if an exception occurs.
|
||||
*/
|
||||
public function editUser($user_id, $updatedUser) {
|
||||
try {
|
||||
$sql = 'UPDATE users_meta SET
|
||||
|
@ -250,7 +359,15 @@ class User {
|
|||
|
||||
}
|
||||
|
||||
// remove an avatar
|
||||
|
||||
/**
|
||||
* Removes a user's avatar from the database and deletes the associated file.
|
||||
*
|
||||
* @param int $user_id The ID of the user whose avatar is being removed.
|
||||
* @param string $old_avatar Optional. The file path of the current avatar to delete. Default is an empty string.
|
||||
*
|
||||
* @return bool|string Returns true if the avatar is successfully removed, or an error message if an exception occurs.
|
||||
*/
|
||||
public function removeAvatar($user_id, $old_avatar = '') {
|
||||
try {
|
||||
// remove from database
|
||||
|
@ -275,7 +392,17 @@ class User {
|
|||
|
||||
}
|
||||
|
||||
// change an avatar
|
||||
|
||||
/**
|
||||
* Updates a user's avatar by uploading a new file and saving its path in the database.
|
||||
*
|
||||
* @param int $user_id The ID of the user whose avatar is being updated.
|
||||
* @param array $avatar_file The uploaded avatar file from the $_FILES array.
|
||||
* Should include 'tmp_name', 'name', 'error', etc.
|
||||
* @param string $avatars_path The directory path where avatar files should be saved.
|
||||
*
|
||||
* @return bool|string Returns true if the avatar is successfully updated, or an error message if an exception occurs.
|
||||
*/
|
||||
public function changeAvatar($user_id, $avatar_file, $avatars_path) {
|
||||
try {
|
||||
// check if the file was uploaded
|
||||
|
|
|
@ -31,7 +31,7 @@ return [
|
|||
// default avatar
|
||||
'default_avatar' => 'static/default_avatar.png',
|
||||
// system info
|
||||
'version' => '0.2.1',
|
||||
'version' => '0.3',
|
||||
// development has verbose error messages, production has not
|
||||
'environment' => 'development',
|
||||
|
||||
|
|
|
@ -1,14 +0,0 @@
|
|||
<?php
|
||||
|
||||
function getError($message, $error = '', $environment = null) {
|
||||
global $config;
|
||||
$environment = $config['environment'] ?? 'production';
|
||||
|
||||
if ($environment === 'production') {
|
||||
return 'There was an unexpected error. Please try again.';
|
||||
} else {
|
||||
return $error ?: $message;
|
||||
}
|
||||
}
|
||||
|
||||
?>
|
|
@ -5,40 +5,30 @@
|
|||
|
||||
<script>
|
||||
var ctx = document.getElementById('graph_<?= $data['graph_name'] ?>').getContext('2d');
|
||||
var chartData0 = <?php echo json_encode($data['data0']); ?>;
|
||||
var chartData1 = <?php echo json_encode($data['data1']); ?>;
|
||||
var timeRangeName = '';
|
||||
|
||||
var labels = chartData0.map(function(item) {
|
||||
return item.date;
|
||||
});
|
||||
var values0 = chartData0.map(function(item) {
|
||||
return item.value;
|
||||
});
|
||||
var values1 = chartData1.map(function(item) {
|
||||
return item.value;
|
||||
});
|
||||
// Prepare datasets
|
||||
var datasets = [];
|
||||
<?php foreach ($data['datasets'] as $dataset): ?>
|
||||
var chartData = <?php echo json_encode($dataset['data']); ?>;
|
||||
datasets.push({
|
||||
label: '<?= $dataset['label'] ?>',
|
||||
data: chartData.map(function(item) {
|
||||
return {
|
||||
x: item.date,
|
||||
y: item.value
|
||||
};
|
||||
}),
|
||||
borderColor: '<?= $dataset['color'] ?>',
|
||||
borderWidth: 1,
|
||||
fill: false
|
||||
});
|
||||
<?php endforeach; ?>
|
||||
|
||||
var graph_<?= $data['graph_name'] ?> = new Chart(ctx, {
|
||||
type: 'line',
|
||||
data: {
|
||||
labels: labels,
|
||||
datasets: [
|
||||
{
|
||||
label: '<?= $data['graph_data0_label'] ?? '' ?>',
|
||||
data: values0,
|
||||
borderColor: 'rgba(75, 192, 192, 1)',
|
||||
borderWidth: 1,
|
||||
fill: false
|
||||
},
|
||||
{
|
||||
label: '<?= $data['graph_data1_label'] ?? '' ?>',
|
||||
data: values1,
|
||||
borderColor: 'rgba(255, 99, 132, 1)',
|
||||
borderWidth: 1,
|
||||
fill: false
|
||||
}
|
||||
]
|
||||
datasets: datasets
|
||||
},
|
||||
options: {
|
||||
layout: {
|
||||
|
@ -64,7 +54,6 @@ var graph_<?= $data['graph_name'] ?> = new Chart(ctx, {
|
|||
mode: 'x'
|
||||
},
|
||||
zoom: {
|
||||
// enabled: true,
|
||||
mode: 'x',
|
||||
drag: {
|
||||
enabled: true, // Enable drag to select range
|
||||
|
@ -99,29 +88,25 @@ var graph_<?= $data['graph_name'] ?> = new Chart(ctx, {
|
|||
}
|
||||
});
|
||||
|
||||
// Store the graphs in an array
|
||||
// Store graph instance and title for later reference
|
||||
graphs.push({
|
||||
graph: graph_<?= $data['graph_name'] ?>,
|
||||
label: document.getElementById('current-period-<?= $data['graph_name'] ?>')
|
||||
label: '<?= $data['graph_title'] ?>'
|
||||
});
|
||||
|
||||
// Update the time range label
|
||||
function updatePeriodLabel(chart, labelElement) {
|
||||
var startDate = chart.scales.x.min;
|
||||
var endDate = chart.scales.x.max;
|
||||
if (timeRangeName == 'today') {
|
||||
labelElement.innerHTML = 'Currently displaying: ' + timeRangeName + ' (' + new Date(startDate).toLocaleDateString() + ')';
|
||||
// Function to update the period label
|
||||
function updatePeriodLabel(chart, label) {
|
||||
var startDate = new Date(chart.scales.x.min);
|
||||
var endDate = new Date(chart.scales.x.max);
|
||||
var periodLabel = document.getElementById('current-period-<?= $data['graph_name'] ?>');
|
||||
|
||||
if (timeRangeName) {
|
||||
periodLabel.textContent = label + ' (' + timeRangeName + ')';
|
||||
} else {
|
||||
labelElement.innerHTML = 'Currently displaying: ' + timeRangeName + ' (' + new Date(startDate).toLocaleDateString() + ' - ' + new Date(endDate).toLocaleDateString() + ')';
|
||||
periodLabel.textContent = label + ' (from ' + startDate.toLocaleDateString() + ' to ' + endDate.toLocaleDateString() + ')';
|
||||
}
|
||||
}
|
||||
|
||||
// Attach the update function to the 'zoom' event
|
||||
graph_<?= $data['graph_name'] ?>.options.plugins.zoom.onZoom = function({ chart }) {
|
||||
updatePeriodLabel(chart, document.getElementById('current-period-<?= $data['graph_name'] ?>'));
|
||||
};
|
||||
|
||||
// Update the label initially when the chart is rendered
|
||||
updatePeriodLabel(graph_<?= $data['graph_name'] ?>, document.getElementById('current-period-<?= $data['graph_name'] ?>'));
|
||||
|
||||
// Initial label update
|
||||
updatePeriodLabel(graph_<?= $data['graph_name'] ?>, '<?= $data['graph_title'] ?>');
|
||||
</script>
|
||||
|
|
|
@ -1,9 +1,7 @@
|
|||
<?php
|
||||
|
||||
// render config variables array
|
||||
function renderConfig($configPart, $indent, $platform=false, $parent='') {
|
||||
global $app_root;
|
||||
global $config;
|
||||
function renderConfig($configPart, $indent) {
|
||||
?>
|
||||
<div style="padding-left: <?= $indent ?>px; padding-bottom: 20px;">
|
||||
<?php foreach ($configPart as $config_item => $config_value) { ?>
|
||||
|
@ -15,20 +13,62 @@ function renderConfig($configPart, $indent, $platform=false, $parent='') {
|
|||
if (is_array($config_value)) {
|
||||
// here we render recursively nested arrays
|
||||
$indent = $indent + 50;
|
||||
if ($parent === 'platforms') {
|
||||
$indent = 100;
|
||||
}
|
||||
if ($config_item === 'platforms') {
|
||||
renderConfig($config_value, $indent, $platform, 'platforms');
|
||||
} else {
|
||||
renderConfig($config_value, $indent, $platform);
|
||||
}
|
||||
renderConfig($config_value, $indent);
|
||||
$indent = 0;
|
||||
} else {
|
||||
// if it's not array, just display it
|
||||
if ($config_item === 'registration_enabled') { ?>
|
||||
<div class="border col-md-8 text-start">
|
||||
<?= ($config_value === 1 || $config_value === true) ? 'true' : 'false' ?>
|
||||
</div>
|
||||
<?php } else { ?>
|
||||
<div class="border col-md-8 text-start">
|
||||
<?= htmlspecialchars($config_value ?? '')?>
|
||||
</div>
|
||||
<?php } ?>
|
||||
<?php } ?>
|
||||
</div>
|
||||
<?php } ?>
|
||||
</div>
|
||||
<?php
|
||||
}
|
||||
|
||||
|
||||
// render config variables array
|
||||
function editConfig($configPart, $indent) {
|
||||
?>
|
||||
<div style="padding-left: <?= $indent ?>px; padding-bottom: 20px;">
|
||||
<?php foreach ($configPart as $config_item => $config_value) { ?>
|
||||
<div class="row mb-1" style="padding-left: <?= $indent ?>px;">
|
||||
<div class="col-md-4 text-end">
|
||||
<label for="<?= htmlspecialchars($config_item) ?>" class="form-label"><?= htmlspecialchars($config_item) ?></label>
|
||||
</div>
|
||||
|
||||
<?php
|
||||
if (is_array($config_value)) {
|
||||
// here we render recursively nested arrays
|
||||
$indent = $indent + 50;
|
||||
editConfig($config_value, $indent);
|
||||
$indent = 0;
|
||||
} else {
|
||||
// if it's not array, just display it
|
||||
?>
|
||||
<div class="border col-md-8 text-start">
|
||||
<?= htmlspecialchars($config_value ?? '')?>
|
||||
<div class="col-md-8 text-start">
|
||||
<?php if ($config_item === 'registration_enabled') { ?>
|
||||
<input type="hidden" name="<?= htmlspecialchars($config_item) ?>" value="false" />
|
||||
<input class="form-check-input" type="checkbox" role="switch" name="<?= htmlspecialchars($config_item) ?>" value="true" <?= ($config_value === 1 || $config_value === true) ? 'checked' : '' ?> />
|
||||
<?php } elseif ($config_item === 'environment') { ?>
|
||||
<select class="form-control" type="text" name="<?= htmlspecialchars($config_item) ?>">
|
||||
<option value="development"<?= ($config_value === 'development') ? ' selected' : '' ?>>development</option>
|
||||
<option value="production"<?= ($config_value === 'production') ? ' selected' : '' ?>>production</option>
|
||||
</select>
|
||||
<?php } elseif ($config_item === 'version') {?>
|
||||
<input class="form-control" type="text" name="<?= htmlspecialchars($config_item) ?>" value="<?= htmlspecialchars($config_value ?? '') ?>" disabled />
|
||||
<?php } elseif ($config_item === 'db_type') {?>
|
||||
<input class="form-control" type="text" name="<?= htmlspecialchars($config_item) ?>" value="<?= htmlspecialchars($config_value ?? '') ?>" disabled />
|
||||
<?php } else { ?>
|
||||
<input class="form-control" type="text" name="<?= htmlspecialchars($config_item) ?>" value="<?= htmlspecialchars($config_value ?? '') ?>" />
|
||||
<?php } ?>
|
||||
</div>
|
||||
<?php } ?>
|
||||
</div>
|
||||
|
@ -36,4 +76,6 @@ function renderConfig($configPart, $indent, $platform=false, $parent='') {
|
|||
</div>
|
||||
<?php
|
||||
}
|
||||
|
||||
|
||||
?>
|
||||
|
|
|
@ -13,10 +13,9 @@ function connectDB($config, $database = '', $dbFile = '', $platformId = '') {
|
|||
'type' => 'sqlite',
|
||||
'dbFile' => $dbFile,
|
||||
]);
|
||||
return ['db' => $db, 'error' => null];
|
||||
} catch (Exception $e) {
|
||||
$error = getError('Error connecting to DB.', $e->getMessage());
|
||||
include '../app/templates/block-message.php';
|
||||
exit();
|
||||
return ['db' => null, 'error' => getError('Error connecting to DB.', $e->getMessage())];
|
||||
}
|
||||
|
||||
// connecting to a jilo-web database of the web app
|
||||
|
@ -30,10 +29,9 @@ function connectDB($config, $database = '', $dbFile = '', $platformId = '') {
|
|||
'dbFile' => $config['db']['sqlite_file'],
|
||||
]);
|
||||
$pdo = $db->getConnection();
|
||||
return ['db' => $db, 'error' => null];
|
||||
} catch (Exception $e) {
|
||||
$error = getError('Error connecting to DB.', $e->getMessage());
|
||||
include '../app/templates/block-message.php';
|
||||
exit();
|
||||
return ['db' => null, 'error' => getError('Error connecting to DB.', $e->getMessage())];
|
||||
}
|
||||
// mysql/mariadb database
|
||||
} elseif ($config['db']['db_type'] === 'mysql' || $config['db']['db_type'] === 'mariadb') {
|
||||
|
@ -47,20 +45,18 @@ function connectDB($config, $database = '', $dbFile = '', $platformId = '') {
|
|||
'password' => $config['db']['sql_password'],
|
||||
]);
|
||||
$pdo = $db->getConnection();
|
||||
return ['db' => $db, 'error' => null];
|
||||
} catch (Exception $e) {
|
||||
$error = getError('Error connecting to DB.', $e->getMessage());
|
||||
include '../app/templates/block-message.php';
|
||||
exit();
|
||||
return ['db' => null, 'error' => getError('Error connecting to DB.', $e->getMessage())];
|
||||
}
|
||||
// unknown database
|
||||
} else {
|
||||
$error = "Error: unknow database type \"{$config['db']['db_type']}\"";
|
||||
include '../app/templates/block-message.php';
|
||||
Messages::flash('ERROR', 'DEFAULT', $error);
|
||||
exit();
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
return $db;
|
||||
}
|
||||
?>
|
|
@ -0,0 +1,42 @@
|
|||
<?php
|
||||
|
||||
/**
|
||||
* Generate an error or notice message based on the environment.
|
||||
*
|
||||
* In a production environment, hides detailed error messages and returns
|
||||
* a generic message. In other environments, returns the provided message.
|
||||
*
|
||||
* @param string $message A user-friendly message to display.
|
||||
* @param string $error The detailed error message for debugging (optional).
|
||||
* @param string|null $environment The environment type ('production', 'development', etc.). If null, defaults to the configured environment.
|
||||
*
|
||||
* @return string The appropriate message based on the environment.
|
||||
*/
|
||||
function getError($message, $error = '', $environment = null) {
|
||||
global $config;
|
||||
$environment = $config['environment'] ?? 'production';
|
||||
|
||||
if ($environment === 'production') {
|
||||
return 'There was an unexpected error. Please try again.';
|
||||
} else {
|
||||
return $error ?: $message;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Render a message if it exists, and optionally unset it after display.
|
||||
*
|
||||
* @param string $message The message to display.
|
||||
* @param string $type The type of message (e.g., 'error', 'notice').
|
||||
* @param bool $unset Whether to unset the message after display.
|
||||
*/
|
||||
function renderMessage(&$message, $type, $unset = false) {
|
||||
if (isset($message)) {
|
||||
echo "\t\t<div class=\"{$type}\">" . $message . "</div>\n";
|
||||
if ($unset) {
|
||||
$message = null;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
?>
|
|
@ -0,0 +1,8 @@
|
|||
<?php
|
||||
if (isset($messages) && is_array($messages)) {
|
||||
foreach ($messages as $msg) {
|
||||
echo Messages::render($msg['category'], $msg['key'], $msg['custom_message'] ?? null, $msg['dismissible'] ?? false, $msg['small'] ?? false);
|
||||
}
|
||||
}
|
||||
|
||||
?>
|
|
@ -0,0 +1,36 @@
|
|||
<?php
|
||||
|
||||
// Message strings for translation
|
||||
return [
|
||||
'LOGIN' => [
|
||||
'LOGIN_SUCCESS' => 'Login successful.',
|
||||
'LOGIN_FAILED' => 'Login failed. Please check your credentials.',
|
||||
'LOGOUT_SUCCESS' => 'Logout successful. You can log in again.',
|
||||
'IP_BLACKLISTED' => 'Access denied. Your IP address is blacklisted.',
|
||||
'IP_NOT_WHITELISTED' => 'Access denied. Your IP address is not whitelisted.',
|
||||
'TOO_MANY_ATTEMPTS' => 'Too many login attempts. Please try again later.',
|
||||
],
|
||||
'SECURITY' => [
|
||||
'WHITELIST_ADD_SUCCESS' => 'IP address successfully added to whitelist.',
|
||||
'WHITELIST_ADD_ERROR' => 'Failed to add IP to whitelist. Please check the IP format.',
|
||||
'WHITELIST_REMOVE_SUCCESS' => 'IP address successfully removed from whitelist.',
|
||||
'WHITELIST_REMOVE_ERROR' => 'Failed to remove IP from whitelist.',
|
||||
'BLACKLIST_ADD_SUCCESS' => 'IP address successfully added to blacklist.',
|
||||
'BLACKLIST_ADD_ERROR' => 'Failed to add IP to blacklist. Please check the IP format.',
|
||||
'BLACKLIST_REMOVE_SUCCESS' => 'IP address successfully removed from blacklist.',
|
||||
'BLACKLIST_REMOVE_ERROR' => 'Failed to remove IP from blacklist.',
|
||||
'RATE_LIMIT_INFO' => 'Rate limiting is active. This helps protect against brute force attacks.',
|
||||
'PERMISSION_DENIED' => 'Permission denied. You do not have the required rights.',
|
||||
'IP_REQUIRED' => 'IP address is required.',
|
||||
],
|
||||
'REGISTER' => [
|
||||
'SUCCESS' => 'Registration successful. You can log in now.',
|
||||
'FAILED' => 'Registration failed: %s',
|
||||
'DISABLED' => 'Registration is disabled.',
|
||||
],
|
||||
'SYSTEM' => [
|
||||
'DB_ERROR' => 'Error connecting to the database: %s',
|
||||
'DB_CONNECT_ERROR' => 'Error connecting to DB: %s',
|
||||
'DB_UNKNOWN_TYPE' => 'Error: unknown database type "%s"',
|
||||
],
|
||||
];
|
|
@ -0,0 +1,17 @@
|
|||
<?php
|
||||
|
||||
// Get any flash messages from previous request
|
||||
$flash_messages = Messages::getFlash();
|
||||
if (!empty($flash_messages)) {
|
||||
$messages = array_merge($messages, array_map(function($flash) {
|
||||
return [
|
||||
'category' => $flash['category'],
|
||||
'key' => $flash['key'],
|
||||
'custom_message' => $flash['custom_message'] ?? null,
|
||||
'dismissible' => $flash['dismissible'] ?? false,
|
||||
'small' => $flash['small'] ?? false
|
||||
];
|
||||
}, $flash_messages));
|
||||
}
|
||||
|
||||
?>
|
|
@ -21,11 +21,24 @@ if (isset($_REQUEST['until_time'])) {
|
|||
$until_time = htmlspecialchars($_REQUEST['until_time']);
|
||||
}
|
||||
|
||||
if (isset($_SESSION['notice'])) {
|
||||
$notice = htmlspecialchars($_SESSION['notice']); // 'notice' for all non-critical messages
|
||||
// sanitize session vars
|
||||
if (isset($_SESSION)) {
|
||||
foreach ($_SESSION as $key => $value) {
|
||||
if (is_string($value)) {
|
||||
$_SESSION[$key] = htmlspecialchars($value);
|
||||
}
|
||||
}
|
||||
}
|
||||
if (isset($_SESSION['error'])) {
|
||||
$error = htmlspecialchars($_SESSION['error']); // 'error' for errors
|
||||
|
||||
// hosts
|
||||
if (isset($_POST['address'])) {
|
||||
$address = htmlspecialchars($_POST['address']);
|
||||
}
|
||||
if (isset($_POST['port'])) {
|
||||
$port = htmlspecialchars($_POST['port']);
|
||||
}
|
||||
if (isset($_POST['name'])) {
|
||||
$name = htmlspecialchars($_POST['name']);
|
||||
}
|
||||
|
||||
// agents
|
||||
|
@ -38,6 +51,9 @@ if (isset($_POST['url'])) {
|
|||
if (isset($_POST['secret_key'])) {
|
||||
$secret_key = htmlspecialchars($_POST['secret_key']);
|
||||
}
|
||||
if (isset($_POST['check_period'])) {
|
||||
$check_period = htmlspecialchars($_POST['check_period']);
|
||||
}
|
||||
|
||||
// platforms
|
||||
if (isset($_POST['name'])) {
|
|
@ -1,5 +1,17 @@
|
|||
<?php
|
||||
|
||||
/**
|
||||
* Agent cache management
|
||||
*
|
||||
* This page ("agents") handles caching for agents. It allows storing, clearing, and retrieving
|
||||
* agent-related data in the session using AJAX requests. The cache is stored with a timestamp
|
||||
* to allow time-based invalidation if needed.
|
||||
*/
|
||||
|
||||
// Get any new messages
|
||||
include '../app/includes/messages.php';
|
||||
include '../app/includes/messages-show.php';
|
||||
|
||||
$action = $_REQUEST['action'] ?? '';
|
||||
$agent = $_REQUEST['agent'] ?? '';
|
||||
require '../app/classes/agent.php';
|
||||
|
|
|
@ -1,85 +1,107 @@
|
|||
<?php
|
||||
|
||||
/**
|
||||
* Components information
|
||||
*
|
||||
* This page ("components") retrieves and displays information about Jitsi components events.
|
||||
* Allows filtering by component ID, name, or event name, and listing within a specified time range.
|
||||
* Supports pagination.
|
||||
*/
|
||||
|
||||
// Get any new messages
|
||||
include '../app/includes/messages.php';
|
||||
include '../app/includes/messages-show.php';
|
||||
|
||||
require '../app/classes/component.php';
|
||||
|
||||
// connect to database
|
||||
$db = connectDB($config, 'jilo', $platformDetails[0]['jilo_database'], $platform_id);
|
||||
$response = connectDB($config, 'jilo', $platformDetails[0]['jilo_database'], $platform_id);
|
||||
|
||||
// specify time range
|
||||
include '../app/helpers/time_range.php';
|
||||
// if DB connection has error, display it and stop here
|
||||
if ($response['db'] === null) {
|
||||
Messages::flash('ERROR', 'DEFAULT', $response['error']);
|
||||
|
||||
// jitsi component events list
|
||||
// we use $_REQUEST, so that both links and forms work
|
||||
// if it's there, but empty, we make it same as the field name; otherwise assign the value
|
||||
$jitsi_component = !empty($_REQUEST['name']) ? "'" . $_REQUEST['name'] . "'" : 'jitsi_component';
|
||||
$component_id = !empty($_REQUEST['id']) ? "'" . $_REQUEST['id'] . "'" : 'component_id';
|
||||
$event_type = !empty($_REQUEST['event']) ? "'" . $_REQUEST['event'] . "'" : 'event_type';
|
||||
|
||||
|
||||
//
|
||||
// Component events listings
|
||||
//
|
||||
|
||||
|
||||
// list of all component events (default)
|
||||
$componentObject = new Component($db);
|
||||
|
||||
// pagination variables
|
||||
$items_per_page = 15;
|
||||
$browse_page = $_REQUEST['p'] ?? 1;
|
||||
$browse_page = (int)$browse_page;
|
||||
$offset = ($browse_page -1) * $items_per_page;
|
||||
|
||||
// prepare the result
|
||||
$search = $componentObject->jitsiComponents($jitsi_component, $component_id, $event_type, $from_time, $until_time, $offset, $items_per_page);
|
||||
$search_all = $componentObject->jitsiComponents($jitsi_component, $component_id, $event_type, $from_time, $until_time);
|
||||
|
||||
if (!empty($search)) {
|
||||
// we get total items and number of pages
|
||||
$item_count = count($search_all);
|
||||
$page_count = ceil($item_count / $items_per_page);
|
||||
|
||||
$components = array();
|
||||
$components['records'] = array();
|
||||
|
||||
foreach ($search as $item) {
|
||||
extract($item);
|
||||
$component_record = array(
|
||||
// assign title to the field in the array record
|
||||
'component' => $jitsi_component,
|
||||
'loglevel' => $loglevel,
|
||||
'time' => $time,
|
||||
'component ID' => $component_id,
|
||||
'event' => $event_type,
|
||||
'param' => $event_param,
|
||||
);
|
||||
// populate the result array
|
||||
array_push($components['records'], $component_record);
|
||||
}
|
||||
}
|
||||
|
||||
// prepare the widget
|
||||
$widget['full'] = false;
|
||||
$widget['name'] = 'AllComponents';
|
||||
$widget['filter'] = true;
|
||||
$widget['pagination'] = true;
|
||||
|
||||
// widget title
|
||||
if (isset($_REQUEST['name']) && $_REQUEST['name'] != '') {
|
||||
$widget['title'] = 'Jitsi events for component <strong>' . $_REQUEST['name'] . '</strong>';
|
||||
} elseif (isset($_REQUEST['id']) && $_REQUEST['id'] != '') {
|
||||
$widget['title'] = 'Jitsi events for component ID <strong>' . $_REQUEST['id'] . '</strong>';
|
||||
// otherwise if DB connection is OK, go on
|
||||
} else {
|
||||
$widget['title'] = 'Jitsi events for <strong>all components</strong>';
|
||||
}
|
||||
// widget records
|
||||
if (!empty($components['records'])) {
|
||||
$widget['full'] = true;
|
||||
$widget['table_headers'] = array_keys($components['records'][0]);
|
||||
$widget['table_records'] = $components['records'];
|
||||
}
|
||||
$db = $response['db'];
|
||||
|
||||
// display the widget
|
||||
include '../app/templates/event-list-components.php';
|
||||
// specify time range
|
||||
include '../app/helpers/time_range.php';
|
||||
|
||||
// jitsi component events list
|
||||
// we use $_REQUEST, so that both links and forms work
|
||||
// if it's there, but empty, we make it same as the field name; otherwise assign the value
|
||||
$jitsi_component = !empty($_REQUEST['name']) ? "'" . $_REQUEST['name'] . "'" : 'jitsi_component';
|
||||
$component_id = !empty($_REQUEST['id']) ? "'" . $_REQUEST['id'] . "'" : 'component_id';
|
||||
$event_type = !empty($_REQUEST['event']) ? "'" . $_REQUEST['event'] . "'" : 'event_type';
|
||||
|
||||
|
||||
//
|
||||
// Component events listings
|
||||
//
|
||||
|
||||
|
||||
// list of all component events (default)
|
||||
$componentObject = new Component($db);
|
||||
|
||||
// pagination variables
|
||||
$items_per_page = 15;
|
||||
$browse_page = $_REQUEST['p'] ?? 1;
|
||||
$browse_page = (int)$browse_page;
|
||||
$offset = ($browse_page -1) * $items_per_page;
|
||||
|
||||
// prepare the result
|
||||
$search = $componentObject->jitsiComponents($jitsi_component, $component_id, $event_type, $from_time, $until_time, $offset, $items_per_page);
|
||||
$search_all = $componentObject->jitsiComponents($jitsi_component, $component_id, $event_type, $from_time, $until_time);
|
||||
|
||||
if (!empty($search)) {
|
||||
// we get total items and number of pages
|
||||
$item_count = count($search_all);
|
||||
$page_count = ceil($item_count / $items_per_page);
|
||||
|
||||
$components = array();
|
||||
$components['records'] = array();
|
||||
|
||||
foreach ($search as $item) {
|
||||
extract($item);
|
||||
$component_record = array(
|
||||
// assign title to the field in the array record
|
||||
'component' => $jitsi_component,
|
||||
'loglevel' => $loglevel,
|
||||
'time' => $time,
|
||||
'component ID' => $component_id,
|
||||
'event' => $event_type,
|
||||
'param' => $event_param,
|
||||
);
|
||||
// populate the result array
|
||||
array_push($components['records'], $component_record);
|
||||
}
|
||||
}
|
||||
|
||||
// prepare the widget
|
||||
$widget['full'] = false;
|
||||
$widget['name'] = 'AllComponents';
|
||||
$widget['filter'] = true;
|
||||
$widget['pagination'] = true;
|
||||
|
||||
// widget title
|
||||
if (isset($_REQUEST['name']) && $_REQUEST['name'] != '') {
|
||||
$widget['title'] = 'Jitsi events for component <strong>' . $_REQUEST['name'] . '</strong>';
|
||||
} elseif (isset($_REQUEST['id']) && $_REQUEST['id'] != '') {
|
||||
$widget['title'] = 'Jitsi events for component ID <strong>' . $_REQUEST['id'] . '</strong>';
|
||||
} else {
|
||||
$widget['title'] = 'Jitsi events for <strong>all components</strong>';
|
||||
}
|
||||
// widget records
|
||||
if (!empty($components['records'])) {
|
||||
$widget['full'] = true;
|
||||
$widget['table_headers'] = array_keys($components['records'][0]);
|
||||
$widget['table_records'] = $components['records'];
|
||||
}
|
||||
|
||||
// display the widget
|
||||
include '../app/templates/event-list-components.php';
|
||||
|
||||
}
|
||||
|
||||
?>
|
||||
|
|
|
@ -1,149 +1,170 @@
|
|||
<?php
|
||||
|
||||
/**
|
||||
* Conference information
|
||||
*
|
||||
* This page ("conferences") retrieves and displays information about conferences.
|
||||
* Allows filtering by conference ID or name, and listing within a specified time range.
|
||||
* Supports pagination.
|
||||
*/
|
||||
|
||||
// Get any new messages
|
||||
include '../app/includes/messages.php';
|
||||
include '../app/includes/messages-show.php';
|
||||
|
||||
require '../app/classes/conference.php';
|
||||
|
||||
// connect to database
|
||||
$db = connectDB($config, 'jilo', $platformDetails[0]['jilo_database'], $platform_id);
|
||||
$response = connectDB($config, 'jilo', $platformDetails[0]['jilo_database'], $platform_id);
|
||||
|
||||
// specify time range
|
||||
include '../app/helpers/time_range.php';
|
||||
// if DB connection has error, display it and stop here
|
||||
if ($response['db'] === null) {
|
||||
Messages::flash('ERROR', 'DEFAULT', $response['error']);
|
||||
|
||||
// conference id/name are specified when searching specific conference(s)
|
||||
// we use $_REQUEST, so that both links and forms work
|
||||
// if it's there, but empty, we make it same as the field name; otherwise assign the value
|
||||
|
||||
//$conferenceName = !empty($_REQUEST['name']) ? "'" . $_REQUEST['name'] . "'" : 'conference_name';
|
||||
//$conferenceId = !empty($_REQUEST['id']) ? "'" . $_REQUEST['id'] . "'" : 'conference_id';
|
||||
|
||||
if (isset($_REQUEST['id']) && $_REQUEST['id'] != '') {
|
||||
$conferenceId = $_REQUEST['id'];
|
||||
unset($_REQUEST['name']);
|
||||
unset($conferenceName);
|
||||
} elseif (isset($_REQUEST['name']) && $_REQUEST['name'] != '') {
|
||||
unset($conferenceId);
|
||||
$conferenceName = $_REQUEST['name'];
|
||||
// otherwise if DB connection is OK, go on
|
||||
} else {
|
||||
unset($conferenceId);
|
||||
unset($conferenceName);
|
||||
}
|
||||
$db = $response['db'];
|
||||
|
||||
// specify time range
|
||||
include '../app/helpers/time_range.php';
|
||||
|
||||
//
|
||||
// Conference listings
|
||||
//
|
||||
// conference id/name are specified when searching specific conference(s)
|
||||
// we use $_REQUEST, so that both links and forms work
|
||||
// if it's there, but empty, we make it same as the field name; otherwise assign the value
|
||||
|
||||
//$conferenceName = !empty($_REQUEST['name']) ? "'" . $_REQUEST['name'] . "'" : 'conference_name';
|
||||
//$conferenceId = !empty($_REQUEST['id']) ? "'" . $_REQUEST['id'] . "'" : 'conference_id';
|
||||
|
||||
$conferenceObject = new Conference($db);
|
||||
|
||||
// pagination variables
|
||||
$items_per_page = 15;
|
||||
$browse_page = $_REQUEST['p'] ?? 1;
|
||||
$browse_page = (int)$browse_page;
|
||||
$offset = ($browse_page -1) * $items_per_page;
|
||||
|
||||
// search and list specific conference ID
|
||||
if (isset($conferenceId)) {
|
||||
$search = $conferenceObject->conferenceById($conferenceId, $from_time, $until_time, $offset, $items_per_page);
|
||||
$search_all = $conferenceObject->conferenceById($conferenceId, $from_time, $until_time);
|
||||
// search and list specific conference name
|
||||
} elseif (isset($conferenceName)) {
|
||||
$search = $conferenceObject->conferenceByName($conferenceName, $from_time, $until_time, $offset, $items_per_page);
|
||||
$search_all = $conferenceObject->conferenceByName($conferenceName, $from_time, $until_time);
|
||||
// list of all conferences (default)
|
||||
} else {
|
||||
$search = $conferenceObject->conferencesAllFormatted($from_time, $until_time, $offset, $items_per_page);
|
||||
$search_all = $conferenceObject->conferencesAllFormatted($from_time, $until_time);
|
||||
}
|
||||
|
||||
if (!empty($search)) {
|
||||
// we get total items and number of pages
|
||||
$item_count = count($search_all);
|
||||
$page_count = ceil($item_count / $items_per_page);
|
||||
|
||||
$conferences = array();
|
||||
$conferences['records'] = array();
|
||||
|
||||
foreach ($search as $item) {
|
||||
extract($item);
|
||||
|
||||
// we don't have duration field, so we calculate it
|
||||
if (!empty($start) && !empty($end)) {
|
||||
$duration = gmdate("H:i:s", abs(strtotime($end) - strtotime($start)));
|
||||
} else {
|
||||
$duration = '';
|
||||
}
|
||||
|
||||
// search and list specific conference ID
|
||||
if (isset($conferenceId)) {
|
||||
$conference_record = array(
|
||||
// assign title to the field in the array record
|
||||
'time' => $time,
|
||||
'conference ID' => $conference_id,
|
||||
'conference name' => $conference_name,
|
||||
'conference host' => $conference_host,
|
||||
'loglevel' => $loglevel,
|
||||
'participant ID' => $participant_id,
|
||||
'event' => $event_type,
|
||||
'parameter' => $event_param
|
||||
);
|
||||
// search and list specific conference name
|
||||
} elseif (isset($conferenceName)) {
|
||||
$conference_record = array(
|
||||
// assign title to the field in the array record
|
||||
'time' => $time,
|
||||
'conference ID' => $conference_id,
|
||||
'conference name' => $conference_name,
|
||||
'conference host' => $conference_host,
|
||||
'loglevel' => $loglevel,
|
||||
'participant ID' => $participant_id,
|
||||
'event' => $event_type,
|
||||
'parameter' => $event_param
|
||||
);
|
||||
// list of all conferences (default)
|
||||
} else {
|
||||
$conference_record = array(
|
||||
// assign title to the field in the array record
|
||||
'component' => $jitsi_component,
|
||||
'start' => $start,
|
||||
'end' => $end,
|
||||
'duration' => $duration,
|
||||
'conference ID' => $conference_id,
|
||||
'conference name' => $conference_name,
|
||||
'participants' => $participants,
|
||||
'name count' => $name_count,
|
||||
'conference host' => $conference_host
|
||||
);
|
||||
}
|
||||
|
||||
// populate the result array
|
||||
array_push($conferences['records'], $conference_record);
|
||||
if (isset($_REQUEST['id']) && $_REQUEST['id'] != '') {
|
||||
$conferenceId = $_REQUEST['id'];
|
||||
unset($_REQUEST['name']);
|
||||
unset($conferenceName);
|
||||
} elseif (isset($_REQUEST['name']) && $_REQUEST['name'] != '') {
|
||||
unset($conferenceId);
|
||||
$conferenceName = $_REQUEST['name'];
|
||||
} else {
|
||||
unset($conferenceId);
|
||||
unset($conferenceName);
|
||||
}
|
||||
}
|
||||
|
||||
// prepare the widget
|
||||
$widget['full'] = false;
|
||||
$widget['name'] = 'Conferences';
|
||||
$widget['collapsible'] = false;
|
||||
$widget['collapsed'] = false;
|
||||
$widget['filter'] = true;
|
||||
$widget['pagination'] = true;
|
||||
|
||||
// widget title
|
||||
if (isset($_REQUEST['name']) && $_REQUEST['name'] != '') {
|
||||
$widget['title'] = 'Conferences with name matching "<strong>' . $_REQUEST['name'] . '"</strong>';
|
||||
} elseif (isset($_REQUEST['id']) && $_REQUEST['id'] != '') {
|
||||
$widget['title'] = 'Conference with ID "<strong>' . $_REQUEST['id'] . '"</strong>';
|
||||
} else {
|
||||
$widget['title'] = 'All conferences';
|
||||
}
|
||||
// widget records
|
||||
if (!empty($conferences['records'])) {
|
||||
$widget['full'] = true;
|
||||
$widget['table_headers'] = array_keys($conferences['records'][0]);
|
||||
$widget['table_records'] = $conferences['records'];
|
||||
}
|
||||
//
|
||||
// Conference listings
|
||||
//
|
||||
|
||||
// display the widget
|
||||
include '../app/templates/event-list-conferences.php';
|
||||
$conferenceObject = new Conference($db);
|
||||
|
||||
// pagination variables
|
||||
$items_per_page = 15;
|
||||
$browse_page = $_REQUEST['p'] ?? 1;
|
||||
$browse_page = (int)$browse_page;
|
||||
$offset = ($browse_page -1) * $items_per_page;
|
||||
|
||||
// search and list specific conference ID
|
||||
if (isset($conferenceId)) {
|
||||
$search = $conferenceObject->conferenceById($conferenceId, $from_time, $until_time, $offset, $items_per_page);
|
||||
$search_all = $conferenceObject->conferenceById($conferenceId, $from_time, $until_time);
|
||||
// search and list specific conference name
|
||||
} elseif (isset($conferenceName)) {
|
||||
$search = $conferenceObject->conferenceByName($conferenceName, $from_time, $until_time, $offset, $items_per_page);
|
||||
$search_all = $conferenceObject->conferenceByName($conferenceName, $from_time, $until_time);
|
||||
// list of all conferences (default)
|
||||
} else {
|
||||
$search = $conferenceObject->conferencesAllFormatted($from_time, $until_time, $offset, $items_per_page);
|
||||
$search_all = $conferenceObject->conferencesAllFormatted($from_time, $until_time);
|
||||
}
|
||||
|
||||
if (!empty($search)) {
|
||||
// we get total items and number of pages
|
||||
$item_count = count($search_all);
|
||||
$page_count = ceil($item_count / $items_per_page);
|
||||
|
||||
$conferences = array();
|
||||
$conferences['records'] = array();
|
||||
|
||||
foreach ($search as $item) {
|
||||
extract($item);
|
||||
|
||||
// we don't have duration field, so we calculate it
|
||||
if (!empty($start) && !empty($end)) {
|
||||
$duration = gmdate("H:i:s", abs(strtotime($end) - strtotime($start)));
|
||||
} else {
|
||||
$duration = '';
|
||||
}
|
||||
|
||||
// search and list specific conference ID
|
||||
if (isset($conferenceId)) {
|
||||
$conference_record = array(
|
||||
// assign title to the field in the array record
|
||||
'time' => $time,
|
||||
'conference ID' => $conference_id,
|
||||
'conference name' => $conference_name,
|
||||
'conference host' => $conference_host,
|
||||
'loglevel' => $loglevel,
|
||||
'participant ID' => $participant_id,
|
||||
'event' => $event_type,
|
||||
'parameter' => $event_param
|
||||
);
|
||||
// search and list specific conference name
|
||||
} elseif (isset($conferenceName)) {
|
||||
$conference_record = array(
|
||||
// assign title to the field in the array record
|
||||
'time' => $time,
|
||||
'conference ID' => $conference_id,
|
||||
'conference name' => $conference_name,
|
||||
'conference host' => $conference_host,
|
||||
'loglevel' => $loglevel,
|
||||
'participant ID' => $participant_id,
|
||||
'event' => $event_type,
|
||||
'parameter' => $event_param
|
||||
);
|
||||
// list of all conferences (default)
|
||||
} else {
|
||||
$conference_record = array(
|
||||
// assign title to the field in the array record
|
||||
'component' => $jitsi_component,
|
||||
'start' => $start,
|
||||
'end' => $end,
|
||||
'duration' => $duration,
|
||||
'conference ID' => $conference_id,
|
||||
'conference name' => $conference_name,
|
||||
'participants' => $participants,
|
||||
'name count' => $name_count,
|
||||
'conference host' => $conference_host
|
||||
);
|
||||
}
|
||||
|
||||
// populate the result array
|
||||
array_push($conferences['records'], $conference_record);
|
||||
}
|
||||
}
|
||||
|
||||
// prepare the widget
|
||||
$widget['full'] = false;
|
||||
$widget['name'] = 'Conferences';
|
||||
$widget['collapsible'] = false;
|
||||
$widget['collapsed'] = false;
|
||||
$widget['filter'] = true;
|
||||
$widget['pagination'] = true;
|
||||
|
||||
// widget title
|
||||
if (isset($_REQUEST['name']) && $_REQUEST['name'] != '') {
|
||||
$widget['title'] = 'Conferences with name matching "<strong>' . $_REQUEST['name'] . '"</strong>';
|
||||
} elseif (isset($_REQUEST['id']) && $_REQUEST['id'] != '') {
|
||||
$widget['title'] = 'Conference with ID "<strong>' . $_REQUEST['id'] . '"</strong>';
|
||||
} else {
|
||||
$widget['title'] = 'All conferences';
|
||||
}
|
||||
// widget records
|
||||
if (!empty($conferences['records'])) {
|
||||
$widget['full'] = true;
|
||||
$widget['table_headers'] = array_keys($conferences['records'][0]);
|
||||
$widget['table_records'] = $conferences['records'];
|
||||
}
|
||||
|
||||
// display the widget
|
||||
include '../app/templates/event-list-conferences.php';
|
||||
|
||||
}
|
||||
|
||||
?>
|
||||
|
|
|
@ -1,28 +1,69 @@
|
|||
<?php
|
||||
|
||||
/**
|
||||
* Configuration management.
|
||||
*
|
||||
* This page ("config") handles configuration by adding, editing, and deleting platforms,
|
||||
* hosts, agents, and the configuration file itself.
|
||||
*/
|
||||
|
||||
// Get any new messages
|
||||
include '../app/includes/messages.php';
|
||||
include '../app/includes/messages-show.php';
|
||||
|
||||
$action = $_REQUEST['action'] ?? '';
|
||||
$agent = $_REQUEST['agent'] ?? '';
|
||||
$host = $_REQUEST['host'] ?? '';
|
||||
|
||||
require '../app/classes/config.php';
|
||||
require '../app/classes/host.php';
|
||||
require '../app/classes/agent.php';
|
||||
|
||||
$configObject = new Config();
|
||||
$hostObject = new Host($dbWeb);
|
||||
$agentObject = new Agent($dbWeb);
|
||||
|
||||
// if a form is submitted, it's from the edit page
|
||||
if ($_SERVER['REQUEST_METHOD'] == 'POST') {
|
||||
/**
|
||||
* Handles form submissions from editing page
|
||||
*/
|
||||
|
||||
// FIXME - if editing the flat file is no more needed, remove this
|
||||
// // load the config file and initialize a copy
|
||||
// $content = file_get_contents($config_file);
|
||||
// $updatedContent = $content;
|
||||
// editing the config file
|
||||
if (isset($_POST['item']) && $_POST['item'] === 'config_file') {
|
||||
// check if file is writable
|
||||
if (!is_writable($config_file)) {
|
||||
$_SESSION['error'] = "Configuration file is not writable.";
|
||||
} else {
|
||||
$result = $configObject->editConfigFile($_POST, $config_file);
|
||||
if ($result === true) {
|
||||
$_SESSION['notice'] = "The config file is edited.";
|
||||
} else {
|
||||
$_SESSION['error'] = "Editing the config file failed. Error: $result";
|
||||
}
|
||||
}
|
||||
|
||||
// new host adding
|
||||
} elseif (isset($_POST['new']) && isset($_POST['item']) && $_POST['new'] === 'true' && $_POST['item'] === 'host') {
|
||||
$newHost = [
|
||||
'address' => $address,
|
||||
'port' => $port,
|
||||
'platform_id' => $platform_id,
|
||||
'name' => $name,
|
||||
];
|
||||
$result = $hostObject->addHost($newHost);
|
||||
if ($result === true) {
|
||||
$_SESSION['notice'] = "New Jilo host added.";
|
||||
} else {
|
||||
$_SESSION['error'] = "Adding the host failed. Error: $result";
|
||||
}
|
||||
|
||||
// new agent adding
|
||||
if (isset($_POST['new']) && isset($_POST['item']) && $_POST['new'] === 'true' && $_POST['item'] === 'agent') {
|
||||
} elseif (isset($_POST['new']) && isset($_POST['item']) && $_POST['new'] === 'true' && $_POST['item'] === 'agent') {
|
||||
$newAgent = [
|
||||
'type_id' => $type,
|
||||
'url' => $url,
|
||||
'secret_key' => $secret_key,
|
||||
'check_period' => $check_period,
|
||||
];
|
||||
$result = $agentObject->addAgent($platform_id, $newAgent);
|
||||
if ($result === true) {
|
||||
|
@ -38,7 +79,21 @@ if ($_SERVER['REQUEST_METHOD'] == 'POST') {
|
|||
'jitsi_url' => $_POST['jitsi_url'],
|
||||
'jilo_database' => $_POST['jilo_database'],
|
||||
];
|
||||
$platformObject->addPlatform($newPlatform);
|
||||
$result = $platformObject->addPlatform($newPlatform);
|
||||
if ($result === true) {
|
||||
$_SESSION['notice'] = "New Jitsi platform added.";
|
||||
} else {
|
||||
$_SESSION['error'] = "Adding the platform failed. Error: $result";
|
||||
}
|
||||
|
||||
// deleting a host
|
||||
} elseif (isset($_POST['delete']) && isset($_POST['host']) && $_POST['delete'] === 'true') {
|
||||
$result = $hostObject->deleteHost($host);
|
||||
if ($result === true) {
|
||||
$_SESSION['notice'] = "Host id \"{$_REQUEST['host']}\" deleted.";
|
||||
} else {
|
||||
$_SESSION['error'] = "Deleting the host failed. Error: $result";
|
||||
}
|
||||
|
||||
// deleting an agent
|
||||
} elseif (isset($_POST['delete']) && isset($_POST['agent']) && $_POST['delete'] === 'true') {
|
||||
|
@ -52,7 +107,27 @@ if ($_SERVER['REQUEST_METHOD'] == 'POST') {
|
|||
// deleting a platform
|
||||
} elseif (isset($_POST['delete']) && $_POST['delete'] === 'true') {
|
||||
$platform = $_POST['platform'];
|
||||
$platformObject->deletePlatform($platform);
|
||||
$result = $platformObject->deletePlatform($platform);
|
||||
if ($result === true) {
|
||||
$_SESSION['notice'] = "Platform \"{$platformObject['name']}\" added.";
|
||||
} else {
|
||||
$_SESSION['error'] = "Adding the platform failed. Error: $result";
|
||||
}
|
||||
|
||||
// an update to an existing host
|
||||
} elseif (isset($_POST['host'])) {
|
||||
$updatedHost = [
|
||||
'id' => $host,
|
||||
'address' => $address,
|
||||
'port' => $port,
|
||||
'name' => $name,
|
||||
];
|
||||
$result = $hostObject->editHost($platform_id, $updatedHost);
|
||||
if ($result === true) {
|
||||
$_SESSION['notice'] = "Host \"{$_REQUEST['address']}:{$_REQUEST['port']}\" edited.";
|
||||
} else {
|
||||
$_SESSION['error'] = "Editing the host failed. Error: $result";
|
||||
}
|
||||
|
||||
// an update to an existing agent
|
||||
} elseif (isset($_POST['agent'])) {
|
||||
|
@ -61,6 +136,7 @@ if ($_SERVER['REQUEST_METHOD'] == 'POST') {
|
|||
'agent_type_id' => $type,
|
||||
'url' => $url,
|
||||
'secret_key' => $secret_key,
|
||||
'check_period' => $check_period,
|
||||
];
|
||||
$result = $agentObject->editAgent($platform_id, $updatedAgent);
|
||||
if ($result === true) {
|
||||
|
@ -77,85 +153,104 @@ if ($_SERVER['REQUEST_METHOD'] == 'POST') {
|
|||
'jitsi_url' => $_POST['jitsi_url'],
|
||||
'jilo_database' => $_POST['jilo_database'],
|
||||
];
|
||||
$platformObject->editPlatform($platform, $updatedPlatform);
|
||||
$result = $platformObject->editPlatform($platform, $updatedPlatform);
|
||||
if ($result === true) {
|
||||
$_SESSION['notice'] = "Platform \"{$_REQUEST['name']}\" edited.";
|
||||
} else {
|
||||
$_SESSION['error'] = "Editing the platform failed. Error: $result";
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
// FIXME - if this is not needed for editing the flat file, remove it
|
||||
// // check if file is writable
|
||||
// if (!is_writable($config_file)) {
|
||||
// $_SESSION['error'] = getError('Configuration file is not writable.');
|
||||
// header("Location: $app_root?platform=$platform_id&page=config");
|
||||
// exit();
|
||||
// }
|
||||
//
|
||||
// // try to update the config file
|
||||
// if (file_put_contents($config_file, $updatedContent) !== false) {
|
||||
// // update successful
|
||||
// $_SESSION['notice'] = "Configuration for {$_POST['name']} is updated.";
|
||||
// } else {
|
||||
// // unsuccessful
|
||||
// $error = error_get_last();
|
||||
// $_SESSION['error'] = getError('Error updating the config: ' . ($error['message'] ?? 'unknown error'));
|
||||
// }
|
||||
|
||||
// FIXME the new file is not loaded on first page load
|
||||
unset($config);
|
||||
header("Location: $app_root?platform=$platform_id&page=config");
|
||||
header("Location: $app_root?page=config&item=$item");
|
||||
exit();
|
||||
|
||||
// no form submitted, show the templates
|
||||
} else {
|
||||
/**
|
||||
* Handles GET requests to display templates.
|
||||
*/
|
||||
|
||||
// $item - config.js and interface_config.js are special case; remote loaded files
|
||||
switch ($item) {
|
||||
case 'configjs':
|
||||
$mode = $_REQUEST['mode'] ?? '';
|
||||
$raw = ($mode === 'raw');
|
||||
$platformConfigjs = $configObject->getPlatformConfigjs($platformDetails[0]['jitsi_url'], $raw);
|
||||
include '../app/templates/config-list-configjs.php';
|
||||
break;
|
||||
case 'interfaceconfigjs':
|
||||
$mode = $_REQUEST['mode'] ?? '';
|
||||
$raw = ($mode === 'raw');
|
||||
$platformInterfaceConfigjs = $configObject->getPlatformInterfaceConfigjs($platformDetails[0]['jitsi_url'], $raw);
|
||||
include '../app/templates/config-list-interfaceconfigjs.php';
|
||||
|
||||
case 'platform':
|
||||
if (isset($action) && $action === 'add') {
|
||||
include '../app/templates/config-platform-add.php';
|
||||
} elseif (isset($action) && $action === 'edit') {
|
||||
include '../app/templates/config-platform-edit.php';
|
||||
} elseif (isset($action) && $action === 'delete') {
|
||||
include '../app/templates/config-platform-delete.php';
|
||||
} else {
|
||||
if ($userObject->hasRight($user_id, 'view config file')) {
|
||||
include '../app/templates/config-platform.php';
|
||||
} else {
|
||||
include '../app/templates/error-unauthorized.php';
|
||||
}
|
||||
}
|
||||
break;
|
||||
|
||||
// if there is no $item, we work on the local config DB
|
||||
default:
|
||||
switch ($action) {
|
||||
case 'add-agent':
|
||||
$jilo_agent_types = $agentObject->getAgentTypes();
|
||||
include '../app/templates/config-add-agent.php';
|
||||
break;
|
||||
case 'add':
|
||||
include '../app/templates/config-add-platform.php';
|
||||
break;
|
||||
case 'edit':
|
||||
if (isset($_GET['agent'])) {
|
||||
$agentDetails = $agentObject->getAgentDetails($platform_id, $agent);
|
||||
$jilo_agent_types = $agentObject->getAgentTypes();
|
||||
include '../app/templates/config-edit-agent.php';
|
||||
} else {
|
||||
include '../app/templates/config-edit-platform.php';
|
||||
}
|
||||
break;
|
||||
case 'delete':
|
||||
if (isset($_GET['agent'])) {
|
||||
$agentDetails = $agentObject->getAgentDetails($platform_id, $agent);
|
||||
include '../app/templates/config-delete-agent.php';
|
||||
} else {
|
||||
include '../app/templates/config-delete-platform.php';
|
||||
}
|
||||
break;
|
||||
default:
|
||||
if ($userObject->hasRight($user_id, 'view config file')) {
|
||||
include '../app/templates/config-list.php';
|
||||
} else {
|
||||
include '../app/templates/error-unauthorized.php';
|
||||
}
|
||||
case 'host':
|
||||
if (isset($action) && $action === 'add') {
|
||||
include '../app/templates/config-host-add.php';
|
||||
} elseif (isset($action) && $action === 'edit') {
|
||||
$hostDetails = $hostObject->getHostDetails($platform_id, $agent);
|
||||
include '../app/templates/config-host-edit.php';
|
||||
} elseif (isset($action) && $action === 'delete') {
|
||||
$hostDetails = $hostObject->getHostDetails($platform_id, $agent);
|
||||
include '../app/templates/config-host-delete.php';
|
||||
} else {
|
||||
if ($userObject->hasRight($user_id, 'view config file')) {
|
||||
$hostDetails = $hostObject->getHostDetails();
|
||||
include '../app/templates/config-host.php';
|
||||
} else {
|
||||
include '../app/templates/error-unauthorized.php';
|
||||
}
|
||||
}
|
||||
break;
|
||||
|
||||
case 'endpoint':
|
||||
// TODO ad here endpoints options
|
||||
echo 'under construction';
|
||||
// switch ($action) {
|
||||
// case 'add-agent':
|
||||
// $jilo_agent_types = $agentObject->getAgentTypes();
|
||||
// $jilo_agents_in_platform = $agentObject->getPlatformAgentTypes($platform_id);
|
||||
// $jilo_agent_types_in_platform = array_column($jilo_agents_in_platform, 'agent_type_id');
|
||||
// include '../app/templates/config-add-agent.php';
|
||||
// break;
|
||||
// case 'edit':
|
||||
// if (isset($_GET['agent'])) {
|
||||
// $agentDetails = $agentObject->getAgentDetails($platform_id, $agent);
|
||||
// $jilo_agent_types = $agentObject->getAgentTypes();
|
||||
// include '../app/templates/config-edit-agent.php';
|
||||
// }
|
||||
// break;
|
||||
// case 'delete':
|
||||
// if (isset($_GET['agent'])) {
|
||||
// $agentDetails = $agentObject->getAgentDetails($platform_id, $agent);
|
||||
// include '../app/templates/config-delete-agent.php';
|
||||
// }
|
||||
// break;
|
||||
// }
|
||||
break;
|
||||
|
||||
case 'config_file':
|
||||
if (isset($action) && $action === 'edit') {
|
||||
include '../app/templates/config-configfile-edit.php';
|
||||
} else {
|
||||
if ($userObject->hasRight($user_id, 'view config file')) {
|
||||
include '../app/templates/config-configfile.php';
|
||||
} else {
|
||||
include '../app/templates/error-unauthorized.php';
|
||||
}
|
||||
}
|
||||
break;
|
||||
|
||||
default:
|
||||
// the default config page is the platforms page
|
||||
header("Location: $app_root?page=config&item=platform");
|
||||
exit();
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -1,204 +1,232 @@
|
|||
<?php
|
||||
|
||||
/**
|
||||
* Main dashboard file for displaying conference statistics.
|
||||
*
|
||||
* This page ("dashboard") connects to the database and displays various widgets:
|
||||
* 1. Monthly statistics for the past year.
|
||||
* 2. Conferences from the last 2 days.
|
||||
* 3. The most recent 10 conferences.
|
||||
*/
|
||||
|
||||
// Get any new messages
|
||||
include '../app/includes/messages.php';
|
||||
include '../app/includes/messages-show.php';
|
||||
|
||||
require '../app/classes/conference.php';
|
||||
require '../app/classes/participant.php';
|
||||
|
||||
// connect to database
|
||||
$db = connectDB($config, 'jilo', $platformDetails[0]['jilo_database'], $platform_id);
|
||||
$response = connectDB($config, 'jilo', $platformDetails[0]['jilo_database'], $platform_id);
|
||||
|
||||
// if DB connection has error, display it and stop here
|
||||
if ($response['db'] === null) {
|
||||
Messages::flash('ERROR', 'DEFAULT', $response['error']);
|
||||
|
||||
// otherwise if DB connection is OK, go on
|
||||
} else {
|
||||
$db = $response['db'];
|
||||
|
||||
$conferenceObject = new Conference($db);
|
||||
$participantObject = new Participant($db);
|
||||
|
||||
|
||||
//
|
||||
// dashboard widget listings
|
||||
//
|
||||
|
||||
|
||||
////
|
||||
// monthly usage
|
||||
$conferenceObject = new Conference($db);
|
||||
$participantObject = new Participant($db);
|
||||
|
||||
// monthly conferences for the last year
|
||||
$fromMonth = (new DateTime())->sub(new DateInterval('P1Y'));
|
||||
$fromMonth->modify('first day of this month');
|
||||
$thisMonth = new DateTime();
|
||||
$from_time = $fromMonth->format('Y-m-d');
|
||||
$until_time = $thisMonth->format('Y-m-d');
|
||||
|
||||
$widget['records'] = array();
|
||||
|
||||
// loop 1 year in the past
|
||||
$i = 0;
|
||||
while ($fromMonth < $thisMonth) {
|
||||
|
||||
$untilMonth = clone $fromMonth;
|
||||
$untilMonth->modify('last day of this month');
|
||||
/**
|
||||
* Monthly usage statistics for the last year.
|
||||
*
|
||||
* Retrieves conference and participant numbers for each month within the past year.
|
||||
*/
|
||||
|
||||
// monthly conferences for the last year
|
||||
$fromMonth = (new DateTime())->sub(new DateInterval('P1Y'));
|
||||
$fromMonth->modify('first day of this month');
|
||||
$thisMonth = new DateTime();
|
||||
$from_time = $fromMonth->format('Y-m-d');
|
||||
$until_time = $untilMonth->format('Y-m-d');
|
||||
$until_time = $thisMonth->format('Y-m-d');
|
||||
|
||||
$searchConferenceNumber = $conferenceObject->conferenceNumber($from_time, $until_time);
|
||||
$searchParticipantNumber = $participantObject->participantNumber($from_time, $until_time);
|
||||
|
||||
// pretty format for displaying the month in the widget
|
||||
$month = $fromMonth->format('F Y');
|
||||
|
||||
// populate the records
|
||||
$widget['records'][$i] = array(
|
||||
'from_time' => $from_time,
|
||||
'until_time' => $until_time,
|
||||
'table_headers' => $month,
|
||||
'conferences' => $searchConferenceNumber[0]['conferences'],
|
||||
'participants' => $searchParticipantNumber[0]['participants'],
|
||||
);
|
||||
|
||||
// move everything one month in future
|
||||
$untilMonth->add(new DateInterval('P1M'));
|
||||
$fromMonth->add(new DateInterval('P1M'));
|
||||
$i++;
|
||||
}
|
||||
|
||||
$time_range_specified = true;
|
||||
|
||||
// prepare the widget
|
||||
$widget['full'] = false;
|
||||
$widget['name'] = 'LastYearMonths';
|
||||
$widget['title'] = 'Conferences monthly stats for the last year';
|
||||
$widget['collapsible'] = true;
|
||||
$widget['collapsed'] = false;
|
||||
$widget['filter'] = false;
|
||||
if (!empty($searchConferenceNumber) && !empty($searchParticipantNumber)) {
|
||||
$widget['full'] = true;
|
||||
}
|
||||
$widget['pagination'] = false;
|
||||
|
||||
|
||||
// display the widget
|
||||
include '../app/templates/widget-monthly.php';
|
||||
|
||||
|
||||
////
|
||||
// conferences in last 2 days
|
||||
|
||||
// time range limit
|
||||
$from_time = date('Y-m-d', time() - 60 * 60 * 24 * 2);
|
||||
$until_time = date('Y-m-d', time());
|
||||
$time_range_specified = true;
|
||||
|
||||
// prepare the result
|
||||
$search = $conferenceObject->conferencesAllFormatted($from_time, $until_time);
|
||||
|
||||
if (!empty($search)) {
|
||||
$conferences = array();
|
||||
$conferences['records'] = array();
|
||||
|
||||
foreach ($search as $item) {
|
||||
extract($item);
|
||||
|
||||
// we don't have duration field, so we calculate it
|
||||
if (!empty($start) && !empty($end)) {
|
||||
$duration = gmdate("H:i:s", abs(strtotime($end) - strtotime($start)));
|
||||
} else {
|
||||
$duration = '';
|
||||
}
|
||||
$conference_record = array(
|
||||
// assign title to the field in the array record
|
||||
'component' => $jitsi_component,
|
||||
'start' => $start,
|
||||
'end' => $end,
|
||||
'duration' => $duration,
|
||||
'conference ID' => $conference_id,
|
||||
'conference name' => $conference_name,
|
||||
'participants' => $participants,
|
||||
'name count' => $name_count,
|
||||
'conference host' => $conference_host
|
||||
);
|
||||
// populate the result array
|
||||
array_push($conferences['records'], $conference_record);
|
||||
}
|
||||
}
|
||||
|
||||
// prepare the widget
|
||||
$widget['full'] = false;
|
||||
$widget['name'] = 'LastDays';
|
||||
$widget['title'] = 'Conferences for the last 2 days';
|
||||
$widget['collapsible'] = true;
|
||||
$widget['collapsed'] = false;
|
||||
$widget['filter'] = false;
|
||||
if (!empty($conferences['records'])) {
|
||||
$widget['full'] = true;
|
||||
$widget['table_headers'] = array_keys($conferences['records'][0]);
|
||||
$widget['table_records'] = $conferences['records'];
|
||||
}
|
||||
$widget['pagination'] = false;
|
||||
|
||||
// display the widget
|
||||
include '../app/templates/widget.php';
|
||||
|
||||
|
||||
////
|
||||
// last 10 conferences
|
||||
|
||||
// all time
|
||||
$from_time = '0000-01-01';
|
||||
$until_time = '9999-12-31';
|
||||
$time_range_specified = false;
|
||||
// number of conferences to show
|
||||
$conference_number = 10;
|
||||
|
||||
// prepare the result
|
||||
$search = $conferenceObject->conferencesAllFormatted($from_time, $until_time);
|
||||
|
||||
if (!empty($search)) {
|
||||
$conferences = array();
|
||||
$conferences['records'] = array();
|
||||
$widget['records'] = array();
|
||||
|
||||
// loop 1 year in the past
|
||||
$i = 0;
|
||||
foreach ($search as $item) {
|
||||
extract($item);
|
||||
while ($fromMonth < $thisMonth) {
|
||||
|
||||
// we don't have duration field, so we calculate it
|
||||
if (!empty($start) && !empty($end)) {
|
||||
$duration = gmdate("H:i:s", abs(strtotime($end) - strtotime($start)));
|
||||
} else {
|
||||
$duration = '';
|
||||
}
|
||||
$conference_record = array(
|
||||
// assign title to the field in the array record
|
||||
'component' => $jitsi_component,
|
||||
'start' => $start,
|
||||
'end' => $end,
|
||||
'duration' => $duration,
|
||||
'conference ID' => $conference_id,
|
||||
'conference name' => $conference_name,
|
||||
'participants' => $participants,
|
||||
'name count' => $name_count,
|
||||
'conference host' => $conference_host
|
||||
$untilMonth = clone $fromMonth;
|
||||
$untilMonth->modify('last day of this month');
|
||||
|
||||
$from_time = $fromMonth->format('Y-m-d');
|
||||
$until_time = $untilMonth->format('Y-m-d');
|
||||
|
||||
$searchConferenceNumber = $conferenceObject->conferenceNumber($from_time, $until_time);
|
||||
$searchParticipantNumber = $participantObject->participantNumber($from_time, $until_time);
|
||||
|
||||
// pretty format for displaying the month in the widget
|
||||
$month = $fromMonth->format('F Y');
|
||||
|
||||
// populate the records
|
||||
$widget['records'][$i] = array(
|
||||
'from_time' => $from_time,
|
||||
'until_time' => $until_time,
|
||||
'table_headers' => $month,
|
||||
'conferences' => $searchConferenceNumber[0]['conferences'],
|
||||
'participants' => $searchParticipantNumber[0]['participants'],
|
||||
);
|
||||
// populate the result array
|
||||
array_push($conferences['records'], $conference_record);
|
||||
|
||||
// we only take the first 10 results
|
||||
// move everything one month in future
|
||||
$untilMonth->add(new DateInterval('P1M'));
|
||||
$fromMonth->add(new DateInterval('P1M'));
|
||||
$i++;
|
||||
if ($i == 10) break;
|
||||
}
|
||||
|
||||
$time_range_specified = true;
|
||||
|
||||
// prepare the widget
|
||||
$widget['full'] = false;
|
||||
$widget['name'] = 'LastYearMonths';
|
||||
$widget['title'] = 'Conferences monthly stats for the last year';
|
||||
$widget['collapsible'] = true;
|
||||
$widget['collapsed'] = false;
|
||||
$widget['filter'] = false;
|
||||
if (!empty($searchConferenceNumber) && !empty($searchParticipantNumber)) {
|
||||
$widget['full'] = true;
|
||||
}
|
||||
$widget['pagination'] = false;
|
||||
|
||||
|
||||
// display the widget
|
||||
include '../app/templates/widget-monthly.php';
|
||||
|
||||
|
||||
/**
|
||||
* Conferences in the last 2 days.
|
||||
*
|
||||
* Displays a summary of all conferences held in the past 48 hours.
|
||||
*/
|
||||
|
||||
// time range limit
|
||||
$from_time = date('Y-m-d', time() - 60 * 60 * 24 * 2);
|
||||
$until_time = date('Y-m-d', time());
|
||||
$time_range_specified = true;
|
||||
|
||||
// prepare the result
|
||||
$search = $conferenceObject->conferencesAllFormatted($from_time, $until_time);
|
||||
|
||||
if (!empty($search)) {
|
||||
$conferences = array();
|
||||
$conferences['records'] = array();
|
||||
|
||||
foreach ($search as $item) {
|
||||
extract($item);
|
||||
|
||||
// we don't have duration field, so we calculate it
|
||||
if (!empty($start) && !empty($end)) {
|
||||
$duration = gmdate("H:i:s", abs(strtotime($end) - strtotime($start)));
|
||||
} else {
|
||||
$duration = '';
|
||||
}
|
||||
$conference_record = array(
|
||||
// assign title to the field in the array record
|
||||
'component' => $jitsi_component,
|
||||
'start' => $start,
|
||||
'end' => $end,
|
||||
'duration' => $duration,
|
||||
'conference ID' => $conference_id,
|
||||
'conference name' => $conference_name,
|
||||
'participants' => $participants,
|
||||
'name count' => $name_count,
|
||||
'conference host' => $conference_host
|
||||
);
|
||||
// populate the result array
|
||||
array_push($conferences['records'], $conference_record);
|
||||
}
|
||||
}
|
||||
|
||||
// prepare the widget
|
||||
$widget['full'] = false;
|
||||
$widget['name'] = 'LastDays';
|
||||
$widget['title'] = 'Conferences for the last 2 days';
|
||||
$widget['collapsible'] = true;
|
||||
$widget['collapsed'] = false;
|
||||
$widget['filter'] = false;
|
||||
if (!empty($conferences['records'])) {
|
||||
$widget['full'] = true;
|
||||
$widget['table_headers'] = array_keys($conferences['records'][0]);
|
||||
$widget['table_records'] = $conferences['records'];
|
||||
}
|
||||
$widget['pagination'] = false;
|
||||
|
||||
// display the widget
|
||||
include '../app/templates/widget.php';
|
||||
|
||||
|
||||
/**
|
||||
* Last 10 conferences.
|
||||
*
|
||||
* Displays the 10 most recent conferences in the database.
|
||||
*/
|
||||
|
||||
// all time
|
||||
$from_time = '0000-01-01';
|
||||
$until_time = '9999-12-31';
|
||||
$time_range_specified = false;
|
||||
// number of conferences to show
|
||||
$conference_number = 10;
|
||||
|
||||
// prepare the result
|
||||
$search = $conferenceObject->conferencesAllFormatted($from_time, $until_time);
|
||||
|
||||
if (!empty($search)) {
|
||||
$conferences = array();
|
||||
$conferences['records'] = array();
|
||||
|
||||
$i = 0;
|
||||
foreach ($search as $item) {
|
||||
extract($item);
|
||||
|
||||
// we don't have duration field, so we calculate it
|
||||
if (!empty($start) && !empty($end)) {
|
||||
$duration = gmdate("H:i:s", abs(strtotime($end) - strtotime($start)));
|
||||
} else {
|
||||
$duration = '';
|
||||
}
|
||||
$conference_record = array(
|
||||
// assign title to the field in the array record
|
||||
'component' => $jitsi_component,
|
||||
'start' => $start,
|
||||
'end' => $end,
|
||||
'duration' => $duration,
|
||||
'conference ID' => $conference_id,
|
||||
'conference name' => $conference_name,
|
||||
'participants' => $participants,
|
||||
'name count' => $name_count,
|
||||
'conference host' => $conference_host
|
||||
);
|
||||
// populate the result array
|
||||
array_push($conferences['records'], $conference_record);
|
||||
|
||||
// we only take the first 10 results
|
||||
$i++;
|
||||
if ($i == 10) break;
|
||||
}
|
||||
}
|
||||
|
||||
// prepare the widget
|
||||
$widget['full'] = false;
|
||||
$widget['name'] = 'LastConferences';
|
||||
$widget['title'] = 'The last ' . $conference_number . ' conferences';
|
||||
$widget['collapsible'] = true;
|
||||
$widget['collapsed'] = false;
|
||||
$widget['filter'] = false;
|
||||
$widget['pagination'] = false;
|
||||
|
||||
if (!empty($conferences['records'])) {
|
||||
$widget['full'] = true;
|
||||
$widget['table_headers'] = array_keys($conferences['records'][0]);
|
||||
$widget['table_records'] = $conferences['records'];
|
||||
}
|
||||
|
||||
// display the widget
|
||||
include '../app/templates/widget.php';
|
||||
|
||||
}
|
||||
|
||||
// prepare the widget
|
||||
$widget['full'] = false;
|
||||
$widget['name'] = 'LastConferences';
|
||||
$widget['title'] = 'The last ' . $conference_number . ' conferences';
|
||||
$widget['collapsible'] = true;
|
||||
$widget['collapsed'] = false;
|
||||
$widget['filter'] = false;
|
||||
$widget['pagination'] = false;
|
||||
|
||||
if (!empty($conferences['records'])) {
|
||||
$widget['full'] = true;
|
||||
$widget['table_headers'] = array_keys($conferences['records'][0]);
|
||||
$widget['table_records'] = $conferences['records'];
|
||||
}
|
||||
|
||||
// display the widget
|
||||
include '../app/templates/widget.php';
|
||||
|
||||
?>
|
||||
|
|
|
@ -0,0 +1,207 @@
|
|||
<?php
|
||||
|
||||
// Get any new messages
|
||||
include '../app/includes/messages.php';
|
||||
include '../app/includes/messages-show.php';
|
||||
|
||||
$action = $_REQUEST['action'] ?? '';
|
||||
$agent = $_REQUEST['agent'] ?? '';
|
||||
|
||||
require '../app/classes/config.php';
|
||||
require '../app/classes/agent.php';
|
||||
require '../app/classes/conference.php';
|
||||
|
||||
$configObject = new Config();
|
||||
$agentObject = new Agent($dbWeb);
|
||||
|
||||
// connect to Jilo database
|
||||
$response = connectDB($config, 'jilo', $platformDetails[0]['jilo_database'], $platform_id);
|
||||
|
||||
// if DB connection has error, display it and stop here
|
||||
if ($response['db'] === null) {
|
||||
Messages::flash('ERROR', 'DEFAULT', $response['error']);
|
||||
|
||||
// otherwise if DB connection is OK, go on
|
||||
} else {
|
||||
$db = $response['db'];
|
||||
|
||||
$conferenceObject = new Conference($db);
|
||||
|
||||
switch ($item) {
|
||||
|
||||
case 'graphs':
|
||||
// Connect to Jilo database for log data
|
||||
$jilo_response = connectDB($config, 'jilo', $platformDetails[0]['jilo_database'], $platform_id);
|
||||
if ($jilo_response['db'] === null) {
|
||||
Messages::flash('ERROR', 'DEFAULT', $jilo_response['error']);
|
||||
break;
|
||||
}
|
||||
$jilo_db = $jilo_response['db'];
|
||||
|
||||
// Get date range for the last 7 days
|
||||
$from_time = date('Y-m-d', strtotime('-7 days'));
|
||||
$until_time = date('Y-m-d');
|
||||
|
||||
// Define graphs to show
|
||||
$graphs = [
|
||||
[
|
||||
'graph_name' => 'conferences',
|
||||
'graph_title' => 'Conferences in "' . htmlspecialchars($platformDetails[0]['name']) . '" over time',
|
||||
'datasets' => []
|
||||
],
|
||||
[
|
||||
'graph_name' => 'participants',
|
||||
'graph_title' => 'Participants in "' . htmlspecialchars($platformDetails[0]['name']) . '" over time',
|
||||
'datasets' => []
|
||||
]
|
||||
];
|
||||
|
||||
// Get Jitsi API data
|
||||
$conferences_api = $agentObject->getHistoricalData(
|
||||
$platform_id,
|
||||
'jicofo',
|
||||
'conferences',
|
||||
$from_time,
|
||||
$until_time
|
||||
);
|
||||
$graphs[0]['datasets'][] = [
|
||||
'data' => $conferences_api,
|
||||
'label' => 'Conferences from Jitsi API',
|
||||
'color' => 'rgba(75, 192, 192, 1)'
|
||||
];
|
||||
|
||||
// Get conference data from logs
|
||||
$conferences_logs = $conferenceObject->conferenceNumber(
|
||||
$from_time,
|
||||
$until_time
|
||||
);
|
||||
$graphs[0]['datasets'][] = [
|
||||
'data' => $conferences_logs,
|
||||
'label' => 'Conferences from Logs',
|
||||
'color' => 'rgba(255, 99, 132, 1)'
|
||||
];
|
||||
|
||||
// Get participants data
|
||||
$participants_api = $agentObject->getHistoricalData(
|
||||
$platform_id,
|
||||
'jicofo',
|
||||
'participants',
|
||||
$from_time,
|
||||
$until_time
|
||||
);
|
||||
$graphs[1]['datasets'][] = [
|
||||
'data' => $participants_api,
|
||||
'label' => 'Participants from Jitsi API',
|
||||
'color' => 'rgba(75, 192, 192, 1)'
|
||||
];
|
||||
|
||||
// Prepare data for template
|
||||
$graph = $graphs;
|
||||
|
||||
// prepare the widget
|
||||
$widget['full'] = false;
|
||||
$widget['name'] = 'Graphs';
|
||||
$widget['title'] = 'Jitsi graphs';
|
||||
|
||||
include '../app/templates/graphs-combined.php';
|
||||
break;
|
||||
|
||||
case 'latest':
|
||||
// Define metrics to display
|
||||
$metrics = [
|
||||
'Basic stats' => [
|
||||
'conferences' => ['label' => 'Current conferences', 'link' => 'conferences'],
|
||||
'participants' => ['label' => 'Current participants', 'link' => 'participants'],
|
||||
'total_conferences_created' => ['label' => 'Total conferences created'],
|
||||
'total_participants' => ['label' => 'Total participants']
|
||||
],
|
||||
'Bridge stats' => [
|
||||
'bridge_selector.bridge_count' => ['label' => 'Bridge count'],
|
||||
'bridge_selector.operational_bridge_count' => ['label' => 'Operational bridges'],
|
||||
'bridge_selector.in_shutdown_bridge_count' => ['label' => 'Bridges in shutdown']
|
||||
],
|
||||
'Jibri stats' => [
|
||||
'jibri_detector.count' => ['label' => 'Jibri count'],
|
||||
'jibri_detector.available' => ['label' => 'Jibri idle'],
|
||||
'jibri.live_streaming_active' => ['label' => 'Jibri active streaming'],
|
||||
'jibri.recording_active' => ['label' => 'Jibri active recording'],
|
||||
|
||||
],
|
||||
'System stats' => [
|
||||
'threads' => ['label' => 'Threads'],
|
||||
'stress_level' => ['label' => 'Stress level'],
|
||||
'version' => ['label' => 'Version']
|
||||
]
|
||||
];
|
||||
|
||||
// Get latest data for all the agents
|
||||
$agents = ['jvb', 'jicofo', 'jibri', 'prosody', 'nginx'];
|
||||
$widget['records'] = [];
|
||||
|
||||
// Initialize records for each agent
|
||||
foreach ($agents as $agent) {
|
||||
$record = [
|
||||
'table_headers' => strtoupper($agent),
|
||||
'metrics' => [],
|
||||
'timestamp' => null
|
||||
];
|
||||
|
||||
// Fetch all metrics for this agent
|
||||
foreach ($metrics as $section => $section_metrics) {
|
||||
foreach ($section_metrics as $metric => $metricConfig) {
|
||||
$data = $agentObject->getLatestData($platform_id, $agent, $metric);
|
||||
if ($data !== null) {
|
||||
$record['metrics'][$section][$metric] = [
|
||||
'value' => $data['value'],
|
||||
'label' => $metricConfig['label'],
|
||||
'link' => isset($metricConfig['link']) ? $metricConfig['link'] : null
|
||||
];
|
||||
// Use the most recent timestamp
|
||||
if ($record['timestamp'] === null || strtotime($data['timestamp']) > strtotime($record['timestamp'])) {
|
||||
$record['timestamp'] = $data['timestamp'];
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (!empty($record['metrics'])) {
|
||||
$widget['records'][] = $record;
|
||||
}
|
||||
}
|
||||
|
||||
// prepare the widget
|
||||
$widget['full'] = false;
|
||||
$widget['name'] = 'LatestData';
|
||||
$widget['title'] = 'Latest data from Jilo Agents';
|
||||
$widget['collapsible'] = false;
|
||||
$widget['collapsed'] = false;
|
||||
$widget['filter'] = false;
|
||||
$widget['metrics'] = $metrics; // Pass metrics configuration to template
|
||||
if (!empty($widget['records'])) {
|
||||
$widget['full'] = true;
|
||||
}
|
||||
$widget['pagination'] = false;
|
||||
|
||||
include '../app/templates/latest-data.php';
|
||||
break;
|
||||
|
||||
case 'configjs':
|
||||
$mode = $_REQUEST['mode'] ?? '';
|
||||
$raw = ($mode === 'raw');
|
||||
$platformConfigjs = $configObject->getPlatformConfigjs($platformDetails[0]['jitsi_url'], $raw);
|
||||
include '../app/templates/data-configjs.php';
|
||||
break;
|
||||
|
||||
case 'interfaceconfigjs':
|
||||
$mode = $_REQUEST['mode'] ?? '';
|
||||
$raw = ($mode === 'raw');
|
||||
$platformInterfaceConfigjs = $configObject->getPlatformInterfaceConfigjs($platformDetails[0]['jitsi_url'], $raw);
|
||||
include '../app/templates/data-interfaceconfigjs.php';
|
||||
break;
|
||||
|
||||
default:
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
?>
|
|
@ -1,49 +0,0 @@
|
|||
<?php
|
||||
|
||||
// FIXME example data
|
||||
$one = date('Y-m-d',strtotime("-5 days"));
|
||||
$two = date('Y-m-d',strtotime("-4 days"));
|
||||
$three = date('Y-m-d',strtotime("-2 days"));
|
||||
$four = date('Y-m-d',strtotime("-1 days"));
|
||||
|
||||
$graph[0]['data0'] = [
|
||||
['date' => $one, 'value' => 10],
|
||||
['date' => $two, 'value' => 20],
|
||||
['date' => $three, 'value' => 15],
|
||||
['date' => $four, 'value' => 25],
|
||||
];
|
||||
|
||||
$graph[0]['data1'] = [
|
||||
['date' => $one, 'value' => 12],
|
||||
['date' => $two, 'value' => 23],
|
||||
['date' => $three, 'value' => 11],
|
||||
['date' => $four, 'value' => 27],
|
||||
];
|
||||
|
||||
$graph[0]['graph_name'] = 'conferences';
|
||||
$graph[0]['graph_title'] = 'Conferences in "' . htmlspecialchars($platformDetails[0]['name']) . '" over time';
|
||||
$graph[0]['graph_data0_label'] = 'Conferences from Jitsi logs (Jilo)';
|
||||
$graph[0]['graph_data1_label'] = 'Conferences from Jitsi API (Jilo Agents)';
|
||||
|
||||
$graph[1]['data0'] = [
|
||||
['date' => $one, 'value' => 20],
|
||||
['date' => $two, 'value' => 30],
|
||||
['date' => $three, 'value' => 15],
|
||||
['date' => $four, 'value' => 55],
|
||||
];
|
||||
|
||||
$graph[1]['data1'] = [
|
||||
['date' => $one, 'value' => 22],
|
||||
['date' => $two, 'value' => 33],
|
||||
['date' => $three, 'value' => 11],
|
||||
['date' => $four, 'value' => 57],
|
||||
];
|
||||
|
||||
$graph[1]['graph_name'] = 'participants';
|
||||
$graph[1]['graph_title'] = 'Participants in "' . htmlspecialchars($platformDetails[0]['name']) . '" over time';
|
||||
$graph[1]['graph_data0_label'] = 'Participants from Jitsi logs (Jilo)';
|
||||
$graph[1]['graph_data1_label'] = 'Participants from Jitsi API (Jilo Agents)';
|
||||
|
||||
include '../app/templates/graphs-combined.php';
|
||||
|
||||
?>
|
|
@ -1,5 +1,9 @@
|
|||
<?php
|
||||
|
||||
// Get any new messages
|
||||
include '../app/includes/messages.php';
|
||||
include '../app/includes/messages-show.php';
|
||||
|
||||
include '../app/templates/help-main.php';
|
||||
|
||||
?>
|
||||
|
|
|
@ -1,29 +0,0 @@
|
|||
<?php
|
||||
|
||||
require '../app/classes/agent.php';
|
||||
|
||||
$agentObject = new Agent($dbWeb);
|
||||
|
||||
$latestJvbConferences = $agentObject->getLatestData($platform_id, 'jvb', 'conferences');
|
||||
$latestJvbParticipants = $agentObject->getLatestData($platform_id, 'jvb', 'participants');
|
||||
$latestJicofoConferences = $agentObject->getLatestData($platform_id, 'jicofo', 'conferences');
|
||||
$latestJicofoParticipants = $agentObject->getLatestData($platform_id, 'jicofo', 'participants');
|
||||
|
||||
$widget['records'] = array();
|
||||
|
||||
// prepare the widget
|
||||
$widget['full'] = false;
|
||||
$widget['name'] = 'LatestData';
|
||||
$widget['title'] = 'Latest data from Jilo Agents';
|
||||
$widget['collapsible'] = false;
|
||||
$widget['collapsed'] = false;
|
||||
$widget['filter'] = false;
|
||||
if (!empty($latestJvbConferences) && !empty($latestJvbParticipants) && !empty($latestJicofoConferences) && !empty($latestJicofoParticipants)) {
|
||||
$widget['full'] = true;
|
||||
}
|
||||
$widget['pagination'] = true;
|
||||
|
||||
|
||||
include '../app/templates/latest-data.php';
|
||||
|
||||
?>
|
|
@ -1,5 +1,18 @@
|
|||
<?php
|
||||
|
||||
/**
|
||||
* User login
|
||||
*
|
||||
* This page ("login") handles user login, session management, cookie handling, and error logging.
|
||||
* Supports "remember me" functionality to extend session duration.
|
||||
*
|
||||
* Actions Performed:
|
||||
* - Validates login credentials.
|
||||
* - Manages session and cookies based on "remember me" option.
|
||||
* - Logs successful and failed login attempts.
|
||||
* - Displays login form and optional custom messages.
|
||||
*/
|
||||
|
||||
// clear the global error var before login
|
||||
unset($error);
|
||||
|
||||
|
@ -8,61 +21,88 @@ try {
|
|||
// connect to database
|
||||
$dbWeb = connectDB($config);
|
||||
|
||||
if ( $_SERVER['REQUEST_METHOD'] == 'POST' ) {
|
||||
$username = $_POST['username'];
|
||||
$password = $_POST['password'];
|
||||
// Initialize RateLimiter
|
||||
require_once '../app/classes/ratelimiter.php';
|
||||
$rateLimiter = new RateLimiter($dbWeb['db']);
|
||||
|
||||
// login successful
|
||||
if ( $userObject->login($username, $password) ) {
|
||||
// if remember_me is checked, max out the session
|
||||
if (isset($_POST['remember_me'])) {
|
||||
// 30*24*60*60 = 30 days
|
||||
$cookie_lifetime = 30 * 24 * 60 * 60;
|
||||
$setcookie_lifetime = time() + 30 * 24 * 60 * 60;
|
||||
$gc_maxlifetime = 30 * 24 * 60 * 60;
|
||||
} else {
|
||||
// 0 - session end on browser close
|
||||
// 1440 - 24 minutes (default)
|
||||
$cookie_lifetime = 0;
|
||||
$setcookie_lifetime = 0;
|
||||
$gc_maxlifetime = 1440;
|
||||
if ( $_SERVER['REQUEST_METHOD'] == 'POST' ) {
|
||||
try {
|
||||
$username = $_POST['username'];
|
||||
$password = $_POST['password'];
|
||||
|
||||
// Check if IP is blacklisted
|
||||
if ($rateLimiter->isIpBlacklisted($user_IP)) {
|
||||
throw new Exception(Messages::get('LOGIN', 'IP_BLACKLISTED')['message']);
|
||||
}
|
||||
|
||||
// set session lifetime and cookies
|
||||
setcookie('username', $username, [
|
||||
'expires' => $setcookie_lifetime,
|
||||
'path' => $config['folder'],
|
||||
'domain' => $config['domain'],
|
||||
'secure' => isset($_SERVER['HTTPS']),
|
||||
'httponly' => true,
|
||||
'samesite' => 'Strict'
|
||||
]);
|
||||
// Check rate limiting (but skip if IP is whitelisted)
|
||||
if (!$rateLimiter->isIpWhitelisted($user_IP)) {
|
||||
$attempts = $rateLimiter->getRecentAttempts($user_IP);
|
||||
if ($attempts >= $rateLimiter->maxAttempts) {
|
||||
throw new Exception(Messages::get('LOGIN', 'LOGIN_BLOCKED')['message']);
|
||||
}
|
||||
}
|
||||
|
||||
// redirect to index
|
||||
$_SESSION['notice'] = "Login successful";
|
||||
$user_id = $userObject->getUserId($username)[0]['id'];
|
||||
$logObject->insertLog($user_id, "Login: User \"$username\" logged in. IP: $user_IP", 'user');
|
||||
header('Location: index.php');
|
||||
exit();
|
||||
// login successful
|
||||
if ( $userObject->login($username, $password) ) {
|
||||
// if remember_me is checked, max out the session
|
||||
if (isset($_POST['remember_me'])) {
|
||||
// 30*24*60*60 = 30 days
|
||||
$cookie_lifetime = 30 * 24 * 60 * 60;
|
||||
$setcookie_lifetime = time() + 30 * 24 * 60 * 60;
|
||||
$gc_maxlifetime = 30 * 24 * 60 * 60;
|
||||
} else {
|
||||
// 0 - session end on browser close
|
||||
// 1440 - 24 minutes (default)
|
||||
$cookie_lifetime = 0;
|
||||
$setcookie_lifetime = 0;
|
||||
$gc_maxlifetime = 1440;
|
||||
}
|
||||
|
||||
// login failed
|
||||
} else {
|
||||
$_SESSION['error'] = "Login failed.";
|
||||
$user_id = $userObject->getUserId($username)[0]['id'];
|
||||
$logObject->insertLog($user_id, "Login: Failed login attempt for user \"$username\". IP: $user_IP", 'user');
|
||||
header('Location: index.php');
|
||||
exit();
|
||||
// set session lifetime and cookies
|
||||
setcookie('username', $username, [
|
||||
'expires' => $setcookie_lifetime,
|
||||
'path' => $config['folder'],
|
||||
'domain' => $config['domain'],
|
||||
'secure' => isset($_SERVER['HTTPS']),
|
||||
'httponly' => true,
|
||||
'samesite' => 'Strict'
|
||||
]);
|
||||
|
||||
// Log successful login
|
||||
$user_id = $userObject->getUserId($username)[0]['id'];
|
||||
$logObject->insertLog($user_id, "Login: User \"$username\" logged in. IP: $user_IP", 'user');
|
||||
|
||||
// Set success message and redirect
|
||||
Messages::flash('LOGIN', 'LOGIN_SUCCESS', null, true);
|
||||
header('Location: ' . htmlspecialchars($app_root));
|
||||
exit();
|
||||
} else {
|
||||
throw new Exception(Messages::get('LOGIN', 'LOGIN_FAILED')['message']);
|
||||
}
|
||||
} catch (Exception $e) {
|
||||
// Log the failed attempt
|
||||
Messages::flash('ERROR', 'DEFAULT', $e->getMessage());
|
||||
if (isset($username)) {
|
||||
$user_id = $userObject->getUserId($username)[0]['id'] ?? 0;
|
||||
$logObject->insertLog($user_id, "Login: Failed login attempt for user \"$username\". IP: $user_IP. Reason: {$e->getMessage()}", 'user');
|
||||
}
|
||||
}
|
||||
}
|
||||
} catch (Exception $e) {
|
||||
$error = getError('There was an unexpected error. Please try again.', $e->getMessage());
|
||||
Messages::flash('ERROR', 'DEFAULT', 'There was an unexpected error. Please try again.');
|
||||
}
|
||||
|
||||
// Show configured login message if any
|
||||
if (!empty($config['login_message'])) {
|
||||
$notice = $config['login_message'];
|
||||
include '../app/templates/block-message.php';
|
||||
echo Messages::render('NOTICE', 'DEFAULT', $config['login_message'], false, false, false);
|
||||
}
|
||||
|
||||
// Get any new messages
|
||||
include '../app/includes/messages.php';
|
||||
include '../app/includes/messages-show.php';
|
||||
|
||||
// Load the template
|
||||
include '../app/templates/form-login.php';
|
||||
|
||||
?>
|
||||
|
|
|
@ -1,8 +1,23 @@
|
|||
<?php
|
||||
|
||||
//
|
||||
// logs listings
|
||||
//
|
||||
/**
|
||||
* Logs listings
|
||||
*
|
||||
* This page ("logs") retrieves and displays logs for a specified user within a time range.
|
||||
* It supports pagination and filtering, and generates a widget to display the logs.
|
||||
*/
|
||||
|
||||
// Get any new messages
|
||||
include '../app/includes/messages.php';
|
||||
include '../app/includes/messages-show.php';
|
||||
|
||||
// Check for rights; user or system
|
||||
if (($userObject->hasRight($user_id, 'superuser') ||
|
||||
$userObject->hasRight($user_id, 'view app logs'))) {
|
||||
$scope = 'system';
|
||||
} else {
|
||||
$scope = 'user';
|
||||
}
|
||||
|
||||
// specify time range
|
||||
include '../app/helpers/time_range.php';
|
||||
|
@ -13,9 +28,6 @@ $browse_page = $_REQUEST['p'] ?? 1;
|
|||
$browse_page = (int)$browse_page;
|
||||
$offset = ($browse_page -1) * $items_per_page;
|
||||
|
||||
// user or system
|
||||
$scope = 'user';
|
||||
|
||||
// prepare the result
|
||||
$search = $logObject->readLog($user_id, $scope, $offset, $items_per_page);
|
||||
$search_all = $logObject->readLog($user_id, $scope);
|
||||
|
|
|
@ -1,155 +1,177 @@
|
|||
<?php
|
||||
|
||||
/**
|
||||
* Participants information
|
||||
*
|
||||
* This page ("participants") retrieves and displays participant information for conferences.
|
||||
* Allows filtering by participant ID, name, or IP address, and listing within a specified time range.
|
||||
* Supports pagination.
|
||||
*/
|
||||
|
||||
// Get any new messages
|
||||
include '../app/includes/messages.php';
|
||||
include '../app/includes/messages-show.php';
|
||||
|
||||
require '../app/classes/participant.php';
|
||||
|
||||
// connect to database
|
||||
$db = connectDB($config, 'jilo', $platformDetails[0]['jilo_database'], $platform_id);
|
||||
$response = connectDB($config, 'jilo', $platformDetails[0]['jilo_database'], $platform_id);
|
||||
|
||||
// specify time range
|
||||
include '../app/helpers/time_range.php';
|
||||
// if DB connection has error, display it and stop here
|
||||
if ($response['db'] === null) {
|
||||
Messages::flash('ERROR', 'DEFAULT', $response['error']);
|
||||
|
||||
// participant id/name/IP are specified when searching specific participant(s)
|
||||
// participant name - this is 'stats_id' in the db
|
||||
// either id, name, OR IP - in that order
|
||||
// we use $_REQUEST, so that both links and forms work
|
||||
if (isset($_REQUEST['id']) && $_REQUEST['id'] != '') {
|
||||
$participantId = $_REQUEST['id'];
|
||||
unset($_REQUEST['name']);
|
||||
unset($participantName);
|
||||
} elseif (isset($_REQUEST['name']) && $_REQUEST['name'] != '') {
|
||||
unset($participantId);
|
||||
$participantName = $_REQUEST['name'];
|
||||
} elseif (isset($_REQUEST['ip']) && $_REQUEST['ip'] != '') {
|
||||
unset($participantId);
|
||||
$participantIp = $_REQUEST['ip'];
|
||||
// otherwise if DB connection is OK, go on
|
||||
} else {
|
||||
unset($participantId);
|
||||
unset($participantName);
|
||||
}
|
||||
$db = $response['db'];
|
||||
|
||||
// specify time range
|
||||
include '../app/helpers/time_range.php';
|
||||
|
||||
//
|
||||
// Participant listings
|
||||
//
|
||||
|
||||
$participantObject = new Participant($db);
|
||||
|
||||
// pagination variables
|
||||
$items_per_page = 15;
|
||||
$browse_page = $_REQUEST['p'] ?? 1;
|
||||
$browse_page = (int)$browse_page;
|
||||
$offset = ($browse_page -1) * $items_per_page;
|
||||
|
||||
// search and list specific participant ID
|
||||
if (isset($participantId)) {
|
||||
$search = $participantObject->conferenceByParticipantId($participantId, $from_time, $until_time, $offset, $items_per_page);
|
||||
$search_all = $participantObject->conferenceByParticipantId($participantId, $from_time, $until_time);
|
||||
// search and list specific participant name (stats_id)
|
||||
} elseif (isset($participantName)) {
|
||||
$search = $participantObject->conferenceByParticipantName($participantName, $from_time, $until_time, $offset, $items_per_page);
|
||||
$search_all = $participantObject->conferenceByParticipantName($participantName, $from_time, $until_time);
|
||||
// search and list specific participant IP
|
||||
} elseif (isset($participantIp)) {
|
||||
$search = $participantObject->conferenceByParticipantIP($participantIp, $from_time, $until_time, $offset, $items_per_page);
|
||||
$search_all = $participantObject->conferenceByParticipantIP($participantIp, $from_time, $until_time);
|
||||
// list of all participants (default)
|
||||
} else {
|
||||
// prepare the result
|
||||
$search = $participantObject->participantsAll($from_time, $until_time, $offset, $items_per_page);
|
||||
$search_all = $participantObject->participantsAll($from_time, $until_time);
|
||||
}
|
||||
|
||||
if (!empty($search)) {
|
||||
// we get total items and number of pages
|
||||
$item_count = count($search_all);
|
||||
$page_count = ceil($item_count / $items_per_page);
|
||||
|
||||
$participants = array();
|
||||
$participants['records'] = array();
|
||||
|
||||
foreach ($search as $item) {
|
||||
extract($item);
|
||||
|
||||
// search and list specific participant ID
|
||||
if (isset($participantId)) {
|
||||
$participant_record = array(
|
||||
// assign title to the field in the array record
|
||||
'time' => $time,
|
||||
'conference ID' => $conference_id,
|
||||
'conference name' => $conference_name,
|
||||
'conference host' => $conference_host,
|
||||
'loglevel' => $loglevel,
|
||||
'participant ID' => $participant_id,
|
||||
'event' => $event_type,
|
||||
'parameter' => $event_param
|
||||
);
|
||||
// search and list specific participant name (stats_id)
|
||||
} elseif (isset($participantName)) {
|
||||
$participant_record = array(
|
||||
// assign title to the field in the array record
|
||||
'time' => $time,
|
||||
'conference ID' => $conference_id,
|
||||
'conference name' => $conference_name,
|
||||
'conference host' => $conference_host,
|
||||
'loglevel' => $loglevel,
|
||||
'participant ID' => $participant_id,
|
||||
'event' => $event_type,
|
||||
'parameter' => $event_param
|
||||
);
|
||||
// search and list specific participant IP
|
||||
} elseif (isset($participantIp)) {
|
||||
$participant_record = array(
|
||||
// assign title to the field in the array record
|
||||
'time' => $time,
|
||||
'conference ID' => $conference_id,
|
||||
'conference name' => $conference_name,
|
||||
'conference host' => $conference_host,
|
||||
'loglevel' => $loglevel,
|
||||
'participant ID' => $participant_id,
|
||||
'event' => $event_type,
|
||||
'parameter' => $event_param
|
||||
);
|
||||
// list of all participants (default)
|
||||
} else {
|
||||
$participant_record = array(
|
||||
// assign title to the field in the array record
|
||||
'component' => $jitsi_component,
|
||||
'participant ID' => $endpoint_id,
|
||||
'conference ID' => $conference_id
|
||||
);
|
||||
}
|
||||
|
||||
// populate the result array
|
||||
array_push($participants['records'], $participant_record);
|
||||
// participant id/name/IP are specified when searching specific participant(s)
|
||||
// participant name - this is 'stats_id' in the db
|
||||
// either id, name, OR IP - in that order
|
||||
// we use $_REQUEST, so that both links and forms work
|
||||
if (isset($_REQUEST['id']) && $_REQUEST['id'] != '') {
|
||||
$participantId = $_REQUEST['id'];
|
||||
unset($_REQUEST['name']);
|
||||
unset($participantName);
|
||||
} elseif (isset($_REQUEST['name']) && $_REQUEST['name'] != '') {
|
||||
unset($participantId);
|
||||
$participantName = $_REQUEST['name'];
|
||||
} elseif (isset($_REQUEST['ip']) && $_REQUEST['ip'] != '') {
|
||||
unset($participantId);
|
||||
$participantIp = $_REQUEST['ip'];
|
||||
} else {
|
||||
unset($participantId);
|
||||
unset($participantName);
|
||||
}
|
||||
}
|
||||
|
||||
// prepare the widget
|
||||
$widget['full'] = false;
|
||||
$widget['name'] = 'Participants';
|
||||
$widget['collapsible'] = false;
|
||||
$widget['collapsed'] = false;
|
||||
$widget['filter'] = true;
|
||||
$widget['pagination'] = true;
|
||||
|
||||
// widget title
|
||||
if (isset($_REQUEST['name']) && $_REQUEST['name'] != '') {
|
||||
$widget['title'] = 'Conferences with participant name (stats_id) matching "<strong>' . $_REQUEST['name'] . '"</strong>';
|
||||
} elseif (isset($_REQUEST['id']) && $_REQUEST['id'] != '') {
|
||||
$widget['title'] = 'Conference with participant ID matching "<strong>' . $_REQUEST['id'] . '"</strong>';
|
||||
} elseif (isset($participantIp)) {
|
||||
$widget['title'] = 'Conference with participant IP matching "<strong>' . $participantIp . '"</strong>';
|
||||
} else {
|
||||
$widget['title'] = 'All participants';
|
||||
}
|
||||
// widget records
|
||||
if (!empty($participants['records'])) {
|
||||
$widget['full'] = true;
|
||||
$widget['table_headers'] = array_keys($participants['records'][0]);
|
||||
$widget['table_records'] = $participants['records'];
|
||||
}
|
||||
//
|
||||
// Participant listings
|
||||
//
|
||||
|
||||
// display the widget
|
||||
include '../app/templates/widget.php';
|
||||
$participantObject = new Participant($db);
|
||||
|
||||
// pagination variables
|
||||
$items_per_page = 15;
|
||||
$browse_page = $_REQUEST['p'] ?? 1;
|
||||
$browse_page = (int)$browse_page;
|
||||
$offset = ($browse_page -1) * $items_per_page;
|
||||
|
||||
// search and list specific participant ID
|
||||
if (isset($participantId)) {
|
||||
$search = $participantObject->conferenceByParticipantId($participantId, $from_time, $until_time, $offset, $items_per_page);
|
||||
$search_all = $participantObject->conferenceByParticipantId($participantId, $from_time, $until_time);
|
||||
// search and list specific participant name (stats_id)
|
||||
} elseif (isset($participantName)) {
|
||||
$search = $participantObject->conferenceByParticipantName($participantName, $from_time, $until_time, $offset, $items_per_page);
|
||||
$search_all = $participantObject->conferenceByParticipantName($participantName, $from_time, $until_time);
|
||||
// search and list specific participant IP
|
||||
} elseif (isset($participantIp)) {
|
||||
$search = $participantObject->conferenceByParticipantIP($participantIp, $from_time, $until_time, $offset, $items_per_page);
|
||||
$search_all = $participantObject->conferenceByParticipantIP($participantIp, $from_time, $until_time);
|
||||
// list of all participants (default)
|
||||
} else {
|
||||
// prepare the result
|
||||
$search = $participantObject->participantsAll($from_time, $until_time, $offset, $items_per_page);
|
||||
$search_all = $participantObject->participantsAll($from_time, $until_time);
|
||||
}
|
||||
|
||||
if (!empty($search)) {
|
||||
// we get total items and number of pages
|
||||
$item_count = count($search_all);
|
||||
$page_count = ceil($item_count / $items_per_page);
|
||||
|
||||
$participants = array();
|
||||
$participants['records'] = array();
|
||||
|
||||
foreach ($search as $item) {
|
||||
extract($item);
|
||||
|
||||
// search and list specific participant ID
|
||||
if (isset($participantId)) {
|
||||
$participant_record = array(
|
||||
// assign title to the field in the array record
|
||||
'time' => $time,
|
||||
'conference ID' => $conference_id,
|
||||
'conference name' => $conference_name,
|
||||
'conference host' => $conference_host,
|
||||
'loglevel' => $loglevel,
|
||||
'participant ID' => $participant_id,
|
||||
'event' => $event_type,
|
||||
'parameter' => $event_param
|
||||
);
|
||||
// search and list specific participant name (stats_id)
|
||||
} elseif (isset($participantName)) {
|
||||
$participant_record = array(
|
||||
// assign title to the field in the array record
|
||||
'time' => $time,
|
||||
'conference ID' => $conference_id,
|
||||
'conference name' => $conference_name,
|
||||
'conference host' => $conference_host,
|
||||
'loglevel' => $loglevel,
|
||||
'participant ID' => $participant_id,
|
||||
'event' => $event_type,
|
||||
'parameter' => $event_param
|
||||
);
|
||||
// search and list specific participant IP
|
||||
} elseif (isset($participantIp)) {
|
||||
$participant_record = array(
|
||||
// assign title to the field in the array record
|
||||
'time' => $time,
|
||||
'conference ID' => $conference_id,
|
||||
'conference name' => $conference_name,
|
||||
'conference host' => $conference_host,
|
||||
'loglevel' => $loglevel,
|
||||
'participant ID' => $participant_id,
|
||||
'event' => $event_type,
|
||||
'parameter' => $event_param
|
||||
);
|
||||
// list of all participants (default)
|
||||
} else {
|
||||
$participant_record = array(
|
||||
// assign title to the field in the array record
|
||||
'component' => $jitsi_component,
|
||||
'participant ID' => $endpoint_id,
|
||||
'conference ID' => $conference_id
|
||||
);
|
||||
}
|
||||
|
||||
// populate the result array
|
||||
array_push($participants['records'], $participant_record);
|
||||
}
|
||||
}
|
||||
|
||||
// prepare the widget
|
||||
$widget['full'] = false;
|
||||
$widget['name'] = 'Participants';
|
||||
$widget['collapsible'] = false;
|
||||
$widget['collapsed'] = false;
|
||||
$widget['filter'] = true;
|
||||
$widget['pagination'] = true;
|
||||
|
||||
// widget title
|
||||
if (isset($_REQUEST['name']) && $_REQUEST['name'] != '') {
|
||||
$widget['title'] = 'Conferences with participant name (stats_id) matching "<strong>' . $_REQUEST['name'] . '"</strong>';
|
||||
} elseif (isset($_REQUEST['id']) && $_REQUEST['id'] != '') {
|
||||
$widget['title'] = 'Conference with participant ID matching "<strong>' . $_REQUEST['id'] . '"</strong>';
|
||||
} elseif (isset($participantIp)) {
|
||||
$widget['title'] = 'Conference with participant IP matching "<strong>' . $participantIp . '"</strong>';
|
||||
} else {
|
||||
$widget['title'] = 'All participants';
|
||||
}
|
||||
// widget records
|
||||
if (!empty($participants['records'])) {
|
||||
$widget['full'] = true;
|
||||
$widget['table_headers'] = array_keys($participants['records'][0]);
|
||||
$widget['table_records'] = $participants['records'];
|
||||
}
|
||||
|
||||
// display the widget
|
||||
include '../app/templates/widget.php';
|
||||
|
||||
}
|
||||
|
||||
?>
|
||||
|
|
|
@ -1,5 +1,21 @@
|
|||
<?php
|
||||
|
||||
/**
|
||||
* User profile management
|
||||
*
|
||||
* This page ("profile") handles user profile actions such as updating user details,
|
||||
* avatar management, and assigning or removing user rights.
|
||||
* It supports both form submissions and displaying profile templates.
|
||||
*
|
||||
* Actions handled:
|
||||
* - `remove`: Remove a user's avatar.
|
||||
* - `edit`: Edit user profile details, rights, or avatar.
|
||||
*/
|
||||
|
||||
// Get any new messages
|
||||
include '../app/includes/messages.php';
|
||||
include '../app/includes/messages-show.php';
|
||||
|
||||
$action = $_REQUEST['action'] ?? '';
|
||||
|
||||
// if a form is submitted, it's from the edit page
|
||||
|
|
|
@ -1,18 +1,21 @@
|
|||
<?php
|
||||
|
||||
/**
|
||||
* User registration
|
||||
*
|
||||
* This page ("register") handles user registration if the feature is enabled in the configuration.
|
||||
* It accepts a POST request with a username and password, attempts to register the user,
|
||||
* and redirects to the login page on success or displays an error message on failure.
|
||||
*/
|
||||
|
||||
// registration is allowed, go on
|
||||
if ($config['registration_enabled'] === true) {
|
||||
|
||||
// require '../app/classes/user.php';
|
||||
unset($error);
|
||||
|
||||
try {
|
||||
|
||||
// connect to database
|
||||
$dbWeb = connectDB($config);
|
||||
|
||||
// $userObject = new User($dbWeb);
|
||||
|
||||
if ( $_SERVER['REQUEST_METHOD'] == 'POST' ) {
|
||||
$username = $_POST['username'];
|
||||
$password = $_POST['password'];
|
||||
|
@ -22,27 +25,30 @@ if ($config['registration_enabled'] === true) {
|
|||
|
||||
// redirect to login
|
||||
if ($result === true) {
|
||||
$_SESSION['notice'] = "Registration successful.<br />You can log in now.";
|
||||
header('Location: index.php');
|
||||
Messages::flash('NOTICE', 'DEFAULT', "Registration successful.<br />You can log in now.");
|
||||
header('Location: ' . htmlspecialchars($app_root));
|
||||
exit();
|
||||
// registration fail, redirect to login
|
||||
} else {
|
||||
$_SESSION['error'] = "Registration failed. $result";
|
||||
header('Location: index.php');
|
||||
Messages::flash('ERROR', 'DEFAULT', "Registration failed. $result");
|
||||
header('Location: ' . htmlspecialchars($app_root));
|
||||
exit();
|
||||
}
|
||||
}
|
||||
} catch (Exception $e) {
|
||||
$error = $e->getMessage();
|
||||
Messages::flash('ERROR', 'DEFAULT', $e->getMessage());
|
||||
}
|
||||
|
||||
include '../app/templates/block-message.php';
|
||||
// Get any new messages
|
||||
include '../app/includes/messages.php';
|
||||
include '../app/includes/messages-show.php';
|
||||
|
||||
// Load the template
|
||||
include '../app/templates/form-register.php';
|
||||
|
||||
// registration disabled
|
||||
} else {
|
||||
$notice = 'Registration is disabled';
|
||||
include '../app/templates/block-message.php';
|
||||
echo Messages::render('NOTICE', 'DEFAULT', 'Registration is disabled', false);
|
||||
}
|
||||
|
||||
?>
|
||||
|
|
|
@ -0,0 +1,113 @@
|
|||
<?php
|
||||
|
||||
// Check if user has any of the required rights
|
||||
if (!($userObject->hasRight($user_id, 'superuser') ||
|
||||
$userObject->hasRight($user_id, 'edit whitelist') ||
|
||||
$userObject->hasRight($user_id, 'edit blacklist') ||
|
||||
$userObject->hasRight($user_id, 'edit ratelimiting'))) {
|
||||
include '../app/templates/error-unauthorized.php';
|
||||
exit;
|
||||
}
|
||||
|
||||
if (!isset($currentUser)) {
|
||||
include '../app/templates/error-unauthorized.php';
|
||||
exit;
|
||||
}
|
||||
|
||||
// Get current section
|
||||
$section = isset($_POST['section']) ? $_POST['section'] : (isset($_GET['section']) ? $_GET['section'] : 'whitelist');
|
||||
|
||||
// Initialize RateLimiter
|
||||
require_once '../app/classes/ratelimiter.php';
|
||||
$rateLimiter = new RateLimiter($dbWeb);
|
||||
|
||||
// Handle form submissions
|
||||
if ($_SERVER['REQUEST_METHOD'] === 'POST' && isset($_POST['action'])) {
|
||||
$action = $_POST['action'];
|
||||
|
||||
try {
|
||||
switch ($action) {
|
||||
case 'add_whitelist':
|
||||
if (!$userObject->hasRight($user_id, 'superuser') && !$userObject->hasRight($user_id, 'edit whitelist')) {
|
||||
throw new Exception(Messages::get('SECURITY', 'PERMISSION_DENIED')['message']);
|
||||
}
|
||||
if (empty($_POST['ip_address'])) {
|
||||
throw new Exception(Messages::get('SECURITY', 'IP_REQUIRED')['message']);
|
||||
}
|
||||
$is_network = isset($_POST['is_network']) ? 1 : 0;
|
||||
if (!$rateLimiter->addToWhitelist($_POST['ip_address'], $is_network, $_POST['description'] ?? '', $currentUser, $user_id)) {
|
||||
throw new Exception(Messages::get('SECURITY', 'WHITELIST_ADD_ERROR')['message']);
|
||||
}
|
||||
Messages::flash('SECURITY', 'WHITELIST_ADD_SUCCESS');
|
||||
break;
|
||||
|
||||
case 'remove_whitelist':
|
||||
if (!$userObject->hasRight($user_id, 'superuser') && !$userObject->hasRight($user_id, 'edit whitelist')) {
|
||||
throw new Exception(Messages::get('SECURITY', 'PERMISSION_DENIED')['message']);
|
||||
}
|
||||
if (empty($_POST['ip_address'])) {
|
||||
throw new Exception(Messages::get('SECURITY', 'IP_REQUIRED')['message']);
|
||||
}
|
||||
if (!$rateLimiter->removeFromWhitelist($_POST['ip_address'], $currentUser, $user_id)) {
|
||||
throw new Exception(Messages::get('SECURITY', 'WHITELIST_REMOVE_ERROR')['message']);
|
||||
}
|
||||
Messages::flash('SECURITY', 'WHITELIST_REMOVE_SUCCESS');
|
||||
break;
|
||||
|
||||
case 'add_blacklist':
|
||||
if (!$userObject->hasRight($user_id, 'superuser') && !$userObject->hasRight($user_id, 'edit blacklist')) {
|
||||
throw new Exception(Messages::get('SECURITY', 'PERMISSION_DENIED')['message']);
|
||||
}
|
||||
if (empty($_POST['ip_address'])) {
|
||||
throw new Exception(Messages::get('SECURITY', 'IP_REQUIRED')['message']);
|
||||
}
|
||||
$is_network = isset($_POST['is_network']) ? 1 : 0;
|
||||
$expiry_hours = !empty($_POST['expiry_hours']) ? intval($_POST['expiry_hours']) : null;
|
||||
if (!$rateLimiter->addToBlacklist($_POST['ip_address'], $is_network, $_POST['reason'] ?? '', $currentUser, $user_id, $expiry_hours)) {
|
||||
throw new Exception(Messages::get('SECURITY', 'BLACKLIST_ADD_ERROR')['message']);
|
||||
}
|
||||
Messages::flash('SECURITY', 'BLACKLIST_ADD_SUCCESS');
|
||||
break;
|
||||
|
||||
case 'remove_blacklist':
|
||||
if (!$userObject->hasRight($user_id, 'superuser') && !$userObject->hasRight($user_id, 'edit blacklist')) {
|
||||
throw new Exception(Messages::get('SECURITY', 'PERMISSION_DENIED')['message']);
|
||||
}
|
||||
if (empty($_POST['ip_address'])) {
|
||||
throw new Exception(Messages::get('SECURITY', 'IP_REQUIRED')['message']);
|
||||
}
|
||||
if (!$rateLimiter->removeFromBlacklist($_POST['ip_address'], $currentUser, $user_id)) {
|
||||
throw new Exception(Messages::get('SECURITY', 'BLACKLIST_REMOVE_ERROR')['message']);
|
||||
}
|
||||
Messages::flash('SECURITY', 'BLACKLIST_REMOVE_SUCCESS');
|
||||
break;
|
||||
}
|
||||
} catch (Exception $e) {
|
||||
$messages[] = ['category' => 'SECURITY', 'key' => 'CUSTOM_ERROR', 'custom_message' => $e->getMessage()];
|
||||
Messages::flash('SECURITY', 'CUSTOM_ERROR', 'custom_message');
|
||||
}
|
||||
|
||||
if (empty($messages)) {
|
||||
// Only redirect if there were no errors
|
||||
header("Location: {$app_root}?page=security§ion={$section}");
|
||||
exit;
|
||||
}
|
||||
}
|
||||
|
||||
// Always show rate limit info message for rate limiting section
|
||||
if ($section === 'ratelimit') {
|
||||
$messages[] = ['category' => 'SECURITY', 'key' => 'RATE_LIMIT_INFO'];
|
||||
}
|
||||
|
||||
// Get current lists
|
||||
$whitelisted = $rateLimiter->getWhitelistedIps();
|
||||
$blacklisted = $rateLimiter->getBlacklistedIps();
|
||||
|
||||
// Get any new messages
|
||||
include '../app/includes/messages.php';
|
||||
include '../app/includes/messages-show.php';
|
||||
|
||||
// Load the template
|
||||
include '../app/templates/security.php';
|
||||
|
||||
?>
|
|
@ -0,0 +1,72 @@
|
|||
<?php
|
||||
|
||||
/**
|
||||
* Jilo components status checks
|
||||
*
|
||||
* This page ("status") checks the status of various Jilo platform components
|
||||
* by fetching data from agents and determining their availability.
|
||||
* It generates output for each platform and agent.
|
||||
*/
|
||||
|
||||
// Get any new messages
|
||||
include '../app/includes/messages.php';
|
||||
include '../app/includes/messages-show.php';
|
||||
|
||||
require '../app/classes/agent.php';
|
||||
$agentObject = new Agent($dbWeb);
|
||||
|
||||
include '../app/templates/status-server.php';
|
||||
|
||||
// loop through all platforms to check their agents
|
||||
foreach ($platformsAll as $platform) {
|
||||
|
||||
// check if we can connect to the jilo database
|
||||
$response = connectDB($config, 'jilo', $platform['jilo_database'], $platform['id']);
|
||||
if ($response['error'] !== null) {
|
||||
$jilo_database_status = '<span class="text-danger">' . htmlspecialchars($response['error']) . '</span>';
|
||||
} else {
|
||||
$jilo_database_status = '<span class="text-success">OK</span>';
|
||||
}
|
||||
|
||||
include '../app/templates/status-platform.php';
|
||||
|
||||
// fetch agent details for the current platform
|
||||
$agentDetails = $agentObject->getAgentDetails($platform['id']);
|
||||
foreach ($agentDetails as $agent) {
|
||||
$agent_url = parse_url($agent['url']);
|
||||
$agent_protocol = isset($agent_url['scheme']) ? $agent_url['scheme']: '';
|
||||
$agent_host = isset($agent_url['host']) ? $agent_url['host']: '';
|
||||
$agent_port = isset($agent_url['port']) ? $agent_url['port']: '';
|
||||
|
||||
// we get agent data to check availability
|
||||
$agent_response = $agentObject->fetchAgent($agent['id'], true);
|
||||
$agent_data = json_decode($agent_response);
|
||||
|
||||
// determine agent availability based on response data
|
||||
if (json_last_error() === JSON_ERROR_NONE) {
|
||||
$agent_availability = '<span class="text-warning">unknown</span>';
|
||||
foreach ($agent_data as $key => $value) {
|
||||
if ($key === 'error') {
|
||||
$agent_availability = '<span class="text-danger">' . htmlspecialchars($value) . '</span>';
|
||||
break;
|
||||
}
|
||||
if (preg_match('/_state$/', $key)) {
|
||||
if ($value === 'error') {
|
||||
$agent_availability = '<span class="text-danger">not running</span>';
|
||||
break;
|
||||
}
|
||||
if ($value === 'running') {
|
||||
$agent_availability = '<span class="text-success">running</span>';
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
} else {
|
||||
$agent_availability = 'json error';
|
||||
}
|
||||
|
||||
include '../app/templates/status-agent.php';
|
||||
}
|
||||
}
|
||||
|
||||
?>
|
|
@ -22,11 +22,13 @@
|
|||
// print_r($_SESSION);
|
||||
?>
|
||||
<?php if (isset($_SESSION["agent{$agent['id']}_cache"])) { ?>
|
||||
<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($jwt) ?>', 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($jwt) ?>', true)">fetch data</button>
|
||||
<button id="agent<?= htmlspecialchars($agent['id']) ?>-cache" 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" class="btn btn-danger" data-toggle="tooltip" data-trigger="hover" data-placement="bottom" title="clear cache" onclick="clearCache('<?= htmlspecialchars($agent['id']) ?>')">clear cache</button>
|
||||
<span id="cacheInfo<?= htmlspecialchars($agent['id']) ?>" style="margin: 5px 0;"></span>
|
||||
<?php } else { ?>
|
||||
<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($jwt) ?>', 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($jwt) ?>')">fetch data</button>
|
||||
<button style="display: none" disabled id="agent<?= htmlspecialchars($agent['id']) ?>-cache" 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 style="display: none" disabled id="agent<?= htmlspecialchars($agent['id']) ?>-clear" class="btn btn-danger" data-toggle="tooltip" data-trigger="hover" data-placement="bottom" title="clear cache" onclick="clearCache('<?= htmlspecialchars($agent['id']) ?>')">clear cache</button>
|
||||
|
|
|
@ -1,7 +0,0 @@
|
|||
<?php if (isset($error)) { ?>
|
||||
<div class="error"><?= $error ?></div>
|
||||
<?php } ?>
|
||||
|
||||
<?php if (isset($notice)) { ?>
|
||||
<div class="notice"><?= $notice ?></div>
|
||||
<?php } ?>
|
|
@ -15,12 +15,15 @@
|
|||
<select class="form-control" type="text" name="type" id="agent_type_id" required>
|
||||
<option></option>
|
||||
<?php foreach ($jilo_agent_types as $agent_type) { ?>
|
||||
<option value="<?= htmlspecialchars($agent_type['id']) ?>">
|
||||
<option value="<?= htmlspecialchars($agent_type['id']) ?>"<?php
|
||||
if (in_array($agent_type['id'], $jilo_agent_types_in_platform)) {
|
||||
echo 'disabled="disabled"';
|
||||
} ?>>
|
||||
<?= htmlspecialchars($agent_type['description']) ?>
|
||||
</option>
|
||||
<?php } ?>
|
||||
</select>
|
||||
<p class="text-start"><small>type of agent (meet, jvb, jibri, all)</small></p>
|
||||
<p class="text-start"><small>type of agent (meet, jvb, jibri, etc.)<br />if a type has already been aded, it's disabled here</small></p>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
|
@ -46,6 +49,17 @@
|
|||
</div>
|
||||
</div>
|
||||
|
||||
<div class="row mb-3">
|
||||
<div class="col-md-4 text-end">
|
||||
<label for="check_period" class="form-label">check period</label>
|
||||
<span class="text-danger" style="margin-right: -12px;">*</span>
|
||||
</div>
|
||||
<div class="col-md-8">
|
||||
<input class="form-control" type="text" name="check_period" value="0" required />
|
||||
<p class="text-start"><small>period in minutes for the automatic agent check (0 disables it)</small></p>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<input type="hidden" name="new" value="true" />
|
||||
<input type="hidden" name="item" value="agent" />
|
||||
|
||||
|
|
|
@ -0,0 +1,27 @@
|
|||
|
||||
<!-- widget "config file" -->
|
||||
<div class="card text-center w-75 mx-lef">
|
||||
<p class="h4 card-header">Jilo configuration file :: edit</p>
|
||||
<div class="card-body">
|
||||
<div class="card-text">
|
||||
<p class="text-danger"><strong>this may break everything, use with extreme caution</strong></p>
|
||||
</div>
|
||||
<form method="POST" action="<?= htmlspecialchars($app_root) ?>?page=config&item=config_file">
|
||||
|
||||
<?php
|
||||
include '../app/helpers/render.php';
|
||||
editConfig($config, '0');
|
||||
echo "\n";
|
||||
?>
|
||||
|
||||
<p class="text-danger"><strong>this may break everything, use with extreme caution</strong></p>
|
||||
<br />
|
||||
<input type="hidden" name="item" value="config_file" />
|
||||
<a class="btn btn-outline-secondary btn-sm" href="<?= htmlspecialchars($app_root) ?>?page=config&item=config_file" />Cancel</a>
|
||||
|
||||
<input type="submit" class="btn btn-danger btn-sm" value="Save" />
|
||||
</form>
|
||||
|
||||
</div>
|
||||
</div>
|
||||
<!-- /widget "config file" -->
|
|
@ -0,0 +1,17 @@
|
|||
|
||||
<!-- widget "config file" -->
|
||||
<div class="card text-center w-75 mx-lef">
|
||||
<p class="h4 card-header">Jilo configuration file</p>
|
||||
<div class="card-body">
|
||||
|
||||
<?php
|
||||
include '../app/helpers/render.php';
|
||||
renderConfig($config, '0');
|
||||
echo "\n";
|
||||
?>
|
||||
|
||||
<br />
|
||||
<a class="btn btn-outline-danger btn-sm" href="<?= htmlspecialchars($app_root) ?>?page=config&item=config_file&action=edit" />Edit</a>
|
||||
</div>
|
||||
</div>
|
||||
<!-- /widget "config file" -->
|
|
@ -46,6 +46,17 @@
|
|||
</div>
|
||||
</div>
|
||||
|
||||
<div class="row mb-3">
|
||||
<div class="col-md-4 text-end">
|
||||
<label for="check_period" class="form-label">check period</label>
|
||||
<span class="text-danger" style="margin-right: -12px;">*</span>
|
||||
</div>
|
||||
<div class="col-md-8">
|
||||
<input class="form-control" type="text" name="check_period" value="<?= htmlspecialchars($agentDetails[0]['check_period']) ?>" required />
|
||||
<p class="text-start"><small>period in minutes for the automatic agent check (0 disables it)</small></p>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
|
||||
<br />
|
||||
<input type="hidden" name="agent" value="<?= htmlspecialchars($agentDetails[0]['id']) ?>" />
|
||||
|
|
|
@ -0,0 +1,50 @@
|
|||
|
||||
<!-- widget "hosts" -->
|
||||
<div class="card text-center w-50 mx-lef">
|
||||
<p class="h4 card-header">Add new host in Jitsi platform <strong><?= htmlspecialchars($platformDetails[0]['name']) ?></strong></p>
|
||||
<div class="card-body">
|
||||
<form method="POST" action="<?= htmlspecialchars($app_root) ?>?page=config&item=host">
|
||||
|
||||
<div class="row mb-3">
|
||||
<div class="col-md-4 text-end">
|
||||
<label for="address" class="form-label">address</label>
|
||||
<span class="text-danger" style="margin-right: -12px;">*</span>
|
||||
</div>
|
||||
<div class="col-md-8">
|
||||
<input class="form-control" type="text" name="address" value="" required autofocus />
|
||||
<p class="text-start"><small>DNS name or IP address of the machine</small></p>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="row mb-3">
|
||||
<div class="col-md-4 text-end">
|
||||
<label for="port" class="form-label">port</label>
|
||||
<span class="text-danger" style="margin-right: -12px;">*</span>
|
||||
</div>
|
||||
<div class="col-md-8">
|
||||
<input class="form-control" type="text" name="port" value="" required />
|
||||
<p class="text-start"><small>port on which the Jilo Agent is listening</small></p>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="row mb-3">
|
||||
<div class="col-md-4 text-end">
|
||||
<label for="name" class="form-label">name</label>
|
||||
</div>
|
||||
<div class="col-md-8">
|
||||
<input class="form-control" type="text" name="name" value="" />
|
||||
<p class="text-start"><small>description or name of the host (optional)</small></p>
|
||||
</div>
|
||||
</div>
|
||||
<input type="hidden" name="platform" value="<?= htmlspecialchars($platformDetails[0]['id'])?>" />
|
||||
<input type="hidden" name="item" value="host" />
|
||||
<input type="hidden" name="new" value="true" />
|
||||
|
||||
<br />
|
||||
<a class="btn btn-outline-secondary btn-sm" href="<?= htmlspecialchars($app_root) ?>?page=config&item=host&platform=<?= htmlspecialchars($platform_id) ?>&host=<?= htmlspecialchars($host) ?>#platform<?= htmlspecialchars($platform_id) ?>host<?= htmlspecialchars($host) ?>" />Cancel</a>
|
||||
|
||||
<input type="submit" class="btn btn-primary btn-sm" value="Save" />
|
||||
</form>
|
||||
</div>
|
||||
</div>
|
||||
<!-- /widget "hosts" -->
|
|
@ -0,0 +1,32 @@
|
|||
|
||||
<!-- widget "hosts" -->
|
||||
<div class="card text-center w-50 mx-lef">
|
||||
<p class="h4 card-header">Jilo configuration for Jitsi platform <strong>"<?= htmlspecialchars($platformDetails[0]['name']) ?>"</strong></p>
|
||||
<div class="card-body">
|
||||
<p class="card-text">delete a host:</p>
|
||||
<form method="POST" action="<?= htmlspecialchars($app_root) ?>?page=config&item=host">
|
||||
<?php
|
||||
foreach ($hostDetails[0] as $key => $value) {
|
||||
?>
|
||||
<div class="row mb-3">
|
||||
<div class="col-md-4 text-end">
|
||||
<label for="<?= htmlspecialchars($key) ?>" class="form-label"><?= htmlspecialchars($key) ?>:</label>
|
||||
</div>
|
||||
<div class="col-md-8">
|
||||
<div class="text-start"><?= htmlspecialchars($value ?? '') ?></div>
|
||||
<input type="hidden" name="<?= htmlspecialchars($key) ?>" value="<?= htmlspecialchars($value ?? '') ?>" />
|
||||
</div>
|
||||
</div>
|
||||
<?php } ?>
|
||||
<br />
|
||||
<input type="hidden" name="host" value="<?= htmlspecialchars($hostDetails[0]['id']) ?>" />
|
||||
<input type="hidden" name="delete" value="true" />
|
||||
<p class="h5 text-danger">Are you sure you want to delete this host?</p>
|
||||
<br />
|
||||
<a class="btn btn-outline-secondary btn-sm" href="<?= htmlspecialchars($app_root) ?>?page=config&item=host&platform=<?= htmlspecialchars($platform_id) ?>&host=<?= htmlspecialchars($host) ?>#platform<?= htmlspecialchars($platform_id) ?>host<?= htmlspecialchars($host) ?>" />Cancel</a>
|
||||
|
||||
<input type="submit" class="btn btn-danger btn-sm" value="Delete" />
|
||||
</form>
|
||||
</div>
|
||||
</div>
|
||||
<!-- /widget "hosts" -->
|
|
@ -0,0 +1,52 @@
|
|||
|
||||
<!-- widget "hosts" -->
|
||||
<div class="card text-center w-50 mx-lef">
|
||||
<p class="h4 card-header">Jilo configuration for Jitsi platform <strong>"<?= htmlspecialchars($platformDetails[0]['name']) ?>"</strong></p>
|
||||
<div class="card-body">
|
||||
<p class="card-text">edit host details:</p>
|
||||
<form method="POST" action="<?= htmlspecialchars($app_root) ?>?page=config&item=host">
|
||||
|
||||
<div class="row mb-3">
|
||||
<div class="col-md-4 text-end">
|
||||
<label for="address" class="form-label">address</label>
|
||||
<span class="text-danger" style="margin-right: -12px;">*</span>
|
||||
</div>
|
||||
<div class="col-md-8">
|
||||
<input class="form-control" type="text" name="address" value="<?= htmlspecialchars($hostDetails[0]['address'] ?? '') ?>" required autofocus />
|
||||
<p class="text-start"><small>DNS name or IP address of the machine</small></p>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="row mb-3">
|
||||
<div class="col-md-4 text-end">
|
||||
<label for="port" class="form-label">port</label>
|
||||
<span class="text-danger" style="margin-right: -12px;">*</span>
|
||||
</div>
|
||||
<div class="col-md-8">
|
||||
<input class="form-control" type="text" name="port" value="<?= htmlspecialchars($hostDetails[0]['port'] ?? '') ?>" required />
|
||||
<p class="text-start"><small>port on which the Jilo Agent is listening</small></p>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="row mb-3">
|
||||
<div class="col-md-4 text-end">
|
||||
<label for="name" class="form-label">name</label>
|
||||
</div>
|
||||
<div class="col-md-8">
|
||||
<input class="form-control" type="text" name="name" value="<?= htmlspecialchars($hostDetails[0]['name'] ?? '') ?>" />
|
||||
<p class="text-start"><small>description or name of the host (optional)</small></p>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<input type="hidden" name="platform" value="<?= htmlspecialchars($platform_id) ?>" />
|
||||
<input type="hidden" name="item" value="host" />
|
||||
<input type="hidden" name="host" value="<?= htmlspecialchars($hostDetails[0]['id']) ?>" />
|
||||
|
||||
<br />
|
||||
<a class="btn btn-outline-secondary btn-sm" href="<?= htmlspecialchars($app_root) ?>?page=config&item=host&platform=<?= htmlspecialchars($platform_id) ?>&host=<?= htmlspecialchars($host) ?>#platform<?= htmlspecialchars($platform_id) ?>host<?= htmlspecialchars($host) ?>" />Cancel</a>
|
||||
|
||||
<input type="submit" class="btn btn-primary btn-sm" value="Save" />
|
||||
</form>
|
||||
</div>
|
||||
</div>
|
||||
<!-- /widget "hosts" -->
|
|
@ -0,0 +1,39 @@
|
|||
|
||||
<!-- widget "hosts" -->
|
||||
<div class="card text-center w-75 mx-lef">
|
||||
<p class="h4 card-header">Jilo configuration :: Jitsi Meet hosts</p>
|
||||
<div class="card-body">
|
||||
<p class="card-text">Jitsi hosts configuration
|
||||
</p>
|
||||
<?php foreach ($platformsAll as $platform_array) {
|
||||
$hosts = $hostObject->getHostDetails($platform_array['id']);
|
||||
?>
|
||||
<a name="platform<?= htmlspecialchars($platform_array['id']) ?>"></a>
|
||||
<div class="row mb-1 border <?= isset($_REQUEST['platform']) && (int)$platform_array['id'] === (int)$_REQUEST['platform'] ? 'rounded bg-light' : '' ?>" style="padding: 20px; padding-bottom: 0px;">
|
||||
<p class="text-start">
|
||||
platform <strong><?= htmlspecialchars($platform_array['name']) ?></strong>
|
||||
</p>
|
||||
<ul class="text-start" style="padding-left: 50px;">
|
||||
<?php foreach ($hosts as $host_array) { ?>
|
||||
<li style="padding-bottom: 10px;">
|
||||
<a name="platform<?= htmlspecialchars($platform_array['id']) ?>host<?= htmlspecialchars($host_array['id']) ?>"></a>
|
||||
<span class="<?= isset($_REQUEST['platform']) && (int)$platform_array['id'] === (int)$_REQUEST['platform'] && isset($_REQUEST['host']) && (int)$host_array['id'] === (int)$_REQUEST['host'] ? 'border rounded bg-light' : '' ?>" style="padding: 10px;">
|
||||
<?= htmlspecialchars($host_array['address']) ?>:<?= htmlspecialchars($host_array['port']) ?>
|
||||
|
||||
<a class="btn btn-outline-secondary btn-sm" href="<?= htmlspecialchars($app_root) ?>?page=config&item=host&platform=<?= htmlspecialchars($host_array['platform_id']) ?>&host=<?= htmlspecialchars($host_array['id']) ?>&action=edit">edit host</a>
|
||||
<a class="btn btn-outline-danger btn-sm" href="<?= htmlspecialchars($app_root) ?>?page=config&item=host&platform=<?= htmlspecialchars($host_array['platform_id']) ?>&host=<?= htmlspecialchars($host_array['id']) ?>&action=delete">delete host</a>
|
||||
</span>
|
||||
</li>
|
||||
<?php } ?>
|
||||
</ul>
|
||||
<p class="text-start" style="padding-left: 50px;">
|
||||
total <?= htmlspecialchars(count($hosts)) ?> jilo <?= htmlspecialchars(count($hosts)) === '1' ? 'host' : 'hosts' ?>
|
||||
|
||||
<a class="btn btn-outline-secondary btn-sm" href="<?= htmlspecialchars($app_root) ?>?page=config&item=host&platform=<?= htmlspecialchars($platform_array['id']) ?>&action=add">add new</a>
|
||||
</p>
|
||||
|
||||
</div>
|
||||
<?php } ?>
|
||||
</div>
|
||||
</div>
|
||||
<!-- /widget "hosts" -->
|
|
@ -1,7 +1,14 @@
|
|||
|
||||
<!-- widget "config" -->
|
||||
<div class="card text-center w-75 mx-lef">
|
||||
<p class="h4 card-header">Jilo web configuration</p>
|
||||
<p class="h4 card-header">Jilo configuration</p>
|
||||
<p class="h6 card-header">
|
||||
<span class="btn btn-outline-primary btn-sm active" aria-pressed="true" style="cursor: default;">platforms</span>
|
||||
<a href="" class="btn btn-outline-primary btn-sm">hosts</a>
|
||||
<a href="" class="btn btn-outline-primary btn-sm">endpoints</a>
|
||||
|
||||
<a href="" class="btn btn-outline-primary btn-sm">config file</a>
|
||||
</p>
|
||||
<div class="card-body">
|
||||
<p class="card-text">main variables</p>
|
||||
<?php
|
||||
|
@ -97,6 +104,16 @@ echo "\n";
|
|||
<?= htmlspecialchars($agent_array['url'].$agent_array['agent_endpoint']) ?>
|
||||
</div>
|
||||
</div>
|
||||
<?php if (isset($agent_array['check_period']) && $agent_array['check_period'] !== 0) { ?>
|
||||
<div class="row mb-1" style="padding-left: 100px;">
|
||||
<div class="col-md-4 text-end">
|
||||
check period:
|
||||
</div>
|
||||
<div class="border col-md-8 text-start">
|
||||
<?= htmlspecialchars($agent_array['check_period']) ?> <?= ($agent_array['check_period'] == 1 ? 'minute' : 'minutes') ?>
|
||||
</div>
|
||||
</div>
|
||||
<?php } ?>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
|
|
@ -1,10 +1,10 @@
|
|||
|
||||
<!-- widget "config" -->
|
||||
<div class="card text-center w-50 mx-auto">
|
||||
<!-- widget "platforms" -->
|
||||
<div class="card text-center w-50 mx-lef">
|
||||
<p class="h4 card-header">Add new Jitsi platform</p>
|
||||
<div class="card-body">
|
||||
<!--p class="card-text">add new platform:</p-->
|
||||
<form method="POST" action="<?= htmlspecialchars($app_root) ?>?platform=<?= htmlspecialchars($platform_id) ?>&page=config">
|
||||
<form method="POST" action="<?= htmlspecialchars($app_root) ?>?platform=<?= htmlspecialchars($platform_id) ?>&page=config&item=platform">
|
||||
|
||||
<div class="row mb-3">
|
||||
<div class="col-md-4 text-end">
|
||||
|
@ -42,9 +42,10 @@
|
|||
<input type="hidden" name="new" value="true" />
|
||||
|
||||
<br />
|
||||
<a class="btn btn-secondary" href="<?= htmlspecialchars($app_root) ?>?page=config" />Cancel</a>
|
||||
<input type="submit" class="btn btn-primary" value="Save" />
|
||||
<a class="btn btn-outline-secondary btn-sm" href="<?= htmlspecialchars($app_root) ?>?page=config&item=platform" />Cancel</a>
|
||||
|
||||
<input type="submit" class="btn btn-primary btn-sm" value="Save" />
|
||||
</form>
|
||||
</div>
|
||||
</div>
|
||||
<!-- /widget "config" -->
|
||||
<!-- /widget "platforms" -->
|
|
@ -1,9 +1,8 @@
|
|||
|
||||
<!-- widget "config" -->
|
||||
<div class="card text-center w-50 mx-auto">
|
||||
<p class="h4 card-header">Jilo web configuration for Jitsi platform <strong>"<?= htmlspecialchars($platformDetails[0]['name']) ?>"</strong></p>
|
||||
<!-- widget "platforms" -->
|
||||
<div class="card text-center w-50 mx-lef">
|
||||
<p class="h4 card-header">Jilo configuration for Jitsi platform <strong>"<?= htmlspecialchars($platformDetails[0]['name']) ?>"</strong> :: delete</p>
|
||||
<div class="card-body">
|
||||
<p class="card-text">delete a platform:</p>
|
||||
<form method="POST" action="<?= htmlspecialchars($app_root) ?>?platform=<?= htmlspecialchars($platform_id) ?>&page=config">
|
||||
<?php
|
||||
foreach ($platformDetails[0] as $key => $value) {
|
||||
|
@ -24,9 +23,10 @@ foreach ($platformDetails[0] as $key => $value) {
|
|||
<input type="hidden" name="delete" value="true" />
|
||||
<p class="h5 text-danger">Are you sure you want to delete this platform?</p>
|
||||
<br />
|
||||
<a class="btn btn-secondary" href="<?= htmlspecialchars($app_root) ?>?page=config#platform<?= htmlspecialchars($platform_id) ?>" />Cancel</a>
|
||||
<input type="submit" class="btn btn-danger" value="Delete" />
|
||||
<a class="btn btn-outline-secondary btn-sm" href="<?= htmlspecialchars($app_root) ?>?page=config&item=platform&platform=<?= htmlspecialchars($platform_id) ?>#platform<?= htmlspecialchars($platform_id) ?>" />Cancel</a>
|
||||
|
||||
<input type="submit" class="btn btn-danger btn-sm" value="Delete" />
|
||||
</form>
|
||||
</div>
|
||||
</div>
|
||||
<!-- /widget "config" -->
|
||||
<!-- /widget "platforms" -->
|
|
@ -1,10 +1,9 @@
|
|||
|
||||
<!-- widget "config" -->
|
||||
<div class="card text-center w-50 mx-auto">
|
||||
<p class="h4 card-header">Jilo web configuration for Jitsi platform <strong>"<?= htmlspecialchars($platformDetails[0]['name']) ?>"</strong></p>
|
||||
<!-- widget "platforms" -->
|
||||
<div class="card text-center w-50 mx-lef">
|
||||
<p class="h4 card-header">Jilo configuration for Jitsi platform <strong>"<?= htmlspecialchars($platformDetails[0]['name']) ?>"</strong> :: edit</p>
|
||||
<div class="card-body">
|
||||
<p class="card-text">edit the platform details:</p>
|
||||
<form method="POST" action="<?= htmlspecialchars($app_root) ?>?platform=<?= htmlspecialchars($platform_id) ?>&page=config">
|
||||
<form method="POST" action="<?= htmlspecialchars($app_root) ?>?platform=<?= htmlspecialchars($platform_id) ?>&page=config&item=platform">
|
||||
<?php
|
||||
foreach ($platformDetails[0] as $key => $value) {
|
||||
if ($key === 'id') continue;
|
||||
|
@ -28,9 +27,10 @@ foreach ($platformDetails[0] as $key => $value) {
|
|||
<?php } ?>
|
||||
<br />
|
||||
<input type="hidden" name="platform" value="<?= htmlspecialchars($platform_id) ?>" />
|
||||
<a class="btn btn-secondary" href="<?= htmlspecialchars($app_root) ?>?page=config#platform<?= htmlspecialchars($platform_id) ?>" />Cancel</a>
|
||||
<input type="submit" class="btn btn-primary" value="Save" />
|
||||
<a class="btn btn-outline-secondary btn-sm" href="<?= htmlspecialchars($app_root) ?>?page=config&item=platform&platform=<?= htmlspecialchars($platform_id) ?>#platform<?= htmlspecialchars($platform_id) ?>" />Cancel</a>
|
||||
|
||||
<input type="submit" class="btn btn-primary btn-sm" value="Save" />
|
||||
</form>
|
||||
</div>
|
||||
</div>
|
||||
<!-- /widget "config" -->
|
||||
<!-- /widget "platforms" -->
|
|
@ -0,0 +1,60 @@
|
|||
|
||||
<!-- widget "platforms" -->
|
||||
<div class="card text-center w-75 mx-lef">
|
||||
<p class="h4 card-header">Jilo configuration :: Jitsi Meet platforms</p>
|
||||
<div class="card-body">
|
||||
<p class="card-text">Jitsi platforms configuration <a class="btn btn-outline-secondary btn-sm" href="<?= htmlspecialchars($app_root) ?>?page=config&item=platform&action=add">add new</a></p>
|
||||
<?php foreach ($platformsAll as $platform_array) {
|
||||
$hosts = $hostObject->getHostDetails($platform_array['id']);
|
||||
$agents = $agentObject->getAgentDetails($platform_array['id']);
|
||||
?>
|
||||
<a name="platform<?= htmlspecialchars($platform_array['id']) ?>"></a>
|
||||
<div class="row mb-1 border<?= isset($_REQUEST['platform']) && (int)$platform_array['id'] === (int)$_REQUEST['platform'] ? ' bg-light' : '' ?>" style="padding: 20px; padding-bottom: 0px;">
|
||||
<p>
|
||||
platform id <?= htmlspecialchars($platform_array['id']) ?> - <strong><?= htmlspecialchars($platform_array['name']) ?></strong>
|
||||
|
||||
<a class="btn btn-outline-secondary btn-sm" href="<?= htmlspecialchars($app_root) ?>?page=config&item=platform&platform=<?= htmlspecialchars($platform_array['id']) ?>&action=edit">edit platform</a>
|
||||
<?php if (count($platformsAll) <= 1) { ?>
|
||||
<span class="btn btn-outline-light btn-sm" href="#" data-toggle="tooltip" data-placement="right" data-offset="30.0" title="can't delete the last platform">delete platform</span>
|
||||
<?php } else { ?>
|
||||
<a class="btn btn-outline-danger btn-sm" href="<?= htmlspecialchars($app_root) ?>?page=config&item=platform&platform=<?= htmlspecialchars($platform_array['id']) ?>&action=delete">delete platform</a>
|
||||
<?php } ?>
|
||||
</p>
|
||||
<div style="padding-left: 100px; padding-bottom: 20px;">
|
||||
<?php foreach ($platform_array as $key => $value) {
|
||||
if ($key === 'id') continue;
|
||||
?>
|
||||
<div class="row mb-1" style="padding-left: 100px;">
|
||||
<div class="col-md-4 text-end">
|
||||
<?= htmlspecialchars($key) ?>:
|
||||
</div>
|
||||
<div class="col-md-8 text-start">
|
||||
<?php if ($key === 'jitsi_url') { ?>
|
||||
<a href="<?= htmlspecialchars($value) ?>" target="_blank" rel="noopener noreferrer" data-toggle="tooltip" data-placement="right" data-offset="30.0" title="open the Jitsi Meet platform in a new window">
|
||||
<?= htmlspecialchars($value) ?>
|
||||
<i class="fas fa-external-link-alt"></i>
|
||||
</a>
|
||||
<?php } else { ?>
|
||||
<?= htmlspecialchars($value) ?>
|
||||
<?php } ?>
|
||||
</div>
|
||||
</div>
|
||||
<?php } ?>
|
||||
<div class="row mb-1" style="padding-left: 100px;">
|
||||
<div class="col-md-4 text-end"></div>
|
||||
<div class="col-md-8 text-start">
|
||||
<a href="<?= htmlspecialchars($app_root) ?>?page=config&item=host&platform=<?= htmlspecialchars($platform_array['id']) ?>#platform<?= htmlspecialchars($platform_array['id']) ?>"><?= htmlspecialchars(count($hosts)) ?> <?= htmlspecialchars(count($hosts)) === '1' ? 'host' : 'hosts' ?></a>
|
||||
</div>
|
||||
</div>
|
||||
<div class="row mb-1" style="padding-left: 100px;">
|
||||
<div class="col-md-4 text-end"></div>
|
||||
<div class="col-md-8 text-start">
|
||||
<a href="<?= htmlspecialchars($app_root) ?>?page=config&item=endpoint&platform=<?= htmlspecialchars($platform_array['id']) ?>#platform<?= htmlspecialchars($platform_array['id']) ?>"><?= htmlspecialchars(count($agents)) ?> <?= htmlspecialchars(count($agents)) === '1' ? 'endpoint' : 'endpoints' ?></a>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<?php } ?>
|
||||
</div>
|
||||
</div>
|
||||
<!-- /widget "platforms" -->
|
|
@ -1,5 +1,5 @@
|
|||
|
||||
<!-- widget "config" -->
|
||||
<!-- widget "config.js" -->
|
||||
<div class="card text-center w-75 mx-lef">
|
||||
<p class="h4 card-header">Configuration of the Jitsi platform <strong><?= htmlspecialchars($platformDetails[0]['name']) ?></strong></p>
|
||||
<div class="card-body">
|
||||
|
@ -7,9 +7,9 @@
|
|||
<span class="m-3">URL: <?= htmlspecialchars($platformDetails[0]['jitsi_url']) ?></span>
|
||||
<span class="m-3">FILE: config.js</span>
|
||||
<?php if ($mode === 'raw') { ?>
|
||||
<span class="m-3"><a class="btn btn-light" href="<?= htmlspecialchars($app_root) ?>?platform=<?= htmlspecialchars($platform_id) ?>&page=config&item=configjs">view only active lines</a></span>
|
||||
<span class="m-3"><a class="btn btn-light" href="<?= htmlspecialchars($app_root) ?>?platform=<?= htmlspecialchars($platform_id) ?>&page=data&item=configjs">view only active lines</a></span>
|
||||
<?php } else { ?>
|
||||
<span class="m-3"><a class="btn btn-light" href="<?= htmlspecialchars($app_root) ?>?platform=<?= htmlspecialchars($platform_id) ?>&page=config&item=configjs&mode=raw">view raw file contents</a></span>
|
||||
<span class="m-3"><a class="btn btn-light" href="<?= htmlspecialchars($app_root) ?>?platform=<?= htmlspecialchars($platform_id) ?>&page=data&item=configjs&mode=raw">view raw file contents</a></span>
|
||||
<?php } ?>
|
||||
</p>
|
||||
<pre class="results">
|
||||
|
@ -19,4 +19,4 @@ echo htmlspecialchars($platformConfigjs);
|
|||
</pre>
|
||||
</div>
|
||||
</div>
|
||||
<!-- /widget "config" -->
|
||||
<!-- /widget "config.js" -->
|
|
@ -1,5 +1,5 @@
|
|||
|
||||
<!-- widget "config" -->
|
||||
<!-- widget "interfaceconfig" -->
|
||||
<div class="card text-center w-75 mx-lef">
|
||||
<p class="h4 card-header">Configuration of the Jitsi platform <strong><?= htmlspecialchars($platformDetails[0]['name']) ?></strong></p>
|
||||
<div class="card-body">
|
||||
|
@ -7,9 +7,9 @@
|
|||
<span class="m-3">URL: <?= htmlspecialchars($platformDetails[0]['jitsi_url']) ?></span>
|
||||
<span class="m-3">FILE: interface_config.js</span>
|
||||
<?php if ($mode === 'raw') { ?>
|
||||
<span class="m-3"><a class="btn btn-light" href="<?= htmlspecialchars($app_root) ?>?platform=<?= htmlspecialchars($platform_id) ?>&page=config&item=interfaceconfigjs">view only active lines</a></span>
|
||||
<span class="m-3"><a class="btn btn-light" href="<?= htmlspecialchars($app_root) ?>?platform=<?= htmlspecialchars($platform_id) ?>&page=data&item=interfaceconfigjs">view only active lines</a></span>
|
||||
<?php } else { ?>
|
||||
<span class="m-3"><a class="btn btn-light" href="<?= htmlspecialchars($app_root) ?>?platform=<?= htmlspecialchars($platform_id) ?>&page=config&item=interfaceconfigjs&mode=raw">view raw file contents</a></span>
|
||||
<span class="m-3"><a class="btn btn-light" href="<?= htmlspecialchars($app_root) ?>?platform=<?= htmlspecialchars($platform_id) ?>&page=data&item=interfaceconfigjs&mode=raw">view raw file contents</a></span>
|
||||
<?php } ?>
|
||||
</p>
|
||||
<pre class="results">
|
||||
|
@ -19,4 +19,4 @@ echo htmlspecialchars($platformInterfaceConfigjs);
|
|||
</pre>
|
||||
</div>
|
||||
</div>
|
||||
<!-- /widget "config" -->
|
||||
<!-- /widget "interfaceconfig" -->
|
|
@ -1,5 +1,7 @@
|
|||
|
||||
<hr /><p class="m-3">NB: This functionality is still under development. The data is just an example.</p><hr /><!-- FIXME remove when implemented -->
|
||||
<div class="row">
|
||||
<div class="card w-auto bg-light border-light card-body" style="flex-direction: row;"><?= $widget['title'] ?></div>
|
||||
</div>
|
||||
|
||||
<div class="row">
|
||||
<div class="card w-auto bg-light border-light card-body filter-results">
|
||||
|
|
|
@ -38,6 +38,14 @@ It's a multi-user web tool with user levels and access rights integrated into it
|
|||
|
||||
The current website you are looking at is running a Jilo Web instance.
|
||||
|
||||
<hr /><strong>"Jilo Server"</strong>
|
||||
|
||||
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.
|
||||
|
||||
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.
|
||||
|
||||
Jilo Web checks for the Jilo Server availability and displays a warning if there is a problem with the server.
|
||||
|
||||
</div>
|
||||
|
||||
</div>
|
||||
|
|
|
@ -1,44 +1,57 @@
|
|||
|
||||
<div class="row">
|
||||
<div class="card w-auto bg-light border-light card-body" style="flex-direction: row;"><?= $widget['title'] ?></div>
|
||||
<div class="card w-auto bg-light border-light card-body" style="flex-direction: row;"><?= $widget['title'] ?></div>
|
||||
</div>
|
||||
|
||||
<div class="collapse show" id="collapse<?= htmlspecialchars($widget['name']) ?>">
|
||||
<div class="mb-5">
|
||||
<hr /><p class="m-3">NB: This functionality is still under development. The data is just an example.</p><hr /><!-- FIXME remove when implemented -->
|
||||
<?php if ($widget['full'] === true) { ?>
|
||||
<table class="table table-results table-striped table-hover table-bordered">
|
||||
<thead class="thead-dark">
|
||||
<tr>
|
||||
<th scope="col"></th>
|
||||
<th scope="col">Metric</th>
|
||||
<?php foreach ($widget['records'] as $record) { ?>
|
||||
<th scope="col"><?= htmlspecialchars($record['table_headers']) ?></th>
|
||||
<th scope="col">
|
||||
<?= htmlspecialchars($record['table_headers']) ?>
|
||||
<?php if ($record['timestamp']) { ?>
|
||||
<br>
|
||||
<small class="text-muted">as of <?= date('Y-m-d H:i:s', strtotime($record['timestamp'])) ?></small>
|
||||
<?php } ?>
|
||||
</th>
|
||||
<?php } ?>
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody>
|
||||
<tr>
|
||||
<td>conferences</td>
|
||||
<?php foreach ($widget['records'] as $record) { ?>
|
||||
<td><?php if (!empty($record['conferences'])) { ?>
|
||||
<a href="<?= htmlspecialchars($app_root) ?>?platform=<?= htmlspecialchars($platform_id) ?>&page=conferences&from_time=<?= htmlspecialchars($record['from_time']) ?>&until_time=<?= htmlspecialchars($record['until_time']) ?>"><?= htmlspecialchars($record['conferences']) ?></a> <?php } else { ?>
|
||||
0<?php } ?>
|
||||
</td>
|
||||
<?php } ?>
|
||||
<?php foreach ($widget['metrics'] as $section => $section_metrics) { ?>
|
||||
<tr class="table-secondary">
|
||||
<th colspan="<?= count($widget['records']) + 1 ?>"><?= htmlspecialchars($section) ?></th>
|
||||
</tr>
|
||||
<?php foreach ($section_metrics as $metric => $metricConfig) { ?>
|
||||
<tr>
|
||||
<td>participants</td>
|
||||
<?php foreach ($widget['records'] as $record) { ?>
|
||||
<td><?php if (!empty($record['participants'])) { ?>
|
||||
<a href="<?= htmlspecialchars($app_root) ?>?platform=<?= htmlspecialchars($platform_id) ?>&page=participants&from_time=<?= htmlspecialchars($record['from_time']) ?>&until_time=<?= htmlspecialchars($record['until_time']) ?>"><?= htmlspecialchars($record['participants']) ?></a> <?php } else { ?>
|
||||
0<?php } ?>
|
||||
<td><?= htmlspecialchars($metricConfig['label']) ?></td>
|
||||
<?php foreach ($widget['records'] as $record) { ?>
|
||||
<td>
|
||||
<?php if (isset($record['metrics'][$section][$metric])) {
|
||||
$metric_data = $record['metrics'][$section][$metric];
|
||||
if ($metric_data['link']) { ?>
|
||||
<a href="<?= htmlspecialchars($app_root) ?>?platform=<?= htmlspecialchars($platform_id) ?>&page=<?= htmlspecialchars($metric_data['link']) ?>&from_time=<?= htmlspecialchars($record['timestamp']) ?>&until_time=<?= htmlspecialchars($record['timestamp']) ?>"><?= htmlspecialchars($metric_data['value']) ?></a>
|
||||
<?php } else { ?>
|
||||
<?= htmlspecialchars($metric_data['value']) ?>
|
||||
<?php }
|
||||
} else { ?>
|
||||
<span class="text-muted">No data</span>
|
||||
<?php } ?>
|
||||
</td>
|
||||
<?php } ?>
|
||||
<?php } ?>
|
||||
</tr>
|
||||
<?php } ?>
|
||||
<?php } ?>
|
||||
</tbody>
|
||||
</table>
|
||||
<?php } else { ?>
|
||||
<p class="m-3">No records found.</p>
|
||||
<div class="alert alert-info m-3" role="alert">
|
||||
No data available from any agents. Please check agent configuration and connectivity.
|
||||
</div>
|
||||
<?php } ?>
|
||||
</div>
|
||||
</div>
|
||||
|
|
|
@ -18,6 +18,28 @@ $(document).ready(function(){
|
|||
$('[data-toggle="tooltip"]').tooltip();
|
||||
});
|
||||
</script>
|
||||
|
||||
|
||||
<script>
|
||||
// dismissible messages
|
||||
document.addEventListener('DOMContentLoaded', function() {
|
||||
// Initialize Bootstrap alerts
|
||||
var alerts = document.querySelectorAll('.alert');
|
||||
alerts.forEach(function(alert) {
|
||||
var closeButton = alert.querySelector('.btn-close');
|
||||
if (closeButton) {
|
||||
closeButton.addEventListener('click', function() {
|
||||
alert.classList.remove('show');
|
||||
setTimeout(function() {
|
||||
alert.remove();
|
||||
}, 150);
|
||||
});
|
||||
}
|
||||
});
|
||||
});
|
||||
</script>
|
||||
|
||||
|
||||
</body>
|
||||
|
||||
</html>
|
||||
|
|
|
@ -26,7 +26,7 @@
|
|||
<?php if ($page === 'agents') { ?>
|
||||
<script src="<?= htmlspecialchars($app_root) ?>static/agents.js"></script>
|
||||
<?php } ?>
|
||||
<?php if ($page === 'graphs') { ?>
|
||||
<?php if ($page === 'data' && $item === 'graphs') { ?>
|
||||
<script src="https://cdn.jsdelivr.net/npm/chart.js"></script>
|
||||
<script src="https://cdn.jsdelivr.net/npm/moment@2.29.1"></script>
|
||||
<script src="https://cdn.jsdelivr.net/npm/chartjs-adapter-moment@1.0.0"></script>
|
||||
|
@ -37,3 +37,13 @@
|
|||
</head>
|
||||
|
||||
<body>
|
||||
<div class="container-fluid">
|
||||
<div class="row">
|
||||
<div class="col">
|
||||
<?php if (isset($messages) && is_array($messages)): ?>
|
||||
<?php foreach ($messages as $msg): ?>
|
||||
<?= Messages::render($msg['category'], $msg['key'], $msg['custom_message'] ?? null) ?>
|
||||
<?php endforeach; ?>
|
||||
<?php endif; ?>
|
||||
</div>
|
||||
</div>
|
||||
|
|
|
@ -1,7 +1,7 @@
|
|||
<div class="row">
|
||||
|
||||
<!-- Sidebar -->
|
||||
<div class="col-md-3 sidebar-wrapper bg-light" id="sidebar">
|
||||
<div class="col-md-3 mb-5 sidebar-wrapper bg-light" id="sidebar">
|
||||
<div class="text-center" style="border: 1px solid #0dcaf0; height: 22px;" id="time_now">
|
||||
<?php
|
||||
$timeNow = new DateTime('now', new DateTimeZone($userTimezone));
|
||||
|
@ -38,28 +38,25 @@ $timeNow = new DateTime('now', new DateTimeZone($userTimezone));
|
|||
</li>
|
||||
</a>
|
||||
|
||||
<li class="list-group-item bg-light" style="border: none;"><p class="text-end mb-0"><small>graphs</small></p></li>
|
||||
<li class="list-group-item bg-light" style="border: none;"><p class="text-end mb-0"><small>live data</small></p></li>
|
||||
|
||||
<a href="<?= htmlspecialchars($app_root) ?>?platform=<?= htmlspecialchars($platform_id) ?>&page=graphs">
|
||||
<li class="list-group-item<?php if ($page === 'graphs') echo ' list-group-item-secondary'; else echo ' list-group-item-action'; ?>">
|
||||
<a href="<?= htmlspecialchars($app_root) ?>?platform=<?= htmlspecialchars($platform_id) ?>&page=data&item=graphs">
|
||||
<li class="list-group-item<?php if ($page === 'data' && $item === 'graphs') echo ' list-group-item-secondary'; else echo ' list-group-item-action'; ?>">
|
||||
<i class="fas fa-chart-bar" data-toggle="tooltip" data-placement="right" data-offset="30.0" title="combined graphs"></i>combined graphs
|
||||
</li>
|
||||
</a>
|
||||
<a href="<?= htmlspecialchars($app_root) ?>?platform=<?= htmlspecialchars($platform_id) ?>&page=latest">
|
||||
<li class="list-group-item<?php if ($page === 'latest') echo ' list-group-item-secondary'; else echo ' list-group-item-action'; ?>">
|
||||
<a href="<?= htmlspecialchars($app_root) ?>?platform=<?= htmlspecialchars($platform_id) ?>&page=data&item=latest">
|
||||
<li class="list-group-item<?php if ($page === 'data' && $item === 'latest') echo ' list-group-item-secondary'; else echo ' list-group-item-action'; ?>">
|
||||
<i class="fas fa-list" data-toggle="tooltip" data-placement="right" data-offset="30.0" title="latest data"></i>latest data
|
||||
</li>
|
||||
</a>
|
||||
|
||||
<li class="list-group-item bg-light" style="border: none;"><p class="text-end mb-0"><small>live data</small></p></li>
|
||||
|
||||
<a href="<?= htmlspecialchars($app_root) ?>?platform=<?= htmlspecialchars($platform_id) ?>&page=config&item=configjs">
|
||||
<li class="list-group-item<?php if ($page === 'config' && $item === 'configjs') echo ' list-group-item-secondary'; else echo ' list-group-item-action'; ?>">
|
||||
<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'; ?>">
|
||||
<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=config&item=interfaceconfigjs">
|
||||
<li class="list-group-item<?php if ($page === 'config' && $item === 'interfaceconfigjs') echo ' list-group-item-secondary'; else echo ' list-group-item-action'; ?>">
|
||||
<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'; ?>">
|
||||
<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>
|
||||
|
@ -69,15 +66,49 @@ $timeNow = new DateTime('now', new DateTimeZone($userTimezone));
|
|||
</li>
|
||||
</a>
|
||||
|
||||
<li class="list-group-item bg-light" style="border: none;"><p class="text-end mb-0"><small>jitsi platforms config</small></p></li>
|
||||
|
||||
<a href="<?= htmlspecialchars($app_root) ?>?page=config&item=platform">
|
||||
<li class="list-group-item<?php if ($page === 'config' && $item === 'platform') echo ' list-group-item-secondary'; else echo ' list-group-item-action'; ?>">
|
||||
<i class="fas fa-sitemap" data-toggle="tooltip" data-placement="right" data-offset="30.0" title="configuration"></i>platforms
|
||||
</li>
|
||||
</a>
|
||||
<a href="<?= htmlspecialchars($app_root) ?>?page=config&item=host">
|
||||
<li class="list-group-item<?php if ($page === 'config' && $item === 'host') echo ' list-group-item-secondary'; else echo ' list-group-item-action'; ?>">
|
||||
<i class="fas fa-laptop" data-toggle="tooltip" data-placement="right" data-offset="30.0" title="configuration"></i>hosts
|
||||
</li>
|
||||
</a>
|
||||
<a href="<?= htmlspecialchars($app_root) ?>?page=config&item=endpoint">
|
||||
<li class="list-group-item<?php if ($page === 'config' && $item === 'endpoint') echo ' list-group-item-secondary'; else echo ' list-group-item-action'; ?>">
|
||||
<i class="fas fa-stethoscope" data-toggle="tooltip" data-placement="right" data-offset="30.0" title="configuration"></i>endpoints
|
||||
</li>
|
||||
</a>
|
||||
|
||||
<li class="list-group-item bg-light" style="border: none;"><p class="text-end mb-0"><small>system</small></p></li>
|
||||
|
||||
<?php if ($userObject->hasRight($user_id, 'view config file')) {?>
|
||||
<a href="<?= htmlspecialchars($app_root) ?>?page=config">
|
||||
<li class="list-group-item<?php if ($page === 'config' && $item === '') echo ' list-group-item-secondary'; else echo ' list-group-item-action'; ?>">
|
||||
<i class="fas fa-wrench" data-toggle="tooltip" data-placement="right" data-offset="30.0" title="configuration"></i>config
|
||||
<a href="<?= htmlspecialchars($app_root) ?>?page=config&item=config_file">
|
||||
<li class="list-group-item<?php if ($page === 'config' && $item === 'config_file') echo ' list-group-item-secondary'; else echo ' list-group-item-action'; ?>">
|
||||
<i class="fas fa-wrench" data-toggle="tooltip" data-placement="right" data-offset="30.0" title="configuration"></i>config file
|
||||
</li>
|
||||
</a>
|
||||
<?php } ?>
|
||||
<?php if ($userObject->hasRight($user_id, 'superuser') ||
|
||||
$userObject->hasRight($user_id, 'edit whitelist') ||
|
||||
$userObject->hasRight($user_id, 'edit blacklist') ||
|
||||
$userObject->hasRight($user_id, 'edit ratelimiting')) { ?>
|
||||
<a href="<?= htmlspecialchars($app_root) ?>?page=security">
|
||||
<li class="list-group-item<?php if ($page === 'security') echo ' list-group-item-secondary'; else echo ' list-group-item-action'; ?>">
|
||||
<i class="fas fa-shield-alt" data-toggle="tooltip" data-placement="right" data-offset="30.0" title="security"></i>security
|
||||
</li>
|
||||
</a>
|
||||
<?php } ?>
|
||||
<a href="<?= htmlspecialchars($app_root) ?>?page=status">
|
||||
<li class="list-group-item<?php if ($page === 'status' && $item === '') echo ' list-group-item-secondary'; else echo ' list-group-item-action'; ?>">
|
||||
<i class="fas fa-heartbeat" data-toggle="tooltip" data-placement="right" data-offset="30.0" title="status"></i>status
|
||||
</li>
|
||||
</a>
|
||||
|
||||
<?php if ($userObject->hasRight($user_id, 'view app logs')) {?>
|
||||
<a href="<?= htmlspecialchars($app_root) ?>?page=logs">
|
||||
<li class="list-group-item<?php if ($page === 'logs') echo ' list-group-item-secondary'; else echo ' list-group-item-action'; ?>">
|
||||
|
|
|
@ -0,0 +1,223 @@
|
|||
<!-- Security Settings -->
|
||||
<div class="container">
|
||||
<div class="row mb-4">
|
||||
<div class="col">
|
||||
<h2>Security Settings</h2>
|
||||
<ul class="nav nav-tabs">
|
||||
<?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§ion=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§ion=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§ion=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>
|
||||
</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 Address</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>
|
||||
<!-- /Security Settings -->
|
|
@ -0,0 +1,14 @@
|
|||
|
||||
<!-- jilo agent status -->
|
||||
<div class="card text-center w-75 mx-lef" style="padding-left: 80px;">
|
||||
<div class="card-body">
|
||||
<p class="card-text text-left" style="text-align: left;">
|
||||
Jilo Agent <a href="<?= htmlspecialchars($app_root) ?>?page=config#platform<?= htmlspecialchars($platform['id']) ?>agent<?= htmlspecialchars($agent['id']) ?>"><?= htmlspecialchars($agent['agent_description']) ?></a>:
|
||||
<strong><?= $agent_availability ?></strong>
|
||||
<br />
|
||||
host: <strong><?= htmlspecialchars($agent_host) ?></strong>,
|
||||
port: <strong><?= htmlspecialchars($agent_port) ?></strong>,
|
||||
endpoint: <strong><?= htmlspecialchars($agent['agent_endpoint']) ?></strong>
|
||||
</p>
|
||||
</div>
|
||||
</div>
|
|
@ -0,0 +1,13 @@
|
|||
|
||||
<!-- jitsi platform status -->
|
||||
<br />
|
||||
<div class="card text-center w-75 mx-lef" style="padding-left: 40px;">
|
||||
<div class="card-body">
|
||||
<p class="card-text text-left" style="text-align: left;">
|
||||
Jitsi Meet platform: <a href="<?= htmlspecialchars($app_root) ?>?page=config#platform<?= htmlspecialchars($platform['id']) ?>"><?= htmlspecialchars($platform['name']) ?></a>
|
||||
<br />
|
||||
jilo database: <strong><?= htmlspecialchars($platform['jilo_database']) ?></strong>,
|
||||
status: <strong><?= $jilo_database_status ?></strong>
|
||||
</p>
|
||||
</div>
|
||||
</div>
|
|
@ -0,0 +1,19 @@
|
|||
|
||||
<!-- jilo status -->
|
||||
<div class="card text-center w-75 mx-lef">
|
||||
<p class="h4 card-header">Jilo platform status</p>
|
||||
<div class="card-body">
|
||||
<p class="card-text text-left" style="text-align: left;">
|
||||
Jilo Server:
|
||||
<?php if ($server_status) { ?>
|
||||
<strong><span class="text-success">running</span></strong>
|
||||
<?php } else { ?>
|
||||
<strong><span class="text-danger">not running</span></strong>
|
||||
<?php } ?>
|
||||
<br />
|
||||
host: <strong><?= htmlspecialchars($server_host) ?></strong>,
|
||||
port: <strong><?= htmlspecialchars($server_port) ?></strong>,
|
||||
endpoint: <strong><?= htmlspecialchars($server_endpoint) ?></strong>
|
||||
</p>
|
||||
</div>
|
||||
</div>
|
|
@ -1,5 +0,0 @@
|
|||
INSERT INTO jilo_agent_types VALUES(1,'jvb','/jvb');
|
||||
INSERT INTO jilo_agent_types VALUES(2,'jicofo','/jicofo');
|
||||
INSERT INTO jilo_agent_types VALUES(3,'prosody','/prosody');
|
||||
INSERT INTO jilo_agent_types VALUES(4,'nginx','/nginx');
|
||||
INSERT INTO jilo_agent_types VALUES(5,'jibri','/jibri');
|
|
@ -1,13 +0,0 @@
|
|||
INSERT INTO rights VALUES(1,'superuser');
|
||||
INSERT INTO rights VALUES(2,'edit users');
|
||||
INSERT INTO rights VALUES(3,'view config file');
|
||||
INSERT INTO rights VALUES(4,'edit config file');
|
||||
INSERT INTO rights VALUES(5,'view own profile');
|
||||
INSERT INTO rights VALUES(6,'edit own profile');
|
||||
INSERT INTO rights VALUES(7,'view all profiles');
|
||||
INSERT INTO rights VALUES(8,'edit all profiles');
|
||||
INSERT INTO rights VALUES(9,'view app logs');
|
||||
INSERT INTO rights VALUES(10,'view all platforms');
|
||||
INSERT INTO rights VALUES(11,'edit all platforms');
|
||||
INSERT INTO rights VALUES(12,'view all agents');
|
||||
INSERT INTO rights VALUES(13,'edit all agents');
|
|
@ -25,36 +25,25 @@ CREATE TABLE rights (
|
|||
id INTEGER PRIMARY KEY AUTOINCREMENT,
|
||||
name TEXT NOT NULL UNIQUE
|
||||
);
|
||||
INSERT INTO rights VALUES(1,'superuser');
|
||||
INSERT INTO rights VALUES(2,'edit users');
|
||||
INSERT INTO rights VALUES(3,'view config file');
|
||||
INSERT INTO rights VALUES(4,'edit config file');
|
||||
INSERT INTO rights VALUES(5,'view own profile');
|
||||
INSERT INTO rights VALUES(6,'edit own profile');
|
||||
INSERT INTO rights VALUES(7,'view all profiles');
|
||||
INSERT INTO rights VALUES(8,'edit all profiles');
|
||||
INSERT INTO rights VALUES(9,'view app logs');
|
||||
INSERT INTO rights VALUES(10,'view all platforms');
|
||||
INSERT INTO rights VALUES(11,'edit all platforms');
|
||||
INSERT INTO rights VALUES(12,'view all agents');
|
||||
INSERT INTO rights VALUES(13,'edit all agents');
|
||||
CREATE TABLE platforms (
|
||||
id INTEGER PRIMARY KEY AUTOINCREMENT,
|
||||
name TEXT NOT NULL UNIQUE,
|
||||
jitsi_url TEXT NOT NULL,
|
||||
jilo_database TEXT NOT NULL
|
||||
);
|
||||
CREATE TABLE jilo_agents (
|
||||
id INTEGER PRIMARY KEY AUTOINCREMENT,
|
||||
platform_id INTEGER NOT NULL,
|
||||
agent_type_id INTEGER NOT NULL,
|
||||
url TEXT NOT NULL,
|
||||
secret_key TEXT,
|
||||
check_period INTEGER DEFAULT 0,
|
||||
FOREIGN KEY (platform_id) REFERENCES platforms(id),
|
||||
FOREIGN KEY (agent_type_id) REFERENCES jilo_agent_types(id)
|
||||
);
|
||||
CREATE TABLE jilo_agent_types (
|
||||
id INTEGER PRIMARY KEY AUTOINCREMENT,
|
||||
description TEXT,
|
||||
endpoint TEXT
|
||||
);
|
||||
CREATE TABLE jilo_agent_checks (
|
||||
id INTEGER PRIMARY KEY AUTOINCREMENT,
|
||||
agent_id INTEGER,
|
||||
timestamp DATETIME DEFAULT CURRENT_TIMESTAMP,
|
||||
status_code INTEGER,
|
||||
response_time_ms INTEGER,
|
||||
response_content TEXT,
|
||||
FOREIGN KEY (agent_id) REFERENCES jilo_agents(id)
|
||||
);
|
||||
CREATE TABLE logs (
|
||||
id INTEGER PRIMARY KEY AUTOINCREMENT,
|
||||
user_id INTEGET NOT NULL,
|
||||
|
@ -63,3 +52,76 @@ CREATE TABLE logs (
|
|||
message TEXT NOT NULL,
|
||||
FOREIGN KEY (user_id) REFERENCES users(id)
|
||||
);
|
||||
CREATE TABLE IF NOT EXISTS "jilo_agent_types" (
|
||||
"id" INTEGER,
|
||||
"description" TEXT,
|
||||
"endpoint" TEXT,
|
||||
PRIMARY KEY("id" AUTOINCREMENT)
|
||||
);
|
||||
INSERT INTO jilo_agent_types VALUES(1,'jvb','/jvb');
|
||||
INSERT INTO jilo_agent_types VALUES(2,'jicofo','/jicofo');
|
||||
INSERT INTO jilo_agent_types VALUES(3,'prosody','/prosody');
|
||||
INSERT INTO jilo_agent_types VALUES(4,'nginx','/nginx');
|
||||
INSERT INTO jilo_agent_types VALUES(5,'jibri','/jibri');
|
||||
CREATE TABLE jilo_agent_checks (
|
||||
id INTEGER PRIMARY KEY AUTOINCREMENT,
|
||||
agent_id INTEGER,
|
||||
timestamp DATETIME DEFAULT CURRENT_TIMESTAMP,
|
||||
status_code INTEGER,
|
||||
response_time_ms INTEGER,
|
||||
response_content TEXT,
|
||||
FOREIGN KEY(agent_id) REFERENCES jilo_agents(id)
|
||||
);
|
||||
CREATE TABLE IF NOT EXISTS "jilo_agents" (
|
||||
"id" INTEGER,
|
||||
"platform_id" INTEGER NOT NULL,
|
||||
"agent_type_id" INTEGER NOT NULL,
|
||||
"url" TEXT NOT NULL,
|
||||
"secret_key" TEXT,
|
||||
"check_period" INTEGER DEFAULT 0,
|
||||
PRIMARY KEY("id" AUTOINCREMENT),
|
||||
FOREIGN KEY("agent_type_id") REFERENCES "jilo_agent_types"("id"),
|
||||
FOREIGN KEY("platform_id") REFERENCES "platforms"("id")
|
||||
);
|
||||
CREATE TABLE IF NOT EXISTS "hosts" (
|
||||
"id" INTEGER NOT NULL,
|
||||
"address" TEXT NOT NULL,
|
||||
"port" INTEGER NOT NULL,
|
||||
"platform_id" INTEGER NOT NULL,
|
||||
"name" TEXT,
|
||||
PRIMARY KEY("id" AUTOINCREMENT),
|
||||
FOREIGN KEY("platform_id") REFERENCES "platforms"("id")
|
||||
);
|
||||
CREATE TABLE login_attempts (
|
||||
id INTEGER PRIMARY KEY AUTOINCREMENT,
|
||||
ip_address TEXT NOT NULL,
|
||||
username TEXT NOT NULL,
|
||||
attempted_at TEXT DEFAULT (DATETIME('now'))
|
||||
);
|
||||
CREATE TABLE ip_whitelist (
|
||||
id INTEGER PRIMARY KEY AUTOINCREMENT,
|
||||
ip_address TEXT NOT NULL UNIQUE,
|
||||
is_network BOOLEAN DEFAULT 0 CHECK(is_network IN (0,1)),
|
||||
description TEXT,
|
||||
created_at TEXT DEFAULT (DATETIME('now')),
|
||||
created_by TEXT
|
||||
);
|
||||
INSERT INTO ip_whitelist VALUES(1,'127.0.0.1',0,'localhost IPv4','2025-01-04 11:39:08','system');
|
||||
INSERT INTO ip_whitelist VALUES(2,'::1',0,'localhost IPv6','2025-01-04 11:39:08','system');
|
||||
INSERT INTO ip_whitelist VALUES(3,'10.0.0.0/8',1,'Private network (Class A)','2025-01-04 11:39:08','system');
|
||||
INSERT INTO ip_whitelist VALUES(4,'172.16.0.0/12',1,'Private network (Class B)','2025-01-04 11:39:08','system');
|
||||
INSERT INTO ip_whitelist VALUES(5,'192.168.0.0/16',1,'Private network (Class C)','2025-01-04 11:39:08','system');
|
||||
CREATE TABLE ip_blacklist (
|
||||
id INTEGER PRIMARY KEY AUTOINCREMENT,
|
||||
ip_address TEXT NOT NULL UNIQUE,
|
||||
is_network BOOLEAN DEFAULT 0 CHECK(is_network IN (0,1)),
|
||||
reason TEXT,
|
||||
expiry_time TEXT NULL,
|
||||
created_at TEXT DEFAULT (DATETIME('now')),
|
||||
created_by TEXT
|
||||
);
|
||||
INSERT INTO ip_blacklist VALUES(1,'0.0.0.0/8',1,'Reserved address space - RFC 1122',NULL,'2025-01-04 11:39:08','system');
|
||||
INSERT INTO ip_blacklist VALUES(2,'100.64.0.0/10',1,'Carrier-grade NAT space - RFC 6598',NULL,'2025-01-04 11:39:08','system');
|
||||
INSERT INTO ip_blacklist VALUES(3,'192.0.2.0/24',1,'TEST-NET-1 Documentation space - RFC 5737',NULL,'2025-01-04 11:39:08','system');
|
||||
INSERT INTO ip_blacklist VALUES(4,'198.51.100.0/24',1,'TEST-NET-2 Documentation space - RFC 5737',NULL,'2025-01-04 11:39:08','system');
|
||||
INSERT INTO ip_blacklist VALUES(5,'203.0.113.0/24',1,'TEST-NET-3 Documentation space - RFC 5737',NULL,'2025-01-04 11:39:08','system');
|
||||
|
|
|
@ -0,0 +1,27 @@
|
|||
|
||||
INSERT INTO users VALUES(1,'demo','$2y$10$tLCLvgYu91gf/zBoc58Am.iVls/SOMcIXO3ykGfgFFei9yneZTrb2');
|
||||
INSERT INTO users VALUES(2,'demo1','$2y$10$LtV9m.rMCJ.K/g45e6tzDexZ8C/9xxu3qFCkvz92pUYa7Jg06np0i');
|
||||
|
||||
INSERT INTO users_meta VALUES(1,1,'demo admin user','admin@example.com',NULL,NULL,'This is a demo user of the demo install of Jilo Web');
|
||||
INSERT INTO users_meta VALUES(2,2,'demo user','demo@example.com',NULL,NULL,'This is a demo user of the demo install of Jilo Web');
|
||||
|
||||
INSERT INTO platforms VALUES(1,'meet.lindeas.com','https://meet.lindeas.com','../jilo-meet.lindeas.db');
|
||||
INSERT INTO platforms VALUES(2,'example.com','https://meet.example.com','../jilo.db');
|
||||
|
||||
INSERT INTO logs VALUES(1,2,'2024-09-30 09:54:50','user','Logout: User "demo" logged out. IP: 151.237.101.43');
|
||||
INSERT INTO logs VALUES(2,2,'2024-09-30 09:54:54','user','Login: User "demo" logged in. IP: 151.237.101.43');
|
||||
INSERT INTO logs VALUES(3,2,'2024-10-03 16:34:49','user','Logout: User "demo" logged out. IP: 151.237.101.43');
|
||||
INSERT INTO logs VALUES(4,2,'2024-10-03 16:34:56','user','Login: User "demo" logged in. IP: 151.237.101.43');
|
||||
INSERT INTO logs VALUES(5,2,'2024-10-09 11:08:16','user','Logout: User "demo" logged out. IP: 151.237.101.43');
|
||||
INSERT INTO logs VALUES(6,2,'2024-10-09 11:08:20','user','Login: User "demo" logged in. IP: 151.237.101.43');
|
||||
INSERT INTO logs VALUES(7,2,'2024-10-17 16:22:57','user','Logout: User "demo" logged out. IP: 151.237.101.43');
|
||||
INSERT INTO logs VALUES(8,2,'2024-10-17 16:23:08','user','Login: User "demo" logged in. IP: 151.237.101.43');
|
||||
INSERT INTO logs VALUES(9,2,'2024-10-18 08:07:25','user','Login: User "demo" logged in. IP: 42.104.201.119');
|
||||
|
||||
INSERT INTO jilo_agents VALUES(1,1,1,'https://meet.lindeas.com:8081','mysecretkey',5);
|
||||
INSERT INTO jilo_agents VALUES(4,1,2,'https://meet.lindeas.com:8081','mysecretkey',5);
|
||||
INSERT INTO jilo_agents VALUES(7,1,3,'http://meet.lindeas.com:8081','mysecretkey',5);
|
||||
INSERT INTO jilo_agents VALUES(8,1,4,'http://meet.lindeas.com:8081','mysecretkey',5);
|
||||
|
||||
INSERT INTO hosts VALUES(1,'meet.lindeas.com',8888,2,'main machine');
|
||||
INSERT INTO hosts VALUES(2,'meet.example.com',9191,2,'test');
|
|
@ -1,5 +1,5 @@
|
|||
Package: jilo-web
|
||||
Version: 0.2.1
|
||||
Version: 0.3
|
||||
Section: web
|
||||
Priority: optional
|
||||
Architecture: all
|
||||
|
|
|
@ -1,4 +1,4 @@
|
|||
.TH JILO-WEB "8" "October 2024" "jilo-web 0.2.1"
|
||||
.TH JILO-WEB "8" "January 2025" "jilo-web 0.3"
|
||||
.SH NAME
|
||||
jilo-web \- PHP frontent to jilo (jitsi logs observer) database.
|
||||
.SH DESCRIPTION
|
||||
|
@ -17,7 +17,7 @@ Written and maintained by Yasen Pramatarov <yasen@lindeas.com>
|
|||
https://lindeas.com/jilo
|
||||
|
||||
.SH VERSION
|
||||
0.2.1
|
||||
0.3
|
||||
|
||||
.SH SEE ALSO
|
||||
jilo(8), jilo-cli(8)
|
||||
|
|
|
@ -1,5 +1,5 @@
|
|||
Name: jilo-web
|
||||
Version: 0.2.1
|
||||
Version: 0.3
|
||||
Release: 1%{?dist}
|
||||
Summary: Jitsi logs web observer
|
||||
|
||||
|
@ -54,6 +54,8 @@ cp %{sourcedir}/man-jilo.8 %{buildroot}/usr/share/man/man8/%{name}.8
|
|||
/usr/share/man/man8/%{name}.8.gz
|
||||
|
||||
%changelog
|
||||
* Wed Jan 15 2025 Yasen Pramatarov <yasen@lindeas.com> 0.3
|
||||
- Build of upstream v0.3
|
||||
* Thu Oct 17 2024 Yasen Pramatarov <yasen@lindeas.com> 0.2.1
|
||||
- Build of upstream v0.2.1
|
||||
* Sat Aug 31 2024 Yasen Pramatarov <yasen@lindeas.com> 0.2
|
||||
|
|
|
@ -7,8 +7,8 @@
|
|||
* Author: Yasen Pramatarov
|
||||
* License: GPLv2
|
||||
* Project URL: https://lindeas.com/jilo
|
||||
* Year: 2024
|
||||
* Version: 0.2.1
|
||||
* Year: 2024-2025
|
||||
* Version: 0.3
|
||||
*/
|
||||
|
||||
// we start output buffering and.
|
||||
|
@ -16,9 +16,15 @@
|
|||
ob_start();
|
||||
|
||||
// sanitize all input vars that may end up in URLs or forms
|
||||
require '../app/helpers/sanitize.php';
|
||||
require '../app/includes/sanitize.php';
|
||||
|
||||
require '../app/helpers/errors.php';
|
||||
// Initialize message system
|
||||
require_once '../app/classes/messages.php';
|
||||
$messages = [];
|
||||
|
||||
//include '../app/includes/messages.php';
|
||||
|
||||
require '../app/includes/errors.php';
|
||||
|
||||
// error reporting, comment out in production
|
||||
ini_set('display_errors', 1);
|
||||
|
@ -34,14 +40,17 @@ $allowed_urls = [
|
|||
'participants',
|
||||
'components',
|
||||
|
||||
'graphs',
|
||||
'data',
|
||||
|
||||
'latest',
|
||||
|
||||
'agents',
|
||||
|
||||
'profile',
|
||||
'config',
|
||||
'status',
|
||||
'logs',
|
||||
'security',
|
||||
'help',
|
||||
|
||||
'login',
|
||||
|
@ -88,14 +97,27 @@ if (isset($_COOKIE['username'])) {
|
|||
|
||||
// redirect to login
|
||||
if ( !isset($_COOKIE['username']) && ($page !== 'login' && $page !== 'register') ) {
|
||||
header('Location: index.php?page=login');
|
||||
header('Location: ' . htmlspecialchars($app_root) . '?page=login');
|
||||
exit();
|
||||
}
|
||||
|
||||
// connect to db of Jilo Web
|
||||
require '../app/classes/database.php';
|
||||
require '../app/helpers/database.php';
|
||||
$dbWeb = connectDB($config);
|
||||
require '../app/includes/database.php';
|
||||
try {
|
||||
$response = connectDB($config);
|
||||
if (!$response['db']) {
|
||||
throw new Exception('Could not connect to database: ' . $response['error']);
|
||||
}
|
||||
$dbWeb = $response['db'];
|
||||
} catch (Exception $e) {
|
||||
Messages::flash('ERROR', 'DEFAULT', getError('Error connecting to the database.', $e->getMessage()));
|
||||
include '../app/templates/page-header.php';
|
||||
include '../app/includes/messages.php';
|
||||
include '../app/includes/messages-show.php';
|
||||
include '../app/templates/page-footer.php';
|
||||
exit();
|
||||
}
|
||||
|
||||
// start logging
|
||||
require '../app/classes/log.php';
|
||||
|
@ -103,6 +125,9 @@ include '../app/helpers/logs.php';
|
|||
$logObject = new Log($dbWeb);
|
||||
$user_IP = getUserIP();
|
||||
|
||||
// init rate limiter
|
||||
require '../app/classes/ratelimiter.php';
|
||||
|
||||
// get platforms details
|
||||
require '../app/classes/platform.php';
|
||||
$platformObject = new Platform($dbWeb);
|
||||
|
@ -128,29 +153,49 @@ if ($page == 'logout') {
|
|||
session_destroy();
|
||||
setcookie('username', "", time() - 100, $config['folder'], $config['domain'], isset($_SERVER['HTTPS']), true);
|
||||
|
||||
$notice = "You were logged out.<br />You can log in again.";
|
||||
// Log successful logout
|
||||
$user_id = $userObject->getUserId($currentUser)[0]['id'];
|
||||
$logObject->insertLog($user_id, "Logout: User \"$currentUser\" logged out. IP: $user_IP", 'user');
|
||||
|
||||
// Set success message
|
||||
Messages::flash('LOGIN', 'LOGOUT_SUCCESS');
|
||||
|
||||
include '../app/templates/page-header.php';
|
||||
include '../app/templates/page-menu.php';
|
||||
include '../app/templates/block-message.php';
|
||||
include '../app/pages/login.php';
|
||||
|
||||
} else {
|
||||
|
||||
// if user is logged in, we need user details and rights
|
||||
if (isset($currentUser)) {
|
||||
|
||||
// If by error a logged in user requests the login page
|
||||
if ($page === 'login') {
|
||||
header('Location: ' . htmlspecialchars($app_root));
|
||||
exit();
|
||||
}
|
||||
$user_id = $userObject->getUserId($currentUser)[0]['id'];
|
||||
$userDetails = $userObject->getUserDetails($user_id);
|
||||
$userRights = $userObject->getUserRights($user_id);
|
||||
$userTimezone = isset($userDetails[0]['timezone']) ? $userDetails[0]['timezone'] : 'UTC'; // Default to UTC if no timezone is set
|
||||
|
||||
|
||||
// check if the Jilo Server is running
|
||||
require '../app/classes/server.php';
|
||||
$serverObject = new Server($dbWeb);
|
||||
|
||||
$server_host = '127.0.0.1';
|
||||
$server_port = '8080';
|
||||
$server_endpoint = '/health';
|
||||
$server_status = $serverObject->getServerStatus($server_host, $server_port, $server_endpoint);
|
||||
if (!$server_status) {
|
||||
Messages::flash('ERROR', 'DEFAULT', 'The Jilo Server is not running. Some data may be old and incorrect.', false, true);
|
||||
}
|
||||
}
|
||||
|
||||
// page building
|
||||
include '../app/templates/page-header.php';
|
||||
include '../app/templates/page-menu.php';
|
||||
include '../app/templates/block-message.php';
|
||||
if (isset($currentUser)) {
|
||||
include '../app/templates/page-sidebar.php';
|
||||
}
|
||||
|
|
|
@ -0,0 +1,3 @@
|
|||
<?php
|
||||
phpinfo();
|
||||
?>
|
|
@ -58,24 +58,27 @@ function fetchData(agent_id, url, endpoint, jwtToken, force = false) {
|
|||
// show the result in the html
|
||||
resultElement.innerHTML = JSON.stringify(result, null, 2);
|
||||
|
||||
// get the cache timestamp from the session
|
||||
const now = Date.now();
|
||||
const cacheTimestamp = new Date(now);
|
||||
// we don't cache the /status
|
||||
if (endpoint !== '/status') {
|
||||
// get the cache timestamp from the session
|
||||
const now = Date.now();
|
||||
const cacheTimestamp = new Date(now);
|
||||
|
||||
// display the cache retrieval date and time
|
||||
const formattedDate = cacheTimestamp.toLocaleDateString();
|
||||
const formattedTime = cacheTimestamp.toLocaleTimeString();
|
||||
cacheInfoElement.style.display = '';
|
||||
cacheInfoElement.innerHTML = `cache refreshed on ${formattedDate} at ${formattedTime}`;
|
||||
// display the cache retrieval date and time
|
||||
const formattedDate = cacheTimestamp.toLocaleDateString();
|
||||
const formattedTime = cacheTimestamp.toLocaleTimeString();
|
||||
cacheInfoElement.style.display = '';
|
||||
cacheInfoElement.innerHTML = `cache refreshed on ${formattedDate} at ${formattedTime}`;
|
||||
|
||||
// show the cache buttons
|
||||
loadCacheButton.disabled = false;
|
||||
loadCacheButton.style.display = '';
|
||||
clearCacheButton.disabled = false;
|
||||
clearCacheButton.style.display = '';
|
||||
// show the cache buttons
|
||||
loadCacheButton.disabled = false;
|
||||
loadCacheButton.style.display = '';
|
||||
clearCacheButton.disabled = false;
|
||||
clearCacheButton.style.display = '';
|
||||
|
||||
// send the result to PHP to store in session
|
||||
saveResultToSession(result, agent_id);
|
||||
// send the result to PHP to store in session
|
||||
saveResultToSession(result, agent_id);
|
||||
}
|
||||
}
|
||||
} catch (e) {
|
||||
// Display the error
|
||||
|
|
|
@ -5,6 +5,7 @@ html, body {
|
|||
padding: 0;
|
||||
}
|
||||
|
||||
/* menu */
|
||||
.menu-container {
|
||||
display: flex;
|
||||
justify-content: space-between;
|
||||
|
@ -43,6 +44,7 @@ html, body {
|
|||
.menu-left li a:hover, .menu-right li a:hover {
|
||||
background-color: #111;
|
||||
}
|
||||
/* /menu */
|
||||
|
||||
.error {
|
||||
color: red;
|
||||
|
@ -140,6 +142,7 @@ html, body {
|
|||
.main-content {
|
||||
flex-grow: 1;
|
||||
transition: width 0.5s ease;
|
||||
margin-bottom: 50px;
|
||||
/* width: 80%;*/
|
||||
}
|
||||
.main-content.expanded {
|
||||
|
@ -149,6 +152,10 @@ html, body {
|
|||
width: calc(70% + 3em);
|
||||
}
|
||||
|
||||
.sidebar-content a {
|
||||
text-decoration: none;
|
||||
}
|
||||
|
||||
.logo {
|
||||
height: 36px;
|
||||
margin-top: 10px;
|
||||
|
@ -161,20 +168,26 @@ html, body {
|
|||
background-color: lightseagreen;
|
||||
}
|
||||
|
||||
.sidebar-content a {
|
||||
text-decoration: none;
|
||||
input[type="select"]:disabled,
|
||||
input[type="radio"]:disabled {
|
||||
background-color: #e0e0e0; /* Light gray background */
|
||||
color: #999999; /* Gray text */
|
||||
border-color: #cccccc; /* Lighter gray border */
|
||||
cursor: not-allowed; /* Shows the 'not-allowed' cursor */
|
||||
opacity: 0.7; /* Makes it slightly transparent */
|
||||
}
|
||||
|
||||
/* pagination */
|
||||
.pagination {
|
||||
font-size: 0.66em;
|
||||
text-align: center;
|
||||
display: block;
|
||||
}
|
||||
|
||||
.pagination span {
|
||||
margin-left: 5px;
|
||||
margin-right: 5px;
|
||||
}
|
||||
/* /pagination */
|
||||
|
||||
.th-time {
|
||||
width: 200px;
|
||||
|
@ -200,3 +213,17 @@ html, body {
|
|||
border: 1px solid gray;
|
||||
border-radius: 4px;
|
||||
}
|
||||
|
||||
/* messages system */
|
||||
.alert-sm {
|
||||
padding: 0.25rem 0.5rem;
|
||||
font-size: 0.875rem;
|
||||
line-height: 1.2;
|
||||
margin-bottom: 0.5rem;
|
||||
}
|
||||
|
||||
.alert-sm .btn-close-sm {
|
||||
padding: 0.25rem 0.25rem;
|
||||
margin: -0.125rem -0.125rem -0.125rem auto;
|
||||
font-size: 0.75rem;
|
||||
}
|
||||
|
|
Loading…
Reference in New Issue