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
|
## 0.2.1 - 2024-10-17
|
||||||
|
|
||||||
#### Links
|
#### Links
|
||||||
|
|
|
@ -26,7 +26,7 @@ To see a demo install, go to https://work.lindeas.com/jilo-web-demo/
|
||||||
|
|
||||||
## version
|
## version
|
||||||
|
|
||||||
Current version: **0.2.1** released on **2024-10-17**
|
Current version: **0.3** released on **2025-01-15**
|
||||||
|
|
||||||
## license
|
## 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)
|
- web server (deb: apache | nginx)
|
||||||
- php support in the web server (deb: php-fpm | libapache2-mod-php)
|
- 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
|
- pdo and pdo_sqlite support in php (deb: php-db, php-sqlite3) uncomment in php.ini: ;extension=pdo_sqlite
|
||||||
|
- php-curl module
|
||||||
|
|
||||||
## installation
|
## installation
|
||||||
|
|
||||||
|
|
|
@ -1,13 +1,36 @@
|
||||||
<?php
|
<?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 {
|
class Agent {
|
||||||
|
/**
|
||||||
|
* @var PDO|null $db The database connection instance.
|
||||||
|
*/
|
||||||
private $db;
|
private $db;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Agent constructor.
|
||||||
|
* Initializes the database connection.
|
||||||
|
*
|
||||||
|
* @param object $database The database object to initialize the connection.
|
||||||
|
*/
|
||||||
public function __construct($database) {
|
public function __construct($database) {
|
||||||
$this->db = $database->getConnection();
|
$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 = '') {
|
public function getAgentDetails($platform_id, $agent_id = '') {
|
||||||
$sql = 'SELECT
|
$sql = 'SELECT
|
||||||
ja.id,
|
ja.id,
|
||||||
|
@ -15,6 +38,7 @@ class Agent {
|
||||||
ja.agent_type_id,
|
ja.agent_type_id,
|
||||||
ja.url,
|
ja.url,
|
||||||
ja.secret_key,
|
ja.secret_key,
|
||||||
|
ja.check_period,
|
||||||
jat.description AS agent_description,
|
jat.description AS agent_description,
|
||||||
jat.endpoint AS agent_endpoint
|
jat.endpoint AS agent_endpoint
|
||||||
FROM
|
FROM
|
||||||
|
@ -40,7 +64,44 @@ class Agent {
|
||||||
return $query->fetchAll(PDO::FETCH_ASSOC);
|
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() {
|
public function getAgentTypes() {
|
||||||
$sql = 'SELECT *
|
$sql = 'SELECT *
|
||||||
FROM jilo_agent_types
|
FROM jilo_agent_types
|
||||||
|
@ -51,13 +112,44 @@ class Agent {
|
||||||
return $query->fetchAll(PDO::FETCH_ASSOC);
|
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) {
|
public function addAgent($platform_id, $newAgent) {
|
||||||
try {
|
try {
|
||||||
$sql = 'INSERT INTO jilo_agents
|
$sql = 'INSERT INTO jilo_agents
|
||||||
(platform_id, agent_type_id, url, secret_key)
|
(platform_id, agent_type_id, url, secret_key, check_period)
|
||||||
VALUES
|
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 = $this->db->prepare($sql);
|
||||||
$query->execute([
|
$query->execute([
|
||||||
|
@ -65,6 +157,7 @@ class Agent {
|
||||||
':agent_type_id' => $newAgent['type_id'],
|
':agent_type_id' => $newAgent['type_id'],
|
||||||
':url' => $newAgent['url'],
|
':url' => $newAgent['url'],
|
||||||
':secret_key' => $newAgent['secret_key'],
|
':secret_key' => $newAgent['secret_key'],
|
||||||
|
':check_period' => $newAgent['check_period'],
|
||||||
]);
|
]);
|
||||||
|
|
||||||
return true;
|
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) {
|
public function editAgent($platform_id, $updatedAgent) {
|
||||||
try {
|
try {
|
||||||
$sql = 'UPDATE jilo_agents SET
|
$sql = 'UPDATE jilo_agents SET
|
||||||
agent_type_id = :agent_type_id,
|
agent_type_id = :agent_type_id,
|
||||||
url = :url,
|
url = :url,
|
||||||
secret_key = :secret_key
|
secret_key = :secret_key,
|
||||||
|
check_period = :check_period
|
||||||
WHERE
|
WHERE
|
||||||
id = :agent_id
|
id = :agent_id
|
||||||
AND
|
AND
|
||||||
|
@ -91,6 +193,7 @@ class Agent {
|
||||||
':agent_type_id' => $updatedAgent['agent_type_id'],
|
':agent_type_id' => $updatedAgent['agent_type_id'],
|
||||||
':url' => $updatedAgent['url'],
|
':url' => $updatedAgent['url'],
|
||||||
':secret_key' => $updatedAgent['secret_key'],
|
':secret_key' => $updatedAgent['secret_key'],
|
||||||
|
':check_period' => $updatedAgent['check_period'],
|
||||||
':agent_id' => $updatedAgent['id'],
|
':agent_id' => $updatedAgent['id'],
|
||||||
':platform_id' => $platform_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) {
|
public function deleteAgent($agent_id) {
|
||||||
try {
|
try {
|
||||||
$sql = 'DELETE FROM jilo_agents
|
$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) {
|
public function checkAgentCache($agent_id) {
|
||||||
$agent_cache_name = 'agent' . $agent_id . '_cache';
|
$agent_cache_name = 'agent' . $agent_id . '_cache';
|
||||||
$agent_cache_time = 'agent' . $agent_id . '_time';
|
$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) {
|
private function base64UrlEncode($data) {
|
||||||
return rtrim(strtr(base64_encode($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) {
|
public function generateAgentToken($payload, $secret_key) {
|
||||||
|
|
||||||
// header
|
// 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) {
|
public function fetchAgent($agent_id, $force = false) {
|
||||||
|
|
||||||
// we need agent details for URL and JWT token
|
// 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_name = 'agent' . $agent_id . '_cache';
|
||||||
$agent_cache_time = 'agent' . $agent_id . '_time';
|
$agent_cache_time = 'agent' . $agent_id . '_time';
|
||||||
|
|
||||||
// check if the cache is still valid, unless force-refresh is requested
|
// 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];
|
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
|
// Make the API request
|
||||||
$ch = curl_init();
|
$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_RETURNTRANSFER, true);
|
||||||
curl_setopt($ch, CURLOPT_TIMEOUT, 10); // timeout 10 seconds
|
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);
|
$response = curl_exec($ch);
|
||||||
$curl_error = curl_error($ch);
|
$curl_error = curl_error($ch);
|
||||||
$curl_errno = curl_errno($ch);
|
$curl_errno = curl_errno($ch);
|
||||||
|
$http_code = curl_getinfo($ch, CURLINFO_HTTP_CODE);
|
||||||
|
|
||||||
curl_close($ch);
|
curl_close($ch);
|
||||||
|
|
||||||
// general curl error
|
// curl error
|
||||||
if ($curl_error) {
|
if ($curl_errno) {
|
||||||
return json_encode(['error' => 'curl error: ' . $curl_error]);
|
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)
|
// other custom error(s)
|
||||||
if (strpos($response, 'Auth header not received') !== false) {
|
if (strpos($response, 'Auth header not received') !== false) {
|
||||||
return json_encode(['error' => 'Auth header not received']);
|
return json_encode(['error' => 'Auth header not received']);
|
||||||
}
|
}
|
||||||
|
|
||||||
// Cache the result and the timestamp if the response is successful
|
// 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();
|
$_SESSION[$agent_cache_time] = time();
|
||||||
|
|
||||||
return $response;
|
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) {
|
public function clearAgentCache($agent_id) {
|
||||||
$_SESSION["agent{$agent_id}_cache"] = '';
|
$_SESSION["agent{$agent_id}_cache"] = '';
|
||||||
$_SESSION["agent{$agent_id}_cache_time"] = '';
|
$_SESSION["agent{$agent_id}_cache_time"] = '';
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
// get latest stored jilo agents data
|
/**
|
||||||
public function getLatestData($platform_id, $agent_type, $metric_type) {
|
* Gets a value from a nested array using dot notation
|
||||||
// retrieves data already stored in db from another function (or the jilo-server to-be)
|
* 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
|
<?php
|
||||||
|
|
||||||
|
/**
|
||||||
|
* class Component
|
||||||
|
*
|
||||||
|
* Provides methods to interact with Jitsi component events in the database.
|
||||||
|
*/
|
||||||
class Component {
|
class Component {
|
||||||
|
/**
|
||||||
|
* @var PDO|null $db The database connection instance.
|
||||||
|
*/
|
||||||
private $db;
|
private $db;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Component constructor.
|
||||||
|
* Initializes the database connection.
|
||||||
|
*
|
||||||
|
* @param object $database The database object to initialize the connection.
|
||||||
|
*/
|
||||||
public function __construct($database) {
|
public function __construct($database) {
|
||||||
$this->db = $database->getConnection();
|
$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='') {
|
public function jitsiComponents($jitsi_component, $component_id, $event_type, $from_time, $until_time, $offset=0, $items_per_page='') {
|
||||||
|
|
||||||
// time period drill-down
|
// time period drill-down
|
||||||
|
@ -63,7 +89,6 @@ ORDER BY
|
||||||
return $query->fetchAll(PDO::FETCH_ASSOC);
|
return $query->fetchAll(PDO::FETCH_ASSOC);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
?>
|
?>
|
||||||
|
|
|
@ -1,14 +1,38 @@
|
||||||
<?php
|
<?php
|
||||||
|
|
||||||
|
/**
|
||||||
|
* class Conference
|
||||||
|
*
|
||||||
|
* Provides methods for querying conference-related data from the database.
|
||||||
|
*/
|
||||||
class Conference {
|
class Conference {
|
||||||
|
/**
|
||||||
|
* @var PDO|null $db The database connection instance.
|
||||||
|
*/
|
||||||
private $db;
|
private $db;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Conference constructor.
|
||||||
|
* Initializes the database connection.
|
||||||
|
*
|
||||||
|
* @param object $database The database object to initialize the connection.
|
||||||
|
*/
|
||||||
public function __construct($database) {
|
public function __construct($database) {
|
||||||
$this->db = $database->getConnection();
|
$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='') {
|
public function conferenceById($conference_id, $from_time, $until_time, $offset=0, $items_per_page='') {
|
||||||
|
|
||||||
// time period drill-down
|
// 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='') {
|
public function conferenceByName($conference_name, $from_time, $until_time, $offset=0, $items_per_page='') {
|
||||||
|
|
||||||
// time period drill-down
|
// 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='') {
|
public function conferencesAllFormatted($from_time, $until_time, $offset=0, $items_per_page='') {
|
||||||
|
|
||||||
// time period drill-down
|
// time period drill-down
|
||||||
|
@ -257,7 +300,15 @@ ORDER BY
|
||||||
return $query->fetchAll(PDO::FETCH_ASSOC);
|
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) {
|
public function conferenceNumber($from_time, $until_time) {
|
||||||
|
|
||||||
// time period drill-down
|
// time period drill-down
|
||||||
|
|
|
@ -1,8 +1,64 @@
|
||||||
<?php
|
<?php
|
||||||
|
|
||||||
|
/**
|
||||||
|
* class Config
|
||||||
|
*
|
||||||
|
* Handles editing and fetching configuration files.
|
||||||
|
*/
|
||||||
class Config {
|
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) {
|
public function getPlatformConfigjs($jitsiUrl, $raw = false) {
|
||||||
// constructing the URL
|
// constructing the URL
|
||||||
$configjsFile = $jitsiUrl . '/config.js';
|
$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) {
|
public function getPlatformInterfaceConfigjs($jitsiUrl, $raw = false) {
|
||||||
// constructing the URL
|
// constructing the URL
|
||||||
$interfaceConfigjsFile = $jitsiUrl . '/interface_config.js';
|
$interfaceConfigjsFile = $jitsiUrl . '/interface_config.js';
|
||||||
|
@ -85,7 +148,6 @@ class Config {
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
?>
|
?>
|
||||||
|
|
|
@ -1,10 +1,33 @@
|
||||||
<?php
|
<?php
|
||||||
|
|
||||||
|
/**
|
||||||
|
* class Database
|
||||||
|
*
|
||||||
|
* Manages database connections for SQLite and MySQL (or MariaDB).
|
||||||
|
*/
|
||||||
class Database {
|
class Database {
|
||||||
|
/**
|
||||||
|
* @var PDO|null $pdo The database connection instance.
|
||||||
|
*/
|
||||||
private $pdo;
|
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) {
|
public function __construct($options) {
|
||||||
// pdo needed
|
// check if PDO extension is loaded
|
||||||
if ( !extension_loaded('pdo') ) {
|
if ( !extension_loaded('pdo') ) {
|
||||||
$error = getError('PDO extension not loaded.');
|
$error = getError('PDO extension not loaded.');
|
||||||
}
|
}
|
||||||
|
@ -14,7 +37,7 @@ class Database {
|
||||||
$error = getError('Database type is not set.');
|
$error = getError('Database type is not set.');
|
||||||
}
|
}
|
||||||
|
|
||||||
// database type
|
// connect based on database type
|
||||||
switch ($options['type']) {
|
switch ($options['type']) {
|
||||||
case 'sqlite':
|
case 'sqlite':
|
||||||
$this->connectSqlite($options);
|
$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) {
|
private function connectSqlite($options) {
|
||||||
// pdo_sqlite extension is needed
|
// pdo_sqlite extension is needed
|
||||||
if (!extension_loaded('pdo_sqlite')) {
|
if (!extension_loaded('pdo_sqlite')) {
|
||||||
|
@ -35,7 +67,7 @@ class Database {
|
||||||
|
|
||||||
// SQLite options
|
// SQLite options
|
||||||
if (empty($options['dbFile']) || !file_exists($options['dbFile'])) {
|
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
|
// 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) {
|
private function connectMysql($options) {
|
||||||
// pdo_mysql extension is needed
|
// pdo_mysql extension is needed
|
||||||
if (!extension_loaded('pdo_mysql')) {
|
if (!extension_loaded('pdo_mysql')) {
|
||||||
|
@ -66,10 +111,16 @@ class Database {
|
||||||
$this->pdo = new PDO($dsn, $options['user'], $options['password'] ?? '');
|
$this->pdo = new PDO($dsn, $options['user'], $options['password'] ?? '');
|
||||||
$this->pdo->setAttribute(PDO::ATTR_ERRMODE, PDO::ERRMODE_EXCEPTION);
|
$this->pdo->setAttribute(PDO::ATTR_ERRMODE, PDO::ERRMODE_EXCEPTION);
|
||||||
} catch (PDOException $e) {
|
} 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() {
|
public function getConnection() {
|
||||||
return $this->pdo;
|
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
|
<?php
|
||||||
|
|
||||||
|
/**
|
||||||
|
* class Log
|
||||||
|
*
|
||||||
|
* Handles logging events into a database and reading log entries.
|
||||||
|
*/
|
||||||
class Log {
|
class Log {
|
||||||
|
/**
|
||||||
|
* @var PDO|null $db The database connection instance.
|
||||||
|
*/
|
||||||
private $db;
|
private $db;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Logs constructor.
|
||||||
|
* Initializes the database connection.
|
||||||
|
*
|
||||||
|
* @param object $database The database object to initialize the connection.
|
||||||
|
*/
|
||||||
public function __construct($database) {
|
public function __construct($database) {
|
||||||
$this->db = $database->getConnection();
|
$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') {
|
public function insertLog($user_id, $message, $scope='user') {
|
||||||
try {
|
try {
|
||||||
$sql = 'INSERT INTO logs
|
$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='') {
|
public function readLog($user_id, $scope, $offset=0, $items_per_page='') {
|
||||||
if ($scope === 'user') {
|
if ($scope === 'user') {
|
||||||
$sql = 'SELECT * FROM logs WHERE user_id = :user_id ORDER BY time DESC';
|
$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
|
<?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 {
|
class Participant {
|
||||||
|
/**
|
||||||
|
* @var PDO|null $db The database connection instance.
|
||||||
|
*/
|
||||||
private $db;
|
private $db;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Constructor
|
||||||
|
* Initializes the database connection.
|
||||||
|
*
|
||||||
|
* @param object $database The database object to initialize the connection.
|
||||||
|
*/
|
||||||
public function __construct($database) {
|
public function __construct($database) {
|
||||||
$this->db = $database->getConnection();
|
$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='') {
|
public function conferenceByParticipantId($participant_id, $from_time, $until_time, $offset=0, $items_per_page='') {
|
||||||
|
|
||||||
// time period drill-down
|
// 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='') {
|
public function conferenceByParticipantName($participant_name, $from_time, $until_time, $offset=0, $items_per_page='') {
|
||||||
|
|
||||||
// time period drill-down
|
// 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='') {
|
public function conferenceByParticipantIP($participant_ip, $from_time, $until_time, $offset=0, $items_per_page='') {
|
||||||
|
|
||||||
// time period drill-down
|
// 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='') {
|
public function participantsAll($from_time, $until_time, $offset=0, $items_per_page='') {
|
||||||
|
|
||||||
// time period drill-down
|
// time period drill-down
|
||||||
|
@ -274,7 +328,15 @@ ORDER BY p.id";
|
||||||
return $query->fetchAll(PDO::FETCH_ASSOC);
|
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) {
|
public function participantNumber($from_time, $until_time) {
|
||||||
|
|
||||||
// time period drill-down
|
// time period drill-down
|
||||||
|
@ -309,7 +371,6 @@ AND pe.event_type = 'participant joining'";
|
||||||
return $query->fetchAll(PDO::FETCH_ASSOC);
|
return $query->fetchAll(PDO::FETCH_ASSOC);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
?>
|
?>
|
||||||
|
|
|
@ -1,13 +1,34 @@
|
||||||
<?php
|
<?php
|
||||||
|
|
||||||
|
/**
|
||||||
|
* class Platform
|
||||||
|
*
|
||||||
|
* Handles platform management in the database, including retrieving, adding, editing, and deleting platforms.
|
||||||
|
*/
|
||||||
class Platform {
|
class Platform {
|
||||||
|
/**
|
||||||
|
* @var PDO|null $db The database connection instance.
|
||||||
|
*/
|
||||||
private $db;
|
private $db;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Platform constructor.
|
||||||
|
* Initializes the database connection.
|
||||||
|
*
|
||||||
|
* @param object $database The database object to initialize the connection.
|
||||||
|
*/
|
||||||
public function __construct($database) {
|
public function __construct($database) {
|
||||||
$this->db = $database->getConnection();
|
$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 = '') {
|
public function getPlatformDetails($platform_id = '') {
|
||||||
$sql = 'SELECT * FROM platforms';
|
$sql = 'SELECT * FROM platforms';
|
||||||
if ($platform_id !== '') {
|
if ($platform_id !== '') {
|
||||||
|
@ -23,7 +44,17 @@ class Platform {
|
||||||
return $query->fetchAll(PDO::FETCH_ASSOC);
|
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) {
|
public function addPlatform($newPlatform) {
|
||||||
try {
|
try {
|
||||||
$sql = 'INSERT INTO platforms
|
$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) {
|
public function editPlatform($platform_id, $updatedPlatform) {
|
||||||
try {
|
try {
|
||||||
$sql = 'UPDATE platforms SET
|
$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) {
|
public function deletePlatform($platform_id) {
|
||||||
try {
|
try {
|
||||||
$sql = 'DELETE FROM platforms
|
$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
|
<?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 {
|
class Router {
|
||||||
|
/**
|
||||||
|
* @var array $routes Associative array of route patterns and their corresponding callbacks.
|
||||||
|
*/
|
||||||
private $routes = [];
|
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;
|
$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) {
|
public function dispatch($url) {
|
||||||
// remove variables from url
|
// remove query string variables from url
|
||||||
$url = strtok($url, '?');
|
$url = strtok($url, '?');
|
||||||
|
|
||||||
foreach ($this->routes as $pattern => $callback) {
|
foreach ($this->routes as $pattern => $callback) {
|
||||||
|
// check if the URL matches the current route pattern
|
||||||
if (preg_match('#^' . $pattern . '$#', $url, $matches)) {
|
if (preg_match('#^' . $pattern . '$#', $url, $matches)) {
|
||||||
// move any exact match
|
// remove the exact match to extrat parameters
|
||||||
array_shift($matches);
|
array_shift($matches);
|
||||||
return $this->invoke($callback, $matches);
|
return $this->invoke($callback, $matches);
|
||||||
}
|
}
|
||||||
|
@ -25,11 +51,34 @@ class Router {
|
||||||
echo '404 page not found';
|
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) {
|
private function invoke($callback, $params) {
|
||||||
list($controllerName, $methodName) = explode('@', $callback);
|
list($controllerName, $methodName) = explode('@', $callback);
|
||||||
// $controllerClass = "\\App\\Controllers\\$controllerName";
|
$controllerClass = "../pages/$controllerName";
|
||||||
$controllerClass = "../pages/$pageName";
|
|
||||||
|
// ensure the controller class exists
|
||||||
|
if (!class_exists($controllerClass)) {
|
||||||
|
throw new Exception("Controller '$controllerClass' not found.");
|
||||||
|
}
|
||||||
|
|
||||||
$controller = new $controllerClass();
|
$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);
|
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
|
<?php
|
||||||
|
|
||||||
|
/**
|
||||||
|
* class User
|
||||||
|
*
|
||||||
|
* Handles user-related functionalities such as registration, login, rights management, and profile updates.
|
||||||
|
*/
|
||||||
class User {
|
class User {
|
||||||
|
/**
|
||||||
|
* @var PDO|null $db The database connection instance.
|
||||||
|
*/
|
||||||
private $db;
|
private $db;
|
||||||
|
private $rateLimiter;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* User constructor.
|
||||||
|
* Initializes the database connection.
|
||||||
|
*
|
||||||
|
* @param object $database The database object to initialize the connection.
|
||||||
|
*/
|
||||||
public function __construct($database) {
|
public function __construct($database) {
|
||||||
$this->db = $database->getConnection();
|
$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) {
|
public function register($username, $password) {
|
||||||
try {
|
try {
|
||||||
// we have two inserts, start a transaction
|
// 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) {
|
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->bindParam(':username', $username);
|
||||||
$query->execute();
|
$query->execute();
|
||||||
|
|
||||||
$user = $query->fetch(PDO::FETCH_ASSOC);
|
$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['user_id'] = $user['id'];
|
||||||
$_SESSION['username'] = $user['username'];
|
$_SESSION['username'] = $user['username'];
|
||||||
return true;
|
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?
|
// FIXME not used now?
|
||||||
public function getUserId($username) {
|
public function getUserId($username) {
|
||||||
$sql = 'SELECT id FROM users WHERE username = :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) {
|
public function getUserDetails($user_id) {
|
||||||
$sql = 'SELECT
|
$sql = 'SELECT
|
||||||
um.*,
|
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) {
|
public function addUserRight($user_id, $right_id) {
|
||||||
$sql = 'INSERT INTO users_rights
|
$sql = 'INSERT INTO users_rights
|
||||||
(user_id, right_id)
|
(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) {
|
public function removeUserRight($user_id, $right_id) {
|
||||||
$sql = 'DELETE FROM users_rights
|
$sql = 'DELETE FROM users_rights
|
||||||
WHERE
|
WHERE
|
||||||
|
@ -133,7 +210,12 @@ class User {
|
||||||
]);
|
]);
|
||||||
}
|
}
|
||||||
|
|
||||||
// get all rights
|
|
||||||
|
/**
|
||||||
|
* Retrieves all rights in the system.
|
||||||
|
*
|
||||||
|
* @return array List of rights.
|
||||||
|
*/
|
||||||
public function getAllRights() {
|
public function getAllRights() {
|
||||||
$sql = 'SELECT
|
$sql = 'SELECT
|
||||||
id AS right_id,
|
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) {
|
public function getUserRights($user_id) {
|
||||||
$sql = 'SELECT
|
$sql = 'SELECT
|
||||||
u.id AS user_id,
|
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) {
|
function hasRight($user_id, $right_name) {
|
||||||
$userRights = $this->getUserRights($user_id);
|
$userRights = $this->getUserRights($user_id);
|
||||||
$userHasRight = false;
|
$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) {
|
public function editUser($user_id, $updatedUser) {
|
||||||
try {
|
try {
|
||||||
$sql = 'UPDATE users_meta SET
|
$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 = '') {
|
public function removeAvatar($user_id, $old_avatar = '') {
|
||||||
try {
|
try {
|
||||||
// remove from database
|
// 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) {
|
public function changeAvatar($user_id, $avatar_file, $avatars_path) {
|
||||||
try {
|
try {
|
||||||
// check if the file was uploaded
|
// check if the file was uploaded
|
||||||
|
|
|
@ -31,7 +31,7 @@ return [
|
||||||
// default avatar
|
// default avatar
|
||||||
'default_avatar' => 'static/default_avatar.png',
|
'default_avatar' => 'static/default_avatar.png',
|
||||||
// system info
|
// system info
|
||||||
'version' => '0.2.1',
|
'version' => '0.3',
|
||||||
// development has verbose error messages, production has not
|
// development has verbose error messages, production has not
|
||||||
'environment' => 'development',
|
'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>
|
<script>
|
||||||
var ctx = document.getElementById('graph_<?= $data['graph_name'] ?>').getContext('2d');
|
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 timeRangeName = '';
|
||||||
|
|
||||||
var labels = chartData0.map(function(item) {
|
// Prepare datasets
|
||||||
return item.date;
|
var datasets = [];
|
||||||
});
|
<?php foreach ($data['datasets'] as $dataset): ?>
|
||||||
var values0 = chartData0.map(function(item) {
|
var chartData = <?php echo json_encode($dataset['data']); ?>;
|
||||||
return item.value;
|
datasets.push({
|
||||||
});
|
label: '<?= $dataset['label'] ?>',
|
||||||
var values1 = chartData1.map(function(item) {
|
data: chartData.map(function(item) {
|
||||||
return item.value;
|
return {
|
||||||
});
|
x: item.date,
|
||||||
|
y: item.value
|
||||||
|
};
|
||||||
|
}),
|
||||||
|
borderColor: '<?= $dataset['color'] ?>',
|
||||||
|
borderWidth: 1,
|
||||||
|
fill: false
|
||||||
|
});
|
||||||
|
<?php endforeach; ?>
|
||||||
|
|
||||||
var graph_<?= $data['graph_name'] ?> = new Chart(ctx, {
|
var graph_<?= $data['graph_name'] ?> = new Chart(ctx, {
|
||||||
type: 'line',
|
type: 'line',
|
||||||
data: {
|
data: {
|
||||||
labels: labels,
|
datasets: datasets
|
||||||
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
|
|
||||||
}
|
|
||||||
]
|
|
||||||
},
|
},
|
||||||
options: {
|
options: {
|
||||||
layout: {
|
layout: {
|
||||||
|
@ -64,7 +54,6 @@ var graph_<?= $data['graph_name'] ?> = new Chart(ctx, {
|
||||||
mode: 'x'
|
mode: 'x'
|
||||||
},
|
},
|
||||||
zoom: {
|
zoom: {
|
||||||
// enabled: true,
|
|
||||||
mode: 'x',
|
mode: 'x',
|
||||||
drag: {
|
drag: {
|
||||||
enabled: true, // Enable drag to select range
|
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({
|
graphs.push({
|
||||||
graph: graph_<?= $data['graph_name'] ?>,
|
graph: graph_<?= $data['graph_name'] ?>,
|
||||||
label: document.getElementById('current-period-<?= $data['graph_name'] ?>')
|
label: '<?= $data['graph_title'] ?>'
|
||||||
});
|
});
|
||||||
|
|
||||||
// Update the time range label
|
// Function to update the period label
|
||||||
function updatePeriodLabel(chart, labelElement) {
|
function updatePeriodLabel(chart, label) {
|
||||||
var startDate = chart.scales.x.min;
|
var startDate = new Date(chart.scales.x.min);
|
||||||
var endDate = chart.scales.x.max;
|
var endDate = new Date(chart.scales.x.max);
|
||||||
if (timeRangeName == 'today') {
|
var periodLabel = document.getElementById('current-period-<?= $data['graph_name'] ?>');
|
||||||
labelElement.innerHTML = 'Currently displaying: ' + timeRangeName + ' (' + new Date(startDate).toLocaleDateString() + ')';
|
|
||||||
|
if (timeRangeName) {
|
||||||
|
periodLabel.textContent = label + ' (' + timeRangeName + ')';
|
||||||
} else {
|
} 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
|
// Initial label update
|
||||||
graph_<?= $data['graph_name'] ?>.options.plugins.zoom.onZoom = function({ chart }) {
|
updatePeriodLabel(graph_<?= $data['graph_name'] ?>, '<?= $data['graph_title'] ?>');
|
||||||
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'] ?>'));
|
|
||||||
|
|
||||||
</script>
|
</script>
|
||||||
|
|
|
@ -1,9 +1,7 @@
|
||||||
<?php
|
<?php
|
||||||
|
|
||||||
// render config variables array
|
// render config variables array
|
||||||
function renderConfig($configPart, $indent, $platform=false, $parent='') {
|
function renderConfig($configPart, $indent) {
|
||||||
global $app_root;
|
|
||||||
global $config;
|
|
||||||
?>
|
?>
|
||||||
<div style="padding-left: <?= $indent ?>px; padding-bottom: 20px;">
|
<div style="padding-left: <?= $indent ?>px; padding-bottom: 20px;">
|
||||||
<?php foreach ($configPart as $config_item => $config_value) { ?>
|
<?php foreach ($configPart as $config_item => $config_value) { ?>
|
||||||
|
@ -15,20 +13,62 @@ function renderConfig($configPart, $indent, $platform=false, $parent='') {
|
||||||
if (is_array($config_value)) {
|
if (is_array($config_value)) {
|
||||||
// here we render recursively nested arrays
|
// here we render recursively nested arrays
|
||||||
$indent = $indent + 50;
|
$indent = $indent + 50;
|
||||||
if ($parent === 'platforms') {
|
renderConfig($config_value, $indent);
|
||||||
$indent = 100;
|
$indent = 0;
|
||||||
}
|
} else {
|
||||||
if ($config_item === 'platforms') {
|
// if it's not array, just display it
|
||||||
renderConfig($config_value, $indent, $platform, 'platforms');
|
if ($config_item === 'registration_enabled') { ?>
|
||||||
} else {
|
<div class="border col-md-8 text-start">
|
||||||
renderConfig($config_value, $indent, $platform);
|
<?= ($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;
|
$indent = 0;
|
||||||
} else {
|
} else {
|
||||||
// if it's not array, just display it
|
// if it's not array, just display it
|
||||||
?>
|
?>
|
||||||
<div class="border col-md-8 text-start">
|
<div class="col-md-8 text-start">
|
||||||
<?= htmlspecialchars($config_value ?? '')?>
|
<?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>
|
</div>
|
||||||
<?php } ?>
|
<?php } ?>
|
||||||
</div>
|
</div>
|
||||||
|
@ -36,4 +76,6 @@ function renderConfig($configPart, $indent, $platform=false, $parent='') {
|
||||||
</div>
|
</div>
|
||||||
<?php
|
<?php
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
?>
|
?>
|
||||||
|
|
|
@ -13,10 +13,9 @@ function connectDB($config, $database = '', $dbFile = '', $platformId = '') {
|
||||||
'type' => 'sqlite',
|
'type' => 'sqlite',
|
||||||
'dbFile' => $dbFile,
|
'dbFile' => $dbFile,
|
||||||
]);
|
]);
|
||||||
|
return ['db' => $db, 'error' => null];
|
||||||
} catch (Exception $e) {
|
} catch (Exception $e) {
|
||||||
$error = getError('Error connecting to DB.', $e->getMessage());
|
return ['db' => null, 'error' => getError('Error connecting to DB.', $e->getMessage())];
|
||||||
include '../app/templates/block-message.php';
|
|
||||||
exit();
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// connecting to a jilo-web database of the web app
|
// 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'],
|
'dbFile' => $config['db']['sqlite_file'],
|
||||||
]);
|
]);
|
||||||
$pdo = $db->getConnection();
|
$pdo = $db->getConnection();
|
||||||
|
return ['db' => $db, 'error' => null];
|
||||||
} catch (Exception $e) {
|
} catch (Exception $e) {
|
||||||
$error = getError('Error connecting to DB.', $e->getMessage());
|
return ['db' => null, 'error' => getError('Error connecting to DB.', $e->getMessage())];
|
||||||
include '../app/templates/block-message.php';
|
|
||||||
exit();
|
|
||||||
}
|
}
|
||||||
// mysql/mariadb database
|
// mysql/mariadb database
|
||||||
} elseif ($config['db']['db_type'] === 'mysql' || $config['db']['db_type'] === 'mariadb') {
|
} 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'],
|
'password' => $config['db']['sql_password'],
|
||||||
]);
|
]);
|
||||||
$pdo = $db->getConnection();
|
$pdo = $db->getConnection();
|
||||||
|
return ['db' => $db, 'error' => null];
|
||||||
} catch (Exception $e) {
|
} catch (Exception $e) {
|
||||||
$error = getError('Error connecting to DB.', $e->getMessage());
|
return ['db' => null, 'error' => getError('Error connecting to DB.', $e->getMessage())];
|
||||||
include '../app/templates/block-message.php';
|
|
||||||
exit();
|
|
||||||
}
|
}
|
||||||
// unknown database
|
// unknown database
|
||||||
} else {
|
} else {
|
||||||
$error = "Error: unknow database type \"{$config['db']['db_type']}\"";
|
$error = "Error: unknow database type \"{$config['db']['db_type']}\"";
|
||||||
include '../app/templates/block-message.php';
|
Messages::flash('ERROR', 'DEFAULT', $error);
|
||||||
exit();
|
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']);
|
$until_time = htmlspecialchars($_REQUEST['until_time']);
|
||||||
}
|
}
|
||||||
|
|
||||||
if (isset($_SESSION['notice'])) {
|
// sanitize session vars
|
||||||
$notice = htmlspecialchars($_SESSION['notice']); // 'notice' for all non-critical messages
|
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
|
// agents
|
||||||
|
@ -38,6 +51,9 @@ if (isset($_POST['url'])) {
|
||||||
if (isset($_POST['secret_key'])) {
|
if (isset($_POST['secret_key'])) {
|
||||||
$secret_key = htmlspecialchars($_POST['secret_key']);
|
$secret_key = htmlspecialchars($_POST['secret_key']);
|
||||||
}
|
}
|
||||||
|
if (isset($_POST['check_period'])) {
|
||||||
|
$check_period = htmlspecialchars($_POST['check_period']);
|
||||||
|
}
|
||||||
|
|
||||||
// platforms
|
// platforms
|
||||||
if (isset($_POST['name'])) {
|
if (isset($_POST['name'])) {
|
|
@ -1,5 +1,17 @@
|
||||||
<?php
|
<?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'] ?? '';
|
$action = $_REQUEST['action'] ?? '';
|
||||||
$agent = $_REQUEST['agent'] ?? '';
|
$agent = $_REQUEST['agent'] ?? '';
|
||||||
require '../app/classes/agent.php';
|
require '../app/classes/agent.php';
|
||||||
|
|
|
@ -1,85 +1,107 @@
|
||||||
<?php
|
<?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';
|
require '../app/classes/component.php';
|
||||||
|
|
||||||
// connect to database
|
// 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
|
// if DB connection has error, display it and stop here
|
||||||
include '../app/helpers/time_range.php';
|
if ($response['db'] === null) {
|
||||||
|
Messages::flash('ERROR', 'DEFAULT', $response['error']);
|
||||||
|
|
||||||
// jitsi component events list
|
// otherwise if DB connection is OK, go on
|
||||||
// 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 {
|
} else {
|
||||||
$widget['title'] = 'Jitsi events for <strong>all components</strong>';
|
$db = $response['db'];
|
||||||
}
|
|
||||||
// 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
|
// specify time range
|
||||||
include '../app/templates/event-list-components.php';
|
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
|
<?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';
|
require '../app/classes/conference.php';
|
||||||
|
|
||||||
// connect to database
|
// 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
|
// if DB connection has error, display it and stop here
|
||||||
include '../app/helpers/time_range.php';
|
if ($response['db'] === null) {
|
||||||
|
Messages::flash('ERROR', 'DEFAULT', $response['error']);
|
||||||
|
|
||||||
// conference id/name are specified when searching specific conference(s)
|
// otherwise if DB connection is OK, go on
|
||||||
// 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'];
|
|
||||||
} else {
|
} else {
|
||||||
unset($conferenceId);
|
$db = $response['db'];
|
||||||
unset($conferenceName);
|
|
||||||
}
|
|
||||||
|
|
||||||
|
// specify time range
|
||||||
|
include '../app/helpers/time_range.php';
|
||||||
|
|
||||||
//
|
// conference id/name are specified when searching specific conference(s)
|
||||||
// Conference listings
|
// 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);
|
if (isset($_REQUEST['id']) && $_REQUEST['id'] != '') {
|
||||||
|
$conferenceId = $_REQUEST['id'];
|
||||||
// pagination variables
|
unset($_REQUEST['name']);
|
||||||
$items_per_page = 15;
|
unset($conferenceName);
|
||||||
$browse_page = $_REQUEST['p'] ?? 1;
|
} elseif (isset($_REQUEST['name']) && $_REQUEST['name'] != '') {
|
||||||
$browse_page = (int)$browse_page;
|
unset($conferenceId);
|
||||||
$offset = ($browse_page -1) * $items_per_page;
|
$conferenceName = $_REQUEST['name'];
|
||||||
|
} else {
|
||||||
// search and list specific conference ID
|
unset($conferenceId);
|
||||||
if (isset($conferenceId)) {
|
unset($conferenceName);
|
||||||
$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'] != '') {
|
// Conference listings
|
||||||
$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
|
$conferenceObject = new Conference($db);
|
||||||
include '../app/templates/event-list-conferences.php';
|
|
||||||
|
// 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
|
<?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'] ?? '';
|
$action = $_REQUEST['action'] ?? '';
|
||||||
$agent = $_REQUEST['agent'] ?? '';
|
$agent = $_REQUEST['agent'] ?? '';
|
||||||
|
$host = $_REQUEST['host'] ?? '';
|
||||||
|
|
||||||
require '../app/classes/config.php';
|
require '../app/classes/config.php';
|
||||||
|
require '../app/classes/host.php';
|
||||||
require '../app/classes/agent.php';
|
require '../app/classes/agent.php';
|
||||||
|
|
||||||
$configObject = new Config();
|
$configObject = new Config();
|
||||||
|
$hostObject = new Host($dbWeb);
|
||||||
$agentObject = new Agent($dbWeb);
|
$agentObject = new Agent($dbWeb);
|
||||||
|
|
||||||
// if a form is submitted, it's from the edit page
|
|
||||||
if ($_SERVER['REQUEST_METHOD'] == 'POST') {
|
if ($_SERVER['REQUEST_METHOD'] == 'POST') {
|
||||||
|
/**
|
||||||
|
* Handles form submissions from editing page
|
||||||
|
*/
|
||||||
|
|
||||||
// FIXME - if editing the flat file is no more needed, remove this
|
// editing the config file
|
||||||
// // load the config file and initialize a copy
|
if (isset($_POST['item']) && $_POST['item'] === 'config_file') {
|
||||||
// $content = file_get_contents($config_file);
|
// check if file is writable
|
||||||
// $updatedContent = $content;
|
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
|
// 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 = [
|
$newAgent = [
|
||||||
'type_id' => $type,
|
'type_id' => $type,
|
||||||
'url' => $url,
|
'url' => $url,
|
||||||
'secret_key' => $secret_key,
|
'secret_key' => $secret_key,
|
||||||
|
'check_period' => $check_period,
|
||||||
];
|
];
|
||||||
$result = $agentObject->addAgent($platform_id, $newAgent);
|
$result = $agentObject->addAgent($platform_id, $newAgent);
|
||||||
if ($result === true) {
|
if ($result === true) {
|
||||||
|
@ -38,7 +79,21 @@ if ($_SERVER['REQUEST_METHOD'] == 'POST') {
|
||||||
'jitsi_url' => $_POST['jitsi_url'],
|
'jitsi_url' => $_POST['jitsi_url'],
|
||||||
'jilo_database' => $_POST['jilo_database'],
|
'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
|
// deleting an agent
|
||||||
} elseif (isset($_POST['delete']) && isset($_POST['agent']) && $_POST['delete'] === 'true') {
|
} elseif (isset($_POST['delete']) && isset($_POST['agent']) && $_POST['delete'] === 'true') {
|
||||||
|
@ -52,7 +107,27 @@ if ($_SERVER['REQUEST_METHOD'] == 'POST') {
|
||||||
// deleting a platform
|
// deleting a platform
|
||||||
} elseif (isset($_POST['delete']) && $_POST['delete'] === 'true') {
|
} elseif (isset($_POST['delete']) && $_POST['delete'] === 'true') {
|
||||||
$platform = $_POST['platform'];
|
$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
|
// an update to an existing agent
|
||||||
} elseif (isset($_POST['agent'])) {
|
} elseif (isset($_POST['agent'])) {
|
||||||
|
@ -61,6 +136,7 @@ if ($_SERVER['REQUEST_METHOD'] == 'POST') {
|
||||||
'agent_type_id' => $type,
|
'agent_type_id' => $type,
|
||||||
'url' => $url,
|
'url' => $url,
|
||||||
'secret_key' => $secret_key,
|
'secret_key' => $secret_key,
|
||||||
|
'check_period' => $check_period,
|
||||||
];
|
];
|
||||||
$result = $agentObject->editAgent($platform_id, $updatedAgent);
|
$result = $agentObject->editAgent($platform_id, $updatedAgent);
|
||||||
if ($result === true) {
|
if ($result === true) {
|
||||||
|
@ -77,85 +153,104 @@ if ($_SERVER['REQUEST_METHOD'] == 'POST') {
|
||||||
'jitsi_url' => $_POST['jitsi_url'],
|
'jitsi_url' => $_POST['jitsi_url'],
|
||||||
'jilo_database' => $_POST['jilo_database'],
|
'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
|
// FIXME the new file is not loaded on first page load
|
||||||
unset($config);
|
unset($config);
|
||||||
header("Location: $app_root?platform=$platform_id&page=config");
|
header("Location: $app_root?page=config&item=$item");
|
||||||
exit();
|
exit();
|
||||||
|
|
||||||
// no form submitted, show the templates
|
|
||||||
} else {
|
} else {
|
||||||
|
/**
|
||||||
|
* Handles GET requests to display templates.
|
||||||
|
*/
|
||||||
|
|
||||||
// $item - config.js and interface_config.js are special case; remote loaded files
|
|
||||||
switch ($item) {
|
switch ($item) {
|
||||||
case 'configjs':
|
|
||||||
$mode = $_REQUEST['mode'] ?? '';
|
case 'platform':
|
||||||
$raw = ($mode === 'raw');
|
if (isset($action) && $action === 'add') {
|
||||||
$platformConfigjs = $configObject->getPlatformConfigjs($platformDetails[0]['jitsi_url'], $raw);
|
include '../app/templates/config-platform-add.php';
|
||||||
include '../app/templates/config-list-configjs.php';
|
} elseif (isset($action) && $action === 'edit') {
|
||||||
break;
|
include '../app/templates/config-platform-edit.php';
|
||||||
case 'interfaceconfigjs':
|
} elseif (isset($action) && $action === 'delete') {
|
||||||
$mode = $_REQUEST['mode'] ?? '';
|
include '../app/templates/config-platform-delete.php';
|
||||||
$raw = ($mode === 'raw');
|
} else {
|
||||||
$platformInterfaceConfigjs = $configObject->getPlatformInterfaceConfigjs($platformDetails[0]['jitsi_url'], $raw);
|
if ($userObject->hasRight($user_id, 'view config file')) {
|
||||||
include '../app/templates/config-list-interfaceconfigjs.php';
|
include '../app/templates/config-platform.php';
|
||||||
|
} else {
|
||||||
|
include '../app/templates/error-unauthorized.php';
|
||||||
|
}
|
||||||
|
}
|
||||||
break;
|
break;
|
||||||
|
|
||||||
// if there is no $item, we work on the local config DB
|
case 'host':
|
||||||
default:
|
if (isset($action) && $action === 'add') {
|
||||||
switch ($action) {
|
include '../app/templates/config-host-add.php';
|
||||||
case 'add-agent':
|
} elseif (isset($action) && $action === 'edit') {
|
||||||
$jilo_agent_types = $agentObject->getAgentTypes();
|
$hostDetails = $hostObject->getHostDetails($platform_id, $agent);
|
||||||
include '../app/templates/config-add-agent.php';
|
include '../app/templates/config-host-edit.php';
|
||||||
break;
|
} elseif (isset($action) && $action === 'delete') {
|
||||||
case 'add':
|
$hostDetails = $hostObject->getHostDetails($platform_id, $agent);
|
||||||
include '../app/templates/config-add-platform.php';
|
include '../app/templates/config-host-delete.php';
|
||||||
break;
|
} else {
|
||||||
case 'edit':
|
if ($userObject->hasRight($user_id, 'view config file')) {
|
||||||
if (isset($_GET['agent'])) {
|
$hostDetails = $hostObject->getHostDetails();
|
||||||
$agentDetails = $agentObject->getAgentDetails($platform_id, $agent);
|
include '../app/templates/config-host.php';
|
||||||
$jilo_agent_types = $agentObject->getAgentTypes();
|
} else {
|
||||||
include '../app/templates/config-edit-agent.php';
|
include '../app/templates/error-unauthorized.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';
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
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
|
<?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/conference.php';
|
||||||
require '../app/classes/participant.php';
|
require '../app/classes/participant.php';
|
||||||
|
|
||||||
// connect to database
|
// 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 statistics for the last year.
|
||||||
//
|
*
|
||||||
|
* Retrieves conference and participant numbers for each month within the past year.
|
||||||
|
*/
|
||||||
////
|
|
||||||
// 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 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');
|
$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);
|
$widget['records'] = array();
|
||||||
$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();
|
|
||||||
|
|
||||||
|
// loop 1 year in the past
|
||||||
$i = 0;
|
$i = 0;
|
||||||
foreach ($search as $item) {
|
while ($fromMonth < $thisMonth) {
|
||||||
extract($item);
|
|
||||||
|
|
||||||
// we don't have duration field, so we calculate it
|
$untilMonth = clone $fromMonth;
|
||||||
if (!empty($start) && !empty($end)) {
|
$untilMonth->modify('last day of this month');
|
||||||
$duration = gmdate("H:i:s", abs(strtotime($end) - strtotime($start)));
|
|
||||||
} else {
|
$from_time = $fromMonth->format('Y-m-d');
|
||||||
$duration = '';
|
$until_time = $untilMonth->format('Y-m-d');
|
||||||
}
|
|
||||||
$conference_record = array(
|
$searchConferenceNumber = $conferenceObject->conferenceNumber($from_time, $until_time);
|
||||||
// assign title to the field in the array record
|
$searchParticipantNumber = $participantObject->participantNumber($from_time, $until_time);
|
||||||
'component' => $jitsi_component,
|
|
||||||
'start' => $start,
|
// pretty format for displaying the month in the widget
|
||||||
'end' => $end,
|
$month = $fromMonth->format('F Y');
|
||||||
'duration' => $duration,
|
|
||||||
'conference ID' => $conference_id,
|
// populate the records
|
||||||
'conference name' => $conference_name,
|
$widget['records'][$i] = array(
|
||||||
'participants' => $participants,
|
'from_time' => $from_time,
|
||||||
'name count' => $name_count,
|
'until_time' => $until_time,
|
||||||
'conference host' => $conference_host
|
'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++;
|
$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
|
<?php
|
||||||
|
|
||||||
|
// Get any new messages
|
||||||
|
include '../app/includes/messages.php';
|
||||||
|
include '../app/includes/messages-show.php';
|
||||||
|
|
||||||
include '../app/templates/help-main.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
|
<?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
|
// clear the global error var before login
|
||||||
unset($error);
|
unset($error);
|
||||||
|
|
||||||
|
@ -8,61 +21,88 @@ try {
|
||||||
// connect to database
|
// connect to database
|
||||||
$dbWeb = connectDB($config);
|
$dbWeb = connectDB($config);
|
||||||
|
|
||||||
if ( $_SERVER['REQUEST_METHOD'] == 'POST' ) {
|
// Initialize RateLimiter
|
||||||
$username = $_POST['username'];
|
require_once '../app/classes/ratelimiter.php';
|
||||||
$password = $_POST['password'];
|
$rateLimiter = new RateLimiter($dbWeb['db']);
|
||||||
|
|
||||||
// login successful
|
if ( $_SERVER['REQUEST_METHOD'] == 'POST' ) {
|
||||||
if ( $userObject->login($username, $password) ) {
|
try {
|
||||||
// if remember_me is checked, max out the session
|
$username = $_POST['username'];
|
||||||
if (isset($_POST['remember_me'])) {
|
$password = $_POST['password'];
|
||||||
// 30*24*60*60 = 30 days
|
|
||||||
$cookie_lifetime = 30 * 24 * 60 * 60;
|
// Check if IP is blacklisted
|
||||||
$setcookie_lifetime = time() + 30 * 24 * 60 * 60;
|
if ($rateLimiter->isIpBlacklisted($user_IP)) {
|
||||||
$gc_maxlifetime = 30 * 24 * 60 * 60;
|
throw new Exception(Messages::get('LOGIN', 'IP_BLACKLISTED')['message']);
|
||||||
} else {
|
|
||||||
// 0 - session end on browser close
|
|
||||||
// 1440 - 24 minutes (default)
|
|
||||||
$cookie_lifetime = 0;
|
|
||||||
$setcookie_lifetime = 0;
|
|
||||||
$gc_maxlifetime = 1440;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// set session lifetime and cookies
|
// Check rate limiting (but skip if IP is whitelisted)
|
||||||
setcookie('username', $username, [
|
if (!$rateLimiter->isIpWhitelisted($user_IP)) {
|
||||||
'expires' => $setcookie_lifetime,
|
$attempts = $rateLimiter->getRecentAttempts($user_IP);
|
||||||
'path' => $config['folder'],
|
if ($attempts >= $rateLimiter->maxAttempts) {
|
||||||
'domain' => $config['domain'],
|
throw new Exception(Messages::get('LOGIN', 'LOGIN_BLOCKED')['message']);
|
||||||
'secure' => isset($_SERVER['HTTPS']),
|
}
|
||||||
'httponly' => true,
|
}
|
||||||
'samesite' => 'Strict'
|
|
||||||
]);
|
|
||||||
|
|
||||||
// redirect to index
|
// login successful
|
||||||
$_SESSION['notice'] = "Login successful";
|
if ( $userObject->login($username, $password) ) {
|
||||||
$user_id = $userObject->getUserId($username)[0]['id'];
|
// if remember_me is checked, max out the session
|
||||||
$logObject->insertLog($user_id, "Login: User \"$username\" logged in. IP: $user_IP", 'user');
|
if (isset($_POST['remember_me'])) {
|
||||||
header('Location: index.php');
|
// 30*24*60*60 = 30 days
|
||||||
exit();
|
$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
|
// set session lifetime and cookies
|
||||||
} else {
|
setcookie('username', $username, [
|
||||||
$_SESSION['error'] = "Login failed.";
|
'expires' => $setcookie_lifetime,
|
||||||
$user_id = $userObject->getUserId($username)[0]['id'];
|
'path' => $config['folder'],
|
||||||
$logObject->insertLog($user_id, "Login: Failed login attempt for user \"$username\". IP: $user_IP", 'user');
|
'domain' => $config['domain'],
|
||||||
header('Location: index.php');
|
'secure' => isset($_SERVER['HTTPS']),
|
||||||
exit();
|
'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) {
|
} 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'])) {
|
if (!empty($config['login_message'])) {
|
||||||
$notice = $config['login_message'];
|
echo Messages::render('NOTICE', 'DEFAULT', $config['login_message'], false, false, false);
|
||||||
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-login.php';
|
include '../app/templates/form-login.php';
|
||||||
|
|
||||||
?>
|
?>
|
||||||
|
|
|
@ -1,8 +1,23 @@
|
||||||
<?php
|
<?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
|
// specify time range
|
||||||
include '../app/helpers/time_range.php';
|
include '../app/helpers/time_range.php';
|
||||||
|
@ -13,9 +28,6 @@ $browse_page = $_REQUEST['p'] ?? 1;
|
||||||
$browse_page = (int)$browse_page;
|
$browse_page = (int)$browse_page;
|
||||||
$offset = ($browse_page -1) * $items_per_page;
|
$offset = ($browse_page -1) * $items_per_page;
|
||||||
|
|
||||||
// user or system
|
|
||||||
$scope = 'user';
|
|
||||||
|
|
||||||
// prepare the result
|
// prepare the result
|
||||||
$search = $logObject->readLog($user_id, $scope, $offset, $items_per_page);
|
$search = $logObject->readLog($user_id, $scope, $offset, $items_per_page);
|
||||||
$search_all = $logObject->readLog($user_id, $scope);
|
$search_all = $logObject->readLog($user_id, $scope);
|
||||||
|
|
|
@ -1,155 +1,177 @@
|
||||||
<?php
|
<?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';
|
require '../app/classes/participant.php';
|
||||||
|
|
||||||
// connect to database
|
// 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
|
// if DB connection has error, display it and stop here
|
||||||
include '../app/helpers/time_range.php';
|
if ($response['db'] === null) {
|
||||||
|
Messages::flash('ERROR', 'DEFAULT', $response['error']);
|
||||||
|
|
||||||
// participant id/name/IP are specified when searching specific participant(s)
|
// otherwise if DB connection is OK, go on
|
||||||
// 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 {
|
} else {
|
||||||
unset($participantId);
|
$db = $response['db'];
|
||||||
unset($participantName);
|
|
||||||
}
|
|
||||||
|
|
||||||
|
// specify time range
|
||||||
|
include '../app/helpers/time_range.php';
|
||||||
|
|
||||||
//
|
// participant id/name/IP are specified when searching specific participant(s)
|
||||||
// Participant listings
|
// 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
|
||||||
$participantObject = new Participant($db);
|
if (isset($_REQUEST['id']) && $_REQUEST['id'] != '') {
|
||||||
|
$participantId = $_REQUEST['id'];
|
||||||
// pagination variables
|
unset($_REQUEST['name']);
|
||||||
$items_per_page = 15;
|
unset($participantName);
|
||||||
$browse_page = $_REQUEST['p'] ?? 1;
|
} elseif (isset($_REQUEST['name']) && $_REQUEST['name'] != '') {
|
||||||
$browse_page = (int)$browse_page;
|
unset($participantId);
|
||||||
$offset = ($browse_page -1) * $items_per_page;
|
$participantName = $_REQUEST['name'];
|
||||||
|
} elseif (isset($_REQUEST['ip']) && $_REQUEST['ip'] != '') {
|
||||||
// search and list specific participant ID
|
unset($participantId);
|
||||||
if (isset($participantId)) {
|
$participantIp = $_REQUEST['ip'];
|
||||||
$search = $participantObject->conferenceByParticipantId($participantId, $from_time, $until_time, $offset, $items_per_page);
|
} else {
|
||||||
$search_all = $participantObject->conferenceByParticipantId($participantId, $from_time, $until_time);
|
unset($participantId);
|
||||||
// search and list specific participant name (stats_id)
|
unset($participantName);
|
||||||
} 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'] != '') {
|
// Participant listings
|
||||||
$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
|
$participantObject = new Participant($db);
|
||||||
include '../app/templates/widget.php';
|
|
||||||
|
// 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
|
<?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'] ?? '';
|
$action = $_REQUEST['action'] ?? '';
|
||||||
|
|
||||||
// if a form is submitted, it's from the edit page
|
// if a form is submitted, it's from the edit page
|
||||||
|
|
|
@ -1,18 +1,21 @@
|
||||||
<?php
|
<?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
|
// registration is allowed, go on
|
||||||
if ($config['registration_enabled'] === true) {
|
if ($config['registration_enabled'] === true) {
|
||||||
|
|
||||||
// require '../app/classes/user.php';
|
|
||||||
unset($error);
|
|
||||||
|
|
||||||
try {
|
try {
|
||||||
|
|
||||||
// connect to database
|
// connect to database
|
||||||
$dbWeb = connectDB($config);
|
$dbWeb = connectDB($config);
|
||||||
|
|
||||||
// $userObject = new User($dbWeb);
|
|
||||||
|
|
||||||
if ( $_SERVER['REQUEST_METHOD'] == 'POST' ) {
|
if ( $_SERVER['REQUEST_METHOD'] == 'POST' ) {
|
||||||
$username = $_POST['username'];
|
$username = $_POST['username'];
|
||||||
$password = $_POST['password'];
|
$password = $_POST['password'];
|
||||||
|
@ -22,27 +25,30 @@ if ($config['registration_enabled'] === true) {
|
||||||
|
|
||||||
// redirect to login
|
// redirect to login
|
||||||
if ($result === true) {
|
if ($result === true) {
|
||||||
$_SESSION['notice'] = "Registration successful.<br />You can log in now.";
|
Messages::flash('NOTICE', 'DEFAULT', "Registration successful.<br />You can log in now.");
|
||||||
header('Location: index.php');
|
header('Location: ' . htmlspecialchars($app_root));
|
||||||
exit();
|
exit();
|
||||||
// registration fail, redirect to login
|
// registration fail, redirect to login
|
||||||
} else {
|
} else {
|
||||||
$_SESSION['error'] = "Registration failed. $result";
|
Messages::flash('ERROR', 'DEFAULT', "Registration failed. $result");
|
||||||
header('Location: index.php');
|
header('Location: ' . htmlspecialchars($app_root));
|
||||||
exit();
|
exit();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
} catch (Exception $e) {
|
} 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';
|
include '../app/templates/form-register.php';
|
||||||
|
|
||||||
// registration disabled
|
// registration disabled
|
||||||
} else {
|
} else {
|
||||||
$notice = 'Registration is disabled';
|
echo Messages::render('NOTICE', 'DEFAULT', 'Registration is disabled', false);
|
||||||
include '../app/templates/block-message.php';
|
|
||||||
}
|
}
|
||||||
|
|
||||||
?>
|
?>
|
||||||
|
|
|
@ -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);
|
// print_r($_SESSION);
|
||||||
?>
|
?>
|
||||||
<?php if (isset($_SESSION["agent{$agent['id']}_cache"])) { ?>
|
<?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']) ?>-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']) ?>-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>
|
<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>
|
<span id="cacheInfo<?= htmlspecialchars($agent['id']) ?>" style="margin: 5px 0;"></span>
|
||||||
<?php } else { ?>
|
<?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 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']) ?>-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>
|
<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>
|
<select class="form-control" type="text" name="type" id="agent_type_id" required>
|
||||||
<option></option>
|
<option></option>
|
||||||
<?php foreach ($jilo_agent_types as $agent_type) { ?>
|
<?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']) ?>
|
<?= htmlspecialchars($agent_type['description']) ?>
|
||||||
</option>
|
</option>
|
||||||
<?php } ?>
|
<?php } ?>
|
||||||
</select>
|
</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>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
|
@ -46,6 +49,17 @@
|
||||||
</div>
|
</div>
|
||||||
</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="new" value="true" />
|
||||||
<input type="hidden" name="item" value="agent" />
|
<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>
|
</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 />
|
<br />
|
||||||
<input type="hidden" name="agent" value="<?= htmlspecialchars($agentDetails[0]['id']) ?>" />
|
<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" -->
|
<!-- widget "config" -->
|
||||||
<div class="card text-center w-75 mx-lef">
|
<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">
|
<div class="card-body">
|
||||||
<p class="card-text">main variables</p>
|
<p class="card-text">main variables</p>
|
||||||
<?php
|
<?php
|
||||||
|
@ -97,6 +104,16 @@ echo "\n";
|
||||||
<?= htmlspecialchars($agent_array['url'].$agent_array['agent_endpoint']) ?>
|
<?= htmlspecialchars($agent_array['url'].$agent_array['agent_endpoint']) ?>
|
||||||
</div>
|
</div>
|
||||||
</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>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
|
@ -1,10 +1,10 @@
|
||||||
|
|
||||||
<!-- widget "config" -->
|
<!-- widget "platforms" -->
|
||||||
<div class="card text-center w-50 mx-auto">
|
<div class="card text-center w-50 mx-lef">
|
||||||
<p class="h4 card-header">Add new Jitsi platform</p>
|
<p class="h4 card-header">Add new Jitsi platform</p>
|
||||||
<div class="card-body">
|
<div class="card-body">
|
||||||
<!--p class="card-text">add new platform:</p-->
|
<!--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="row mb-3">
|
||||||
<div class="col-md-4 text-end">
|
<div class="col-md-4 text-end">
|
||||||
|
@ -42,9 +42,10 @@
|
||||||
<input type="hidden" name="new" value="true" />
|
<input type="hidden" name="new" value="true" />
|
||||||
|
|
||||||
<br />
|
<br />
|
||||||
<a class="btn btn-secondary" href="<?= htmlspecialchars($app_root) ?>?page=config" />Cancel</a>
|
<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" value="Save" />
|
|
||||||
|
<input type="submit" class="btn btn-primary btn-sm" value="Save" />
|
||||||
</form>
|
</form>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<!-- /widget "config" -->
|
<!-- /widget "platforms" -->
|
|
@ -1,9 +1,8 @@
|
||||||
|
|
||||||
<!-- widget "config" -->
|
<!-- widget "platforms" -->
|
||||||
<div class="card text-center w-50 mx-auto">
|
<div class="card text-center w-50 mx-lef">
|
||||||
<p class="h4 card-header">Jilo web configuration for Jitsi platform <strong>"<?= htmlspecialchars($platformDetails[0]['name']) ?>"</strong></p>
|
<p class="h4 card-header">Jilo configuration for Jitsi platform <strong>"<?= htmlspecialchars($platformDetails[0]['name']) ?>"</strong> :: delete</p>
|
||||||
<div class="card-body">
|
<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">
|
<form method="POST" action="<?= htmlspecialchars($app_root) ?>?platform=<?= htmlspecialchars($platform_id) ?>&page=config">
|
||||||
<?php
|
<?php
|
||||||
foreach ($platformDetails[0] as $key => $value) {
|
foreach ($platformDetails[0] as $key => $value) {
|
||||||
|
@ -24,9 +23,10 @@ foreach ($platformDetails[0] as $key => $value) {
|
||||||
<input type="hidden" name="delete" value="true" />
|
<input type="hidden" name="delete" value="true" />
|
||||||
<p class="h5 text-danger">Are you sure you want to delete this platform?</p>
|
<p class="h5 text-danger">Are you sure you want to delete this platform?</p>
|
||||||
<br />
|
<br />
|
||||||
<a class="btn btn-secondary" href="<?= htmlspecialchars($app_root) ?>?page=config#platform<?= htmlspecialchars($platform_id) ?>" />Cancel</a>
|
<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" value="Delete" />
|
|
||||||
|
<input type="submit" class="btn btn-danger btn-sm" value="Delete" />
|
||||||
</form>
|
</form>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<!-- /widget "config" -->
|
<!-- /widget "platforms" -->
|
|
@ -1,10 +1,9 @@
|
||||||
|
|
||||||
<!-- widget "config" -->
|
<!-- widget "platforms" -->
|
||||||
<div class="card text-center w-50 mx-auto">
|
<div class="card text-center w-50 mx-lef">
|
||||||
<p class="h4 card-header">Jilo web configuration for Jitsi platform <strong>"<?= htmlspecialchars($platformDetails[0]['name']) ?>"</strong></p>
|
<p class="h4 card-header">Jilo configuration for Jitsi platform <strong>"<?= htmlspecialchars($platformDetails[0]['name']) ?>"</strong> :: edit</p>
|
||||||
<div class="card-body">
|
<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&item=platform">
|
||||||
<form method="POST" action="<?= htmlspecialchars($app_root) ?>?platform=<?= htmlspecialchars($platform_id) ?>&page=config">
|
|
||||||
<?php
|
<?php
|
||||||
foreach ($platformDetails[0] as $key => $value) {
|
foreach ($platformDetails[0] as $key => $value) {
|
||||||
if ($key === 'id') continue;
|
if ($key === 'id') continue;
|
||||||
|
@ -28,9 +27,10 @@ foreach ($platformDetails[0] as $key => $value) {
|
||||||
<?php } ?>
|
<?php } ?>
|
||||||
<br />
|
<br />
|
||||||
<input type="hidden" name="platform" value="<?= htmlspecialchars($platform_id) ?>" />
|
<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>
|
<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" value="Save" />
|
|
||||||
|
<input type="submit" class="btn btn-primary btn-sm" value="Save" />
|
||||||
</form>
|
</form>
|
||||||
</div>
|
</div>
|
||||||
</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">
|
<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>
|
<p class="h4 card-header">Configuration of the Jitsi platform <strong><?= htmlspecialchars($platformDetails[0]['name']) ?></strong></p>
|
||||||
<div class="card-body">
|
<div class="card-body">
|
||||||
|
@ -7,9 +7,9 @@
|
||||||
<span class="m-3">URL: <?= htmlspecialchars($platformDetails[0]['jitsi_url']) ?></span>
|
<span class="m-3">URL: <?= htmlspecialchars($platformDetails[0]['jitsi_url']) ?></span>
|
||||||
<span class="m-3">FILE: config.js</span>
|
<span class="m-3">FILE: config.js</span>
|
||||||
<?php if ($mode === 'raw') { ?>
|
<?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 { ?>
|
<?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 } ?>
|
<?php } ?>
|
||||||
</p>
|
</p>
|
||||||
<pre class="results">
|
<pre class="results">
|
||||||
|
@ -19,4 +19,4 @@ echo htmlspecialchars($platformConfigjs);
|
||||||
</pre>
|
</pre>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<!-- /widget "config" -->
|
<!-- /widget "config.js" -->
|
|
@ -1,5 +1,5 @@
|
||||||
|
|
||||||
<!-- widget "config" -->
|
<!-- widget "interfaceconfig" -->
|
||||||
<div class="card text-center w-75 mx-lef">
|
<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>
|
<p class="h4 card-header">Configuration of the Jitsi platform <strong><?= htmlspecialchars($platformDetails[0]['name']) ?></strong></p>
|
||||||
<div class="card-body">
|
<div class="card-body">
|
||||||
|
@ -7,9 +7,9 @@
|
||||||
<span class="m-3">URL: <?= htmlspecialchars($platformDetails[0]['jitsi_url']) ?></span>
|
<span class="m-3">URL: <?= htmlspecialchars($platformDetails[0]['jitsi_url']) ?></span>
|
||||||
<span class="m-3">FILE: interface_config.js</span>
|
<span class="m-3">FILE: interface_config.js</span>
|
||||||
<?php if ($mode === 'raw') { ?>
|
<?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 { ?>
|
<?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 } ?>
|
<?php } ?>
|
||||||
</p>
|
</p>
|
||||||
<pre class="results">
|
<pre class="results">
|
||||||
|
@ -19,4 +19,4 @@ echo htmlspecialchars($platformInterfaceConfigjs);
|
||||||
</pre>
|
</pre>
|
||||||
</div>
|
</div>
|
||||||
</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="row">
|
||||||
<div class="card w-auto bg-light border-light card-body filter-results">
|
<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.
|
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>
|
||||||
|
|
||||||
</div>
|
</div>
|
||||||
|
|
|
@ -1,44 +1,57 @@
|
||||||
|
|
||||||
<div class="row">
|
<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>
|
||||||
|
|
||||||
<div class="collapse show" id="collapse<?= htmlspecialchars($widget['name']) ?>">
|
<div class="collapse show" id="collapse<?= htmlspecialchars($widget['name']) ?>">
|
||||||
<div class="mb-5">
|
<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) { ?>
|
<?php if ($widget['full'] === true) { ?>
|
||||||
<table class="table table-results table-striped table-hover table-bordered">
|
<table class="table table-results table-striped table-hover table-bordered">
|
||||||
<thead class="thead-dark">
|
<thead class="thead-dark">
|
||||||
<tr>
|
<tr>
|
||||||
<th scope="col"></th>
|
<th scope="col">Metric</th>
|
||||||
<?php foreach ($widget['records'] as $record) { ?>
|
<?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 } ?>
|
<?php } ?>
|
||||||
</tr>
|
</tr>
|
||||||
</thead>
|
</thead>
|
||||||
<tbody>
|
<tbody>
|
||||||
<tr>
|
<?php foreach ($widget['metrics'] as $section => $section_metrics) { ?>
|
||||||
<td>conferences</td>
|
<tr class="table-secondary">
|
||||||
<?php foreach ($widget['records'] as $record) { ?>
|
<th colspan="<?= count($widget['records']) + 1 ?>"><?= htmlspecialchars($section) ?></th>
|
||||||
<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 } ?>
|
|
||||||
</tr>
|
</tr>
|
||||||
|
<?php foreach ($section_metrics as $metric => $metricConfig) { ?>
|
||||||
<tr>
|
<tr>
|
||||||
<td>participants</td>
|
<td><?= htmlspecialchars($metricConfig['label']) ?></td>
|
||||||
<?php foreach ($widget['records'] as $record) { ?>
|
<?php foreach ($widget['records'] as $record) { ?>
|
||||||
<td><?php if (!empty($record['participants'])) { ?>
|
<td>
|
||||||
<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 { ?>
|
<?php if (isset($record['metrics'][$section][$metric])) {
|
||||||
0<?php } ?>
|
$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>
|
</td>
|
||||||
<?php } ?>
|
<?php } ?>
|
||||||
</tr>
|
</tr>
|
||||||
|
<?php } ?>
|
||||||
|
<?php } ?>
|
||||||
</tbody>
|
</tbody>
|
||||||
</table>
|
</table>
|
||||||
<?php } else { ?>
|
<?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 } ?>
|
<?php } ?>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
|
@ -18,6 +18,28 @@ $(document).ready(function(){
|
||||||
$('[data-toggle="tooltip"]').tooltip();
|
$('[data-toggle="tooltip"]').tooltip();
|
||||||
});
|
});
|
||||||
</script>
|
</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>
|
</body>
|
||||||
|
|
||||||
</html>
|
</html>
|
||||||
|
|
|
@ -26,7 +26,7 @@
|
||||||
<?php if ($page === 'agents') { ?>
|
<?php if ($page === 'agents') { ?>
|
||||||
<script src="<?= htmlspecialchars($app_root) ?>static/agents.js"></script>
|
<script src="<?= htmlspecialchars($app_root) ?>static/agents.js"></script>
|
||||||
<?php } ?>
|
<?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/chart.js"></script>
|
||||||
<script src="https://cdn.jsdelivr.net/npm/moment@2.29.1"></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>
|
<script src="https://cdn.jsdelivr.net/npm/chartjs-adapter-moment@1.0.0"></script>
|
||||||
|
@ -37,3 +37,13 @@
|
||||||
</head>
|
</head>
|
||||||
|
|
||||||
<body>
|
<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">
|
<div class="row">
|
||||||
|
|
||||||
<!-- Sidebar -->
|
<!-- 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">
|
<div class="text-center" style="border: 1px solid #0dcaf0; height: 22px;" id="time_now">
|
||||||
<?php
|
<?php
|
||||||
$timeNow = new DateTime('now', new DateTimeZone($userTimezone));
|
$timeNow = new DateTime('now', new DateTimeZone($userTimezone));
|
||||||
|
@ -38,28 +38,25 @@ $timeNow = new DateTime('now', new DateTimeZone($userTimezone));
|
||||||
</li>
|
</li>
|
||||||
</a>
|
</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">
|
<a href="<?= htmlspecialchars($app_root) ?>?platform=<?= htmlspecialchars($platform_id) ?>&page=data&item=graphs">
|
||||||
<li class="list-group-item<?php if ($page === 'graphs') echo ' list-group-item-secondary'; else echo ' list-group-item-action'; ?>">
|
<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
|
<i class="fas fa-chart-bar" data-toggle="tooltip" data-placement="right" data-offset="30.0" title="combined graphs"></i>combined graphs
|
||||||
</li>
|
</li>
|
||||||
</a>
|
</a>
|
||||||
<a href="<?= htmlspecialchars($app_root) ?>?platform=<?= htmlspecialchars($platform_id) ?>&page=latest">
|
<a href="<?= htmlspecialchars($app_root) ?>?platform=<?= htmlspecialchars($platform_id) ?>&page=data&item=latest">
|
||||||
<li class="list-group-item<?php if ($page === 'latest') echo ' list-group-item-secondary'; else echo ' list-group-item-action'; ?>">
|
<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
|
<i class="fas fa-list" data-toggle="tooltip" data-placement="right" data-offset="30.0" title="latest data"></i>latest data
|
||||||
</li>
|
</li>
|
||||||
</a>
|
</a>
|
||||||
|
<a href="<?= htmlspecialchars($app_root) ?>?platform=<?= htmlspecialchars($platform_id) ?>&page=data&item=configjs">
|
||||||
<li class="list-group-item bg-light" style="border: none;"><p class="text-end mb-0"><small>live data</small></p></li>
|
<li class="list-group-item<?php if ($page === 'data' && $item === 'configjs') echo ' list-group-item-secondary'; else echo ' list-group-item-action'; ?>">
|
||||||
|
|
||||||
<a href="<?= htmlspecialchars($app_root) ?>?platform=<?= htmlspecialchars($platform_id) ?>&page=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'; ?>">
|
|
||||||
<i class="fas fa-tv" data-toggle="tooltip" data-placement="right" data-offset="30.0" title="config.js"></i>config.js
|
<i class="fas fa-tv" data-toggle="tooltip" data-placement="right" data-offset="30.0" title="config.js"></i>config.js
|
||||||
</li>
|
</li>
|
||||||
</a>
|
</a>
|
||||||
<a href="<?= htmlspecialchars($app_root) ?>?platform=<?= htmlspecialchars($platform_id) ?>&page=config&item=interfaceconfigjs">
|
<a href="<?= htmlspecialchars($app_root) ?>?platform=<?= htmlspecialchars($platform_id) ?>&page=data&item=interfaceconfigjs">
|
||||||
<li class="list-group-item<?php if ($page === 'config' && $item === 'interfaceconfigjs') echo ' list-group-item-secondary'; else echo ' list-group-item-action'; ?>">
|
<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
|
<i class="fas fa-th" data-toggle="tooltip" data-placement="right" data-offset="30.0" title="interface_config.js"></i>interface_config.js
|
||||||
</li>
|
</li>
|
||||||
</a>
|
</a>
|
||||||
|
@ -69,15 +66,49 @@ $timeNow = new DateTime('now', new DateTimeZone($userTimezone));
|
||||||
</li>
|
</li>
|
||||||
</a>
|
</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>
|
<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')) {?>
|
<?php if ($userObject->hasRight($user_id, 'view config file')) {?>
|
||||||
<a href="<?= htmlspecialchars($app_root) ?>?page=config">
|
<a href="<?= htmlspecialchars($app_root) ?>?page=config&item=config_file">
|
||||||
<li class="list-group-item<?php if ($page === 'config' && $item === '') echo ' list-group-item-secondary'; else echo ' list-group-item-action'; ?>">
|
<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
|
<i class="fas fa-wrench" data-toggle="tooltip" data-placement="right" data-offset="30.0" title="configuration"></i>config file
|
||||||
</li>
|
</li>
|
||||||
</a>
|
</a>
|
||||||
<?php } ?>
|
<?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')) {?>
|
<?php if ($userObject->hasRight($user_id, 'view app logs')) {?>
|
||||||
<a href="<?= htmlspecialchars($app_root) ?>?page=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'; ?>">
|
<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,
|
id INTEGER PRIMARY KEY AUTOINCREMENT,
|
||||||
name TEXT NOT NULL UNIQUE
|
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 (
|
CREATE TABLE platforms (
|
||||||
id INTEGER PRIMARY KEY AUTOINCREMENT,
|
id INTEGER PRIMARY KEY AUTOINCREMENT,
|
||||||
name TEXT NOT NULL UNIQUE,
|
name TEXT NOT NULL UNIQUE,
|
||||||
jitsi_url TEXT NOT NULL,
|
jitsi_url TEXT NOT NULL,
|
||||||
jilo_database 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 (
|
CREATE TABLE logs (
|
||||||
id INTEGER PRIMARY KEY AUTOINCREMENT,
|
id INTEGER PRIMARY KEY AUTOINCREMENT,
|
||||||
user_id INTEGET NOT NULL,
|
user_id INTEGET NOT NULL,
|
||||||
|
@ -63,3 +52,76 @@ CREATE TABLE logs (
|
||||||
message TEXT NOT NULL,
|
message TEXT NOT NULL,
|
||||||
FOREIGN KEY (user_id) REFERENCES users(id)
|
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
|
Package: jilo-web
|
||||||
Version: 0.2.1
|
Version: 0.3
|
||||||
Section: web
|
Section: web
|
||||||
Priority: optional
|
Priority: optional
|
||||||
Architecture: all
|
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
|
.SH NAME
|
||||||
jilo-web \- PHP frontent to jilo (jitsi logs observer) database.
|
jilo-web \- PHP frontent to jilo (jitsi logs observer) database.
|
||||||
.SH DESCRIPTION
|
.SH DESCRIPTION
|
||||||
|
@ -17,7 +17,7 @@ Written and maintained by Yasen Pramatarov <yasen@lindeas.com>
|
||||||
https://lindeas.com/jilo
|
https://lindeas.com/jilo
|
||||||
|
|
||||||
.SH VERSION
|
.SH VERSION
|
||||||
0.2.1
|
0.3
|
||||||
|
|
||||||
.SH SEE ALSO
|
.SH SEE ALSO
|
||||||
jilo(8), jilo-cli(8)
|
jilo(8), jilo-cli(8)
|
||||||
|
|
|
@ -1,5 +1,5 @@
|
||||||
Name: jilo-web
|
Name: jilo-web
|
||||||
Version: 0.2.1
|
Version: 0.3
|
||||||
Release: 1%{?dist}
|
Release: 1%{?dist}
|
||||||
Summary: Jitsi logs web observer
|
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
|
/usr/share/man/man8/%{name}.8.gz
|
||||||
|
|
||||||
%changelog
|
%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
|
* Thu Oct 17 2024 Yasen Pramatarov <yasen@lindeas.com> 0.2.1
|
||||||
- Build of upstream v0.2.1
|
- Build of upstream v0.2.1
|
||||||
* Sat Aug 31 2024 Yasen Pramatarov <yasen@lindeas.com> 0.2
|
* Sat Aug 31 2024 Yasen Pramatarov <yasen@lindeas.com> 0.2
|
||||||
|
|
|
@ -7,8 +7,8 @@
|
||||||
* Author: Yasen Pramatarov
|
* Author: Yasen Pramatarov
|
||||||
* License: GPLv2
|
* License: GPLv2
|
||||||
* Project URL: https://lindeas.com/jilo
|
* Project URL: https://lindeas.com/jilo
|
||||||
* Year: 2024
|
* Year: 2024-2025
|
||||||
* Version: 0.2.1
|
* Version: 0.3
|
||||||
*/
|
*/
|
||||||
|
|
||||||
// we start output buffering and.
|
// we start output buffering and.
|
||||||
|
@ -16,9 +16,15 @@
|
||||||
ob_start();
|
ob_start();
|
||||||
|
|
||||||
// sanitize all input vars that may end up in URLs or forms
|
// 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
|
// error reporting, comment out in production
|
||||||
ini_set('display_errors', 1);
|
ini_set('display_errors', 1);
|
||||||
|
@ -34,14 +40,17 @@ $allowed_urls = [
|
||||||
'participants',
|
'participants',
|
||||||
'components',
|
'components',
|
||||||
|
|
||||||
'graphs',
|
'data',
|
||||||
|
|
||||||
'latest',
|
'latest',
|
||||||
|
|
||||||
'agents',
|
'agents',
|
||||||
|
|
||||||
'profile',
|
'profile',
|
||||||
'config',
|
'config',
|
||||||
|
'status',
|
||||||
'logs',
|
'logs',
|
||||||
|
'security',
|
||||||
'help',
|
'help',
|
||||||
|
|
||||||
'login',
|
'login',
|
||||||
|
@ -88,14 +97,27 @@ if (isset($_COOKIE['username'])) {
|
||||||
|
|
||||||
// redirect to login
|
// redirect to login
|
||||||
if ( !isset($_COOKIE['username']) && ($page !== 'login' && $page !== 'register') ) {
|
if ( !isset($_COOKIE['username']) && ($page !== 'login' && $page !== 'register') ) {
|
||||||
header('Location: index.php?page=login');
|
header('Location: ' . htmlspecialchars($app_root) . '?page=login');
|
||||||
exit();
|
exit();
|
||||||
}
|
}
|
||||||
|
|
||||||
// connect to db of Jilo Web
|
// connect to db of Jilo Web
|
||||||
require '../app/classes/database.php';
|
require '../app/classes/database.php';
|
||||||
require '../app/helpers/database.php';
|
require '../app/includes/database.php';
|
||||||
$dbWeb = connectDB($config);
|
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
|
// start logging
|
||||||
require '../app/classes/log.php';
|
require '../app/classes/log.php';
|
||||||
|
@ -103,6 +125,9 @@ include '../app/helpers/logs.php';
|
||||||
$logObject = new Log($dbWeb);
|
$logObject = new Log($dbWeb);
|
||||||
$user_IP = getUserIP();
|
$user_IP = getUserIP();
|
||||||
|
|
||||||
|
// init rate limiter
|
||||||
|
require '../app/classes/ratelimiter.php';
|
||||||
|
|
||||||
// get platforms details
|
// get platforms details
|
||||||
require '../app/classes/platform.php';
|
require '../app/classes/platform.php';
|
||||||
$platformObject = new Platform($dbWeb);
|
$platformObject = new Platform($dbWeb);
|
||||||
|
@ -128,29 +153,49 @@ if ($page == 'logout') {
|
||||||
session_destroy();
|
session_destroy();
|
||||||
setcookie('username', "", time() - 100, $config['folder'], $config['domain'], isset($_SERVER['HTTPS']), true);
|
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'];
|
$user_id = $userObject->getUserId($currentUser)[0]['id'];
|
||||||
$logObject->insertLog($user_id, "Logout: User \"$currentUser\" logged out. IP: $user_IP", 'user');
|
$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-header.php';
|
||||||
include '../app/templates/page-menu.php';
|
include '../app/templates/page-menu.php';
|
||||||
include '../app/templates/block-message.php';
|
|
||||||
include '../app/pages/login.php';
|
include '../app/pages/login.php';
|
||||||
|
|
||||||
} else {
|
} else {
|
||||||
|
|
||||||
// if user is logged in, we need user details and rights
|
// if user is logged in, we need user details and rights
|
||||||
if (isset($currentUser)) {
|
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'];
|
$user_id = $userObject->getUserId($currentUser)[0]['id'];
|
||||||
$userDetails = $userObject->getUserDetails($user_id);
|
$userDetails = $userObject->getUserDetails($user_id);
|
||||||
$userRights = $userObject->getUserRights($user_id);
|
$userRights = $userObject->getUserRights($user_id);
|
||||||
$userTimezone = isset($userDetails[0]['timezone']) ? $userDetails[0]['timezone'] : 'UTC'; // Default to UTC if no timezone is set
|
$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
|
// page building
|
||||||
include '../app/templates/page-header.php';
|
include '../app/templates/page-header.php';
|
||||||
include '../app/templates/page-menu.php';
|
include '../app/templates/page-menu.php';
|
||||||
include '../app/templates/block-message.php';
|
|
||||||
if (isset($currentUser)) {
|
if (isset($currentUser)) {
|
||||||
include '../app/templates/page-sidebar.php';
|
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
|
// show the result in the html
|
||||||
resultElement.innerHTML = JSON.stringify(result, null, 2);
|
resultElement.innerHTML = JSON.stringify(result, null, 2);
|
||||||
|
|
||||||
// get the cache timestamp from the session
|
// we don't cache the /status
|
||||||
const now = Date.now();
|
if (endpoint !== '/status') {
|
||||||
const cacheTimestamp = new Date(now);
|
// get the cache timestamp from the session
|
||||||
|
const now = Date.now();
|
||||||
|
const cacheTimestamp = new Date(now);
|
||||||
|
|
||||||
// display the cache retrieval date and time
|
// display the cache retrieval date and time
|
||||||
const formattedDate = cacheTimestamp.toLocaleDateString();
|
const formattedDate = cacheTimestamp.toLocaleDateString();
|
||||||
const formattedTime = cacheTimestamp.toLocaleTimeString();
|
const formattedTime = cacheTimestamp.toLocaleTimeString();
|
||||||
cacheInfoElement.style.display = '';
|
cacheInfoElement.style.display = '';
|
||||||
cacheInfoElement.innerHTML = `cache refreshed on ${formattedDate} at ${formattedTime}`;
|
cacheInfoElement.innerHTML = `cache refreshed on ${formattedDate} at ${formattedTime}`;
|
||||||
|
|
||||||
// show the cache buttons
|
// show the cache buttons
|
||||||
loadCacheButton.disabled = false;
|
loadCacheButton.disabled = false;
|
||||||
loadCacheButton.style.display = '';
|
loadCacheButton.style.display = '';
|
||||||
clearCacheButton.disabled = false;
|
clearCacheButton.disabled = false;
|
||||||
clearCacheButton.style.display = '';
|
clearCacheButton.style.display = '';
|
||||||
|
|
||||||
// send the result to PHP to store in session
|
// send the result to PHP to store in session
|
||||||
saveResultToSession(result, agent_id);
|
saveResultToSession(result, agent_id);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
} catch (e) {
|
} catch (e) {
|
||||||
// Display the error
|
// Display the error
|
||||||
|
|
|
@ -5,6 +5,7 @@ html, body {
|
||||||
padding: 0;
|
padding: 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/* menu */
|
||||||
.menu-container {
|
.menu-container {
|
||||||
display: flex;
|
display: flex;
|
||||||
justify-content: space-between;
|
justify-content: space-between;
|
||||||
|
@ -43,6 +44,7 @@ html, body {
|
||||||
.menu-left li a:hover, .menu-right li a:hover {
|
.menu-left li a:hover, .menu-right li a:hover {
|
||||||
background-color: #111;
|
background-color: #111;
|
||||||
}
|
}
|
||||||
|
/* /menu */
|
||||||
|
|
||||||
.error {
|
.error {
|
||||||
color: red;
|
color: red;
|
||||||
|
@ -140,6 +142,7 @@ html, body {
|
||||||
.main-content {
|
.main-content {
|
||||||
flex-grow: 1;
|
flex-grow: 1;
|
||||||
transition: width 0.5s ease;
|
transition: width 0.5s ease;
|
||||||
|
margin-bottom: 50px;
|
||||||
/* width: 80%;*/
|
/* width: 80%;*/
|
||||||
}
|
}
|
||||||
.main-content.expanded {
|
.main-content.expanded {
|
||||||
|
@ -149,6 +152,10 @@ html, body {
|
||||||
width: calc(70% + 3em);
|
width: calc(70% + 3em);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
.sidebar-content a {
|
||||||
|
text-decoration: none;
|
||||||
|
}
|
||||||
|
|
||||||
.logo {
|
.logo {
|
||||||
height: 36px;
|
height: 36px;
|
||||||
margin-top: 10px;
|
margin-top: 10px;
|
||||||
|
@ -161,20 +168,26 @@ html, body {
|
||||||
background-color: lightseagreen;
|
background-color: lightseagreen;
|
||||||
}
|
}
|
||||||
|
|
||||||
.sidebar-content a {
|
input[type="select"]:disabled,
|
||||||
text-decoration: none;
|
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 {
|
.pagination {
|
||||||
font-size: 0.66em;
|
font-size: 0.66em;
|
||||||
text-align: center;
|
text-align: center;
|
||||||
display: block;
|
display: block;
|
||||||
}
|
}
|
||||||
|
|
||||||
.pagination span {
|
.pagination span {
|
||||||
margin-left: 5px;
|
margin-left: 5px;
|
||||||
margin-right: 5px;
|
margin-right: 5px;
|
||||||
}
|
}
|
||||||
|
/* /pagination */
|
||||||
|
|
||||||
.th-time {
|
.th-time {
|
||||||
width: 200px;
|
width: 200px;
|
||||||
|
@ -200,3 +213,17 @@ html, body {
|
||||||
border: 1px solid gray;
|
border: 1px solid gray;
|
||||||
border-radius: 4px;
|
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