webhook_receiver.php 2.7 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111
  1. <?php
  2. /**
  3. * Webhook receiver: accepts POST with JSON body and appends to data/einsaetze.json.
  4. * Payload is stored under webhook_events[] with a received timestamp; einsaetze is unchanged.
  5. */
  6. declare(strict_types=1);
  7. header('Content-Type: application/json; charset=utf-8');
  8. const DEFAULT_DATA_PATH = 'data/einsaetze.json';
  9. const MAX_BODY_SIZE = 1024 * 1024 * 10; // 10 MB
  10. $dataPath = getenv('EINSATZ_JSON_PATH') ?: DEFAULT_DATA_PATH;
  11. if ($_SERVER['REQUEST_METHOD'] !== 'POST') {
  12. http_response_code(405);
  13. echo json_encode(['error' => 'Method Not Allowed']);
  14. exit;
  15. }
  16. $raw = file_get_contents('php://input');
  17. if ($raw === false || $raw === '') {
  18. http_response_code(400);
  19. echo json_encode(['error' => 'Invalid or empty JSON']);
  20. exit;
  21. }
  22. if (strlen($raw) > MAX_BODY_SIZE) {
  23. http_response_code(400);
  24. echo json_encode(['error' => 'Request body too large']);
  25. exit;
  26. }
  27. $payload = json_decode($raw, true);
  28. if (json_last_error() !== JSON_ERROR_NONE) {
  29. http_response_code(400);
  30. echo json_encode(['error' => 'Invalid or empty JSON']);
  31. exit;
  32. }
  33. $received = (new DateTimeImmutable('now', new DateTimeZone('UTC')))->format('Y-m-d\TH:i:s.uP');
  34. $dir = dirname($dataPath);
  35. if (!is_dir($dir)) {
  36. if (!@mkdir($dir, 0755, true)) {
  37. http_response_code(500);
  38. echo json_encode(['error' => 'Could not create data directory']);
  39. exit;
  40. }
  41. }
  42. $fp = fopen($dataPath, 'c+');
  43. if ($fp === false) {
  44. http_response_code(500);
  45. echo json_encode(['error' => 'Could not open data file']);
  46. exit;
  47. }
  48. if (!flock($fp, LOCK_EX)) {
  49. fclose($fp);
  50. http_response_code(500);
  51. echo json_encode(['error' => 'Could not lock data file']);
  52. exit;
  53. }
  54. $content = stream_get_contents($fp);
  55. $data = [];
  56. if ($content !== false && $content !== '') {
  57. $data = json_decode($content, true);
  58. if (!is_array($data)) {
  59. $data = [];
  60. }
  61. }
  62. if (!isset($data['einsaetze'])) {
  63. $data['einsaetze'] = [];
  64. }
  65. if (!isset($data['webhook_events'])) {
  66. $data['webhook_events'] = [];
  67. }
  68. $data['webhook_events'][] = [
  69. 'received' => $received,
  70. 'data' => $payload,
  71. ];
  72. $data['updated'] = $received;
  73. $json = json_encode($data, JSON_PRETTY_PRINT | JSON_UNESCAPED_UNICODE);
  74. if ($json === false) {
  75. flock($fp, LOCK_UN);
  76. fclose($fp);
  77. http_response_code(500);
  78. echo json_encode(['error' => 'Could not encode JSON']);
  79. exit;
  80. }
  81. ftruncate($fp, 0);
  82. rewind($fp);
  83. $written = fwrite($fp, $json);
  84. flock($fp, LOCK_UN);
  85. fclose($fp);
  86. if ($written === false || $written !== strlen($json)) {
  87. http_response_code(500);
  88. echo json_encode(['error' => 'Could not write data file']);
  89. exit;
  90. }
  91. http_response_code(200);
  92. echo json_encode(['ok' => true, 'received' => $received]);