diff --git a/Todolist b/Todolist
index 68f778ce..5ccd7179 100644
--- a/Todolist
+++ b/Todolist
@@ -12,11 +12,6 @@
https://wiki.archlinux.org/title/DeveloperWiki:NewMirrors
- [ TASKS ]
-
- - Use standard websocket client to update tasks log output https://github.com/websockets/ws
-
-
[ CVE ]
- Check for affected hosts when sending packages with linupdate
diff --git a/docker/Dockerfile b/docker/Dockerfile
index 1039af3a..04011c9c 100644
--- a/docker/Dockerfile
+++ b/docker/Dockerfile
@@ -25,9 +25,9 @@ RUN apt-get install findutils iputils-ping git gnupg2 rpm librpmsign9 createrepo
RUN apt-get install postfix -y
# Add nginx and PHP 8.3 repositories
-RUN curl -sS https://packages.bespin.ovh/repo/gpgkeys/packages.bespin.ovh.pub | gpg --dearmor > /etc/apt/trusted.gpg.d/packages.bespin.ovh.gpg
-RUN echo "deb https://packages.bespin.ovh/repo/repomanager-nginx/bookworm/nginx_prod bookworm nginx" > /etc/apt/sources.list.d/nginx.list
-RUN echo "deb https://packages.bespin.ovh/repo/repomanager-php/bookworm/main_prod bookworm main" > /etc/apt/sources.list.d/php.list
+RUN curl -sS https://packages.repomanager.net/repo/gpgkeys/packages.repomanager.net.pub | gpg --dearmor > /etc/apt/trusted.gpg.d/packages.repomanager.net.gpg
+RUN echo "deb https://packages.repomanager.net/repo/repomanager-nginx/bookworm/nginx_prod bookworm nginx" > /etc/apt/sources.list.d/nginx.list
+RUN echo "deb https://packages.repomanager.net/repo/repomanager-php/bookworm/main_prod bookworm main" > /etc/apt/sources.list.d/php.list
RUN apt-get update -y
# Install nginx and PHP 8.3
diff --git a/docker/init b/docker/init
index 89bd4fb5..8e8692da 100644
--- a/docker/init
+++ b/docker/init
@@ -39,6 +39,9 @@ fi
/bin/su -s /bin/bash -c "/usr/bin/php $WWW_DIR/tools/database/initialize.php" www-data
/bin/su -s /bin/bash -c "/usr/bin/php $WWW_DIR/tools/database/update.php" www-data
+# Start shell service in background
+/bin/bash "$WWW_DIR/bin/service" &
+
# Start repomanager service
/bin/su -s /bin/bash -c "/usr/bin/php $WWW_DIR/tools/service.php" www-data
diff --git a/www/bin/service b/www/bin/service
new file mode 100644
index 00000000..4e17395c
--- /dev/null
+++ b/www/bin/service
@@ -0,0 +1,10 @@
+#!/bin/bash
+
+# Commands to run every day
+while true; do
+ /usr/bin/apt-get clean all > /dev/null
+ /usr/bin/apt-get update -y -qq > /dev/null
+
+ # Sleep for a day
+ /usr/bin/sleep 86400
+done
diff --git a/www/controllers/App/Config/Main.php b/www/controllers/App/Config/Main.php
index 76c2c4c8..629e36a3 100644
--- a/www/controllers/App/Config/Main.php
+++ b/www/controllers/App/Config/Main.php
@@ -32,6 +32,10 @@ public static function get()
if (!defined('HOSTS_DB')) {
define('HOSTS_DB', DB_DIR . "/repomanager-hosts.db");
}
+ // Websocket server database
+ if (!defined('WS_DB')) {
+ define('WS_DB', DB_DIR . "/repomanager-ws.db");
+ }
// GnuPG home
if (!defined('GPGHOME')) {
define('GPGHOME', DATA_DIR . "/.gnupg");
diff --git a/www/controllers/FatalErrorHandler.php b/www/controllers/FatalErrorHandler.php
index 80f3a0cb..dd931d2c 100644
--- a/www/controllers/FatalErrorHandler.php
+++ b/www/controllers/FatalErrorHandler.php
@@ -9,13 +9,13 @@ class FatalErrorHandler
{
private $taskId;
private $logController;
- private $layoutContainerStateController;
+ private $layoutContainerReloadController;
private $reservedMemory;
public function __construct()
{
$this->logController = new \Controllers\Log\Log();
- $this->layoutContainerStateController = new \Controllers\Layout\ContainerState();
+ $this->layoutContainerReloadController = new \Controllers\Layout\ContainerReload();
/**
* Keep some memory reserved for fatalHandler() to run even in a memory error state
@@ -62,7 +62,7 @@ public function fatalHandler()
$this->logController->log('error', 'Fatal error occured', $error['message']);
}
- $this->layoutContainerStateController->update('header/general-log-messages');
+ $this->layoutContainerReloadController->reload('header/general-log-messages');
/**
* Exit
diff --git a/www/controllers/Host.php b/www/controllers/Host.php
index f1af31e6..272c95af 100644
--- a/www/controllers/Host.php
+++ b/www/controllers/Host.php
@@ -9,7 +9,7 @@ class Host
{
protected $host_db; // BDD dédiée de l'hôte
private $model;
- private $layoutContainerStateController;
+ private $layoutContainerReloadController;
private $id;
private $idArray = array();
private $ip;
@@ -35,11 +35,8 @@ class Host
public function __construct()
{
- /**
- * Ouverture de la base de données 'hosts' (repomanager-hosts.db)
- */
$this->model = new \Models\Host();
- $this->layoutContainerStateController = new \Controllers\Layout\ContainerState();
+ $this->layoutContainerReloadController = new \Controllers\Layout\ContainerReload();
}
public function setId(string $id)
@@ -1136,6 +1133,8 @@ public function unregister()
*/
public function hostExec(array $hostsId, string $action)
{
+ $hostRequestController = new \Controllers\Host\Request();
+
/**
* Only admins should be able to perform actions
*/
@@ -1152,16 +1151,16 @@ public function hostExec(array $hostsId, string $action)
throw new Exception('Action to execute is invalid');
}
- $hostUpdateError = array();
- $hostUpdateOK = array();
- $hostResetError = array();
- $hostResetOK = array();
- $hostDeleteError = array();
- $hostDeleteOK = array();
- $hostGeneralUpdateError = array();
- $hostGeneralUpdateOK = array();
- $hostPackagesStatusUpdateError = array();
- $hostPackagesStatusUpdateOK = array();
+ $hostUpdateError = array();
+ $hostUpdateOK = array();
+ $hostResetError = array();
+ $hostResetOK = array();
+ $hostDeleteError = array();
+ $hostDeleteOK = array();
+ $hostGeneralUpdateError = array();
+ $hostGeneralUpdateOK = array();
+ $hostPackagesStatusUpdateError = array();
+ $hostPackagesStatusUpdateOK = array();
/**
* First check that hosts Id are valid
@@ -1225,7 +1224,7 @@ public function hostExec(array $hostsId, string $action)
/**
* Add a new ws request to disconnect the host
*/
- $this->newWsRequest($hostId, 'disconnect');
+ $hostRequestController->new($hostId, 'disconnect');
}
/**
@@ -1235,7 +1234,7 @@ public function hostExec(array $hostsId, string $action)
/**
* Add a new websocket request in the database
*/
- $this->newWsRequest($hostId, 'request-general-infos');
+ $hostRequestController->new($hostId, 'request-general-infos');
}
/**
@@ -1245,7 +1244,7 @@ public function hostExec(array $hostsId, string $action)
/**
* Add a new websocket request in the database
*/
- $this->newWsRequest($hostId, 'request-packages-infos');
+ $hostRequestController->new($hostId, 'request-packages-infos');
}
/**
@@ -1268,32 +1267,32 @@ public function hostExec(array $hostsId, string $action)
*/
if ($action == 'reset') {
$message = 'Following hosts have been reseted:';
- $this->layoutContainerStateController->update('hosts/overview');
- $this->layoutContainerStateController->update('hosts/list');
- $this->layoutContainerStateController->update('host/summary');
- $this->layoutContainerStateController->update('host/packages');
- $this->layoutContainerStateController->update('host/history');
+ $this->layoutContainerReloadController->reload('hosts/overview');
+ $this->layoutContainerReloadController->reload('hosts/list');
+ $this->layoutContainerReloadController->reload('host/summary');
+ $this->layoutContainerReloadController->reload('host/packages');
+ $this->layoutContainerReloadController->reload('host/history');
}
if ($action == 'delete') {
$message = 'Following hosts have been deleted:';
- $this->layoutContainerStateController->update('hosts/overview');
- $this->layoutContainerStateController->update('hosts/list');
+ $this->layoutContainerReloadController->reload('hosts/overview');
+ $this->layoutContainerReloadController->reload('hosts/list');
}
if ($action == 'request-all-packages-update') {
$message = 'Requesting packages update to the following hosts:';
- $this->layoutContainerStateController->update('host/requests');
+ $this->layoutContainerReloadController->reload('host/requests');
}
if ($action == 'request-general-infos') {
$message = 'Requesting general informations to the following hosts:';
- $this->layoutContainerStateController->update('host/requests');
+ $this->layoutContainerReloadController->reload('host/requests');
}
if ($action == 'request-packages-infos') {
$message = 'Requesting packages informations to the following hosts:';
- $this->layoutContainerStateController->update('host/requests');
+ $this->layoutContainerReloadController->reload('host/requests');
}
$message .= '
';
@@ -1504,171 +1503,4 @@ public function addHostsIdToGroup(array $hostsId = null, int $groupId)
}
}
}
-
- /**
- * Clean websocket connections from database
- */
- public function cleanWsConnections()
- {
- $this->model->cleanWsConnections();
- }
-
- /**
- * Return all websocket connections from database
- * If a status is specified, only requests with this status will be returned, otherwise all requests will be returned
- */
- public function getWsConnections(string|null $status = null)
- {
- return $this->model->getWsConnections($status);
- }
-
- /**
- * Add new websocket connection in database
- */
- public function newWsConnection(int $connectionId)
- {
- $this->model->newWsConnection($connectionId);
- }
-
- /**
- * Delete websocket connection from database
- */
- public function deleteWsConnection(int $connectionId)
- {
- $this->model->deleteWsConnection($connectionId);
- }
-
- /**
- * Update websocket connection in database
- */
- public function updateWsConnection(int $connectionId, int $hostId, string $authenticated)
- {
- $this->model->updateWsConnection($connectionId, $hostId, $authenticated);
- }
-
- /**
- * Add new websocket request in database
- */
- public function newWsRequest(int $hostId, string $request, array $requestData = [])
- {
- /**
- * Define the request name
- */
- $json['request'] = $request;
-
- /**
- * If additional json data is provided, we add it to the request
- */
- if (!empty($requestData)) {
- $json['data'] = $requestData;
- }
-
- $this->model->newWsRequest($hostId, json_encode($json));
- }
-
- /**
- * Return all websocket requests from database
- * If a status is specified, only requests with this status will be returned, otherwise all requests will be returned
- */
- public function getWsRequests(string|null $status = null)
- {
- return $this->model->getWsRequests($status);
- }
-
- /**
- * Update websocket request in database
- */
- public function updateWsRequest(int $id, string $status, string $info, string $responseJson)
- {
- $this->model->updateWsRequest($id, $status, $info, $responseJson);
- }
-
- /**
- * Update websocket request status in database
- */
- public function updateWsRequestStatus(int $id, string $status)
- {
- $this->model->updateWsRequestStatus($id, $status);
- }
-
- /**
- * Update websocket request info message in database
- */
- public function updateWsRequestInfo(int $id, string $info)
- {
- $this->model->updateWsRequestInfo($id, $info);
- }
-
- /**
- * Update websocket request retry in database
- */
- public function updateWsRequestRetry(int $id, int $retry)
- {
- $this->model->updateWsRequestRetry($id, $retry);
- }
-
- /**
- * Update websocket request next retry time in database
- */
- public function updateWsRequestNextRetry(int $id, string $nextRetry)
- {
- $this->model->updateWsRequestNextRetry($id, $nextRetry);
- }
-
- /**
- * Return websocket connection Id by host Id
- */
- public function getWsConnectionIdByHostId(int $hostId)
- {
- return $this->model->getWsConnectionIdByHostId($hostId);
- }
-
- /**
- * Cancel websocket request in database
- */
- public function cancelWsRequest(int $id)
- {
- $this->model->cancelWsRequest($id);
- }
-
- /**
- * Delete websocket request from database
- */
- public function deleteWsRequest(int $id)
- {
- $this->model->deleteWsRequest($id);
- }
-
- /**
- * Get request log details
- * Request log is a file stored in the websocket-requests logs directory
- */
- public function getRequestLog(int $id) : string
- {
- $logFile = WS_REQUESTS_LOGS_DIR . '/request-' . $id . '.log';
-
- if (!file_exists($logFile)) {
- throw new Exception('Log file does not exist');
- }
-
- if (!is_readable($logFile)) {
- throw new Exception('Log file is not readable');
- }
-
- $content = file_get_contents($logFile);
-
- if ($content === false) {
- throw new Exception('Error while reading log file');
- }
-
- return $content;
- }
-
- /**
- * Get request package log details
- */
- public function getRequestPackageLog(int $id, string $package, string $status) : string|null
- {
- return $this->model->getRequestPackageLog($id, $package, $status);
- }
}
diff --git a/www/controllers/Host/Execute.php b/www/controllers/Host/Execute.php
index 7da25df7..d6ff7a05 100644
--- a/www/controllers/Host/Execute.php
+++ b/www/controllers/Host/Execute.php
@@ -3,10 +3,16 @@
namespace Controllers\Host;
use Exception;
-use Datetime;
-class Execute extends \Controllers\Host
+class Execute
{
+ private $hostRequestController;
+
+ public function __construct()
+ {
+ $this->hostRequestController = new \Controllers\Host\Request();
+ }
+
/**
* Update selected available packages on a host
*/
@@ -22,7 +28,7 @@ public function updateSelectedAvailablePackages(int $hostId, array $packages)
/**
* Add a new request to the database
*/
- $this->newWsRequest($hostId, 'request-packages-update', $packages);
+ $this->hostRequestController->new($hostId, 'request-packages-update', $packages);
}
/**
@@ -55,7 +61,7 @@ public function updateSelectedAvailablePackages(int $hostId, array $packages)
// * Add a new request to the database, for each host
// */
// foreach ($params['hosts'] as $hostId) {
- // $this->newWsRequest($hostId, 'request-packages-installation', $params['packages']);
+ // $this->hostRequestController->new($hostId, 'request-packages-installation', $params['packages']);
// }
// }
@@ -142,7 +148,7 @@ public function updatePackages(array $params)
foreach ($params['hosts'] as $hostId) {
// Case of all packages
if ($params['update-type'] == 'all') {
- $this->newWsRequest(
+ $this->hostRequestController->new(
$hostId,
'request-all-packages-update',
array(
@@ -158,7 +164,7 @@ public function updatePackages(array $params)
// Case of specific packages
if ($params['update-type'] == 'specific') {
- $this->newWsRequest(
+ $this->hostRequestController->new(
$hostId,
'request-packages-update',
array(
diff --git a/www/controllers/Host/Request.php b/www/controllers/Host/Request.php
new file mode 100644
index 00000000..95f84b5c
--- /dev/null
+++ b/www/controllers/Host/Request.php
@@ -0,0 +1,133 @@
+model = new \Models\Host\Request();
+ }
+
+ /**
+ * Add a new request in database
+ */
+ public function new(int $hostId, string $request, array $requestData = [])
+ {
+ /**
+ * Define the request name
+ */
+ $json['request'] = $request;
+
+ /**
+ * If additional json data is provided, we add it to the request
+ */
+ if (!empty($requestData)) {
+ $json['data'] = $requestData;
+ }
+
+ $this->model->new($hostId, json_encode($json));
+ }
+
+ /**
+ * Return all requests from database
+ * If a status is specified, only requests with this status will be returned, otherwise all requests will be returned
+ */
+ public function get(string|null $status = null)
+ {
+ return $this->model->get($status);
+ }
+
+ /**
+ * Update request in database
+ */
+ public function update(int $id, string $status, string $info, string $responseJson)
+ {
+ $this->model->update($id, $status, $info, $responseJson);
+ }
+
+ /**
+ * Update request status in database
+ */
+ public function updateStatus(int $id, string $status)
+ {
+ $this->model->updateStatus($id, $status);
+ }
+
+ /**
+ * Update request info message in database
+ */
+ public function updateInfo(int $id, string $info)
+ {
+ $this->model->updateInfo($id, $info);
+ }
+
+ /**
+ * Update request retry in database
+ */
+ public function updateRetry(int $id, int $retry)
+ {
+ $this->model->updateRetry($id, $retry);
+ }
+
+ /**
+ * Update request next retry time in database
+ */
+ public function updateNextRetry(int $id, string $nextRetry)
+ {
+ $this->model->updateNextRetry($id, $nextRetry);
+ }
+
+ /**
+ * Cancel request in database
+ */
+ public function cancel(int $id)
+ {
+ $this->model->cancel($id);
+ }
+
+ /**
+ * Delete request from database
+ */
+ public function delete(int $id)
+ {
+ $this->model->delete($id);
+ }
+
+ /**
+ * Get request log details
+ * Request log is a file stored in the websocket-requests logs directory
+ */
+ public function getRequestLog(int $id) : string
+ {
+ $logFile = WS_REQUESTS_LOGS_DIR . '/request-' . $id . '.log';
+
+ if (!file_exists($logFile)) {
+ throw new Exception('Log file does not exist');
+ }
+
+ if (!is_readable($logFile)) {
+ throw new Exception('Log file is not readable');
+ }
+
+ $content = file_get_contents($logFile);
+
+ if ($content === false) {
+ throw new Exception('Error while reading log file');
+ }
+
+ return $content;
+ }
+
+ /**
+ * Get request package log details
+ */
+ public function getRequestPackageLog(int $id, string $package, string $status) : string|null
+ {
+ return $this->model->getRequestPackageLog($id, $package, $status);
+ }
+}
diff --git a/www/controllers/Layout/ContainerState.php b/www/controllers/Layout/ContainerReload.php
similarity index 50%
rename from www/controllers/Layout/ContainerState.php
rename to www/controllers/Layout/ContainerReload.php
index 723d7f63..a09a691d 100644
--- a/www/controllers/Layout/ContainerState.php
+++ b/www/controllers/Layout/ContainerReload.php
@@ -5,13 +5,13 @@
use Exception;
use Datetime;
-class ContainerState
+class ContainerReload
{
private $model;
public function __construct()
{
- $this->model = new \Models\Layout\ContainerState();
+ $this->model = new \Models\Layout\ContainerReload();
}
/**
@@ -23,35 +23,18 @@ public function get()
}
/**
- * Add a new layout container state
+ * Add a container to reload in database
*/
- public function add(string $name, string $id)
+ public function reload(string $name)
{
- $this->model->add($name, $id);
- }
-
- /**
- * Update a layout container state
- */
- public function update(string $name)
- {
- /**
- * Generate a new random Id
- */
- $id = rand(10000, 100000);
-
/**
- * Check if container name exists
+ * Ignore if container already exists in the database
*/
if ($this->model->exists($name)) {
- $this->model->update($name, $id);
return;
}
- /**
- * If not, add it
- */
- $this->add($name, $id);
+ $this->model->add($name);
}
/**
@@ -61,4 +44,12 @@ public function exists(string $name)
{
return $this->model->exists($name);
}
+
+ /**
+ * Clean all containers entries
+ */
+ public function clean()
+ {
+ $this->model->clean();
+ }
}
diff --git a/www/controllers/Repo/Mirror/Deb.php b/www/controllers/Repo/Mirror/Deb.php
index 771ad29e..2a35ea77 100644
--- a/www/controllers/Repo/Mirror/Deb.php
+++ b/www/controllers/Repo/Mirror/Deb.php
@@ -752,8 +752,6 @@ private function downloadDebPackages($url)
*/
if (isset($this->previousSnapshotDirPath)) {
if (file_exists($this->previousSnapshotDirPath . '/' . $relativeDir . '/' . $debPackageName)) {
- $this->logOK('Linked to previous snapshot');
-
/**
* Create hard link to the package
*/
@@ -761,6 +759,8 @@ private function downloadDebPackages($url)
$this->logError('Cannot create hard link to package: ' . $this->previousSnapshotDirPath . '/' . $relativeDir . '/' . $debPackageName, 'Error while creating hard link');
}
+ $this->logOK('Linked to previous snapshot');
+
continue;
}
}
@@ -850,13 +850,12 @@ private function downloadDebSourcesPackages($url)
}
/**
- * Check if file does not already exists before downloading it (e.g. copied from a previously snapshot)
+ * Check if file does not already exists in the working dir before downloading it (e.g. when a package has multiple possible archs, it can have
+ * been downloaded or linked already from another arch)
*/
if (file_exists($absoluteDir . '/' . $sourcePackageName)) {
- if ($this->checksum($absoluteDir . '/' . $sourcePackageName, $sourcePackageMd5)) {
- $this->logOK('Already exists (ignoring)');
- continue;
- }
+ $this->logOK('Already exists (ignoring)');
+ continue;
}
/**
diff --git a/www/controllers/Repo/Mirror/Rpm.php b/www/controllers/Repo/Mirror/Rpm.php
index b509598b..cff066ee 100644
--- a/www/controllers/Repo/Mirror/Rpm.php
+++ b/www/controllers/Repo/Mirror/Rpm.php
@@ -738,13 +738,12 @@ private function downloadRpmPackages(string $url)
}
/**
- * Check if file does not already exists before downloading it (e.g. copied from a previously snapshot)
+ * Check if file does not already exists in the working dir before downloading it (e.g. when a package has multiple possible archs, it can have
+ * been downloaded or linked already from another arch)
*/
if (file_exists($absoluteDir . '/' . $rpmPackageName)) {
- if ($this->checksum($absoluteDir . '/' . $rpmPackageName, $rpmPackageChecksum)) {
- $this->logOK('Already exists (ignoring)');
- continue;
- }
+ $this->logOK($absoluteDir . '/' . $rpmPackageName . ' Already exists (ignoring)');
+ continue;
}
/**
@@ -753,8 +752,6 @@ private function downloadRpmPackages(string $url)
*/
if (isset($this->previousSnapshotDirPath)) {
if (file_exists($this->previousSnapshotDirPath . '/' . $relativeDir . '/' . $rpmPackageName)) {
- $this->logOK('Linked to previous snapshot');
-
/**
* Create hard link to the package
*/
@@ -762,6 +759,8 @@ private function downloadRpmPackages(string $url)
$this->logError('Cannot create hard link to package: ' . $this->previousSnapshotDirPath . '/' . $relativeDir . '/' . $rpmPackageName, 'Error while creating hard link');
}
+ $this->logOK('Linked to previous snapshot');
+
continue;
}
}
diff --git a/www/controllers/Service/Service.php b/www/controllers/Service/Service.php
index b3ee3984..81a6133b 100644
--- a/www/controllers/Service/Service.php
+++ b/www/controllers/Service/Service.php
@@ -273,11 +273,9 @@ public function run()
}
/**
- * Start websocket server if manage hosts is enabled
+ * Start websocket server
*/
- if ($this->manageHostsEnabled == 'true') {
- $this->runService('websocket server', 'wss');
- }
+ $this->runService('websocket server', 'wss');
/**
* Parse access logs to generate stats (if enabled)
diff --git a/www/controllers/Task/Log.php b/www/controllers/Task/Log.php
index 2da1c6f4..ae0b73ff 100644
--- a/www/controllers/Task/Log.php
+++ b/www/controllers/Task/Log.php
@@ -351,7 +351,7 @@ public function runLogBuilder(int $taskId, string $location)
*/
public function logBuilder(int $taskId, string $logFile)
{
- $mylayoutContainer = new \Controllers\Layout\ContainerState();
+ $mylayoutContainer = new \Controllers\Layout\ContainerReload();
/**
* While the "completed" file doesn't exist in the temporary directory, we rewrite the main log file to make sure it's up to date
@@ -362,7 +362,7 @@ public function logBuilder(int $taskId, string $logFile)
/**
* Make the following container refreshable by the client
*/
- $mylayoutContainer->update('tasks/log');
+ $mylayoutContainer->reload('tasks/log');
sleep(1);
}
@@ -375,7 +375,7 @@ public function logBuilder(int $taskId, string $logFile)
/**
* Make the following container refreshable by the client
*/
- $mylayoutContainer->update('tasks/log');
+ $mylayoutContainer->reload('tasks/log');
/**
* Clean temporary directory
diff --git a/www/controllers/Task/Notify.php b/www/controllers/Task/Notify.php
index 45de38a5..c3840157 100644
--- a/www/controllers/Task/Notify.php
+++ b/www/controllers/Task/Notify.php
@@ -152,7 +152,7 @@ public function error(array $task, string $error)
$message .= ' ';
$mailSubject = '[ ERROR ] Scheduled task #' . $task['Id'] . ' failed on ' . WWW_HOSTNAME;
- $mymail = new \Controllers\Mail(implode(',', $taskRawParams['schedule']['schedule-recipient']), $mailSubject, $message, 'https://' . WWW_HOSTNAME . '/run?task-log=' . $task['Logfile'], 'View log file');
+ $mymail = new \Controllers\Mail(implode(',', $taskRawParams['schedule']['schedule-recipient']), $mailSubject, $message, __SERVER_PROTOCOL__ . '://' . WWW_HOSTNAME . '/run?task-log=' . $task['Logfile'], 'View log file');
}
/**
@@ -168,6 +168,6 @@ public function success(array $task)
$message .= ' ';
$mailSubject = '[ SUCCESS ] Scheduled task #' . $task['Id'] . ' succeeded on ' . WWW_HOSTNAME;
- $mymail = new \Controllers\Mail(implode(',', $taskRawParams['schedule']['schedule-recipient']), $mailSubject, $message, 'https://' . WWW_HOSTNAME . '/run?task-log=' . $task['Logfile'], 'View log file');
+ $mymail = new \Controllers\Mail(implode(',', $taskRawParams['schedule']['schedule-recipient']), $mailSubject, $message, __SERVER_PROTOCOL__ . '://' . WWW_HOSTNAME . '/run?task-log=' . $task['Logfile'], 'View log file');
}
}
diff --git a/www/controllers/Task/Task.php b/www/controllers/Task/Task.php
index 3066725d..1523ebb6 100644
--- a/www/controllers/Task/Task.php
+++ b/www/controllers/Task/Task.php
@@ -25,13 +25,13 @@ class Task
private $taskNotifyController;
private $profileController;
- private $layoutContainerStateController;
+ private $layoutContainerReloadController;
public function __construct()
{
$this->model = new \Models\Task\Task();
$this->profileController = new \Controllers\Profile();
- $this->layoutContainerStateController = new \Controllers\Layout\ContainerState();
+ $this->layoutContainerReloadController = new \Controllers\Layout\ContainerReload();
}
public function getId()
@@ -512,11 +512,11 @@ public function start()
/**
* Update layout containers states
*/
- $this->layoutContainerStateController->update('header/menu');
- $this->layoutContainerStateController->update('repos/list');
- $this->layoutContainerStateController->update('tasks/list');
- $this->layoutContainerStateController->update('browse/list');
- $this->layoutContainerStateController->update('browse/actions');
+ $this->layoutContainerReloadController->reload('header/menu');
+ $this->layoutContainerReloadController->reload('repos/list');
+ $this->layoutContainerReloadController->reload('tasks/list');
+ $this->layoutContainerReloadController->reload('browse/list');
+ $this->layoutContainerReloadController->reload('browse/actions');
/**
* Create the PID file
@@ -612,12 +612,12 @@ public function end()
/**
* Update layout containers states
*/
- $this->layoutContainerStateController->update('header/menu');
- $this->layoutContainerStateController->update('repos/list');
- $this->layoutContainerStateController->update('repos/properties');
- $this->layoutContainerStateController->update('tasks/list');
- $this->layoutContainerStateController->update('browse/list');
- $this->layoutContainerStateController->update('browse/actions');
+ $this->layoutContainerReloadController->reload('header/menu');
+ $this->layoutContainerReloadController->reload('repos/list');
+ $this->layoutContainerReloadController->reload('repos/properties');
+ $this->layoutContainerReloadController->reload('tasks/list');
+ $this->layoutContainerReloadController->reload('browse/list');
+ $this->layoutContainerReloadController->reload('browse/actions');
}
/**
@@ -746,9 +746,9 @@ public function kill(string $pid)
/**
* Update layout containers states
*/
- $this->layoutContainerStateController->update('header/menu');
- $this->layoutContainerStateController->update('repos/list');
- $this->layoutContainerStateController->update('tasks/list');
+ $this->layoutContainerReloadController->reload('header/menu');
+ $this->layoutContainerReloadController->reload('repos/list');
+ $this->layoutContainerReloadController->reload('tasks/list');
if (!empty($killError)) {
throw new Exception($killError);
diff --git a/www/controllers/Websocket/BrowserClient/Process.php b/www/controllers/Websocket/BrowserClient/Process.php
new file mode 100644
index 00000000..674e677f
--- /dev/null
+++ b/www/controllers/Websocket/BrowserClient/Process.php
@@ -0,0 +1,36 @@
+layoutContainerReloadController->get();
+
+ // Quit if there are no containers to reload
+ if (empty($containers)) {
+ return;
+ }
+
+ // For each container, send a reload request to all browser clients
+ foreach ($containers as $container) {
+ $this->broadcast($socket, 'browser-client', array(
+ 'type' => 'reload-container',
+ 'container' => $container['Container']
+ ));
+ }
+
+ // Clean up the layout container state
+ $this->layoutContainerReloadController->clean();
+ }
+}
diff --git a/www/controllers/Websocket/Process.php b/www/controllers/Websocket/Host/Process.php
similarity index 80%
rename from www/controllers/Websocket/Process.php
rename to www/controllers/Websocket/Host/Process.php
index 9d746a2a..30f43746 100644
--- a/www/controllers/Websocket/Process.php
+++ b/www/controllers/Websocket/Host/Process.php
@@ -1,20 +1,27 @@
hostRequestController = new \Controllers\Host\Request();
+ }
+
/**
* Process host authentication
*/
- public function authentication($conn, $message)
+ public function authenticate($conn, $message)
{
$this->log('[conn #' . $conn->resourceId . '] Authenticating...');
@@ -47,7 +54,7 @@ public function authentication($conn, $message)
/**
* Update connection in database with host Id
*/
- $this->hostController->updateWsConnection($conn->resourceId, $hostId, 'true');
+ $this->updateWsConnection($conn->resourceId, $hostId, 'true');
} catch (Exception $e) {
$this->log('conn #' . $conn->resourceId . '] Error while finishing authentication: ' . $e->getMessage());
throw new Exception('Error while finishing authentication');
@@ -109,7 +116,7 @@ public function responseFromRequestId($conn, $message)
/**
* Update request status and response in database
*/
- $this->hostController->updateWsRequest($requestId, $status, $info, $responseJson);
+ $this->hostRequestController->update($requestId, $status, $info, $responseJson);
/**
* Send a message to the client to inform that the response was received
@@ -145,12 +152,12 @@ public function responseFromRequestId($conn, $message)
*/
$conn->send(json_encode($confirmMessage));
- $this->layoutContainerStateController->update('hosts/overview');
- $this->layoutContainerStateController->update('hosts/list');
- $this->layoutContainerStateController->update('host/summary');
- $this->layoutContainerStateController->update('host/packages');
- $this->layoutContainerStateController->update('host/history');
- $this->layoutContainerStateController->update('host/requests');
+ $this->layoutContainerReloadController->reload('hosts/overview');
+ $this->layoutContainerReloadController->reload('hosts/list');
+ $this->layoutContainerReloadController->reload('host/summary');
+ $this->layoutContainerReloadController->reload('host/packages');
+ $this->layoutContainerReloadController->reload('host/history');
+ $this->layoutContainerReloadController->reload('host/requests');
}
/**
@@ -159,14 +166,14 @@ public function responseFromRequestId($conn, $message)
public function requests($socket)
{
/**
- * Retrieve all 'new' websocket requests from database
+ * Retrieve all 'new' requests from database
*/
- $requests = $this->hostController->getWsRequests('new');
+ $requests = $this->hostRequestController->get('new');
/**
* Retrieve all authenticated (true) clients
*/
- $clients = $this->hostController->getWsConnections('true');
+ $clients = $this->getAuthenticatedWsConnections();
/**
* If no new requests, quit
@@ -204,7 +211,7 @@ public function requests($socket)
* If retry count is less than 3, increment it and set new retry date in database
*/
if ($request['Retry'] < 3) {
- $this->hostController->updateWsRequestRetry($request['Id'], $request['Retry'] + 1);
+ $this->hostRequestController->updateRetry($request['Id'], $request['Retry'] + 1);
/**
* Calculate timestamp for next retry
@@ -223,9 +230,9 @@ public function requests($socket)
/**
* Set new retry date in database and add an info message
*/
- $this->hostController->updateWsRequestNextRetry($request['Id'], $nextRetry);
- $this->hostController->updateWsRequestInfo($request['Id'], 'Host is not connected or not authenticated (retry ' . $request['Retry'] . '/3 - next retry ~' . date('H:i:s', $nextRetry) . ')');
- $this->layoutContainerStateController->update('host/requests');
+ $this->hostRequestController->updateNextRetry($request['Id'], $nextRetry);
+ $this->hostRequestController->updateInfo($request['Id'], 'Host is not connected or not authenticated (retry ' . $request['Retry'] . '/3 - next retry ~' . date('H:i:s', $nextRetry) . ')');
+ $this->layoutContainerReloadController->reload('host/requests');
continue;
}
@@ -235,16 +242,16 @@ public function requests($socket)
* If all retries failed, update request status to 'failed' in database
* Update request status to 'failed' in database
*/
- $this->hostController->updateWsRequestStatus($request['Id'], 'failed');
- $this->hostController->updateWsRequestInfo($request['Id'], 'Host is not connected or not authenticated (retried 3 times)');
- $this->layoutContainerStateController->update('host/requests');
+ $this->hostRequestController->updateStatus($request['Id'], 'failed');
+ $this->hostRequestController->updateInfo($request['Id'], 'Host is not connected or not authenticated (retried 3 times)');
+ $this->layoutContainerReloadController->reload('host/requests');
continue;
}
/**
* First, retrieve websocket connection Id of target host
*/
- $hostWsConnectionId = $this->hostController->getWsConnectionIdByHostId($request['Id_host']);
+ $hostWsConnectionId = $this->getWsConnectionIdByHostId($request['Id_host']);
/**
* If request is 'disconnect', close connection and remove it from database
@@ -268,7 +275,7 @@ public function requests($socket)
/**
* Delete the disconnect request from database
*/
- $this->hostController->deleteWsRequest($request['Id']);
+ $this->hostRequestController->delete($request['Id']);
continue;
}
@@ -292,9 +299,9 @@ public function requests($socket)
/**
* Update request status to 'sent' in database
*/
- $this->hostController->updateWsRequestStatus($request['Id'], 'sent');
- $this->hostController->updateWsRequestInfo($request['Id'], 'Request sent to the host');
- $this->layoutContainerStateController->update('host/requests');
+ $this->hostRequestController->updateStatus($request['Id'], 'sent');
+ $this->hostRequestController->updateInfo($request['Id'], 'Request sent to the host');
+ $this->layoutContainerReloadController->reload('host/requests');
}
}
}
diff --git a/www/controllers/Websocket/Socket.php b/www/controllers/Websocket/Socket.php
index 3217a19e..c3c23d2a 100644
--- a/www/controllers/Websocket/Socket.php
+++ b/www/controllers/Websocket/Socket.php
@@ -12,7 +12,7 @@
use Ratchet\ConnectionInterface;
/**
- * Class Socker extends WebsocketServer to gain access to the log method and hostController
+ * Class Socker extends WebsocketServer to gain access to its methods
*/
class Socket extends WebsocketServer implements MessageComponentInterface
{
@@ -31,10 +31,10 @@ public function initialize()
/**
* Clean database from old connections
- * (connections that were not removed from database because of a crash or a bug)
+ * (e.g. connections that were not removed from database because of a crash or a bug)
*/
try {
- $this->hostController->cleanWsConnections();
+ $this->cleanWsConnections();
} catch (Exception $e) {
$this->log('Error while cleaning database from old connections: ' . $e->getMessage());
}
@@ -54,15 +54,15 @@ public function getClients()
public function onOpen(ConnectionInterface $conn)
{
$this->clients->attach($conn);
- $this->log('[conn #' . $conn->resourceId . '] New connection!');
+ $this->log('[connection #' . $conn->resourceId . '] New connection!');
/**
* Adding connection Id to database, waiting for a message from the host to authenticate
*/
try {
- $this->hostController->newWsConnection($conn->resourceId);
+ $this->newWsConnection($conn->resourceId);
} catch (Exception $e) {
- $this->log('[conn #' . $conn->resourceId . '] Error while adding connection to database: ' . $e->getMessage());
+ $this->log('[connection #' . $conn->resourceId . '] Error while adding connection to database: ' . $e->getMessage());
/**
* Send a message to the host to inform that the connection is not allowed, and close it
@@ -71,58 +71,75 @@ public function onOpen(ConnectionInterface $conn)
$conn->send(json_encode(array('error' => "You've been connected but an error occured on the server side. Please try again later.")));
$conn->close();
}
-
- /**
- * Ask the host to authenticate
- */
- $conn->send(json_encode(array('request' => 'authenticate')));
}
/**
* On websocket message received
*/
- public function onMessage(ConnectionInterface $conn, $msg)
+ public function onMessage(ConnectionInterface $conn, $message)
{
/**
* Decode JSON message
*/
try {
- $message = json_decode($msg, true);
+ $message = json_decode($message, true);
} catch (Exception $e) {
- $this->log('[conn #' . $conn->resourceId . '] Error while decoding message: ' . $e->getMessage());
+ $this->log('[connection #' . $conn->resourceId . '] Error while decoding message: ' . $e->getMessage());
return;
}
+ /**
+ * If the client is sending its connection type
+ */
+ if (!empty($message['connection-type'])) {
+ // Connection type must be either 'host' or 'browser-client'
+ if (!in_array($message['connection-type'], array('host', 'browser-client'))) {
+ $this->log('[connection #' . $conn->resourceId . '] Invalid connection type: ' . $message['connection-type']);
+
+ // Close connection
+ $conn->close();
+ }
+
+ // Set connection type in database
+ $this->setWsConnectionType($conn->resourceId, $message['connection-type']);
+
+ // If the connection type is 'host'
+ if ($message['connection-type'] == 'host') {
+ // Ask the host to authenticate
+ $conn->send(json_encode(array('request' => 'authenticate')));
+ }
+ }
+
/**
* If the host is sending a response to a request
* A response can either contain a string request or a request Id
*/
if (!empty($message['response-to-request'])) {
try {
- $process = new \Controllers\Websocket\Process();
+ $hostProcessController = new \Controllers\Websocket\Host\Process();
/**
* If the host is trying to authenticate
*/
if (isset($message['response-to-request']['request']) and $message['response-to-request']['request'] == 'authenticate') {
- $process->authentication($conn, $message);
+ $hostProcessController->authenticate($conn, $message);
}
/**
* If the host is sending a response to a request, with a request Id
*/
if (isset($message['response-to-request']['request-id'])) {
- $process->responseFromRequestId($conn, $message);
+ $hostProcessController->responseFromRequestId($conn, $message);
}
} catch (Exception $e) {
/**
* Print, send an error message to the host and close connection
*/
if (isset($message['response-to-request']['request'])) {
- $this->log('[conn #' . $conn->resourceId . '] Error while processing host\'s response to request "' . $message['response-to-request']['request'] . '": ' . $e->getMessage());
+ $this->log('[connection #' . $conn->resourceId . '] Error while processing host\'s response to request "' . $message['response-to-request']['request'] . '": ' . $e->getMessage());
$conn->send(json_encode(array('error' => 'error while processing response to request "' . $message['response-to-request']['request'] . '"')));
} else if (isset($message['response-to-request']['request-id'])) {
- $this->log('[conn #' . $conn->resourceId . '] Error while processing host\'s response to request #' . $message['response-to-request']['request-id'] . ': ' . $e->getMessage());
+ $this->log('[connection #' . $conn->resourceId . '] Error while processing host\'s response to request #' . $message['response-to-request']['request-id'] . ': ' . $e->getMessage());
$conn->send(json_encode(array('error' => 'error while processing response to request #' . $message['response-to-request']['request-id'])));
}
@@ -138,15 +155,15 @@ public function onMessage(ConnectionInterface $conn, $msg)
public function onClose(ConnectionInterface $conn)
{
$this->clients->detach($conn);
- $this->log('[conn #' . $conn->resourceId . '] Connection is gone');
+ $this->log('[connection #' . $conn->resourceId . '] Connection closed');
/**
* Removing connection Id from database
*/
try {
- $this->hostController->deleteWsConnection($conn->resourceId);
+ $this->deleteWsConnection($conn->resourceId);
} catch (Exception $e) {
- $this->log('[conn #' . $conn->resourceId . '] Error while removing connection from database: ' . $e->getMessage());
+ $this->log('[connection #' . $conn->resourceId . '] Error while removing connection from database: ' . $e->getMessage());
}
}
@@ -155,7 +172,7 @@ public function onClose(ConnectionInterface $conn)
*/
public function onError(ConnectionInterface $conn, \Exception $e)
{
- $this->log('[conn #' . $conn->resourceId . '] An error occured with connection: ' . $e->getMessage());
+ $this->log('[connection #' . $conn->resourceId . '] An error occured with connection: ' . $e->getMessage());
$conn->close();
}
}
diff --git a/www/controllers/Websocket/WebsocketServer.php b/www/controllers/Websocket/WebsocketServer.php
index f1e24819..1e848c23 100644
--- a/www/controllers/Websocket/WebsocketServer.php
+++ b/www/controllers/Websocket/WebsocketServer.php
@@ -14,14 +14,18 @@
class WebsocketServer
{
+ protected $model;
protected $hostController;
- protected $layoutContainerStateController;
+ protected $layoutContainerReloadController;
+ protected $hostProcessController;
protected $logFile;
+ protected $socket;
public function __construct()
{
+ $this->model = new \Models\Websocket\WebsocketServer();
$this->hostController = new \Controllers\Host();
- $this->layoutContainerStateController = new \Controllers\Layout\ContainerState();
+ $this->layoutContainerReloadController = new \Controllers\Layout\ContainerReload();
}
/**
@@ -29,13 +33,16 @@ public function __construct()
*/
public function run(int $port)
{
- $socket = new Socket();
- $socket->initialize();
+ $hostProcessController = new \Controllers\Websocket\Host\Process();
+ $browserClientProcessController = new \Controllers\Websocket\BrowserClient\Process();
+
+ $this->socket = new Socket();
+ $this->socket->initialize();
$server = IoServer::factory(
new HttpServer(
new WsServer(
- $socket
+ $this->socket
)
),
$port
@@ -44,18 +51,114 @@ public function run(int $port)
/**
* Periodic timer to send requests to target hosts
*/
- $server->loop->addPeriodicTimer(5, function () use ($socket) {
+ $server->loop->addPeriodicTimer(2, function () use ($hostProcessController, $browserClientProcessController) {
+ /**
+ * Process all browser clients reloads
+ */
+ $browserClientProcessController->reload($this->socket);
+
/**
* Process all requests to send to hosts
*/
- $process = new Process();
- $process->requests($socket);
+ $hostProcessController->requests($this->socket);
});
$this->log('[server] Server started on port ' . $port);
$server->run();
}
+ /**
+ * Clean websocket connections from database
+ */
+ protected function cleanWsConnections()
+ {
+ $this->model->cleanWsConnections();
+ }
+
+ /**
+ * Add new websocket connection in database
+ */
+ public function newWsConnection(int $connectionId)
+ {
+ $this->model->newWsConnection($connectionId);
+ }
+
+ /**
+ * Set websocket connection type
+ */
+ public function setWsConnectionType(int $connectionId, string $type)
+ {
+ $this->model->setWsConnectionType($connectionId, $type);
+ }
+
+ /**
+ * Update websocket connection in database
+ */
+ public function updateWsConnection(int $connectionId, int $hostId, string $authenticated)
+ {
+ $this->model->updateWsConnection($connectionId, $hostId, $authenticated);
+ }
+
+ /**
+ * Return all authenticated websocket connections from database
+ */
+ public function getAuthenticatedWsConnections()
+ {
+ return $this->model->getAuthenticatedWsConnections();
+ }
+
+ /**
+ * Return all websocket connections from database
+ */
+ public function getWsConnections(string $type = null)
+ {
+ return $this->model->getWsConnections($type);
+ }
+
+ /**
+ * Return websocket connection Id by host Id
+ */
+ public function getWsConnectionIdByHostId(int $hostId)
+ {
+ return $this->model->getWsConnectionIdByHostId($hostId);
+ }
+
+ /**
+ * Delete websocket connection from database
+ */
+ public function deleteWsConnection(int $connectionId)
+ {
+ $this->model->deleteWsConnection($connectionId);
+ }
+
+ /**
+ * Broadcast a message to all clients
+ */
+ protected function broadcast($socket, $connectionType, array $message)
+ {
+ $this->log('[server] Broadcasting message to ' . $connectionType . ' clients: ' . print_r($message, true));
+
+ /**
+ * Retrieve all browser-client connections
+ */
+ $connections = $this->getWsConnections('browser-client');
+
+ /**
+ * Retrieve all socket connections
+ */
+ $socketConnections = $socket->getClients();
+
+ foreach ($socketConnections as $socketConnection) {
+ // Search in $connections subarrays if a Connection_id corresponds to the current resourceId
+ $key = array_search($socketConnection->resourceId, array_column($connections, 'Connection_id'));
+
+ if ($key !== false) {
+ $this->log('[server] Sending message to connection #' . $socketConnection->resourceId);
+ $socketConnection->send(json_encode($message));
+ }
+ }
+ }
+
/**
* Log a message to the log file and to the console
*/
@@ -64,7 +167,7 @@ protected function log($message)
/**
* Always recalculate the log file name, in case the date changes
*/
- $this->logFile = WS_LOGS_DIR . '/' . date('Y-m-d') . '_websocketserver.log';
+ $this->logFile = WS_LOGS_DIR . '/' . DATE_YMD . '_websocketserver.log';
/**
* Define the message with a timestamp
diff --git a/www/controllers/ajax/general.php b/www/controllers/ajax/general.php
index da05ea46..ffe224b8 100644
--- a/www/controllers/ajax/general.php
+++ b/www/controllers/ajax/general.php
@@ -30,21 +30,6 @@
response(HTTP_OK, $content);
}
-/**
- * Get all layout containers state
- */
-if ($action == "getContainerState") {
- $mycontainerState = new \Controllers\Layout\ContainerState();
-
- try {
- $result = $mycontainerState->get();
- } catch (\Exception $e) {
- response(HTTP_BAD_REQUEST, $e->getMessage());
- }
-
- response(HTTP_OK, json_encode($result));
-}
-
/**
* Return specified table content
*/
diff --git a/www/controllers/ajax/host.php b/www/controllers/ajax/host.php
index 13270e96..c231d877 100644
--- a/www/controllers/ajax/host.php
+++ b/www/controllers/ajax/host.php
@@ -85,10 +85,10 @@
* Show request log details
*/
if ($action == "getRequestLog" and !empty($_POST['id'])) {
- $myhost = new \Controllers\Host();
+ $hostRequestController = new \Controllers\Host\Request();
try {
- $content = $myhost->getRequestLog($_POST['id']);
+ $content = $hostRequestController->getRequestLog($_POST['id']);
} catch (\Exception $e) {
response(HTTP_BAD_REQUEST, $e->getMessage());
}
@@ -100,10 +100,10 @@
* Show request package log details
*/
if ($action == "getRequestPackageLog" and !empty($_POST['id']) and !empty($_POST['package']) and !empty($_POST['status'])) {
- $myhost = new \Controllers\Host();
+ $hostRequestController = new \Controllers\Host\Request();
try {
- $content = $myhost->getRequestPackageLog($_POST['id'], $_POST['package'], $_POST['status']);
+ $content = $hostRequestController->getRequestPackageLog($_POST['id'], $_POST['package'], $_POST['status']);
} catch (\Exception $e) {
response(HTTP_BAD_REQUEST, $e->getMessage());
}
@@ -115,10 +115,10 @@
* Cancel a request sent to a host
*/
if ($action == "cancelRequest" and !empty($_POST['id'])) {
- $myhost = new \Controllers\Host();
+ $hostRequestController = new \Controllers\Host\Request();
try {
- $myhost->cancelWsRequest($_POST['id']);
+ $hostRequestController->cancel($_POST['id']);
} catch (\Exception $e) {
response(HTTP_BAD_REQUEST, $e->getMessage());
}
diff --git a/www/controllers/ajax/task.php b/www/controllers/ajax/task.php
index 78cf583f..3e73c91b 100644
--- a/www/controllers/ajax/task.php
+++ b/www/controllers/ajax/task.php
@@ -16,10 +16,10 @@
}
if (isset($taskRawParams[0]['schedule']['scheduled']) and $taskRawParams[0]['schedule']['scheduled'] == 'true') {
- response(HTTP_OK, 'Task is scheduled: visualize');
+ response(HTTP_OK, 'Task is scheduled: visualize');
}
- response(HTTP_OK, 'Task is running: visualize');
+ response(HTTP_OK, 'Task is running: visualize');
}
/**
diff --git a/www/models/Connection.php b/www/models/Connection.php
index 23265e83..9a6f5d1e 100644
--- a/www/models/Connection.php
+++ b/www/models/Connection.php
@@ -88,6 +88,12 @@ public function __construct(string $database, string|null $hostId = null, bool $
$this->enableExceptions(true);
$this->enableWAL();
$this->generateHostTables();
+ } elseif ($database == 'ws') {
+ $this->open(WS_DB);
+ $this->busyTimeout(30000);
+ $this->enableExceptions(true);
+ $this->enableWAL();
+ $this->generateWsTables();
/**
* Case where database is not 'main', 'stats', 'hosts' or 'host'
@@ -178,8 +184,7 @@ public function countStatsTables()
public function countHostsTables()
{
$result = $this->query("SELECT name FROM sqlite_master WHERE type='table'
- and name='ws_connections'
- OR name='ws_requests'
+ and name='requests'
OR name='hosts'
OR name='groups'
OR name='group_members'
@@ -241,7 +246,7 @@ public function checkStatsTables()
*/
public function checkHostsTables()
{
- $required = 6;
+ $required = 5;
/**
* If the number of tables != $required then we try to regenerate the tables
@@ -700,8 +705,7 @@ private function generateMainTables()
* Generate layout_container_state table if not exists
*/
$this->exec("CREATE TABLE IF NOT EXISTS layout_container_state (
- Container VARCHAR(255) NOT NULL,
- Id INTEGER NOT NULL)");
+ Container VARCHAR(255) NOT NULL)");
}
/**
@@ -776,17 +780,9 @@ private function generateStatsTables()
private function generateHostsTables()
{
/**
- * ws_connections table
+ * requests table
*/
- $this->exec("CREATE TABLE IF NOT EXISTS ws_connections (
- Connection_id INTEGER,
- Id_host INTEGER,
- Authenticated CHAR(5))"); /* true, false */
-
- /**
- * ws_requests table
- */
- $this->exec("CREATE TABLE IF NOT EXISTS ws_requests (
+ $this->exec("CREATE TABLE IF NOT EXISTS requests (
Id INTEGER PRIMARY KEY AUTOINCREMENT NOT NULL,
Date DATE NOT NULL,
Time TIME NOT NULL,
@@ -871,14 +867,10 @@ private function generateHostsTables()
$this->exec("CREATE INDEX IF NOT EXISTS group_members_index ON group_members (Id_host, Id_group)");
$this->exec("CREATE INDEX IF NOT EXISTS group_members_id_host_index ON group_members (Id_host)");
$this->exec("CREATE INDEX IF NOT EXISTS group_members_id_group_index ON group_members (Id_group)");
- // ws_connections table indexes:
- $this->exec("CREATE INDEX IF NOT EXISTS ws_connections_authenticated ON ws_connections (Authenticated)");
- $this->exec("CREATE INDEX IF NOT EXISTS ws_connections_connection_id ON ws_connections (Connection_id)");
- $this->exec("CREATE INDEX IF NOT EXISTS ws_connections_id_host ON ws_connections (Id_host)");
- // ws_requests table indexes:
- $this->exec("CREATE INDEX IF NOT EXISTS ws_requests_id_host ON ws_requests (Id_host)");
- $this->exec("CREATE INDEX IF NOT EXISTS ws_requests_status ON ws_requests (Status)");
- $this->exec("CREATE INDEX IF NOT EXISTS ws_requests_date_time ON ws_requests (Date, Time)");
+ // requests table indexes:
+ $this->exec("CREATE INDEX IF NOT EXISTS requests_id_host ON requests (Id_host)");
+ $this->exec("CREATE INDEX IF NOT EXISTS requests_status ON requests (Status)");
+ $this->exec("CREATE INDEX IF NOT EXISTS requests_date_time ON requests (Date, Time)");
}
/**
@@ -951,6 +943,27 @@ public function generateHostTables()
$this->exec("CREATE INDEX IF NOT EXISTS host_packages_history_state_date ON packages_history (State, Date)");
}
+ /**
+ * Generate tables in the ws database
+ */
+ public function generateWsTables()
+ {
+ /**
+ * ws_connections table
+ */
+ $this->exec("CREATE TABLE IF NOT EXISTS ws_connections (
+ Connection_id INTEGER,
+ Type VARCHAR(255),
+ Id_host INTEGER,
+ Authenticated CHAR(5))"); /* true, false */
+
+ // ws_connections table indexes:
+ $this->exec("CREATE INDEX IF NOT EXISTS ws_connections_type ON ws_connections (Type)");
+ $this->exec("CREATE INDEX IF NOT EXISTS ws_connections_authenticated ON ws_connections (Authenticated)");
+ $this->exec("CREATE INDEX IF NOT EXISTS ws_connections_connection_id ON ws_connections (Connection_id)");
+ $this->exec("CREATE INDEX IF NOT EXISTS ws_connections_id_host ON ws_connections (Id_host)");
+ }
+
/**
* Return true if result is empty and false if not
*/
diff --git a/www/models/Host.php b/www/models/Host.php
index 0f24cd93..1d2d3efe 100644
--- a/www/models/Host.php
+++ b/www/models/Host.php
@@ -1122,7 +1122,7 @@ public function getRequests(int $id, bool $withOffset, int $offset)
$data = array();
try {
- $query = "SELECT * FROM ws_requests WHERE Id_host = :id ORDER BY Date DESC, Time DESC";
+ $query = "SELECT * FROM requests WHERE Id_host = :id ORDER BY Date DESC, Time DESC";
/**
* Add offset if needed
@@ -1158,7 +1158,7 @@ public function getLastPendingRequest(int $id)
$data = array();
try {
- $stmt = $this->db->prepare("SELECT * from ws_requests WHERE Id_host = :id ORDER BY DATE DESC, TIME DESC LIMIT 1");
+ $stmt = $this->db->prepare("SELECT * from requests WHERE Id_host = :id ORDER BY DATE DESC, TIME DESC LIMIT 1");
$stmt->bindValue(':id', $id, SQLITE3_INTEGER);
$result = $stmt->execute();
} catch (\Exception $e) {
@@ -1480,7 +1480,7 @@ public function resetHost(string $hostId)
/**
* Retrieve all requests made to the host
*/
- $stmt = $this->db->prepare("SELECT Id FROM ws_requests WHERE Id_host = :id");
+ $stmt = $this->db->prepare("SELECT Id FROM requests WHERE Id_host = :id");
$stmt->bindValue(':id', $hostId);
$result = $stmt->execute();
@@ -1496,9 +1496,9 @@ public function resetHost(string $hostId)
}
/**
- * Delete all requests in ws_requests table
+ * Delete all requests in requests table
*/
- $stmt = $this->db->prepare("DELETE FROM ws_requests WHERE Id_host = :id");
+ $stmt = $this->db->prepare("DELETE FROM requests WHERE Id_host = :id");
$stmt->bindValue(':id', $hostId);
$stmt->execute();
@@ -1625,311 +1625,4 @@ public function countByProfile(string $profile)
return count($hosts);
}
-
- /**
- * Clean ws connections from database
- */
- public function cleanWsConnections()
- {
- try {
- $stmt = $this->db->prepare("DELETE FROM ws_connections");
- $result = $stmt->execute();
- } catch (\Exception $e) {
- $this->db->logError($e);
- }
- }
-
- /**
- * Return all websocket connections from database
- * If a status is specified, only requests with this status will be returned, otherwise all requests will be returned
- */
- public function getWsConnections(string|null $status)
- {
- $connections = array();
-
- try {
- /**
- * If a status is specified, we only get requests with this status
- */
- if (!empty($status)) {
- $stmt = $this->db->prepare("SELECT * FROM ws_connections WHERE Authenticated = :status");
- $stmt->bindValue(':status', $status);
- } else {
- $stmt = $this->db->prepare("SELECT * FROM ws_connections");
- }
- $result = $stmt->execute();
- } catch (\Exception $e) {
- $this->db->logError($e);
- }
-
- while ($row = $result->fetchArray(SQLITE3_ASSOC)) {
- $connections[] = $row;
- }
-
- return $connections;
- }
-
- /**
- * Add new ws connection in database
- */
- public function newWsConnection(int $connectionId)
- {
- try {
- $stmt = $this->db->prepare("INSERT INTO ws_connections ('Connection_id', 'Authenticated') VALUES (:id, 'false')");
- $stmt->bindValue(':id', $connectionId);
- $result = $stmt->execute();
- } catch (\Exception $e) {
- $this->db->logError($e);
- }
- }
-
- /**
- * Delete ws connection from database
- */
- public function deleteWsConnection(int $connectionId)
- {
- try {
- $stmt = $this->db->prepare("DELETE FROM ws_connections WHERE Connection_id = :id");
- $stmt->bindValue(':id', $connectionId);
- $result = $stmt->execute();
- } catch (\Exception $e) {
- $this->db->logError($e);
- }
- }
-
- /**
- * Update ws connection in database
- */
- public function updateWsConnection(int $connectionId, int $hostId, string $authenticated)
- {
- try {
- $stmt = $this->db->prepare("UPDATE ws_connections SET Id_host = :hostId, Authenticated = :authenticated WHERE Connection_id = :connectionId");
- $stmt->bindValue(':hostId', $hostId);
- $stmt->bindValue(':authenticated', $authenticated);
- $stmt->bindValue(':connectionId', $connectionId);
- $result = $stmt->execute();
- } catch (\Exception $e) {
- $this->db->logError($e);
- }
- }
-
- /**
- * Add new websocket request in database
- */
- public function newWsRequest(int $hostId, string $request)
- {
- try {
- $stmt = $this->db->prepare("INSERT INTO ws_requests ('Date', 'Time', 'Request', 'Status', 'Retry', 'Id_host') VALUES (:date, :time, :request, 'new', '0', :hostId)");
- $stmt->bindValue(':date', date('Y-m-d'));
- $stmt->bindValue(':time', date('H:i:s'));
- $stmt->bindValue(':request', $request);
- $stmt->bindValue(':hostId', $hostId);
- $result = $stmt->execute();
- } catch (\Exception $e) {
- $this->db->logError($e);
- }
- }
-
- /**
- * Return all websocket requests from database
- */
- public function getWsRequests(string|null $status)
- {
- $requests = array();
-
- try {
- /**
- * If a status is specified, we only get requests with this status
- */
- if (!empty($status)) {
- $stmt = $this->db->prepare("SELECT * FROM ws_requests WHERE Status = :status ORDER BY Date DESC, Time DESC");
- $stmt->bindValue(':status', $status);
- } else {
- $stmt = $this->db->prepare("SELECT * FROM ws_requests ORDER BY Date DESC, Time DESC");
- }
- $result = $stmt->execute();
- } catch (\Exception $e) {
- $this->db->logError($e);
- }
-
- while ($row = $result->fetchArray(SQLITE3_ASSOC)) {
- $requests[] = $row;
- }
-
- return $requests;
- }
-
- /**
- * Update websocket request in database
- */
- public function updateWsRequest(int $id, string $status, string $info, string $responseJson)
- {
- try {
- $stmt = $this->db->prepare("UPDATE ws_requests SET Status = :status, Info = :info, Response_json = :responseJson WHERE Id = :id");
- $stmt->bindValue(':status', $status);
- $stmt->bindValue(':info', $info);
- $stmt->bindValue(':responseJson', $responseJson);
- $stmt->bindValue(':id', $id);
- $result = $stmt->execute();
- } catch (\Exception $e) {
- $this->db->logError($e);
- }
- }
-
- /**
- * Update websocket request status in database
- */
- public function updateWsRequestStatus(int $id, string $status)
- {
- try {
- $stmt = $this->db->prepare("UPDATE ws_requests SET Status = :status WHERE Id = :id");
- $stmt->bindValue(':status', $status);
- $stmt->bindValue(':id', $id);
- $result = $stmt->execute();
- } catch (\Exception $e) {
- $this->db->logError($e);
- }
- }
-
- /**
- * Update websocket request info message in database
- */
- public function updateWsRequestInfo(int $id, string $info)
- {
- try {
- $stmt = $this->db->prepare("UPDATE ws_requests SET Info = :info WHERE Id = :id");
- $stmt->bindValue(':info', $info);
- $stmt->bindValue(':id', $id);
- $result = $stmt->execute();
- } catch (\Exception $e) {
- $this->db->logError($e);
- }
- }
-
- /**
- * Update websocket request retry in database
- */
- public function updateWsRequestRetry(int $id, int $retry)
- {
- try {
- $stmt = $this->db->prepare("UPDATE ws_requests SET Retry = :retry WHERE Id = :id");
- $stmt->bindValue(':retry', $retry);
- $stmt->bindValue(':id', $id);
- $result = $stmt->execute();
- } catch (\Exception $e) {
- $this->db->logError($e);
- }
- }
-
- /**
- * Update websocket request next retry time in database
- */
- public function updateWsRequestNextRetry(int $id, string $nextRetry)
- {
- try {
- $stmt = $this->db->prepare("UPDATE ws_requests SET Next_retry = :nextRetry WHERE Id = :id");
- $stmt->bindValue(':nextRetry', $nextRetry);
- $stmt->bindValue(':id', $id);
- $result = $stmt->execute();
- } catch (\Exception $e) {
- $this->db->logError($e);
- }
- }
-
- /**
- * Return websocket connection Id by host Id
- */
- public function getWsConnectionIdByHostId(int $hostId)
- {
- $connectionId = '';
-
- try {
- $stmt = $this->db->prepare("SELECT Connection_id FROM ws_connections WHERE Id_host = :hostId");
- $stmt->bindValue(':hostId', $hostId);
- $result = $stmt->execute();
- } catch (\Exception $e) {
- $this->db->logError($e);
- }
-
- $connectionId = '';
-
- while ($row = $result->fetchArray(SQLITE3_ASSOC)) {
- $connectionId = $row['Connection_id'];
- }
-
- return $connectionId;
- }
-
- /**
- * Cancel websocket request in database
- */
- public function cancelWsRequest(int $id)
- {
- try {
- $stmt = $this->db->prepare("UPDATE ws_requests SET Status = 'canceled', Info = Info || ' (canceled)' WHERE Id = :id");
- $stmt->bindValue(':id', $id);
- $result = $stmt->execute();
- } catch (\Exception $e) {
- $this->db->logError($e);
- }
- }
-
- /**
- * Delete websocket request from database
- */
- public function deleteWsRequest(int $id)
- {
- try {
- $stmt = $this->db->prepare("DELETE FROM ws_requests WHERE Id = :id");
- $stmt->bindValue(':id', $id);
- $stmt->execute();
- } catch (\Exception $e) {
- $this->db->logError($e);
- }
- }
-
- /**
- * Get request package log details
- */
- public function getRequestPackageLog(int $id, string $package, string $status) : string|null
- {
- $data = '';
-
- // Example of Response_json content:
- // "update":{
- // "status":"done",
- // "success":{
- // "count":7,
- // "packages":{
- // "brave-browser":{
- // "version":"xxxx",
- // "log":"xxxx"
- // },
- // "firefox":{
- // "version":"xxxx",
- // "log":"xxxx"
- // },
- // }
- // }
- // }
-
- try {
- /**
- * Extract the log of the specified package in the specified status
- */
- $stmt = $this->db->prepare("SELECT json_extract(Response_json, :path) as log FROM ws_requests WHERE Id = :id");
- // Add quotes around the package name to avoid issues with package names containing dots (e.g. php8.1)
- $stmt->bindValue(':path', '$.update.' . $status . '.packages."' . $package . '".log');
- $stmt->bindValue(':id', $id);
- $result = $stmt->execute();
- } catch (\Exception $e) {
- $this->db->logError($e);
- }
-
- while ($row = $result->fetchArray(SQLITE3_ASSOC)) {
- $data = $row['log'];
- }
-
- return $data;
- }
}
diff --git a/www/models/Host/Request.php b/www/models/Host/Request.php
new file mode 100644
index 00000000..a6864d38
--- /dev/null
+++ b/www/models/Host/Request.php
@@ -0,0 +1,212 @@
+getConnection('hosts');
+ }
+
+ /**
+ * Add a new request in database
+ */
+ public function new(int $hostId, string $request)
+ {
+ try {
+ $stmt = $this->db->prepare("INSERT INTO requests ('Date', 'Time', 'Request', 'Status', 'Retry', 'Id_host') VALUES (:date, :time, :request, 'new', '0', :hostId)");
+ $stmt->bindValue(':date', date('Y-m-d'));
+ $stmt->bindValue(':time', date('H:i:s'));
+ $stmt->bindValue(':request', $request);
+ $stmt->bindValue(':hostId', $hostId);
+ $result = $stmt->execute();
+ } catch (\Exception $e) {
+ $this->db->logError($e);
+ }
+ }
+
+ /**
+ * Return all requests from database
+ */
+ public function get(string|null $status)
+ {
+ $requests = array();
+
+ try {
+ /**
+ * If a status is specified, we only get requests with this status
+ */
+ if (!empty($status)) {
+ $stmt = $this->db->prepare("SELECT * FROM requests WHERE Status = :status ORDER BY Date DESC, Time DESC");
+ $stmt->bindValue(':status', $status);
+ } else {
+ $stmt = $this->db->prepare("SELECT * FROM requests ORDER BY Date DESC, Time DESC");
+ }
+ $result = $stmt->execute();
+ } catch (\Exception $e) {
+ $this->db->logError($e);
+ }
+
+ while ($row = $result->fetchArray(SQLITE3_ASSOC)) {
+ $requests[] = $row;
+ }
+
+ return $requests;
+ }
+
+ /**
+ * Update request in database
+ */
+ public function update(int $id, string $status, string $info, string $responseJson)
+ {
+ try {
+ $stmt = $this->db->prepare("UPDATE requests SET Status = :status, Info = :info, Response_json = :responseJson WHERE Id = :id");
+ $stmt->bindValue(':status', $status);
+ $stmt->bindValue(':info', $info);
+ $stmt->bindValue(':responseJson', $responseJson);
+ $stmt->bindValue(':id', $id);
+ $result = $stmt->execute();
+ } catch (\Exception $e) {
+ $this->db->logError($e);
+ }
+ }
+
+ /**
+ * Update request status in database
+ */
+ public function updateStatus(int $id, string $status)
+ {
+ try {
+ $stmt = $this->db->prepare("UPDATE requests SET Status = :status WHERE Id = :id");
+ $stmt->bindValue(':status', $status);
+ $stmt->bindValue(':id', $id);
+ $result = $stmt->execute();
+ } catch (\Exception $e) {
+ $this->db->logError($e);
+ }
+ }
+
+ /**
+ * Update request info message in database
+ */
+ public function updateInfo(int $id, string $info)
+ {
+ try {
+ $stmt = $this->db->prepare("UPDATE requests SET Info = :info WHERE Id = :id");
+ $stmt->bindValue(':info', $info);
+ $stmt->bindValue(':id', $id);
+ $result = $stmt->execute();
+ } catch (\Exception $e) {
+ $this->db->logError($e);
+ }
+ }
+
+ /**
+ * Update request retry in database
+ */
+ public function updateRetry(int $id, int $retry)
+ {
+ try {
+ $stmt = $this->db->prepare("UPDATE requests SET Retry = :retry WHERE Id = :id");
+ $stmt->bindValue(':retry', $retry);
+ $stmt->bindValue(':id', $id);
+ $result = $stmt->execute();
+ } catch (\Exception $e) {
+ $this->db->logError($e);
+ }
+ }
+
+ /**
+ * Update request next retry time in database
+ */
+ public function updateNextRetry(int $id, string $nextRetry)
+ {
+ try {
+ $stmt = $this->db->prepare("UPDATE requests SET Next_retry = :nextRetry WHERE Id = :id");
+ $stmt->bindValue(':nextRetry', $nextRetry);
+ $stmt->bindValue(':id', $id);
+ $result = $stmt->execute();
+ } catch (\Exception $e) {
+ $this->db->logError($e);
+ }
+ }
+
+ /**
+ * Cancel request in database
+ */
+ public function cancel(int $id)
+ {
+ try {
+ $stmt = $this->db->prepare("UPDATE requests SET Status = 'canceled', Info = Info || ' (canceled)' WHERE Id = :id");
+ $stmt->bindValue(':id', $id);
+ $result = $stmt->execute();
+ } catch (\Exception $e) {
+ $this->db->logError($e);
+ }
+ }
+
+ /**
+ * Delete request from database
+ */
+ public function delete(int $id)
+ {
+ try {
+ $stmt = $this->db->prepare("DELETE FROM requests WHERE Id = :id");
+ $stmt->bindValue(':id', $id);
+ $stmt->execute();
+ } catch (\Exception $e) {
+ $this->db->logError($e);
+ }
+ }
+
+ /**
+ * Get request package log details
+ */
+ public function getRequestPackageLog(int $id, string $package, string $status) : string|null
+ {
+ $data = '';
+
+ // Example of Response_json content:
+ // "update":{
+ // "status":"done",
+ // "success":{
+ // "count":7,
+ // "packages":{
+ // "brave-browser":{
+ // "version":"xxxx",
+ // "log":"xxxx"
+ // },
+ // "firefox":{
+ // "version":"xxxx",
+ // "log":"xxxx"
+ // },
+ // }
+ // }
+ // }
+
+ try {
+ /**
+ * Extract the log of the specified package in the specified status
+ */
+ $stmt = $this->db->prepare("SELECT json_extract(Response_json, :path) as log FROM requests WHERE Id = :id");
+ // Add quotes around the package name to avoid issues with package names containing dots (e.g. php8.1)
+ $stmt->bindValue(':path', '$.update.' . $status . '.packages."' . $package . '".log');
+ $stmt->bindValue(':id', $id);
+ $result = $stmt->execute();
+ } catch (\Exception $e) {
+ $this->db->logError($e);
+ }
+
+ while ($row = $result->fetchArray(SQLITE3_ASSOC)) {
+ $data = $row['log'];
+ }
+
+ return $data;
+ }
+}
diff --git a/www/models/Layout/ContainerState.php b/www/models/Layout/ContainerReload.php
similarity index 75%
rename from www/models/Layout/ContainerState.php
rename to www/models/Layout/ContainerReload.php
index dc0d65f8..e452bd20 100644
--- a/www/models/Layout/ContainerState.php
+++ b/www/models/Layout/ContainerReload.php
@@ -4,7 +4,7 @@
use Exception;
-class ContainerState extends \Models\Model
+class ContainerReload extends \Models\Model
{
public function __construct()
{
@@ -34,26 +34,10 @@ public function get()
/**
* Add a new layout container state
*/
- public function add(string $name, string $id)
+ public function add(string $name)
{
try {
- $stmt = $this->db->prepare("INSERT INTO layout_container_state (Container, Id) VALUES (:name, :id)");
- $stmt->bindValue(':name', $name);
- $stmt->bindValue(':id', $id);
- $stmt->execute();
- } catch (\Exception $e) {
- $this->db->logError($e);
- }
- }
-
- /**
- * Update a layout container state
- */
- public function update(string $name, $id)
- {
- try {
- $stmt = $this->db->prepare("UPDATE layout_container_state SET Id = :id WHERE Container = :name");
- $stmt->bindValue(':id', $id);
+ $stmt = $this->db->prepare("INSERT INTO layout_container_state (Container) VALUES (:name)");
$stmt->bindValue(':name', $name);
$stmt->execute();
} catch (\Exception $e) {
@@ -80,4 +64,16 @@ public function exists(string $name)
return true;
}
+
+ /**
+ * Clean all containers entries
+ */
+ public function clean()
+ {
+ try {
+ $this->db->exec("DELETE FROM layout_container_state");
+ } catch (\Exception $e) {
+ $this->db->logError($e);
+ }
+ }
}
diff --git a/www/models/Websocket/WebsocketServer.php b/www/models/Websocket/WebsocketServer.php
new file mode 100644
index 00000000..85f38476
--- /dev/null
+++ b/www/models/Websocket/WebsocketServer.php
@@ -0,0 +1,161 @@
+getConnection('ws');
+ }
+
+ /**
+ * Clean ws connections from database
+ */
+ public function cleanWsConnections()
+ {
+ try {
+ $stmt = $this->db->prepare("DELETE FROM ws_connections");
+ $result = $stmt->execute();
+ } catch (\Exception $e) {
+ $this->db->logError($e);
+ }
+ }
+
+ /**
+ * Add new ws connection in database
+ */
+ public function newWsConnection(int $connectionId)
+ {
+ try {
+ $stmt = $this->db->prepare("INSERT INTO ws_connections ('Connection_id', 'Authenticated') VALUES (:id, 'false')");
+ $stmt->bindValue(':id', $connectionId);
+ $result = $stmt->execute();
+ } catch (\Exception $e) {
+ $this->db->logError($e);
+ }
+ }
+
+ /**
+ * Set websocket connection type
+ */
+ public function setWsConnectionType(int $connectionId, string $type)
+ {
+ try {
+ $stmt = $this->db->prepare("UPDATE ws_connections SET Type = :type WHERE Connection_id = :id");
+ $stmt->bindValue(':id', $connectionId);
+ $stmt->bindValue(':type', $type);
+ $result = $stmt->execute();
+ } catch (\Exception $e) {
+ $this->db->logError($e);
+ }
+ }
+
+ /**
+ * Update ws connection in database
+ */
+ public function updateWsConnection(int $connectionId, int $hostId, string $authenticated)
+ {
+ try {
+ $stmt = $this->db->prepare("UPDATE ws_connections SET Id_host = :hostId, Authenticated = :authenticated WHERE Connection_id = :connectionId");
+ $stmt->bindValue(':hostId', $hostId);
+ $stmt->bindValue(':authenticated', $authenticated);
+ $stmt->bindValue(':connectionId', $connectionId);
+ $result = $stmt->execute();
+ } catch (\Exception $e) {
+ $this->db->logError($e);
+ }
+ }
+
+ /**
+ * Return all authenticated websocket connections from database
+ */
+ public function getAuthenticatedWsConnections()
+ {
+ $connections = array();
+
+ try {
+ $stmt = $this->db->prepare("SELECT * FROM ws_connections WHERE Authenticated = 'true'");
+ $result = $stmt->execute();
+ } catch (\Exception $e) {
+ $this->db->logError($e);
+ }
+
+ while ($row = $result->fetchArray(SQLITE3_ASSOC)) {
+ $connections[] = $row;
+ }
+
+ return $connections;
+ }
+
+ /**
+ * Return all websocket connections from database
+ */
+ public function getWsConnections(string|null $type = null)
+ {
+ $connections = array();
+
+ try {
+ // If a connection type is provided, return only connections of that type
+ if (!empty($type)) {
+ $stmt = $this->db->prepare("SELECT * FROM ws_connections WHERE Type = :type");
+ $stmt->bindValue(':type', $type);
+ } else {
+ $stmt = $this->db->prepare("SELECT * FROM ws_connections");
+ }
+
+ $result = $stmt->execute();
+ } catch (\Exception $e) {
+ $this->db->logError($e);
+ }
+
+ while ($row = $result->fetchArray(SQLITE3_ASSOC)) {
+ $connections[] = $row;
+ }
+
+ return $connections;
+ }
+
+ /**
+ * Return websocket connection Id by host Id
+ */
+ public function getWsConnectionIdByHostId(int $hostId)
+ {
+ $connectionId = '';
+
+ try {
+ $stmt = $this->db->prepare("SELECT Connection_id FROM ws_connections WHERE Id_host = :hostId");
+ $stmt->bindValue(':hostId', $hostId);
+ $result = $stmt->execute();
+ } catch (\Exception $e) {
+ $this->db->logError($e);
+ }
+
+ $connectionId = '';
+
+ while ($row = $result->fetchArray(SQLITE3_ASSOC)) {
+ $connectionId = $row['Connection_id'];
+ }
+
+ return $connectionId;
+ }
+
+ /**
+ * Delete ws connection from database
+ */
+ public function deleteWsConnection(int $connectionId)
+ {
+ try {
+ $stmt = $this->db->prepare("DELETE FROM ws_connections WHERE Connection_id = :id");
+ $stmt->bindValue(':id', $connectionId);
+ $result = $stmt->execute();
+ } catch (\Exception $e) {
+ $this->db->logError($e);
+ }
+ }
+}
diff --git a/www/public/resources/js/functions.js b/www/public/resources/js/functions.js
index c9f5c903..97c86b44 100644
--- a/www/public/resources/js/functions.js
+++ b/www/public/resources/js/functions.js
@@ -1,3 +1,63 @@
+/**
+ * Open websocket connection with server
+ */
+function websocket_client()
+{
+ const server = window.location.hostname;
+
+ // If the target server uses https, then use wss://
+ if (window.location.protocol == 'https:') {
+ var path = 'wss://' + server + '/ws';
+ } else {
+ var path = 'ws://' + server + '/ws';
+ }
+
+ const socket = new WebSocket(path);
+
+ // Handle connection open
+ socket.onopen = function (event) {
+ console.log('Websocket connection opened at ' + path);
+
+ // Send connection type to server
+ message = JSON.stringify({
+ 'connection-type': 'browser-client'
+ });
+
+ sendMessage(message);
+ };
+
+ // Handle received message
+ socket.onmessage = function (event) {
+ // Parse message
+ message = JSON.parse(event.data);
+
+ // If message type is reload-container, then reload container
+ if (message.type == 'reload-container') {
+ reloadContainer(message.container);
+ }
+ };
+
+ // Handle connection close
+ socket.onclose = function (event) {
+ console.log('Websocket connection closed with ' + server);
+
+ // If the connection was closed cleanly
+ if (event.wasClean) {
+ console.log('Websocket connection with ' + server + ' closed (code=' + event.code + ' reason=' + event.reason + ')');
+
+ // If the connection was closed unexpectedly
+ // For example, the server process was killed or network problems occurred
+ } else {
+ console.log('Websocket connection with ' + server + ' closed unexpectedly');
+ }
+ };
+
+ function sendMessage(message)
+ {
+ socket.send(message);
+ }
+}
+
/**
* Get panel by name
* @param {*} name
diff --git a/www/public/resources/js/general.js b/www/public/resources/js/general.js
index eaf6c443..8c78c017 100644
--- a/www/public/resources/js/general.js
+++ b/www/public/resources/js/general.js
@@ -1,6 +1,7 @@
-setInterval(function () {
- getContainerState();
-}, 2000);
+/**
+ * Open websocket connection with server
+ */
+websocket_client();
/**
* Event: get panel
@@ -285,63 +286,6 @@ function reloadContainer(container)
hideLoading();
}
-/**
- * Ajax: Get all containers state and reload them if needed
- */
-function getContainerState()
-{
- $.ajax({
- type: "POST",
- url: "/ajax/controller.php",
- data: {
- controller: "general",
- action: "getContainerState"
- },
- dataType: "json",
- success: function (data, textStatus, jqXHR) {
- /**
- * Parse results and compare with current state
- */
- jsonValue = jQuery.parseJSON(jqXHR.responseText);
- containersArray = jQuery.parseJSON(jsonValue.message);
- containersArray.forEach(obj => {
- Object.entries(obj).forEach(([key, value]) => {
- if (key == 'Container') {
- containerName = value;
- }
- if (key == 'Id') {
- containerStateId = value;
- }
- });
-
- /**
- * If current container does not appear in cookies yet, add it
- */
- if (getCookie(containerName) == "") {
- setCookie(containerName, containerStateId, 365);
- /**
- * Else compare current state with cookie state
- */
- } else {
- var cookieState = getCookie(containerName);
-
- /**
- * If state has changed, reload container and update cookie
- */
- if (cookieState != containerStateId) {
- setCookie(containerName, containerStateId, 365);
- reloadContainer(containerName);
- }
- }
- });
- },
- error: function (jqXHR, textStatus, thrownError) {
- jsonValue = jQuery.parseJSON(jqXHR.responseText);
- printAlert(jsonValue.message, 'error');
- },
- });
-}
-
/**
* Ajax: Get and reload table
* @param {*} table
diff --git a/www/public/resources/js/host.js b/www/public/resources/js/host.js
index 9ea8e6d3..4ab5acdb 100644
--- a/www/public/resources/js/host.js
+++ b/www/public/resources/js/host.js
@@ -863,7 +863,7 @@ $(document).on('click','.cancel-request-btn',function () {
// Print error alert:
true,
// Reload container:
- ['host/requests']
+ ['hosts/list', 'host/requests']
);
});
@@ -924,14 +924,14 @@ $(document).on('mouseenter', '.event-packages-btn', function (e) {
/**
* Create a new
event-packages-details
*/
- $('footer').append('
Loading
');
+ $('footer').append('
Loading
');
/**
* Get screen width
* Then reduce the width of screen by 200px to have some margin
*/
var screenWidth = window.screen.width;
- screenWidth = screenWidth - 200;
+ screenWidth = screenWidth - 500;
/**
* If event-packages-details is outside the screen on the right
diff --git a/www/templates/mail/mail.template.html.php b/www/templates/mail/mail.template.html.php
index 0211486a..e092fd7b 100644
--- a/www/templates/mail/mail.template.html.php
+++ b/www/templates/mail/mail.template.html.php
@@ -138,7 +138,7 @@
-
+
@@ -170,13 +170,13 @@
-
+
-
+
@@ -215,7 +215,7 @@
-
+
diff --git a/www/tools/database/initialize.php b/www/tools/database/initialize.php
index 1c7ddd43..f3fc87de 100644
--- a/www/tools/database/initialize.php
+++ b/www/tools/database/initialize.php
@@ -9,7 +9,7 @@
new \Controllers\Autoloader('minimal');
try {
- $databases = array('main', 'stats', 'hosts');
+ $databases = array('main', 'stats', 'hosts', 'ws');
/**
* Open a connection to each database and create tables if they do not exist
diff --git a/www/update/database/4.14.1.php b/www/update/database/4.14.1.php
new file mode 100644
index 00000000..9004ea9e
--- /dev/null
+++ b/www/update/database/4.14.1.php
@@ -0,0 +1,44 @@
+db->columnExist('layout_container_state', 'Id') === true) {
+ $this->db->exec("ALTER TABLE layout_container_state DROP COLUMN Id");
+ $this->db->exec("VACUUM");
+}
+
+/**
+ * Move 'ws_requests' table to 'requests' table
+ */
+if ($hostsDb->tableExist('ws_requests') === true) {
+ $hostsDb->exec("INSERT INTO requests SELECT * FROM ws_requests");
+ $hostsDb->exec("DROP TABLE IF EXISTS ws_requests");
+ $hostsDb->exec("DROP INDEX IF EXISTS ws_requests_status");
+ $hostsDb->exec("DROP INDEX IF EXISTS ws_requests_date_time");
+ $hostsDb->exec("DROP INDEX IF EXISTS ws_requests_id_host");
+}
+
+/**
+ * Drop table 'ws_connections' from hosts database
+ */
+if ($hostsDb->tableExist('ws_connections') === true) {
+ $hostsDb->exec("DROP TABLE IF EXISTS ws_connections");
+}
+
+/**
+ * Clean up
+ */
+$hostsDb->exec("VACUUM");
+$hostsDb->exec("ANALYZE");
+$hostsDb->close();
+
+unset($hostsDb);
diff --git a/www/update/database/4.10.0.php b/www/update/database/archives/4.10.0.php
similarity index 100%
rename from www/update/database/4.10.0.php
rename to www/update/database/archives/4.10.0.php
diff --git a/www/version b/www/version
index 09ce0ce7..5704b893 100644
--- a/www/version
+++ b/www/version
@@ -1 +1 @@
-4.14.0
\ No newline at end of file
+4.14.1
\ No newline at end of file
diff --git a/www/views/includes/containers/hosts/list.inc.php b/www/views/includes/containers/hosts/list.inc.php
index 7562087e..63144d78 100644
--- a/www/views/includes/containers/hosts/list.inc.php
+++ b/www/views/includes/containers/hosts/list.inc.php
@@ -133,6 +133,7 @@
$agentVersion = 'unknown';
$rebootRequired = 'unknown';
$agentStatus = 'unknown';
+ $requestInfo = null;
$responseDetails = null;
if (!empty($host['Hostname'])) {
@@ -387,6 +388,11 @@
$requestStatusIcon = 'check.svg';
}
+ /**
+ * Request info
+ */
+ $requestInfo = $lastPendingRequest['Info'];
+
/**
* Request title
*/
@@ -479,6 +485,10 @@
diff --git a/www/views/includes/containers/hosts/overview.inc.php b/www/views/includes/containers/hosts/overview.inc.php
index fbdcd562..9a0f90a9 100644
--- a/www/views/includes/containers/hosts/overview.inc.php
+++ b/www/views/includes/containers/hosts/overview.inc.php
@@ -4,11 +4,7 @@
-
No host registered yet.
-
-
- You can register hosts that use linupdate with reposerver module enabled. This page will then display dashboards and informations about your hosts and their packages status (installed, available, updated...).
-
+
No host registered yet! Install linupdate on your hosts to register them to Repomanager. This page will display dashboards and informations about the hosts and their packages (installed, available, updated...). See quick setup example.
diff --git a/www/views/includes/containers/settings/health.inc.php b/www/views/includes/containers/settings/health.inc.php
index 7a78255e..cf870569 100644
--- a/www/views/includes/containers/settings/health.inc.php
+++ b/www/views/includes/containers/settings/health.inc.php
@@ -30,7 +30,7 @@
if ($statusError == 0) {
echo '
Healthy
';
} else {
- echo '
' . $statusMsg . '
';
+ echo '
' . $statusMsg . '
';
}
if (STATS_ENABLED == "true") : ?>
@@ -65,7 +65,7 @@
if ($statusError == 0) {
echo '
- You can create and manage configuration profiles for your client hosts that use linupdate with reposerver module enabled.
- On every package update, hosts will first retrieve their configuration from this reposerver including the list of repositories they have access to, packages to exclude, services to restart after update, etc...
+
You can create and manage configuration profiles for hosts that use linupdate with reposerver module enabled. On every package update, the hosts will first retrieve their configuration from Repomanager including the list of repositories they have access to, packages to exclude, services to restart after update, etc...