releases.php 6.6 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171
  1. <?php
  2. declare(strict_types=1);
  3. require_once __DIR__ . "/../includes/auth.php";
  4. require_once __DIR__ . "/../includes/layout.php";
  5. require_once __DIR__ . "/../includes/releases.php";
  6. manageRequireLogin();
  7. $messages = [];
  8. $errors = [];
  9. if (($_SERVER["REQUEST_METHOD"] ?? "") === "POST") {
  10. try {
  11. manageRequireCsrf();
  12. $action = (string) ($_POST["action"] ?? "");
  13. $version = trim((string) ($_POST["version"] ?? ""));
  14. if ($action === "upload") {
  15. $release = manageReleasePublishUpload($version, $_FILES["package"] ?? []);
  16. $messages[] = "Release " . $version . " wurde hochgeladen und als aktuell veröffentlicht.";
  17. $messages[] = "SHA-256: " . $release["sha256"];
  18. } elseif ($action === "set_latest") {
  19. manageReleaseSetLatest($version);
  20. $messages[] = "Aktuelles Release ist jetzt " . $version . ".";
  21. } elseif ($action === "delete") {
  22. manageReleaseDelete($version);
  23. $messages[] = "Release " . $version . " wurde gelöscht.";
  24. }
  25. } catch (Throwable $exception) {
  26. $errors[] = $exception->getMessage();
  27. }
  28. }
  29. try {
  30. $manifest = manageReleaseReadManifest();
  31. $releases = manageReleaseListDescending();
  32. } catch (Throwable $exception) {
  33. $manifest = ["latest" => "", "releases" => []];
  34. $releases = [];
  35. $errors[] = $exception->getMessage();
  36. }
  37. // Server-side upload ceiling, so the form can state the real limit instead of
  38. // failing with an empty $_FILES entry.
  39. function manageUploadLimitBytes(): int
  40. {
  41. $toBytes = static function (string $value): int {
  42. $value = trim($value);
  43. if ($value === "") {
  44. return 0;
  45. }
  46. $unit = strtolower(substr($value, -1));
  47. $number = (int) $value;
  48. return match ($unit) {
  49. "g" => $number * 1073741824,
  50. "m" => $number * 1048576,
  51. "k" => $number * 1024,
  52. default => $number,
  53. };
  54. };
  55. $limits = array_filter([
  56. $toBytes((string) ini_get("upload_max_filesize")),
  57. $toBytes((string) ini_get("post_max_size")),
  58. ]);
  59. return $limits === [] ? 0 : (int) min($limits);
  60. }
  61. $uploadLimit = manageUploadLimitBytes();
  62. manageRenderHeader("Releases", $messages, $errors);
  63. ?>
  64. <section class="cards">
  65. <div class="card">
  66. <p class="card-label">Aktuelles Release</p>
  67. <p class="card-value"><?php echo manageEscape($manifest["latest"] !== "" ? $manifest["latest"] : "keines"); ?></p>
  68. </div>
  69. <div class="card">
  70. <p class="card-label">Paket-Präfix</p>
  71. <p class="card-value"><?php echo manageEscape(MANAGE_PACKAGE_PREFIX); ?></p>
  72. </div>
  73. <div class="card">
  74. <p class="card-label">Upload-Limit</p>
  75. <p class="card-value"><?php echo $uploadLimit > 0 ? manageEscape(manageFormatBytes($uploadLimit)) : "unbekannt"; ?></p>
  76. <p class="card-hint">PHP: upload_max_filesize / post_max_size</p>
  77. </div>
  78. </section>
  79. <h2>Release hochladen</h2>
  80. <form method="POST" enctype="multipart/form-data" class="panel">
  81. <?php echo manageCsrfField(); ?>
  82. <input type="hidden" name="action" value="upload">
  83. <div class="form-row">
  84. <div class="form-group">
  85. <label for="version">Version</label>
  86. <input type="text" id="version" name="version" required placeholder="v1.3.15"
  87. pattern="v[0-9]+\.[0-9]+\.[0-9]+">
  88. </div>
  89. <div class="form-group">
  90. <label for="package">ZIP-Paket</label>
  91. <input type="file" id="package" name="package" accept=".zip,application/zip" required>
  92. </div>
  93. </div>
  94. <p class="hint">
  95. Prüfsumme und Größe werden serverseitig berechnet. Ein Upload setzt das Release automatisch als aktuell.
  96. </p>
  97. <button type="submit" class="btn">Hochladen und veröffentlichen</button>
  98. </form>
  99. <h2>Veröffentlichte Releases</h2>
  100. <?php if ($releases === []): ?>
  101. <p class="empty">Noch keine Releases vorhanden. Paket mit dem Build-Skript aus dem Client-Paket (<code>scripts/create-release-zip.sh</code>) bauen und hier hochladen.</p>
  102. <?php else: ?>
  103. <div class="table-scroll">
  104. <table class="data-table">
  105. <thead>
  106. <tr>
  107. <th>Version</th>
  108. <th>Paket</th>
  109. <th>SHA-256</th>
  110. <th>Größe</th>
  111. <th>Veröffentlicht</th>
  112. <th>Aktionen</th>
  113. </tr>
  114. </thead>
  115. <tbody>
  116. <?php foreach ($releases as $version => $release): ?>
  117. <?php $isLatest = $manifest["latest"] === $version; ?>
  118. <tr>
  119. <td>
  120. <strong><?php echo manageEscape($version); ?></strong>
  121. <?php if ($isLatest): ?>
  122. <br><span class="badge badge-ok">aktuell</span>
  123. <?php endif; ?>
  124. </td>
  125. <td><code><?php echo manageEscape($release["package"] ?? ""); ?></code></td>
  126. <td class="mono-cell"><?php echo manageEscape($release["sha256"] ?? ""); ?></td>
  127. <td><?php echo manageEscape(manageFormatBytes((int) ($release["size"] ?? 0))); ?></td>
  128. <td><?php echo manageEscape($release["published_at"] ?? ""); ?></td>
  129. <td>
  130. <div class="button-row">
  131. <?php if (!$isLatest): ?>
  132. <form method="POST">
  133. <?php echo manageCsrfField(); ?>
  134. <input type="hidden" name="action" value="set_latest">
  135. <input type="hidden" name="version" value="<?php echo manageEscape($version); ?>">
  136. <button type="submit" class="btn btn-secondary">Als aktuell setzen</button>
  137. </form>
  138. <?php endif; ?>
  139. <form method="POST" onsubmit="return confirm('Dieses Release wirklich löschen?');">
  140. <?php echo manageCsrfField(); ?>
  141. <input type="hidden" name="action" value="delete">
  142. <input type="hidden" name="version" value="<?php echo manageEscape($version); ?>">
  143. <button type="submit" class="btn btn-danger">Löschen</button>
  144. </form>
  145. </div>
  146. </td>
  147. </tr>
  148. <?php endforeach; ?>
  149. </tbody>
  150. </table>
  151. </div>
  152. <?php endif; ?>
  153. <?php manageRenderFooter(); ?>