WebhookController.php 16 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478
  1. <?php
  2. namespace App\Module\OpenAPI\Controllers;
  3. use App\Module\OpenAPI\Models\OpenApiWebhook;
  4. use App\Module\OpenAPI\Services\WebhookService;
  5. use App\Module\OpenAPI\Validators\ApiValidator;
  6. use Illuminate\Http\Request;
  7. use Illuminate\Http\JsonResponse;
  8. use Spatie\RouteAttributes\Attributes\Route;
  9. use Spatie\RouteAttributes\Attributes\Prefix;
  10. use Spatie\RouteAttributes\Attributes\Middleware;
  11. /**
  12. * OpenAPI Webhook控制器
  13. *
  14. * 提供Webhook配置和管理的API接口
  15. */
  16. #[Prefix('openapi/webhook')]
  17. #[Middleware(['api', 'openapi.auth'])]
  18. class WebhookController
  19. {
  20. protected WebhookService $webhookService;
  21. public function __construct(WebhookService $webhookService)
  22. {
  23. $this->webhookService = $webhookService;
  24. }
  25. /**
  26. * 获取Webhook列表
  27. *
  28. * @param Request $request
  29. * @return JsonResponse
  30. */
  31. #[Route('GET', '/', name: 'openapi.webhook.index')]
  32. #[Middleware('openapi.scope:WEBHOOK_READ')]
  33. public function index(Request $request): JsonResponse
  34. {
  35. try {
  36. $app = $request->attributes->get('openapi_app');
  37. if (!$app) {
  38. return response()->json([
  39. 'success' => false,
  40. 'message' => '应用信息不存在',
  41. ], 404);
  42. }
  43. $webhooks = OpenApiWebhook::where('app_id', $app->app_id)
  44. ->orderBy('created_at', 'desc')
  45. ->get()
  46. ->map(function ($webhook) {
  47. return [
  48. 'id' => $webhook->id,
  49. 'name' => $webhook->name,
  50. 'url' => $webhook->url,
  51. 'events' => $webhook->events,
  52. 'status' => $webhook->status,
  53. 'status_label' => $webhook->status_label,
  54. 'success_rate' => $webhook->success_rate,
  55. 'total_deliveries' => $webhook->total_deliveries,
  56. 'successful_deliveries' => $webhook->successful_deliveries,
  57. 'failed_deliveries' => $webhook->failed_deliveries,
  58. 'last_success_at' => $webhook->last_success_at,
  59. 'last_failure_at' => $webhook->last_failure_at,
  60. 'created_at' => $webhook->created_at,
  61. ];
  62. });
  63. return response()->json([
  64. 'success' => true,
  65. 'data' => $webhooks,
  66. ]);
  67. } catch (\Exception $e) {
  68. return response()->json([
  69. 'success' => false,
  70. 'message' => '获取Webhook列表失败',
  71. 'error' => $e->getMessage(),
  72. ], 500);
  73. }
  74. }
  75. /**
  76. * 创建Webhook
  77. *
  78. * @param Request $request
  79. * @return JsonResponse
  80. */
  81. #[Route('POST', '/', name: 'openapi.webhook.store')]
  82. #[Middleware('openapi.scope:WEBHOOK_WRITE')]
  83. public function store(Request $request): JsonResponse
  84. {
  85. try {
  86. $app = $request->attributes->get('openapi_app');
  87. if (!$app) {
  88. return response()->json([
  89. 'success' => false,
  90. 'message' => '应用信息不存在',
  91. ], 404);
  92. }
  93. // 验证请求数据
  94. $validator = new ApiValidator();
  95. $data = $request->only(['name', 'url', 'events', 'timeout', 'retry_count']);
  96. if (empty($data['name'])) {
  97. $validator->addError('name', 'Webhook名称不能为空');
  98. }
  99. if (empty($data['url'])) {
  100. $validator->addError('url', 'Webhook URL不能为空');
  101. } elseif (!filter_var($data['url'], FILTER_VALIDATE_URL)) {
  102. $validator->addError('url', 'Webhook URL格式错误');
  103. }
  104. if (empty($data['events']) || !is_array($data['events'])) {
  105. $validator->addError('events', '事件类型不能为空');
  106. }
  107. if (isset($data['timeout']) && (!is_numeric($data['timeout']) || $data['timeout'] < 1 || $data['timeout'] > 300)) {
  108. $validator->addError('timeout', '超时时间必须在1-300秒之间');
  109. }
  110. if (isset($data['retry_count']) && (!is_numeric($data['retry_count']) || $data['retry_count'] < 0 || $data['retry_count'] > 10)) {
  111. $validator->addError('retry_count', '重试次数必须在0-10次之间');
  112. }
  113. if ($validator->hasErrors()) {
  114. return response()->json([
  115. 'success' => false,
  116. 'message' => '数据验证失败',
  117. 'errors' => $validator->getErrors(),
  118. ], 422);
  119. }
  120. // 创建Webhook
  121. $webhook = $this->webhookService->createWebhook($app, $data);
  122. return response()->json([
  123. 'success' => true,
  124. 'message' => 'Webhook创建成功',
  125. 'data' => [
  126. 'id' => $webhook->id,
  127. 'name' => $webhook->name,
  128. 'url' => $webhook->url,
  129. 'events' => $webhook->events,
  130. 'secret' => $webhook->secret,
  131. 'status' => $webhook->status,
  132. 'timeout' => $webhook->timeout,
  133. 'retry_count' => $webhook->retry_count,
  134. 'created_at' => $webhook->created_at,
  135. ],
  136. ]);
  137. } catch (\Exception $e) {
  138. return response()->json([
  139. 'success' => false,
  140. 'message' => '创建Webhook失败',
  141. 'error' => $e->getMessage(),
  142. ], 500);
  143. }
  144. }
  145. /**
  146. * 获取Webhook详情
  147. *
  148. * @param Request $request
  149. * @param int $id
  150. * @return JsonResponse
  151. */
  152. #[Route('GET', '/{id}', name: 'openapi.webhook.show')]
  153. #[Middleware('openapi.scope:WEBHOOK_READ')]
  154. public function show(Request $request, int $id): JsonResponse
  155. {
  156. try {
  157. $app = $request->attributes->get('openapi_app');
  158. if (!$app) {
  159. return response()->json([
  160. 'success' => false,
  161. 'message' => '应用信息不存在',
  162. ], 404);
  163. }
  164. $webhook = OpenApiWebhook::where('app_id', $app->app_id)
  165. ->where('id', $id)
  166. ->first();
  167. if (!$webhook) {
  168. return response()->json([
  169. 'success' => false,
  170. 'message' => 'Webhook不存在',
  171. ], 404);
  172. }
  173. return response()->json([
  174. 'success' => true,
  175. 'data' => [
  176. 'id' => $webhook->id,
  177. 'name' => $webhook->name,
  178. 'url' => $webhook->url,
  179. 'events' => $webhook->events,
  180. 'secret' => $webhook->masked_secret,
  181. 'status' => $webhook->status,
  182. 'status_label' => $webhook->status_label,
  183. 'timeout' => $webhook->timeout,
  184. 'retry_count' => $webhook->retry_count,
  185. 'current_retry_count' => $webhook->current_retry_count,
  186. 'total_deliveries' => $webhook->total_deliveries,
  187. 'successful_deliveries' => $webhook->successful_deliveries,
  188. 'failed_deliveries' => $webhook->failed_deliveries,
  189. 'success_rate' => $webhook->success_rate,
  190. 'last_success_at' => $webhook->last_success_at,
  191. 'last_failure_at' => $webhook->last_failure_at,
  192. 'created_at' => $webhook->created_at,
  193. 'updated_at' => $webhook->updated_at,
  194. ],
  195. ]);
  196. } catch (\Exception $e) {
  197. return response()->json([
  198. 'success' => false,
  199. 'message' => '获取Webhook详情失败',
  200. 'error' => $e->getMessage(),
  201. ], 500);
  202. }
  203. }
  204. /**
  205. * 更新Webhook
  206. *
  207. * @param Request $request
  208. * @param int $id
  209. * @return JsonResponse
  210. */
  211. #[Route('PUT', '/{id}', name: 'openapi.webhook.update')]
  212. #[Middleware('openapi.scope:WEBHOOK_WRITE')]
  213. public function update(Request $request, int $id): JsonResponse
  214. {
  215. try {
  216. $app = $request->attributes->get('openapi_app');
  217. if (!$app) {
  218. return response()->json([
  219. 'success' => false,
  220. 'message' => '应用信息不存在',
  221. ], 404);
  222. }
  223. $webhook = OpenApiWebhook::where('app_id', $app->app_id)
  224. ->where('id', $id)
  225. ->first();
  226. if (!$webhook) {
  227. return response()->json([
  228. 'success' => false,
  229. 'message' => 'Webhook不存在',
  230. ], 404);
  231. }
  232. // 验证请求数据
  233. $validator = new ApiValidator();
  234. $data = $request->only(['name', 'url', 'events', 'status', 'timeout', 'retry_count']);
  235. if (isset($data['name']) && empty($data['name'])) {
  236. $validator->addError('name', 'Webhook名称不能为空');
  237. }
  238. if (isset($data['url']) && (!empty($data['url']) && !filter_var($data['url'], FILTER_VALIDATE_URL))) {
  239. $validator->addError('url', 'Webhook URL格式错误');
  240. }
  241. if (isset($data['events']) && (!is_array($data['events']) || empty($data['events']))) {
  242. $validator->addError('events', '事件类型不能为空');
  243. }
  244. if (isset($data['status']) && !in_array($data['status'], ['ACTIVE', 'INACTIVE'])) {
  245. $validator->addError('status', '状态值无效');
  246. }
  247. if (isset($data['timeout']) && (!is_numeric($data['timeout']) || $data['timeout'] < 1 || $data['timeout'] > 300)) {
  248. $validator->addError('timeout', '超时时间必须在1-300秒之间');
  249. }
  250. if (isset($data['retry_count']) && (!is_numeric($data['retry_count']) || $data['retry_count'] < 0 || $data['retry_count'] > 10)) {
  251. $validator->addError('retry_count', '重试次数必须在0-10次之间');
  252. }
  253. if ($validator->hasErrors()) {
  254. return response()->json([
  255. 'success' => false,
  256. 'message' => '数据验证失败',
  257. 'errors' => $validator->getErrors(),
  258. ], 422);
  259. }
  260. // 更新Webhook
  261. $webhook->update(array_filter($data));
  262. return response()->json([
  263. 'success' => true,
  264. 'message' => 'Webhook更新成功',
  265. 'data' => [
  266. 'id' => $webhook->id,
  267. 'name' => $webhook->name,
  268. 'url' => $webhook->url,
  269. 'events' => $webhook->events,
  270. 'status' => $webhook->status,
  271. 'timeout' => $webhook->timeout,
  272. 'retry_count' => $webhook->retry_count,
  273. 'updated_at' => $webhook->updated_at,
  274. ],
  275. ]);
  276. } catch (\Exception $e) {
  277. return response()->json([
  278. 'success' => false,
  279. 'message' => '更新Webhook失败',
  280. 'error' => $e->getMessage(),
  281. ], 500);
  282. }
  283. }
  284. /**
  285. * 删除Webhook
  286. *
  287. * @param Request $request
  288. * @param int $id
  289. * @return JsonResponse
  290. */
  291. #[Route('DELETE', '/{id}', name: 'openapi.webhook.destroy')]
  292. #[Middleware('openapi.scope:WEBHOOK_WRITE')]
  293. public function destroy(Request $request, int $id): JsonResponse
  294. {
  295. try {
  296. $app = $request->attributes->get('openapi_app');
  297. if (!$app) {
  298. return response()->json([
  299. 'success' => false,
  300. 'message' => '应用信息不存在',
  301. ], 404);
  302. }
  303. $webhook = OpenApiWebhook::where('app_id', $app->app_id)
  304. ->where('id', $id)
  305. ->first();
  306. if (!$webhook) {
  307. return response()->json([
  308. 'success' => false,
  309. 'message' => 'Webhook不存在',
  310. ], 404);
  311. }
  312. $webhook->delete();
  313. return response()->json([
  314. 'success' => true,
  315. 'message' => 'Webhook删除成功',
  316. ]);
  317. } catch (\Exception $e) {
  318. return response()->json([
  319. 'success' => false,
  320. 'message' => '删除Webhook失败',
  321. 'error' => $e->getMessage(),
  322. ], 500);
  323. }
  324. }
  325. /**
  326. * 测试Webhook
  327. *
  328. * @param Request $request
  329. * @param int $id
  330. * @return JsonResponse
  331. */
  332. #[Route('POST', '/{id}/test', name: 'openapi.webhook.test')]
  333. #[Middleware('openapi.scope:WEBHOOK_WRITE')]
  334. public function test(Request $request, int $id): JsonResponse
  335. {
  336. try {
  337. $app = $request->attributes->get('openapi_app');
  338. if (!$app) {
  339. return response()->json([
  340. 'success' => false,
  341. 'message' => '应用信息不存在',
  342. ], 404);
  343. }
  344. $webhook = OpenApiWebhook::where('app_id', $app->app_id)
  345. ->where('id', $id)
  346. ->first();
  347. if (!$webhook) {
  348. return response()->json([
  349. 'success' => false,
  350. 'message' => 'Webhook不存在',
  351. ], 404);
  352. }
  353. // 测试Webhook
  354. $result = $this->webhookService->testWebhook($webhook);
  355. return response()->json([
  356. 'success' => true,
  357. 'message' => 'Webhook测试完成',
  358. 'data' => $result,
  359. ]);
  360. } catch (\Exception $e) {
  361. return response()->json([
  362. 'success' => false,
  363. 'message' => 'Webhook测试失败',
  364. 'error' => $e->getMessage(),
  365. ], 500);
  366. }
  367. }
  368. /**
  369. * 重新生成Webhook密钥
  370. *
  371. * @param Request $request
  372. * @param int $id
  373. * @return JsonResponse
  374. */
  375. #[Route('POST', '/{id}/regenerate-secret', name: 'openapi.webhook.regenerate_secret')]
  376. #[Middleware('openapi.scope:WEBHOOK_WRITE')]
  377. public function regenerateSecret(Request $request, int $id): JsonResponse
  378. {
  379. try {
  380. $app = $request->attributes->get('openapi_app');
  381. if (!$app) {
  382. return response()->json([
  383. 'success' => false,
  384. 'message' => '应用信息不存在',
  385. ], 404);
  386. }
  387. $webhook = OpenApiWebhook::where('app_id', $app->app_id)
  388. ->where('id', $id)
  389. ->first();
  390. if (!$webhook) {
  391. return response()->json([
  392. 'success' => false,
  393. 'message' => 'Webhook不存在',
  394. ], 404);
  395. }
  396. // 重新生成密钥
  397. $newSecret = $webhook->regenerateSecret();
  398. return response()->json([
  399. 'success' => true,
  400. 'message' => 'Webhook密钥重新生成成功',
  401. 'data' => [
  402. 'webhook_id' => $webhook->id,
  403. 'secret' => $newSecret,
  404. 'regenerated_at' => now(),
  405. ],
  406. ]);
  407. } catch (\Exception $e) {
  408. return response()->json([
  409. 'success' => false,
  410. 'message' => '重新生成密钥失败',
  411. 'error' => $e->getMessage(),
  412. ], 500);
  413. }
  414. }
  415. }