Server: faster releases (cache hashes), per-version skip, longer signed-URL TTL
- gate.php : ETag now derived from size+mtime instead of md5_file($zip). Previously the gate hashed the entire 14 GB ZIP on every HEAD/GET call (including each of 8 parallel segments) → 30s-5min preparation lag for clients before the first byte. Now <1ms per request. - SignManifest : disk-backed cache for SHA-256 keyed by (path,size,mtime). Re-signing 5×14 GB versions used to take ~25 min, now ~1s when nothing changed. New "Force re-hash" toggle in admin to ignore the cache. - versions.php : per-row "🔁 Hash" button to sign a single version, plus a "⚙ Hash" dropdown to toggle hashAlgorithm:none for builds where the user accepts skipping client-side verification (manifest stays signed Ed25519, only the per-ZIP SHA-256 verification is bypassed). - DownloadUrl.php : signed-URL TTL bumped 1h → 6h to cover slow ADSL users who need >1h to finish a 14 GB download. - .gitignore : track server/builds/.htaccess + gate.php (still ignore the actual ZIP/exe binaries). Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
This commit is contained in:
@@ -70,8 +70,10 @@ if ($_SERVER['REQUEST_METHOD'] === 'POST') {
|
||||
elseif ($action === 'sync_launcher') {
|
||||
require_once "$root/tools/SignManifest.php";
|
||||
$signer = new \PSLauncher\Tools\SignManifest($root);
|
||||
$result = $signer->run('launcher');
|
||||
$message = "Sortie du script (scope: launcher) :\n" . implode("\n", $result['log']);
|
||||
$force = !empty($_POST['force']);
|
||||
$result = $signer->run('launcher', $force);
|
||||
$forceLabel = $force ? ' [FORCE: cache ignoré]' : '';
|
||||
$message = "Sortie du script (scope: launcher{$forceLabel}) :\n" . implode("\n", $result['log']);
|
||||
if (!$result['ok']) $messageType = 'error';
|
||||
}
|
||||
} catch (Exception $e) {
|
||||
@@ -159,6 +161,9 @@ Layout::header('Launcher', 'launcher');
|
||||
<?= Layout::csrfField() ?>
|
||||
<input type="hidden" name="action" value="sync_launcher">
|
||||
<button class="btn btn-primary" type="submit">🔁 Hasher le launcher + signer</button>
|
||||
<label style="margin-left:12px;font-size:12px;color:#888" title="Recalcule le SHA-256 même si l'exe n'a pas changé.">
|
||||
<input type="checkbox" name="force" value="1"> Force re-hash
|
||||
</label>
|
||||
</form>
|
||||
|
||||
<?php if ($launcher): ?>
|
||||
|
||||
@@ -136,11 +136,56 @@ if ($_SERVER['REQUEST_METHOD'] === 'POST') {
|
||||
require_once "$root/tools/SignManifest.php";
|
||||
$signer = new \PSLauncher\Tools\SignManifest($root);
|
||||
$scope = $action === 'sync_versions' ? 'versions' : 'all';
|
||||
$result = $signer->run($scope);
|
||||
$force = !empty($_POST['force']);
|
||||
$result = $signer->run($scope, $force);
|
||||
$label = $scope === 'versions' ? 'versions Proserve' : 'manifest complet';
|
||||
$message = "Sortie du script (scope: {$label}) :\n" . implode("\n", $result['log']);
|
||||
$forceLabel = $force ? ' [FORCE: cache ignoré]' : ' [cache utilisé pour les ZIPs inchangés]';
|
||||
$message = "Sortie du script (scope: {$label}{$forceLabel}) :\n" . implode("\n", $result['log']);
|
||||
if (!$result['ok']) $messageType = 'error';
|
||||
}
|
||||
elseif ($action === 'sync_one') {
|
||||
$version = $_POST['version'] ?? '';
|
||||
if ($version === '') throw new Exception("Version manquante");
|
||||
require_once "$root/tools/SignManifest.php";
|
||||
$signer = new \PSLauncher\Tools\SignManifest($root);
|
||||
$force = !empty($_POST['force']);
|
||||
$result = $signer->run('versions', $force, $version);
|
||||
$forceLabel = $force ? ' [FORCE]' : '';
|
||||
$message = "Hash de v{$version}{$forceLabel} :\n" . implode("\n", $result['log']);
|
||||
if (!$result['ok']) $messageType = 'error';
|
||||
}
|
||||
elseif ($action === 'set_skip_hash') {
|
||||
$version = $_POST['version'] ?? '';
|
||||
$skipHash = !empty($_POST['skip']);
|
||||
foreach ($manifest['versions'] as &$v) {
|
||||
if ($v['version'] === $version) {
|
||||
if ($skipHash) {
|
||||
$v['download']['hashAlgorithm'] = 'none';
|
||||
$v['download']['sha256'] = '';
|
||||
} else {
|
||||
unset($v['download']['hashAlgorithm']);
|
||||
if (empty($v['download']['sha256'])) {
|
||||
$v['download']['sha256'] = 'REPLACE_AFTER_BUILD';
|
||||
}
|
||||
}
|
||||
break;
|
||||
}
|
||||
}
|
||||
unset($v);
|
||||
saveManifest($manifestPath, $manifest);
|
||||
|
||||
// Re-signature auto pour garder le manifest valide après le changement.
|
||||
// En mode skip, on n'a rien à hasher : on appelle run() qui se contentera
|
||||
// de détecter hashAlgorithm=none et re-signera. Pas de calcul lourd.
|
||||
require_once "$root/tools/SignManifest.php";
|
||||
$signer = new \PSLauncher\Tools\SignManifest($root);
|
||||
$resign = $signer->run('versions', false);
|
||||
$message = ($skipHash
|
||||
? "Vérif SHA-256 désactivée pour v{$version} et manifest re-signé."
|
||||
: "Vérif SHA-256 réactivée pour v{$version}. Clique « 🔁 Hash » sur cette ligne pour calculer le SHA-256.")
|
||||
. "\n" . implode("\n", $resign['log']);
|
||||
if (!$resign['ok']) $messageType = 'error';
|
||||
}
|
||||
} catch (Exception $e) {
|
||||
$message = $e->getMessage();
|
||||
$messageType = 'error';
|
||||
@@ -221,6 +266,9 @@ Layout::header('Versions', 'versions');
|
||||
<?= Layout::csrfField() ?>
|
||||
<input type="hidden" name="action" value="sync_versions">
|
||||
<button class="btn btn-primary" type="submit">🔁 Hasher les versions + signer</button>
|
||||
<label style="margin-left:12px;font-size:12px;color:#888" title="Recalcule TOUS les SHA-256 même si les ZIPs n'ont pas changé. Lent. Cocher seulement en cas de doute sur le cache.">
|
||||
<input type="checkbox" name="force" value="1"> Force re-hash
|
||||
</label>
|
||||
</form>
|
||||
</div>
|
||||
|
||||
@@ -238,9 +286,10 @@ Layout::header('Versions', 'versions');
|
||||
</thead>
|
||||
<tbody>
|
||||
<?php foreach ($manifest['versions'] ?? [] as $v):
|
||||
$zipBase = basename(parse_url($v['download']['url'], PHP_URL_PATH) ?? '');
|
||||
$zipExists = in_array($zipBase, $zips, true);
|
||||
$hashed = !empty($v['download']['sha256']) && !str_starts_with($v['download']['sha256'], 'REPLACE');
|
||||
$zipBase = basename(parse_url($v['download']['url'], PHP_URL_PATH) ?? '');
|
||||
$zipExists = in_array($zipBase, $zips, true);
|
||||
$hashed = !empty($v['download']['sha256']) && !str_starts_with($v['download']['sha256'], 'REPLACE');
|
||||
$hashSkipped = strtolower((string)($v['download']['hashAlgorithm'] ?? 'sha256')) === 'none';
|
||||
?>
|
||||
<tr>
|
||||
<td><strong>v<?= htmlspecialchars($v['version']) ?></strong></td>
|
||||
@@ -256,7 +305,9 @@ Layout::header('Versions', 'versions');
|
||||
<?php endif; ?>
|
||||
</td>
|
||||
<td>
|
||||
<?php if ($hashed): ?>
|
||||
<?php if ($hashSkipped): ?>
|
||||
<span class="badge badge-warning" title="hashAlgorithm=none : la vérif SHA-256 est désactivée pour cette release">SKIP</span>
|
||||
<?php elseif ($hashed): ?>
|
||||
<code title="<?= htmlspecialchars($v['download']['sha256']) ?>"><?= substr($v['download']['sha256'], 0, 12) ?>…</code>
|
||||
<?php else: ?>
|
||||
<span class="badge badge-warning">à calculer</span>
|
||||
@@ -273,6 +324,47 @@ Layout::header('Versions', 'versions');
|
||||
</form>
|
||||
</td>
|
||||
<td style="text-align: right; white-space: nowrap;">
|
||||
<?php if ($zipExists && !$hashSkipped): ?>
|
||||
<form method="post" style="display:inline" title="Recalcule le SHA-256 de cette version uniquement (utilise le cache si le ZIP n'a pas changé)">
|
||||
<?= Layout::csrfField() ?>
|
||||
<input type="hidden" name="action" value="sync_one">
|
||||
<input type="hidden" name="version" value="<?= htmlspecialchars($v['version']) ?>">
|
||||
<button class="btn btn-secondary" type="submit">🔁 Hash</button>
|
||||
</form>
|
||||
<?php endif; ?>
|
||||
<details style="display: inline-block; margin: 0 4px;">
|
||||
<summary class="btn btn-secondary">⚙ Hash</summary>
|
||||
<div style="margin-top: 8px; min-width: 280px;">
|
||||
<p class="muted" style="font-size: 12px; margin: 0 0 8px;">
|
||||
<?php if ($hashSkipped): ?>
|
||||
La vérif SHA-256 est <strong>désactivée</strong> pour cette release.
|
||||
Le manifest sert <code>hashAlgorithm: "none"</code>. La sécurité repose
|
||||
uniquement sur la signature Ed25519 du manifest + HTTPS.
|
||||
<?php else: ?>
|
||||
Désactive la vérif SHA-256 chez les clients pour cette release
|
||||
(utile sur très gros builds où on accepte le compromis perf/sécurité).
|
||||
<?php endif; ?>
|
||||
</p>
|
||||
<form method="post" style="display:inline">
|
||||
<?= Layout::csrfField() ?>
|
||||
<input type="hidden" name="action" value="set_skip_hash">
|
||||
<input type="hidden" name="version" value="<?= htmlspecialchars($v['version']) ?>">
|
||||
<input type="hidden" name="skip" value="<?= $hashSkipped ? '0' : '1' ?>">
|
||||
<button class="btn <?= $hashSkipped ? 'btn-success' : 'btn-warning' ?>" type="submit">
|
||||
<?= $hashSkipped ? '✓ Réactiver la vérif' : '⏭ Désactiver la vérif' ?>
|
||||
</button>
|
||||
</form>
|
||||
<?php if ($zipExists): ?>
|
||||
<form method="post" style="margin-top: 8px;" title="Recalcule en ignorant le cache même si le ZIP n'a pas changé">
|
||||
<?= Layout::csrfField() ?>
|
||||
<input type="hidden" name="action" value="sync_one">
|
||||
<input type="hidden" name="version" value="<?= htmlspecialchars($v['version']) ?>">
|
||||
<input type="hidden" name="force" value="1">
|
||||
<button class="btn btn-secondary" type="submit">🔄 Force re-hash</button>
|
||||
</form>
|
||||
<?php endif; ?>
|
||||
</div>
|
||||
</details>
|
||||
<details style="display: inline-block; margin: 0 4px;">
|
||||
<summary class="btn btn-secondary">Méta</summary>
|
||||
<form method="post" style="margin-top: 8px;">
|
||||
|
||||
Reference in New Issue
Block a user