backup.php 2.5 KB

12345678910111213141516171819202122232425262728293031323334353637383940414243444546474849505152535455565758596061626364656667686970717273747576
  1. <?php
  2. declare(strict_types=1);
  3. // POST api/v1/backup.php
  4. // Receives a backup archive from an authenticated instance.
  5. //
  6. // multipart/form-data fields:
  7. // filename backup-YYYYmmdd-HHMMSS[-N].zip
  8. // sha256 client-side checksum, re-verified here
  9. // meta optional JSON (trigger, file_count, source_bytes, app_version)
  10. // backup the ZIP file itself
  11. require_once __DIR__ . "/../../includes/api.php";
  12. require_once __DIR__ . "/../../includes/backups.php";
  13. manageApiRequireMethod("POST");
  14. $instance = manageApiAuthenticate();
  15. try {
  16. $file = $_FILES["backup"] ?? null;
  17. if (!is_array($file)) {
  18. throw new RuntimeException("Backup-Datei fehlt.");
  19. }
  20. $uploadError = (int) ($file["error"] ?? UPLOAD_ERR_NO_FILE);
  21. if ($uploadError !== UPLOAD_ERR_OK) {
  22. // INI_SIZE / FORM_SIZE are the common real-world failure and deserve a
  23. // message that names the cause instead of a bare error code.
  24. if ($uploadError === UPLOAD_ERR_INI_SIZE || $uploadError === UPLOAD_ERR_FORM_SIZE) {
  25. throw new RuntimeException(
  26. "Backup überschreitet das Upload-Limit des Servers (upload_max_filesize / post_max_size).",
  27. );
  28. }
  29. throw new RuntimeException("Upload fehlgeschlagen (Fehlercode " . $uploadError . ").");
  30. }
  31. $tmpName = (string) ($file["tmp_name"] ?? "");
  32. if ($tmpName === "" || !is_uploaded_file($tmpName)) {
  33. throw new RuntimeException("Upload ist ungültig.");
  34. }
  35. $maxBytes = (int) MANAGE_BACKUP_MAX_UPLOAD_BYTES;
  36. if ($maxBytes > 0 && (int) ($file["size"] ?? 0) > $maxBytes) {
  37. throw new RuntimeException("Backup überschreitet die konfigurierte Maximalgröße.");
  38. }
  39. if (!manageFileIsZip($tmpName)) {
  40. throw new RuntimeException("Die hochgeladene Datei muss ein ZIP-Archiv sein.");
  41. }
  42. $meta = [];
  43. $rawMeta = trim((string) ($_POST["meta"] ?? ""));
  44. if ($rawMeta !== "") {
  45. $decoded = json_decode($rawMeta, true);
  46. if (is_array($decoded)) {
  47. $meta = $decoded;
  48. }
  49. }
  50. $result = manageBackupStoreUpload(
  51. $instance["id"],
  52. $tmpName,
  53. (string) ($_POST["filename"] ?? ""),
  54. (string) ($_POST["sha256"] ?? ""),
  55. $meta,
  56. );
  57. manageApiSendJson(200, array_merge(["success" => true, "instance" => $instance["id"]], $result));
  58. } catch (Throwable $exception) {
  59. manageLogError("Backup upload rejected", [
  60. "instance" => $instance["id"],
  61. "error" => $exception->getMessage(),
  62. ]);
  63. manageApiFail(400, $exception->getMessage());
  64. }