isUuid($uuid)) { return null; } $row = DB::selectOne( 'select id, uuid, user_id, name, selector, tenant_id, last_used_at, last_ip, expires_at, revoked_at, created_by, created from user_api_tokens where uuid = ? limit 1', $uuid ); if (!$row || !isset($row['user_api_tokens'])) { return null; } return $row['user_api_tokens']; } public function findByUuidForUser(string $uuid, int $userId): ?array { $uuid = trim($uuid); if ($userId <= 0 || !$this->isUuid($uuid)) { return null; } $row = DB::selectOne( 'select id, uuid, user_id, name, selector, tenant_id, last_used_at, last_ip, expires_at, revoked_at, created_by, created from user_api_tokens where uuid = ? and user_id = ? limit 1', $uuid, (string) $userId ); if (!$row || !isset($row['user_api_tokens'])) { return null; } return $row['user_api_tokens']; } public function updateLastUsed(int $id, string $ip): bool { $result = DB::update( 'update user_api_tokens set last_used_at = NOW(), last_ip = ? where id = ?', $ip, (string) $id ); return $result !== false; } public function revoke(int $id): bool { $result = DB::update( 'update user_api_tokens set revoked_at = NOW() where id = ? and revoked_at is null', (string) $id ); return $result !== false; } public function revokeByUuidForUser(string $uuid, int $userId): bool { $uuid = trim($uuid); if ($userId <= 0 || !$this->isUuid($uuid)) { return false; } $result = DB::update( 'update user_api_tokens set revoked_at = NOW() where uuid = ? and user_id = ? and revoked_at is null', $uuid, (string) $userId ); return $result !== false; } public function revokeAllForUser(int $userId, ?int $tenantId = null): int { if ($tenantId !== null && $tenantId > 0) { $result = DB::update( 'update user_api_tokens set revoked_at = NOW() where user_id = ? and (tenant_id = ? or tenant_id is null) and revoked_at is null', (string) $userId, (string) $tenantId ); } else { $result = DB::update( 'update user_api_tokens set revoked_at = NOW() where user_id = ? and revoked_at is null', (string) $userId ); } return $result !== false ? (int) $result : 0; } public function revokeAllActiveByAdmin(): int { $result = DB::update( 'update user_api_tokens set revoked_at = NOW() where revoked_at is null and (expires_at is null or expires_at > NOW())' ); return $result !== false ? (int) $result : 0; } public function listByUserId(int $userId, int $limit = 25): array { if ($userId <= 0) { return []; } if ($limit < 1) { $limit = 25; } elseif ($limit > 100) { $limit = 100; } $rows = DB::select( 'select id, uuid, user_id, name, selector, tenant_id, last_used_at, last_ip, expires_at, revoked_at, created_by, created from user_api_tokens where user_id = ? order by id desc limit ?', (string) $userId, (string) $limit ); return $this->unwrapList($rows); } public function countActiveForUser(int $userId): int { $count = DB::selectValue( 'select count(*) from user_api_tokens where user_id = ? and revoked_at is null and (expires_at is null or expires_at > NOW())', (string) $userId ); return $count ? (int) $count : 0; } public function countActiveForUserExcludingId(int $userId, int $excludeId): int { if ($userId <= 0) { return 0; } $count = DB::selectValue( 'select count(*) from user_api_tokens where user_id = ? and id != ? and revoked_at is null and (expires_at is null or expires_at > NOW())', (string) $userId, (string) $excludeId ); return $count ? (int) $count : 0; } public function countActive(): int { $count = DB::selectValue( 'select count(*) from user_api_tokens where revoked_at is null and (expires_at is null or expires_at > NOW())' ); return $count ? (int) $count : 0; } }