diff --git a/server/admin/versions.php b/server/admin/versions.php index 33c24a1..44fef6c 100644 --- a/server/admin/versions.php +++ b/server/admin/versions.php @@ -671,11 +671,11 @@ if ($_SERVER['REQUEST_METHOD'] === 'POST') { require_once "$root/tools/SignManifest.php"; $signer = new \PSLauncher\Tools\SignManifest($root); $force = !empty($_POST['force']); - // SignManifest::run filtre par numéro de version ; en cas d'entries - // de même version sur des channels différents, le hash sera recalculé - // pour toutes celles qui matchent (chacune pointe sur son propre ZIP, - // donc le résultat est correct, juste un peu plus de boulot). - $result = $signer->run('versions', $force, $version); + // Filtre par entryId (pas par numéro de version). Sinon, quand deux + // entrées partagent le même numéro (channels firefighter vs full sur + // 1.5.4.32 p.ex.), les DEUX ZIPs sont hashés dans la même requête HTTP + // → 2 × 14 Go = risque de dépasser le timeout front OVH sur mutualisé. + $result = $signer->run('versions', $force, null, $entryId); $forceLabel = $force ? ' [FORCE]' : ''; $message = "Hash de v{$version}{$forceLabel} :\n" . implode("\n", $result['log']); if (!$result['ok']) $messageType = 'error'; diff --git a/server/tools/SignManifest.php b/server/tools/SignManifest.php index 2c70af4..87eff39 100644 --- a/server/tools/SignManifest.php +++ b/server/tools/SignManifest.php @@ -139,10 +139,19 @@ final class SignManifest * @param bool $force Si true, ignore le cache et recalcule tous les hashs * @param ?string $onlyVersion Si non null, ne touche QUE cette version dans la section * versions[] (les autres restent inchangées). Le scope 'launcher' - * est ignoré dans ce cas. + * est ignoré dans ce cas. ATTENTION : si plusieurs entrées + * partagent le même numéro de version (channels firefighter + * vs full sur 1.5.4.32 p.ex.), TOUTES sont hashées — d'où + * $onlyEntryId ci-dessous pour cibler une seule ligne. + * @param ?string $onlyEntryId Si non null, ne touche QUE l'entrée avec cet id (généré par + * generate_entry_id() au backoffice). Prend le pas sur + * $onlyVersion. Utilisé par le bouton « 🔁 Hash » d'une + * ligne isolée pour éviter de re-hasher les autres channels + * qui partagent le même numéro de version (2 × 14 Go dans une + * requête HTTP → risque de timeout front OVH). * @return array{ok:bool, log:string[]} */ - public function run(string $scope = 'all', bool $force = false, ?string $onlyVersion = null): array + public function run(string $scope = 'all', bool $force = false, ?string $onlyVersion = null, ?string $onlyEntryId = null): array { // Sur OVH mutualisé, un SHA-256 d'un ZIP de 14 Go peut prendre 5-10 min via // le SAN partagé. Le max_execution_time par défaut (30-60s) tue le process @@ -166,7 +175,8 @@ final class SignManifest $ts = date('Y-m-d H:i:s'); @file_put_contents($errorLogPath, "[{$ts}] --- SignManifest::run(scope={$scope}, force=" . ($force?'1':'0') - . ", onlyVersion=" . ($onlyVersion ?? 'null') . ") ---\n", + . ", onlyVersion=" . ($onlyVersion ?? 'null') + . ", onlyEntryId=" . ($onlyEntryId ?? 'null') . ") ---\n", FILE_APPEND); // Fatal errors → capturés par un shutdown handler. Sinon Apache renvoie // juste 500 sans qu'on sache ce qui a claqué. @@ -193,9 +203,9 @@ final class SignManifest $doVersions = ($scope === 'all' || $scope === 'versions'); $doLauncher = ($scope === 'all' || $scope === 'launcher'); - if ($onlyVersion !== null) { - // Mode "hash une seule version" : on ne touche pas au launcher, - // et on ne hash que la version demandée dans la section versions[]. + if ($onlyVersion !== null || $onlyEntryId !== null) { + // Mode "hash une seule entrée / version" : on ne touche pas au launcher, + // et on ne hash que ce qui correspond au filtre demandé dans versions[]. $doLauncher = false; $doVersions = true; } @@ -207,7 +217,15 @@ final class SignManifest $hashedVersions = []; if ($doVersions) foreach ($manifest['versions'] as &$v) { $version = $v['version'] ?? '?'; - if ($onlyVersion !== null && $version !== $onlyVersion) { + $entryId = (string)($v['id'] ?? ''); + // Filtre par id d'entrée prioritaire sur filtre par version (cas + // channels multiples partageant un numéro de version identique — + // ex : proserve-firefighter-1.5.4.32 vs proserve-full-1.5.4.32). + // Sinon, filtre par numéro de version (rétro-compat). + if ($onlyEntryId !== null && $entryId !== $onlyEntryId) { + continue; + } + if ($onlyEntryId === null && $onlyVersion !== null && $version !== $onlyVersion) { continue; // skip silently les autres versions } $url = $v['download']['url'] ?? '';