$code, 'message' => $message, 'time' => date('Y-m-d H:i:s'), ), $extra), JSON_UNESCAPED_UNICODE); exit; } if ($_SERVER['REQUEST_METHOD'] !== 'POST') { json_exit(405, 'Method Not Allowed', 405); } $secret = ''; if (is_readable($secretFile)) { $secret = trim((string)file_get_contents($secretFile)); } if ($secret === '') { webhook_log($logFile, '未配置 .webhook_secret'); json_exit(500, 'Webhook secret not configured', 500); } $payload = file_get_contents('php://input'); if ($payload === false) { $payload = ''; } $remoteIp = isset($_SERVER['REMOTE_ADDR']) ? $_SERVER['REMOTE_ADDR'] : 'unknown'; $event = ''; if (!empty($_SERVER['HTTP_X_GITEA_EVENT'])) { $event = $_SERVER['HTTP_X_GITEA_EVENT']; } elseif (!empty($_SERVER['HTTP_X_GITHUB_EVENT'])) { $event = $_SERVER['HTTP_X_GITHUB_EVENT']; } $signature = ''; if (!empty($_SERVER['HTTP_X_GITEA_SIGNATURE'])) { $signature = $_SERVER['HTTP_X_GITEA_SIGNATURE']; } elseif (!empty($_SERVER['HTTP_X_GOGS_SIGNATURE'])) { $signature = $_SERVER['HTTP_X_GOGS_SIGNATURE']; } elseif (!empty($_SERVER['HTTP_X_HUB_SIGNATURE_256'])) { $signature = preg_replace('/^sha256=/', '', $_SERVER['HTTP_X_HUB_SIGNATURE_256']); } $signatureMatched = false; if ($signature !== '') { $expected = hash_hmac('sha256', $payload, $secret); if (hash_equals($expected, $signature)) { $signatureMatched = true; } } $token = ''; if (!empty($_GET['token'])) { $token = $_GET['token']; } elseif (!empty($_SERVER['HTTP_X_GITEA_TOKEN'])) { $token = $_SERVER['HTTP_X_GITEA_TOKEN']; } elseif (!empty($_SERVER['HTTP_X_TOKEN'])) { $token = $_SERVER['HTTP_X_TOKEN']; } elseif (!empty($_SERVER['HTTP_AUTHORIZATION'])) { if (preg_match('/Bearer\s+(.+)/i', $_SERVER['HTTP_AUTHORIZATION'], $m)) { $token = $m[1]; } } $tokenMatched = ($token !== '' && hash_equals($secret, $token)); if (!$signatureMatched && !$tokenMatched) { webhook_log($logFile, '密钥验证失败 IP=' . $remoteIp); json_exit(403, 'Forbidden', 403); } $data = json_decode($payload, true); if (!is_array($data)) { $data = array(); } $ref = isset($data['ref']) ? $data['ref'] : ''; $branch = $ref !== '' ? preg_replace('#^refs/heads/#', '', $ref) : 'main'; if ($ref !== '' && strpos($ref, 'refs/heads/') !== 0) { webhook_log($logFile, '忽略非分支推送: ' . $ref); json_exit(0, 'Ref ignored', 200, array('ref' => $ref)); } if ($ref !== '' && !in_array($branch, $allowBranches, true)) { webhook_log($logFile, '分支不在白名单: ' . $branch); json_exit(0, 'Branch ignored', 200, array('branch' => $branch)); } $pusher = 'unknown'; if (!empty($data['pusher']['login'])) { $pusher = $data['pusher']['login']; } elseif (!empty($data['pusher']['username'])) { $pusher = $data['pusher']['username']; } elseif (!empty($data['pusher']['name'])) { $pusher = $data['pusher']['name']; } elseif (!empty($data['sender']['login'])) { $pusher = $data['sender']['login']; } $after = isset($data['after']) ? $data['after'] : ''; $trigger = json_encode(array( 'time' => date('Y-m-d H:i:s'), 'event' => $event !== '' ? $event : 'push', 'branch' => $branch, 'pusher' => $pusher, 'after' => $after, 'ip' => $remoteIp, ), JSON_UNESCAPED_UNICODE); if (@file_put_contents($triggerFile, $trigger) === false) { webhook_log($logFile, '写入触发文件失败: ' . $triggerFile); json_exit(500, 'Failed to write trigger', 500); } @chmod($triggerFile, 0664); $started = false; if (is_file($deployScript) && function_exists('exec') && !in_array('exec', array_map('trim', explode(',', (string)ini_get('disable_functions'))), true)) { $cmd = '/bin/bash ' . escapeshellarg($deployScript) . ' --if-triggered > /dev/null 2>&1 &'; @exec($cmd); $started = true; } webhook_log($logFile, '部署已触发 branch=' . $branch . ' pusher=' . $pusher . ' after=' . substr($after, 0, 12) . ' exec=' . ($started ? '1' : '0')); json_exit(0, $started ? '部署已触发' : '部署任务已写入,等待 cron 执行', 200, array( 'branch' => $branch, 'pusher' => $pusher, 'started' => $started, ));