From 4c1cc1175fd3bb030fe90ec734116e63f24f793e Mon Sep 17 00:00:00 2001 From: salmonik Date: Fri, 30 Oct 2026 00:04:50 +0100 Subject: make the download page dynamic: scan builds, show versions and SHA-256 --- web/index.php | 437 ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 437 insertions(+) create mode 100644 web/index.php (limited to 'web/index.php') diff --git a/web/index.php b/web/index.php new file mode 100644 index 0000000..cd77dad --- /dev/null +++ b/web/index.php @@ -0,0 +1,437 @@ +-- + * bin/client/zagan-bet-client---.exe + * bin/server/zagan-bet-server--- + * + * is MAJOR.MINOR.PATCH, is linux|windows|macos and + * is x86_64|i686|aarch64. Example: + * + * bin/client/zagan-bet-client-3.1.1-linux-x86_64 + * bin/server/zagan-bet-server-3.1.1-linux-x86_64 + * + * The hashes are computed per request with hash_file(). That is a few tens of + * milliseconds for a handful of builds; add a cache keyed on mtime if the + * directory ever grows. + */ + +const KINDS = [ + 'client' => [ + 'title' => 'Client', + 'hint' => 'Needs a terminal of at least 80×24 cells.', + ], + 'server' => [ + 'title' => 'Server', + 'hint' => 'Generates its own TLS identity on first run.', + ], +]; + +const PLATFORMS = [ + 'linux' => 'Linux', + 'windows' => 'Windows', + 'macos' => 'macOS', +]; + +const ARCHES = [ + 'x86_64' => '64-bit', + 'i686' => '32-bit', + 'aarch64' => 'ARM64', +]; + +// Newest version first, then 64-bit builds before 32-bit. +const PLATFORM_ORDER = ['linux' => 0, 'windows' => 1, 'macos' => 2]; +const ARCH_ORDER = ['x86_64' => 0, 'aarch64' => 1, 'i686' => 2]; + +function e(?string $value): string +{ + return htmlspecialchars($value ?? '', ENT_QUOTES | ENT_SUBSTITUTE, 'UTF-8'); +} + +function human_size(int $bytes): string +{ + if ($bytes >= 1048576) { + return number_format($bytes / 1048576, 1) . ' MB'; + } + if ($bytes >= 1024) { + return number_format($bytes / 1024) . ' KB'; + } + return $bytes . ' B'; +} + +/** + * @return array{version:string,platform:string,arch:string,file:string,size:int,mtime:int,sha256:string}|null + */ +function read_release(string $kind, string $directory, string $entry): ?array +{ + $pattern = '/^zagan-bet-' . preg_quote($kind, '/') + . '-v?(\d+\.\d+\.\d+)-([a-z]+)-([a-z0-9_]+)(\.exe)?$/i'; + if (preg_match($pattern, $entry, $matches) !== 1) { + return null; + } + + $realDirectory = realpath($directory); + $real = realpath($directory . '/' . $entry); + if ($realDirectory === false || $real === false) { + return null; + } + // Never serve anything that resolves outside the published directory. + if (!str_starts_with($real, $realDirectory . DIRECTORY_SEPARATOR)) { + return null; + } + if (!is_file($real)) { + return null; + } + + $size = filesize($real); + $hash = hash_file('sha256', $real); + if ($size === false || $hash === false) { + return null; + } + + return [ + 'version' => $matches[1], + 'platform' => strtolower($matches[2]), + 'arch' => strtolower($matches[3]), + 'file' => $entry, + 'size' => $size, + 'mtime' => filemtime($real) ?: 0, + 'sha256' => $hash, + ]; +} + +$base = rtrim(str_replace('\\', '/', dirname($_SERVER['SCRIPT_NAME'] ?? '/')), '/'); +if ($base === '.' || $base === '/') { + $base = ''; +} + +$releases = []; +$newest = 0; + +foreach (KINDS as $kind => $meta) { + $found = []; + $directory = __DIR__ . '/bin/' . $kind; + + if (is_dir($directory)) { + $entries = scandir($directory); + if ($entries !== false) { + foreach ($entries as $entry) { + if ($entry === '.' || $entry === '..') { + continue; + } + $release = read_release($kind, $directory, $entry); + if ($release === null) { + continue; + } + $release['href'] = $base . '/bin/' . $kind . '/' . rawurlencode($release['file']); + $found[] = $release; + $newest = max($newest, $release['mtime']); + } + } + } + + usort($found, static function (array $a, array $b): int { + $byVersion = version_compare($b['version'], $a['version']); + if ($byVersion !== 0) { + return $byVersion; + } + $byPlatform = (PLATFORM_ORDER[$a['platform']] ?? 9) <=> (PLATFORM_ORDER[$b['platform']] ?? 9); + if ($byPlatform !== 0) { + return $byPlatform; + } + $byArch = (ARCH_ORDER[$a['arch']] ?? 9) <=> (ARCH_ORDER[$b['arch']] ?? 9); + if ($byArch !== 0) { + return $byArch; + } + return strcmp($a['file'], $b['file']); + }); + + $releases[$kind] = $found; +} +?> + + + + + +Download · zagan.bet + + + + + + +
+ +
+

zagan.bet

+

Terminal betting. Slots, roulette, blackjack and crash.

+ 0): ?> +

Builds published UTC

+ +
+ +
+ $meta): ?> +
+

+

+ + +

Nothing published yet.

+ +
    + +
  • +
    +

    + v +
    +

    + + · + +

    +

    + Download +
  • + +
+ +
+ + + + +
+

Checksums

+

SHA-256 of every published file.

+
+ + + + + + + + + + + + + + + + + + + +
FileVersionSizeSHA-256
+
+
+ +
+ + + +
+ + -- cgit v1.2.3