json([ 'success' => true, 'data' => [ 'abilities' => TokenAbilities::catalog(), 'wildcard' => TokenAbilities::WILDCARD, ], ]); } public function index(Request $request) { $tokens = PersonalAccessToken::query() ->where('tokenable_type', User::class) ->orderByDesc('id') ->get() ->map(fn ($t) => $this->present($t)); return response()->json(['success' => true, 'data' => $tokens]); } public function store(Request $request) { $validated = $request->validate([ 'name' => 'required|string|max:120', 'description' => 'nullable|string|max:500', 'abilities' => 'required|array|min:1', 'abilities.*' => 'string', 'allowed_ips' => 'nullable|array', 'allowed_ips.*' => 'string', 'expires_at' => 'nullable|date', 'tokenable_user_id' => 'nullable|integer', ]); foreach ($validated['abilities'] as $ability) { if (! TokenAbilities::exists($ability)) { return ResponseHelper::returnError("Unknown ability: {$ability}", 422); } } $owner = Auth::user(); $tokenable = $owner; if (! empty($validated['tokenable_user_id'])) { $tokenable = User::query()->find($validated['tokenable_user_id']); if (! $tokenable) { return ResponseHelper::returnError('Target user not found.', 404); } } $result = $tokenable->createToken( name: $validated['name'], abilities: $validated['abilities'], allowedIps: $validated['allowed_ips'] ?? null, expiresAt: !empty($validated['expires_at']) ? new \DateTimeImmutable($validated['expires_at']) : null, description: $validated['description'] ?? null, createdBy: $owner->id, ); return response()->json([ 'success' => true, 'data' => [ 'token' => $this->present($result['token']), 'plain_text_token' => $result['plainTextToken'], ], 'message' => 'Token created. Copy it now — it will not be shown again.', ]); } public function revoke(Request $request, int $id) { $token = PersonalAccessToken::query()->find($id); if (! $token) { return ResponseHelper::returnError('Token not found.', 404); } if ($token->revoked_at !== null) { return ResponseHelper::returnError('Token already revoked.', 422); } $token->forceFill([ 'revoked_at' => now(), 'revoked_by' => Auth::id(), ])->save(); return response()->json(['success' => true, 'data' => $this->present($token)]); } public function destroy(Request $request, int $id) { $token = PersonalAccessToken::query()->find($id); if (! $token) { return ResponseHelper::returnError('Token not found.', 404); } $token->delete(); return response()->json(['success' => true]); } private function present(PersonalAccessToken $t): array { return [ 'id' => $t->id, 'name' => $t->name, 'description' => $t->description, 'tokenable_id' => $t->tokenable_id, 'tokenable_type' => $t->tokenable_type, 'abilities' => $t->abilities ?? [], 'allowed_ips' => $t->allowed_ips ?? [], 'expires_at' => $t->expires_at, 'last_used_at' => $t->last_used_at, 'last_used_ip' => $t->last_used_ip, 'revoked_at' => $t->revoked_at, 'created_by' => $t->created_by, 'created_at' => $t->created_at, 'is_active' => $t->isActive(), ]; } }