middleware(function ($request, $next) { $provided = $this->extractToken($request); $expected = config('services.moderation_api.token'); if (!$expected || !hash_equals((string) $expected, (string) $provided)) { return response()->json(['message' => 'Unauthorized'], 401); } return $next($request); }); } private function extractToken(Request $request): ?string { $auth = $request->header('Authorization'); if ($auth && str_starts_with($auth, 'Bearer ')) { return substr($auth, 7); } return $request->query('api_token'); } // GET /api/users/{id}/restrictions?active_only=1 public function listForUser(Request $request, int $userId) { try { $query = []; if ($request->boolean('active_only')) { $query['active_only'] = 1; } $res = $this->client->get($request, "/users/{$userId}/restrictions", $query, retry: true); if ($res->successful()) { return response()->json($res->json() ?: []); } if ($res->clientError()) return $this->mapClientError($res); if ($res->serverError()) return $this->mapServiceUnavailable($res); return $this->mapBadGateway(); } catch (\Throwable $e) { return $this->mapBadGateway('API server not reachable'); } } // GET /api/users/{id}/restrictions/check public function checkForUser(Request $request, int $userId) { try { $res = $this->client->get($request, "/users/{$userId}/restrictions/check", [], retry: true); if ($res->successful()) { return response()->json($res->json() ?: []); } if ($res->clientError()) return $this->mapClientError($res); if ($res->serverError()) return $this->mapServiceUnavailable($res); return $this->mapBadGateway(); } catch (\Throwable $e) { return $this->mapBadGateway('API server not reachable'); } } // POST /api/users/{id}/restrictions public function createForUser(Request $request, int $userId) { $data = $this->validatePayload($request, partial: false); try { $res = $this->client->post($request, "/users/{$userId}/restrictions", $data); if ($res->successful()) { return response()->json($res->json() ?: [], 201); } if ($res->clientError()) return $this->mapClientError($res); if ($res->serverError()) return $this->mapServiceUnavailable($res); return $this->mapBadGateway(); } catch (\Throwable $e) { return $this->mapBadGateway('API server not reachable'); } } // POST /api/users/{id}/restrictions/upsert public function upsertForUser(Request $request, int $userId) { $data = $this->validatePayload($request, partial: true); if (empty($data['type'])) { return response()->json(['message' => 'type is required for upsert'], 422); } try { $res = $this->client->post($request, "/users/{$userId}/restrictions/upsert", $data); if ($res->successful()) { // 200 or 201 upstream; forward as given $status = $res->status() === 201 ? 201 : 200; return response()->json($res->json() ?: [], $status); } if ($res->clientError()) return $this->mapClientError($res); if ($res->serverError()) return $this->mapServiceUnavailable($res); return $this->mapBadGateway(); } catch (\Throwable $e) { return $this->mapBadGateway('API server not reachable'); } } // PATCH /api/restrictions/{id} public function update(Request $request, int $id) { $data = $this->validatePayload($request, partial: true); try { $res = $this->client->patch($request, "/restrictions/{$id}", $data); if ($res->successful()) { return response()->json($res->json() ?: []); } if ($res->clientError()) return $this->mapClientError($res); if ($res->serverError()) return $this->mapServiceUnavailable($res); return $this->mapBadGateway(); } catch (\Throwable $e) { return $this->mapBadGateway('API server not reachable'); } } // DELETE /api/restrictions/{id} public function destroy(Request $request, int $id) { try { $res = $this->client->delete($request, "/restrictions/{$id}"); if ($res->successful()) { return response()->json($res->json() ?: ['message' => 'Deactivated']); } if ($res->clientError()) return $this->mapClientError($res); if ($res->serverError()) return $this->mapServiceUnavailable($res); return $this->mapBadGateway(); } catch (\Throwable $e) { return $this->mapBadGateway('API server not reachable'); } } private function validatePayload(Request $request, bool $partial = false): array { $required = $partial ? 'sometimes' : 'required'; $rules = [ 'type' => [$required, Rule::in($this->allowedTypes)], 'reason' => ['sometimes', 'nullable', 'string', 'max:255'], 'notes' => ['sometimes', 'nullable', 'string'], 'imposed_by' => ['sometimes', 'nullable', 'integer'], 'starts_at' => ['sometimes', 'nullable', 'date'], 'ends_at' => ['sometimes', 'nullable', 'date', 'after_or_equal:starts_at'], 'active' => ['sometimes', 'boolean'], 'source' => ['sometimes', 'nullable', 'string', 'max:64'], 'metadata' => ['sometimes', 'nullable', 'array'], ]; $validated = $request->validate($rules); return $validated; } }