Skip to content

Commit

Permalink
Isolate progress function more
Browse files Browse the repository at this point in the history
  • Loading branch information
Pierstoval committed Nov 21, 2024
1 parent e79c34d commit 19db5f5
Show file tree
Hide file tree
Showing 5 changed files with 107 additions and 71 deletions.
3 changes: 2 additions & 1 deletion install/install.php
Original file line number Diff line number Diff line change
Expand Up @@ -345,7 +345,8 @@ public function __construct($dbh)
$prev_form($host, $user, $password);

echo \sprintf(
'<script defer>startDatabaseInstall();</script>',
'<script defer>start_database_install("%s");</script>',
\Glpi\Controller\Install\InstallController::STORED_PROGRESS_KEY,
);
} else { // can't create config_db file
echo "<p>" . __s('Impossible to write the database setup file') . "</p>";
Expand Down
122 changes: 69 additions & 53 deletions js/glpi_install.js
Original file line number Diff line number Diff line change
Expand Up @@ -30,7 +30,7 @@
* ---------------------------------------------------------------------
*/
(() => {
function updateProgress(single_message_element, progress_element, value, max, text) {
function update_progress(single_message_element, progress_element, value, max, text) {
value = value || 0;
max = max || 1;
const percentage = (value / max * 100);
Expand All @@ -52,7 +52,7 @@
message_list_element.appendChild(alert);
}

function startDatabaseInstall()
async function start_database_install(progress_key)
{
const message_element_id = 'glpi_install_messages_container';
const success_element_id = 'glpi_install_success';
Expand All @@ -76,71 +76,87 @@
messages_container.appendChild(progress_container_element);

setTimeout(() => {
checkProgress(message_list_element, single_message_element, progress_element);
check_progress({
key: progress_key,
error_callback: (error) => {
message(message_list_element, `Progress error:\n${error}`);
update_progress(single_message_element, progress_element, 0, 1);
},
progress_callback: (json) => {
if (!json || !json.length || json['finished_at']) {
update_progress(single_message_element, progress_element, 100, 100);
return false;
}
update_progress(single_message_element, progress_element, json.current, json.max, json.data);
},
});
}, 1500);

fetch("/install/database_setup/start_db_inserts", {
method: 'POST',
})
.then((res) => res.text())
.then((text) => {
if (text && text.trim().length) {
message(message_list_element, `Error:\n${text}`);
} else {
updateProgress(single_message_element, progress_element, 1, 1);
success_element.querySelector('button').removeAttribute('disabled');
}
})
.catch(function (err) {
message(message_list_element, `Database install error:\n${err.message||err.toString()}`);
updateProgress(single_message_element, progress_element, 0, 10);
});
try {
const res = await fetch("/install/database_setup/start_db_inserts", {method: 'POST'});
const text = await res.text();
if (text && text.trim().length) {
message(message_list_element, `Error:\n${text}`);
} else {
update_progress(single_message_element, progress_element, 1, 1);
success_element.querySelector('button').removeAttribute('disabled');
}
} catch (err) {
message(message_list_element, `Database install error:\n${err.message||err.toString()}`);
update_progress(single_message_element, progress_element, 0, 1);
}
}

function checkProgress(message_list_element, single_message_element, progress_element)
/**
* @param parameters
* @param {string} parameters.key Mandatory. The progress bar's unique key.
* @param {function} parameters.progress_callback The function that will be called for each progress response. If the return value is "false", this stops the progress checks.
* @param {function} parameters.error_callback The function that will be called for each error, either exceptions or non-200 HTTP responses. Stops the progress checks by default, unless you return a true-ish value from the callback.
*/
function check_progress(parameters)
{
setTimeout(() => {
if (!parameters.key) {
throw new Error('Progress key is mandatory.');
}

fetch("/install/database_setup/check_progress", {
method: 'POST',
})
.then((res) => {
if (res.status === 404) {
// Progress not found, let's stop here.
updateProgress(single_message_element, progress_element, 1, 1);
return null;
}
const start_timeout = 250;

if (res.status >= 300) {
throw new Error(`Invalid response from server, expected 200 or 404, found "${res.status}".`);
setTimeout(async () => {
try {
const res = await fetch('/progress/check/' + parameters.key, {
method: 'POST',
});

if (res.status === 404) {
const cb_err_result = parameters.error_callback('Not found');
if (!cb_err_result) {
return;
}
}

return res.json();
})
.then((json) => {
if (json.started_at) {
if (json.finished_at) {
// Finished, nothing else to do!
updateProgress(single_message_element, progress_element, 1, 1, json.data);
return;
}
if (res.status >= 300) {
parameters.error_callback(`Invalid response from server, expected 200 or 404, found "${res.status}".`);
return;
}

updateProgress(single_message_element, progress_element, json.current, json.max, json.data);
const json = await res.json();

checkProgress(message_list_element, single_message_element, progress_element);
return;
debugger;
if (json['key'] && json['started_at']) {
if (parameters.progress_callback(json) !== false) {
// Recursive call, including the timeout
check_progress(parameters);
}

console.info(json);
message(message_list_element, `Result Error when checking progress:\n${JSON.stringify(json)}`);
})
.catch((err) => {
message(message_list_element, `Request Error when checking progress:\n${err.message || err.toString()}`);
updateProgress(single_message_element, progress_element, 0, 1);
});
return;
}

}, 500);
parameters.error_callback(`Result error when checking progress:\n${err.message || err.toString()}`);
} catch (err) {
parameters.error_callback(`Request error when checking progress:\n${err.message || err.toString()}`);
}
}, start_timeout);
}

window.startDatabaseInstall = startDatabaseInstall;
window.start_database_install = start_database_install;
})();
23 changes: 6 additions & 17 deletions src/Glpi/Controller/Install/InstallController.php
Original file line number Diff line number Diff line change
Expand Up @@ -34,12 +34,8 @@

namespace Glpi\Controller\Install;

use Glpi\Cache\CacheManager;
use Glpi\Http\Firewall;
use Glpi\Http\HeaderlessStreamedResponse;
use Glpi\Security\Attribute\SecurityStrategy;
use Psr\SimpleCache\CacheInterface;
use Symfony\Component\HttpFoundation\JsonResponse;
use Toolbox;
use Glpi\Controller\AbstractController;
use Glpi\Progress\ProgressChecker;
Expand All @@ -49,7 +45,7 @@

class InstallController extends AbstractController
{
private const STORED_PROGRESS_KEY = 'install_db_inserts';
public const STORED_PROGRESS_KEY = 'install_db_inserts';

public function __construct(
private readonly ProgressChecker $progressChecker,
Expand All @@ -68,7 +64,11 @@ public function start_inserts(): Response
try {
$progress_callback = static function (?int $current = null, ?int $max = null, ?string $data = null) use ($progressChecker) {
$progress = $progressChecker->getCurrentProgress(self::STORED_PROGRESS_KEY);
$progress->current += $current ?? 1;
if (!$current) {
$progress->current++;
} else {
$progress->current = $current;
}
$progress->max = (int) $max;
$progress->data .= $data ? ("\n" . $data) : '';
$progressChecker->save($progress);
Expand All @@ -87,15 +87,4 @@ public function start_inserts(): Response
}
});
}

#[Route("/install/database_setup/check_progress", methods: 'POST')]
#[SecurityStrategy(Firewall::STRATEGY_NO_CHECK)]
public function check_progress(): Response
{
if (!$this->progressChecker->hasProgress(self::STORED_PROGRESS_KEY)) {
return new JsonResponse([], 404);
}

return new JsonResponse($this->progressChecker->getCurrentProgress(self::STORED_PROGRESS_KEY));
}
}
29 changes: 29 additions & 0 deletions src/Glpi/Controller/ProgressController.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,29 @@
<?php

namespace Glpi\Controller;

use Glpi\Http\Firewall;
use Glpi\Security\Attribute\SecurityStrategy;
use Symfony\Component\HttpFoundation\JsonResponse;
use Glpi\Progress\ProgressChecker;
use Symfony\Component\HttpFoundation\Response;
use Symfony\Component\Routing\Attribute\Route;

class ProgressController extends AbstractController
{
public function __construct(
private readonly ProgressChecker $progressChecker,
) {
}

#[Route("/progress/check/{key}", methods: 'POST')]
#[SecurityStrategy(Firewall::STRATEGY_NO_CHECK)]
public function check_progress(string $key): Response
{
if (!$this->progressChecker->hasProgress($key)) {
return new JsonResponse([], 404);
}

return new JsonResponse($this->progressChecker->getCurrentProgress($key));
}
}
1 change: 1 addition & 0 deletions src/Glpi/Http/Firewall.php
Original file line number Diff line number Diff line change
Expand Up @@ -226,6 +226,7 @@ private function computeFallbackStrategyForCore(string $path): string
'/front/lostpassword.php' => self::STRATEGY_NO_CHECK,
'/front/updatepassword.php' => self::STRATEGY_NO_CHECK,
'/install/' => self::STRATEGY_NO_CHECK, // No check during install/update
'/progress/check/' => self::STRATEGY_NO_CHECK,
];

foreach ($paths as $checkPath => $strategy) {
Expand Down

0 comments on commit 19db5f5

Please sign in to comment.