@knn_labs/conduit-admin-client
Version:
TypeScript client library for Conduit Admin API
1 lines • 118 kB
Source Map (JSON)
{"version":3,"sources":["../src/client/BaseApiClient.ts","../src/utils/errors.ts","../src/constants.ts","../src/services/VirtualKeyService.ts","../src/services/ProviderService.ts","../src/services/ModelMappingService.ts","../src/services/SettingsService.ts","../src/services/IpFilterService.ts","../src/services/ModelCostService.ts","../src/services/AnalyticsService.ts","../src/services/SystemService.ts","../src/client/ConduitAdminClient.ts","../src/index.ts"],"sourcesContent":["import axios, { AxiosInstance, AxiosRequestConfig, AxiosResponse } from 'axios';\nimport { ApiClientConfig, RequestConfig, RetryConfig, Logger, CacheProvider, AxiosError } from './types';\nimport { handleApiError } from '../utils/errors';\n\nexport abstract class BaseApiClient {\n protected readonly axios: AxiosInstance;\n protected readonly logger?: Logger;\n protected readonly cache?: CacheProvider;\n protected readonly retryConfig: RetryConfig;\n\n constructor(config: ApiClientConfig) {\n this.logger = config.logger;\n this.cache = config.cache;\n \n this.retryConfig = this.normalizeRetryConfig(config.retries);\n\n this.axios = axios.create({\n baseURL: config.baseUrl,\n timeout: config.timeout || 30000,\n headers: {\n 'Content-Type': 'application/json',\n 'X-API-Key': config.masterKey,\n ...config.defaultHeaders,\n },\n });\n\n this.setupInterceptors();\n }\n\n private normalizeRetryConfig(retries?: number | RetryConfig): RetryConfig {\n if (typeof retries === 'number') {\n return {\n maxRetries: retries,\n retryDelay: 1000,\n retryCondition: (error: AxiosError): boolean => {\n return (\n error.code === 'ECONNABORTED' ||\n error.code === 'ETIMEDOUT' ||\n (error.response?.status !== undefined && error.response.status >= 500)\n );\n },\n };\n }\n return retries || { maxRetries: 3, retryDelay: 1000 };\n }\n\n private setupInterceptors(): void {\n this.axios.interceptors.request.use(\n (config) => {\n this.logger?.debug(`[${config.method?.toUpperCase()}] ${config.url}`, {\n params: config.params,\n data: config.data,\n });\n return config;\n },\n (error) => {\n this.logger?.error('Request interceptor error:', error);\n return Promise.reject(error);\n }\n );\n\n this.axios.interceptors.response.use(\n (response) => {\n this.logger?.debug(`[${response.status}] ${response.config.url}`, {\n data: response.data,\n });\n return response;\n },\n async (error: AxiosError) => {\n const config = error.config;\n if (!config || typeof config._retry !== 'number') {\n if (config) {\n config._retry = 0;\n }\n }\n\n if (\n config &&\n typeof config._retry === 'number' &&\n config._retry < this.retryConfig.maxRetries &&\n this.retryConfig.retryCondition?.(error)\n ) {\n config._retry = (config._retry || 0) + 1;\n const delay = this.retryConfig.retryDelay * Math.pow(2, (config._retry || 1) - 1);\n \n this.logger?.warn(\n `Retrying request (attempt ${config._retry || 1}/${this.retryConfig.maxRetries})`,\n { url: config?.url, delay }\n );\n\n await new Promise((resolve) => setTimeout(resolve, delay));\n return this.axios(config);\n }\n\n this.logger?.error(`Request failed: ${error.message}`, {\n url: config?.url,\n status: error.response?.status,\n data: error.response?.data,\n });\n\n return Promise.reject(error);\n }\n );\n }\n\n protected async request<T>(config: RequestConfig): Promise<T> {\n try {\n const axiosConfig: AxiosRequestConfig = {\n method: config.method,\n url: config.url,\n data: config.data,\n params: config.params,\n headers: config.headers,\n timeout: config.timeout,\n };\n\n const response: AxiosResponse<T> = await this.axios.request(axiosConfig);\n return response.data;\n } catch (error) {\n handleApiError(error, config.url, config.method);\n }\n }\n\n protected async get<T>(url: string, params?: Record<string, unknown>, options?: RequestConfig): Promise<T> {\n return this.request<T>({\n method: 'GET',\n url,\n params,\n ...options,\n });\n }\n\n protected async post<T>(url: string, data?: unknown, options?: RequestConfig): Promise<T> {\n return this.request<T>({\n method: 'POST',\n url,\n data,\n ...options,\n });\n }\n\n protected async put<T>(url: string, data?: unknown, options?: RequestConfig): Promise<T> {\n return this.request<T>({\n method: 'PUT',\n url,\n data,\n ...options,\n });\n }\n\n protected async delete<T>(url: string, options?: RequestConfig): Promise<T> {\n return this.request<T>({\n method: 'DELETE',\n url,\n ...options,\n });\n }\n\n protected async patch<T>(url: string, data?: unknown, options?: RequestConfig): Promise<T> {\n return this.request<T>({\n method: 'PATCH',\n url,\n data,\n ...options,\n });\n }\n\n protected getCacheKey(prefix: string, ...parts: unknown[]): string {\n return [prefix, ...parts.map(p => JSON.stringify(p))].join(':');\n }\n\n protected async withCache<T>(\n key: string,\n fetcher: () => Promise<T>,\n ttl?: number\n ): Promise<T> {\n if (!this.cache) {\n return fetcher();\n }\n\n const cached = await this.cache.get<T>(key);\n if (cached) {\n this.logger?.debug(`Cache hit: ${key}`);\n return cached;\n }\n\n this.logger?.debug(`Cache miss: ${key}`);\n const result = await fetcher();\n await this.cache.set(key, result, ttl);\n return result;\n }\n}","export class ConduitError extends Error {\n public statusCode?: number;\n public details?: unknown;\n public endpoint?: string;\n public method?: string;\n\n constructor(\n message: string,\n statusCode?: number,\n details?: unknown,\n endpoint?: string,\n method?: string\n ) {\n super(message);\n this.name = this.constructor.name;\n this.statusCode = statusCode;\n this.details = details;\n this.endpoint = endpoint;\n this.method = method;\n Object.setPrototypeOf(this, new.target.prototype);\n }\n\n toJSON() {\n return {\n name: this.name,\n message: this.message,\n statusCode: this.statusCode,\n details: this.details,\n endpoint: this.endpoint,\n method: this.method,\n };\n }\n}\n\nexport class AuthenticationError extends ConduitError {\n constructor(message = 'Authentication failed', details?: unknown, endpoint?: string, method?: string) {\n super(message, 401, details, endpoint, method);\n }\n}\n\nexport class AuthorizationError extends ConduitError {\n constructor(message = 'Access forbidden', details?: unknown, endpoint?: string, method?: string) {\n super(message, 403, details, endpoint, method);\n }\n}\n\nexport class ValidationError extends ConduitError {\n constructor(message = 'Validation failed', details?: unknown, endpoint?: string, method?: string) {\n super(message, 400, details, endpoint, method);\n }\n}\n\nexport class NotFoundError extends ConduitError {\n constructor(message = 'Resource not found', details?: unknown, endpoint?: string, method?: string) {\n super(message, 404, details, endpoint, method);\n }\n}\n\nexport class ConflictError extends ConduitError {\n constructor(message = 'Resource conflict', details?: unknown, endpoint?: string, method?: string) {\n super(message, 409, details, endpoint, method);\n }\n}\n\nexport class RateLimitError extends ConduitError {\n public retryAfter?: number;\n\n constructor(message = 'Rate limit exceeded', retryAfter?: number, details?: unknown, endpoint?: string, method?: string) {\n super(message, 429, details, endpoint, method);\n this.retryAfter = retryAfter;\n }\n}\n\nexport class ServerError extends ConduitError {\n constructor(message = 'Internal server error', details?: unknown, endpoint?: string, method?: string) {\n super(message, 500, details, endpoint, method);\n }\n}\n\nexport class NetworkError extends ConduitError {\n constructor(message = 'Network error', details?: unknown) {\n super(message, undefined, details);\n }\n}\n\nexport class TimeoutError extends ConduitError {\n constructor(message = 'Request timeout', details?: unknown) {\n super(message, 408, details);\n }\n}\n\nexport class NotImplementedError extends ConduitError {\n constructor(message: string, details?: unknown) {\n super(message, 501, details);\n }\n}\n\nexport function isConduitError(error: unknown): error is ConduitError {\n return error instanceof ConduitError;\n}\n\n// Type guard for axios-like errors\nfunction isAxiosError(error: unknown): error is {\n response: { status: number; data: unknown; headers: Record<string, string> };\n message: string;\n request?: unknown;\n code?: string;\n} {\n return (\n typeof error === 'object' &&\n error !== null &&\n 'response' in error &&\n typeof (error as any).response === 'object'\n );\n}\n\n// Type guard for network errors\nfunction isNetworkError(error: unknown): error is {\n request: unknown;\n message: string;\n code?: string;\n} {\n return (\n typeof error === 'object' &&\n error !== null &&\n 'request' in error &&\n !('response' in error)\n );\n}\n\n// Type guard for generic errors\nfunction isErrorLike(error: unknown): error is {\n message: string;\n} {\n return (\n typeof error === 'object' &&\n error !== null &&\n 'message' in error &&\n typeof (error as any).message === 'string'\n );\n}\n\nexport function handleApiError(error: unknown, endpoint?: string, method?: string): never {\n if (isAxiosError(error)) {\n const { status, data } = error.response;\n const errorData = data as any; // We need to handle data as any since it can be anything\n const baseMessage = errorData?.error || errorData?.message || error.message;\n const details = errorData?.details || data;\n \n // Enhanced error messages with endpoint information\n const endpointInfo = endpoint && method ? ` (${method.toUpperCase()} ${endpoint})` : '';\n const enhancedMessage = `${baseMessage}${endpointInfo}`;\n\n switch (status) {\n case 400:\n throw new ValidationError(enhancedMessage, details, endpoint, method);\n case 401:\n throw new AuthenticationError(enhancedMessage, details, endpoint, method);\n case 403:\n throw new AuthorizationError(enhancedMessage, details, endpoint, method);\n case 404:\n throw new NotFoundError(enhancedMessage, details, endpoint, method);\n case 409:\n throw new ConflictError(enhancedMessage, details, endpoint, method);\n case 429: {\n const retryAfterHeader = error.response.headers['retry-after'];\n const retryAfter = typeof retryAfterHeader === 'string' ? parseInt(retryAfterHeader, 10) : undefined;\n throw new RateLimitError(enhancedMessage, retryAfter, details, endpoint, method);\n }\n case 500:\n case 502:\n case 503:\n case 504:\n throw new ServerError(enhancedMessage, details, endpoint, method);\n default:\n throw new ConduitError(enhancedMessage, status, details, endpoint, method);\n }\n } else if (isNetworkError(error)) {\n const endpointInfo = endpoint && method ? ` (${method.toUpperCase()} ${endpoint})` : '';\n if (error.code === 'ECONNABORTED') {\n throw new TimeoutError(`Request timeout${endpointInfo}`, { endpoint, method });\n }\n throw new NetworkError(`Network error: No response received${endpointInfo}`, {\n code: error.code,\n endpoint,\n method,\n });\n } else if (isErrorLike(error)) {\n throw new ConduitError(\n error.message,\n undefined,\n { originalError: error },\n endpoint,\n method\n );\n } else {\n throw new ConduitError(\n 'Unknown error',\n undefined,\n { originalError: error },\n endpoint,\n method\n );\n }\n}","export const API_VERSION = 'v1';\nexport const API_PREFIX = '/api';\n\nexport const ENDPOINTS = {\n // Virtual Keys\n VIRTUAL_KEYS: {\n BASE: '/virtualkeys',\n BY_ID: (id: number) => `/virtualkeys/${id}`,\n RESET_SPEND: (id: number) => `/virtualkeys/${id}/reset-spend`,\n VALIDATE: '/virtualkeys/validate',\n SPEND: (id: number) => `/virtualkeys/${id}/spend`,\n CHECK_BUDGET: (id: number) => `/virtualkeys/${id}/check-budget`,\n VALIDATION_INFO: (id: number) => `/virtualkeys/${id}/validation-info`,\n MAINTENANCE: '/virtualkeys/maintenance',\n },\n\n // Provider Credentials\n PROVIDERS: {\n BASE: '/providercredentials',\n BY_ID: (id: number) => `/providercredentials/${id}`,\n BY_NAME: (name: string) => `/providercredentials/name/${name}`,\n NAMES: '/providercredentials/names',\n TEST_BY_ID: (id: number) => `/providercredentials/test/${id}`,\n TEST: '/providercredentials/test',\n },\n\n // Model Provider Mappings\n MODEL_MAPPINGS: {\n BASE: '/modelprovidermapping',\n BY_ID: (id: number) => `/modelprovidermapping/${id}`,\n BY_MODEL: (modelId: string) => `/modelprovidermapping/by-model/${modelId}`,\n PROVIDERS: '/modelprovidermapping/providers',\n },\n\n // IP Filters\n IP_FILTERS: {\n BASE: '/ipfilter',\n BY_ID: (id: number) => `/ipfilter/${id}`,\n ENABLED: '/ipfilter/enabled',\n SETTINGS: '/ipfilter/settings',\n CHECK: '/ipfilter/check',\n },\n\n // Model Costs\n MODEL_COSTS: {\n BASE: '/modelcosts',\n BY_ID: (id: number) => `/modelcosts/${id}`,\n BY_MODEL: (modelId: string) => `/modelcosts/model/${modelId}`,\n BATCH: '/modelcosts/batch',\n },\n\n // Analytics & Cost Dashboard\n ANALYTICS: {\n COST_SUMMARY: '/costdashboard/summary',\n COST_BY_PERIOD: '/costdashboard/by-period',\n COST_BY_MODEL: '/costdashboard/by-model',\n COST_BY_KEY: '/costdashboard/by-key',\n REQUEST_LOGS: '/logs',\n REQUEST_LOG_BY_ID: (id: string) => `/logs/${id}`,\n },\n\n // Provider Health\n HEALTH: {\n CONFIGURATIONS: '/providerhealth/configurations',\n CONFIG_BY_PROVIDER: (provider: string) => `/providerhealth/configurations/${provider}`,\n STATUS: '/providerhealth/status',\n STATUS_BY_PROVIDER: (provider: string) => `/providerhealth/status/${provider}`,\n HISTORY: '/providerhealth/history',\n CHECK: (provider: string) => `/providerhealth/check/${provider}`,\n },\n\n // System\n SYSTEM: {\n INFO: '/systeminfo/info',\n HEALTH: '/systeminfo/health',\n BACKUP: '/databasebackup',\n RESTORE: '/databasebackup/restore',\n NOTIFICATIONS: '/notifications',\n NOTIFICATION_BY_ID: (id: number) => `/notifications/${id}`,\n },\n\n // Settings\n SETTINGS: {\n GLOBAL: '/globalsettings',\n GLOBAL_BY_KEY: (key: string) => `/globalsettings/${key}`,\n AUDIO: '/audioconfiguration',\n AUDIO_BY_PROVIDER: (provider: string) => `/audioconfiguration/${provider}`,\n ROUTER: '/router/configuration',\n },\n} as const;\n\nexport const DEFAULT_PAGE_SIZE = 20;\nexport const MAX_PAGE_SIZE = 100;\n\nexport const CACHE_TTL = {\n SHORT: 60, // 1 minute\n MEDIUM: 300, // 5 minutes\n LONG: 3600, // 1 hour\n VERY_LONG: 86400, // 24 hours\n} as const;\n\nexport const HTTP_STATUS = {\n OK: 200,\n CREATED: 201,\n NO_CONTENT: 204,\n BAD_REQUEST: 400,\n UNAUTHORIZED: 401,\n FORBIDDEN: 403,\n NOT_FOUND: 404,\n CONFLICT: 409,\n RATE_LIMITED: 429,\n INTERNAL_ERROR: 500,\n BAD_GATEWAY: 502,\n SERVICE_UNAVAILABLE: 503,\n GATEWAY_TIMEOUT: 504,\n} as const;\n\nexport const BUDGET_DURATION = {\n TOTAL: 'Total',\n DAILY: 'Daily',\n WEEKLY: 'Weekly',\n MONTHLY: 'Monthly',\n} as const;\n\nexport const FILTER_TYPE = {\n ALLOW: 'Allow',\n DENY: 'Deny',\n} as const;\n\nexport const FILTER_MODE = {\n PERMISSIVE: 'permissive',\n RESTRICTIVE: 'restrictive',\n} as const;","import { BaseApiClient } from '../client/BaseApiClient';\nimport { ENDPOINTS, CACHE_TTL, DEFAULT_PAGE_SIZE } from '../constants';\nimport {\n VirtualKeyDto,\n CreateVirtualKeyRequest,\n CreateVirtualKeyResponse,\n UpdateVirtualKeyRequest,\n VirtualKeyValidationRequest,\n VirtualKeyValidationResult,\n UpdateSpendRequest,\n CheckBudgetRequest,\n CheckBudgetResponse,\n VirtualKeyValidationInfo,\n VirtualKeyMaintenanceRequest,\n VirtualKeyMaintenanceResponse,\n VirtualKeyFilters,\n VirtualKeyStatistics,\n} from '../models/virtualKey';\nimport { ValidationError, NotImplementedError } from '../utils/errors';\nimport { z } from 'zod';\n\nconst createVirtualKeySchema = z.object({\n keyName: z.string().min(1).max(100),\n allowedModels: z.string().optional(),\n maxBudget: z.number().min(0).max(1000000).optional(),\n budgetDuration: z.enum(['Total', 'Daily', 'Weekly', 'Monthly']).optional(),\n expiresAt: z.string().datetime().optional(),\n metadata: z.string().optional(),\n rateLimitRpm: z.number().min(0).optional(),\n rateLimitRpd: z.number().min(0).optional(),\n});\n\nexport class VirtualKeyService extends BaseApiClient {\n async create(request: CreateVirtualKeyRequest): Promise<CreateVirtualKeyResponse> {\n try {\n createVirtualKeySchema.parse(request);\n } catch (error) {\n throw new ValidationError('Invalid virtual key request', error);\n }\n\n const response = await this.post<CreateVirtualKeyResponse>(\n ENDPOINTS.VIRTUAL_KEYS.BASE,\n request\n );\n\n await this.invalidateCache();\n return response;\n }\n\n async list(filters?: VirtualKeyFilters): Promise<VirtualKeyDto[]> {\n const params = {\n pageNumber: filters?.pageNumber || 1,\n pageSize: filters?.pageSize || DEFAULT_PAGE_SIZE,\n search: filters?.search,\n sortBy: filters?.sortBy?.field,\n sortDirection: filters?.sortBy?.direction,\n isEnabled: filters?.isEnabled,\n hasExpired: filters?.hasExpired,\n budgetDuration: filters?.budgetDuration,\n minBudget: filters?.minBudget,\n maxBudget: filters?.maxBudget,\n allowedModel: filters?.allowedModel,\n createdAfter: filters?.createdAfter,\n createdBefore: filters?.createdBefore,\n lastUsedAfter: filters?.lastUsedAfter,\n lastUsedBefore: filters?.lastUsedBefore,\n };\n\n const cacheKey = this.getCacheKey('virtual-keys', params);\n return this.withCache<VirtualKeyDto[]>(\n cacheKey,\n () => super.get<VirtualKeyDto[]>(ENDPOINTS.VIRTUAL_KEYS.BASE, params),\n CACHE_TTL.SHORT\n );\n }\n\n async getById(id: number): Promise<VirtualKeyDto> {\n const cacheKey = this.getCacheKey('virtual-key', id);\n return this.withCache<VirtualKeyDto>(\n cacheKey,\n () => super.get<VirtualKeyDto>(ENDPOINTS.VIRTUAL_KEYS.BY_ID(id)),\n CACHE_TTL.MEDIUM\n );\n }\n\n async update(id: number, request: UpdateVirtualKeyRequest): Promise<void> {\n await this.put(ENDPOINTS.VIRTUAL_KEYS.BY_ID(id), request);\n await this.invalidateCache();\n }\n\n async deleteById(id: number): Promise<void> {\n await super.delete(ENDPOINTS.VIRTUAL_KEYS.BY_ID(id));\n await this.invalidateCache();\n }\n\n async search(query: string): Promise<VirtualKeyDto[]> {\n const filters: VirtualKeyFilters = {\n search: query,\n pageSize: 100,\n };\n return this.list(filters);\n }\n\n async resetSpend(id: number): Promise<void> {\n await this.post(ENDPOINTS.VIRTUAL_KEYS.RESET_SPEND(id));\n await this.cache?.delete(this.getCacheKey('virtual-key', id));\n }\n\n async validate(key: string): Promise<VirtualKeyValidationResult> {\n const request: VirtualKeyValidationRequest = { key };\n return this.post<VirtualKeyValidationResult>(\n ENDPOINTS.VIRTUAL_KEYS.VALIDATE,\n request\n );\n }\n\n async updateSpend(id: number, request: UpdateSpendRequest): Promise<void> {\n await this.post(ENDPOINTS.VIRTUAL_KEYS.SPEND(id), request);\n await this.cache?.delete(this.getCacheKey('virtual-key', id));\n }\n\n async checkBudget(id: number, estimatedCost: number): Promise<CheckBudgetResponse> {\n const request: CheckBudgetRequest = { estimatedCost };\n return this.post<CheckBudgetResponse>(\n ENDPOINTS.VIRTUAL_KEYS.CHECK_BUDGET(id),\n request\n );\n }\n\n async getValidationInfo(id: number): Promise<VirtualKeyValidationInfo> {\n return super.get<VirtualKeyValidationInfo>(\n ENDPOINTS.VIRTUAL_KEYS.VALIDATION_INFO(id)\n );\n }\n\n async performMaintenance(\n request?: VirtualKeyMaintenanceRequest\n ): Promise<VirtualKeyMaintenanceResponse> {\n return this.post<VirtualKeyMaintenanceResponse>(\n ENDPOINTS.VIRTUAL_KEYS.MAINTENANCE,\n request || {}\n );\n }\n\n async getStatistics(): Promise<VirtualKeyStatistics> {\n // STUB: This endpoint needs to be implemented in the Admin API\n throw new NotImplementedError(\n 'getStatistics requires Admin API endpoint implementation. ' +\n 'The WebUI currently calculates statistics client-side by fetching all keys.'\n );\n }\n\n async bulkCreate(_requests: CreateVirtualKeyRequest[]): Promise<CreateVirtualKeyResponse[]> {\n // STUB: This endpoint needs to be implemented in the Admin API\n throw new NotImplementedError(\n 'bulkCreate requires Admin API endpoint implementation. ' +\n 'Consider implementing POST /api/virtualkeys/bulk for batch creation.'\n );\n }\n\n async exportKeys(_format: 'csv' | 'json'): Promise<Blob> {\n // STUB: This endpoint needs to be implemented in the Admin API\n throw new NotImplementedError(\n 'exportKeys requires Admin API endpoint implementation. ' +\n 'Consider implementing GET /api/virtualkeys/export with format parameter.'\n );\n }\n\n private async invalidateCache(): Promise<void> {\n if (!this.cache) return;\n \n // For now, clear all cache\n await this.cache.clear();\n }\n}","import { BaseApiClient } from '../client/BaseApiClient';\nimport { ENDPOINTS, CACHE_TTL, DEFAULT_PAGE_SIZE } from '../constants';\nimport {\n ProviderCredentialDto,\n CreateProviderCredentialDto,\n UpdateProviderCredentialDto,\n ProviderConnectionTestRequest,\n ProviderConnectionTestResultDto,\n ProviderDataDto,\n ProviderHealthConfigurationDto,\n UpdateProviderHealthConfigurationDto,\n ProviderHealthRecordDto,\n ProviderHealthStatusDto,\n ProviderHealthSummaryDto,\n ProviderFilters,\n ProviderHealthFilters,\n ProviderUsageStatistics,\n} from '../models/provider';\nimport { PaginatedResponse } from '../models/common';\nimport { ValidationError, NotImplementedError } from '../utils/errors';\nimport { z } from 'zod';\n\nconst createProviderSchema = z.object({\n providerName: z.string().min(1),\n apiKey: z.string().optional(),\n apiEndpoint: z.string().url().optional(),\n organizationId: z.string().optional(),\n additionalConfig: z.string().optional(),\n isEnabled: z.boolean().optional(),\n});\n\nconst updateHealthConfigSchema = z.object({\n isEnabled: z.boolean().optional(),\n checkIntervalSeconds: z.number().min(30).max(3600).optional(),\n timeoutSeconds: z.number().min(5).max(300).optional(),\n unhealthyThreshold: z.number().min(1).max(10).optional(),\n healthyThreshold: z.number().min(1).max(10).optional(),\n testModel: z.string().optional(),\n});\n\nexport class ProviderService extends BaseApiClient {\n async create(request: CreateProviderCredentialDto): Promise<ProviderCredentialDto> {\n try {\n createProviderSchema.parse(request);\n } catch (error) {\n throw new ValidationError('Invalid provider credential request', error);\n }\n\n const response = await this.post<ProviderCredentialDto>(\n ENDPOINTS.PROVIDERS.BASE,\n request\n );\n\n await this.invalidateCache();\n return response;\n }\n\n async list(filters?: ProviderFilters): Promise<ProviderCredentialDto[]> {\n const cacheKey = this.getCacheKey('providers', filters);\n return this.withCache(\n cacheKey,\n () => super.get<ProviderCredentialDto[]>(ENDPOINTS.PROVIDERS.BASE),\n CACHE_TTL.MEDIUM\n );\n }\n\n async getById(id: number): Promise<ProviderCredentialDto> {\n const cacheKey = this.getCacheKey('provider', id);\n return this.withCache(\n cacheKey,\n () => super.get<ProviderCredentialDto>(ENDPOINTS.PROVIDERS.BY_ID(id)),\n CACHE_TTL.MEDIUM\n );\n }\n\n async getByName(providerName: string): Promise<ProviderCredentialDto> {\n const cacheKey = this.getCacheKey('provider-name', providerName);\n return this.withCache(\n cacheKey,\n () => super.get<ProviderCredentialDto>(ENDPOINTS.PROVIDERS.BY_NAME(providerName)),\n CACHE_TTL.MEDIUM\n );\n }\n\n async getProviderNames(): Promise<string[]> {\n const cacheKey = 'provider-names';\n return this.withCache(\n cacheKey,\n () => super.get<string[]>(ENDPOINTS.PROVIDERS.NAMES),\n CACHE_TTL.LONG\n );\n }\n\n async update(id: number, request: UpdateProviderCredentialDto): Promise<void> {\n await this.put(ENDPOINTS.PROVIDERS.BY_ID(id), request);\n await this.invalidateCache();\n }\n\n async deleteById(id: number): Promise<void> {\n await super.delete(ENDPOINTS.PROVIDERS.BY_ID(id));\n await this.invalidateCache();\n }\n\n async testConnectionById(id: number): Promise<ProviderConnectionTestResultDto> {\n return this.post<ProviderConnectionTestResultDto>(\n ENDPOINTS.PROVIDERS.TEST_BY_ID(id)\n );\n }\n\n async testConnection(\n request: ProviderConnectionTestRequest\n ): Promise<ProviderConnectionTestResultDto> {\n return this.post<ProviderConnectionTestResultDto>(\n ENDPOINTS.PROVIDERS.TEST,\n request\n );\n }\n\n // Health monitoring methods\n async getHealthConfigurations(): Promise<ProviderHealthConfigurationDto[]> {\n const cacheKey = 'provider-health-configs';\n return this.withCache(\n cacheKey,\n () => super.get<ProviderHealthConfigurationDto[]>(ENDPOINTS.HEALTH.CONFIGURATIONS),\n CACHE_TTL.SHORT\n );\n }\n\n async getHealthConfiguration(providerName: string): Promise<ProviderHealthConfigurationDto> {\n const cacheKey = this.getCacheKey('provider-health-config', providerName);\n return this.withCache(\n cacheKey,\n () =>\n this.get<ProviderHealthConfigurationDto>(\n ENDPOINTS.HEALTH.CONFIG_BY_PROVIDER(providerName)\n ),\n CACHE_TTL.SHORT\n );\n }\n\n async updateHealthConfiguration(\n providerName: string,\n request: UpdateProviderHealthConfigurationDto\n ): Promise<void> {\n try {\n updateHealthConfigSchema.parse(request);\n } catch (error) {\n throw new ValidationError('Invalid health configuration request', error);\n }\n\n await this.put(ENDPOINTS.HEALTH.CONFIG_BY_PROVIDER(providerName), request);\n await this.invalidateCachePattern('provider-health');\n }\n\n async getHealthStatus(): Promise<ProviderHealthSummaryDto> {\n return super.get<ProviderHealthSummaryDto>(ENDPOINTS.HEALTH.STATUS);\n }\n\n async getProviderHealthStatus(providerName: string): Promise<ProviderHealthStatusDto> {\n return super.get<ProviderHealthStatusDto>(\n ENDPOINTS.HEALTH.STATUS_BY_PROVIDER(providerName)\n );\n }\n\n async getHealthHistory(\n filters?: ProviderHealthFilters\n ): Promise<PaginatedResponse<ProviderHealthRecordDto>> {\n const params = {\n pageNumber: filters?.pageNumber || 1,\n pageSize: filters?.pageSize || DEFAULT_PAGE_SIZE,\n providerName: filters?.providerName,\n isHealthy: filters?.isHealthy,\n startDate: filters?.startDate,\n endDate: filters?.endDate,\n minResponseTime: filters?.minResponseTime,\n maxResponseTime: filters?.maxResponseTime,\n sortBy: filters?.sortBy?.field,\n sortDirection: filters?.sortBy?.direction,\n };\n\n return super.get<PaginatedResponse<ProviderHealthRecordDto>>(\n ENDPOINTS.HEALTH.HISTORY,\n params\n );\n }\n\n async checkHealth(providerName: string): Promise<ProviderConnectionTestResultDto> {\n return this.post<ProviderConnectionTestResultDto>(\n ENDPOINTS.HEALTH.CHECK(providerName)\n );\n }\n\n // Stub methods\n async getUsageStatistics(\n _providerName: string,\n _startDate?: string,\n _endDate?: string\n ): Promise<ProviderUsageStatistics> {\n // STUB: This endpoint needs to be implemented in the Admin API\n throw new NotImplementedError(\n 'getUsageStatistics requires Admin API endpoint implementation. ' +\n 'Consider implementing GET /api/providercredentials/{name}/statistics'\n );\n }\n\n async bulkTest(_providerNames: string[]): Promise<ProviderConnectionTestResultDto[]> {\n // STUB: This endpoint needs to be implemented in the Admin API\n throw new NotImplementedError(\n 'bulkTest requires Admin API endpoint implementation. ' +\n 'Consider implementing POST /api/providercredentials/test/bulk'\n );\n }\n\n async getAvailableProviders(): Promise<ProviderDataDto[]> {\n // STUB: This endpoint needs to be implemented in the Admin API\n throw new NotImplementedError(\n 'getAvailableProviders requires Admin API endpoint implementation. ' +\n 'Consider implementing GET /api/providercredentials/available'\n );\n }\n\n private async invalidateCache(): Promise<void> {\n if (!this.cache) return;\n await this.cache.clear();\n }\n\n private async invalidateCachePattern(_pattern: string): Promise<void> {\n if (!this.cache) return;\n await this.cache.clear();\n }\n}","import { BaseApiClient } from '../client/BaseApiClient';\nimport { ENDPOINTS, CACHE_TTL } from '../constants';\nimport {\n ModelProviderMappingDto,\n CreateModelProviderMappingDto,\n UpdateModelProviderMappingDto,\n ModelMappingFilters,\n ModelRoutingInfo,\n BulkMappingRequest,\n BulkMappingResponse,\n ModelMappingSuggestion,\n} from '../models/modelMapping';\nimport { ValidationError, NotImplementedError } from '../utils/errors';\nimport { z } from 'zod';\n\nconst createMappingSchema = z.object({\n modelId: z.string().min(1),\n providerId: z.string().min(1),\n providerModelId: z.string().min(1),\n isEnabled: z.boolean().optional(),\n priority: z.number().min(0).max(100).optional(),\n metadata: z.string().optional(),\n});\n\nconst updateMappingSchema = z.object({\n providerId: z.string().min(1).optional(),\n providerModelId: z.string().min(1).optional(),\n isEnabled: z.boolean().optional(),\n priority: z.number().min(0).max(100).optional(),\n metadata: z.string().optional(),\n});\n\nexport class ModelMappingService extends BaseApiClient {\n async create(request: CreateModelProviderMappingDto): Promise<ModelProviderMappingDto> {\n try {\n createMappingSchema.parse(request);\n } catch (error) {\n throw new ValidationError('Invalid model mapping request', error);\n }\n\n const response = await this.post<ModelProviderMappingDto>(\n ENDPOINTS.MODEL_MAPPINGS.BASE,\n request\n );\n\n await this.invalidateCache();\n return response;\n }\n\n async list(filters?: ModelMappingFilters): Promise<ModelProviderMappingDto[]> {\n const params = filters\n ? {\n modelId: filters.modelId,\n providerId: filters.providerId,\n isEnabled: filters.isEnabled,\n minPriority: filters.minPriority,\n maxPriority: filters.maxPriority,\n sortBy: filters.sortBy?.field,\n sortDirection: filters.sortBy?.direction,\n }\n : undefined;\n\n const cacheKey = this.getCacheKey('model-mappings', params);\n return this.withCache(\n cacheKey,\n () => super.get<ModelProviderMappingDto[]>(ENDPOINTS.MODEL_MAPPINGS.BASE, params),\n CACHE_TTL.MEDIUM\n );\n }\n\n async getById(id: number): Promise<ModelProviderMappingDto> {\n const cacheKey = this.getCacheKey('model-mapping', id);\n return this.withCache(\n cacheKey,\n () => super.get<ModelProviderMappingDto>(ENDPOINTS.MODEL_MAPPINGS.BY_ID(id)),\n CACHE_TTL.MEDIUM\n );\n }\n\n async getByModel(modelId: string): Promise<ModelProviderMappingDto[]> {\n const cacheKey = this.getCacheKey('model-mapping-by-model', modelId);\n return this.withCache(\n cacheKey,\n () =>\n super.get<ModelProviderMappingDto[]>(ENDPOINTS.MODEL_MAPPINGS.BY_MODEL(modelId)),\n CACHE_TTL.MEDIUM\n );\n }\n\n async update(id: number, request: UpdateModelProviderMappingDto): Promise<void> {\n try {\n updateMappingSchema.parse(request);\n } catch (error) {\n throw new ValidationError('Invalid model mapping update request', error);\n }\n\n await this.put(ENDPOINTS.MODEL_MAPPINGS.BY_ID(id), request);\n await this.invalidateCache();\n }\n\n async deleteById(id: number): Promise<void> {\n await super.delete(ENDPOINTS.MODEL_MAPPINGS.BY_ID(id));\n await this.invalidateCache();\n }\n\n async getAvailableProviders(): Promise<string[]> {\n const cacheKey = 'available-providers';\n return this.withCache(\n cacheKey,\n () => super.get<string[]>(ENDPOINTS.MODEL_MAPPINGS.PROVIDERS),\n CACHE_TTL.LONG\n );\n }\n\n async updatePriority(id: number, priority: number): Promise<void> {\n await this.update(id, { priority });\n }\n\n async enableMapping(id: number): Promise<void> {\n await this.update(id, { isEnabled: true });\n }\n\n async disableMapping(id: number): Promise<void> {\n await this.update(id, { isEnabled: false });\n }\n\n async reorderMappings(_modelId: string, mappingIds: number[]): Promise<void> {\n const updates = mappingIds.map((id, index) => ({\n id,\n priority: mappingIds.length - index,\n }));\n\n await Promise.all(\n updates.map((update) =>\n this.updatePriority(update.id, update.priority)\n )\n );\n }\n\n // Stub methods\n async getRoutingInfo(_modelId: string): Promise<ModelRoutingInfo> {\n // STUB: This endpoint needs to be implemented in the Admin API\n throw new NotImplementedError(\n 'getRoutingInfo requires Admin API endpoint implementation. ' +\n 'Consider implementing GET /api/modelprovidermapping/routing/{modelId}'\n );\n }\n\n async bulkCreate(_request: BulkMappingRequest): Promise<BulkMappingResponse> {\n // STUB: This endpoint needs to be implemented in the Admin API\n throw new NotImplementedError(\n 'bulkCreate requires Admin API endpoint implementation. ' +\n 'Consider implementing POST /api/modelprovidermapping/bulk'\n );\n }\n\n async importMappings(_file: File | Blob, _format: 'csv' | 'json'): Promise<BulkMappingResponse> {\n // STUB: This endpoint needs to be implemented in the Admin API\n throw new NotImplementedError(\n 'importMappings requires Admin API endpoint implementation. ' +\n 'Consider implementing POST /api/modelprovidermapping/import'\n );\n }\n\n async exportMappings(_format: 'csv' | 'json'): Promise<Blob> {\n // STUB: This endpoint needs to be implemented in the Admin API\n throw new NotImplementedError(\n 'exportMappings requires Admin API endpoint implementation. ' +\n 'Consider implementing GET /api/modelprovidermapping/export'\n );\n }\n\n async suggestOptimalMapping(_modelId: string): Promise<ModelMappingSuggestion> {\n // STUB: This endpoint needs to be implemented in the Admin API\n throw new NotImplementedError(\n 'suggestOptimalMapping requires Admin API endpoint implementation. ' +\n 'Consider implementing POST /api/modelprovidermapping/suggest'\n );\n }\n\n private async invalidateCache(): Promise<void> {\n if (!this.cache) return;\n await this.cache.clear();\n }\n}","import { BaseApiClient } from '../client/BaseApiClient';\nimport { ENDPOINTS, CACHE_TTL } from '../constants';\nimport {\n GlobalSettingDto,\n CreateGlobalSettingDto,\n UpdateGlobalSettingDto,\n AudioConfigurationDto,\n CreateAudioConfigurationDto,\n UpdateAudioConfigurationDto,\n RouterConfigurationDto,\n UpdateRouterConfigurationDto,\n SystemConfiguration,\n SettingFilters,\n} from '../models/settings';\nimport { ValidationError, NotImplementedError } from '../utils/errors';\nimport { z } from 'zod';\n\nconst createSettingSchema = z.object({\n key: z.string().min(1).regex(/^[A-Z_][A-Z0-9_]*$/, 'Key must be uppercase with underscores'),\n value: z.string(),\n description: z.string().optional(),\n dataType: z.enum(['string', 'number', 'boolean', 'json']).optional(),\n category: z.string().optional(),\n isSecret: z.boolean().optional(),\n});\n\nconst audioConfigSchema = z.object({\n provider: z.string().min(1),\n isEnabled: z.boolean().optional(),\n apiKey: z.string().optional(),\n apiEndpoint: z.string().url().optional(),\n defaultVoice: z.string().optional(),\n defaultModel: z.string().optional(),\n maxDuration: z.number().positive().optional(),\n allowedVoices: z.array(z.string()).optional(),\n customSettings: z.record(z.any()).optional(),\n});\n\nexport class SettingsService extends BaseApiClient {\n // Global Settings\n async getGlobalSettings(filters?: SettingFilters): Promise<GlobalSettingDto[]> {\n const params = filters\n ? {\n category: filters.category,\n dataType: filters.dataType,\n isSecret: filters.isSecret,\n search: filters.searchKey,\n }\n : undefined;\n\n const cacheKey = this.getCacheKey('global-settings', params);\n return this.withCache(\n cacheKey,\n () => super.get<GlobalSettingDto[]>(ENDPOINTS.SETTINGS.GLOBAL, params),\n CACHE_TTL.MEDIUM\n );\n }\n\n async getGlobalSetting(key: string): Promise<GlobalSettingDto> {\n const cacheKey = this.getCacheKey('global-setting', key);\n return this.withCache(\n cacheKey,\n () => super.get<GlobalSettingDto>(ENDPOINTS.SETTINGS.GLOBAL_BY_KEY(key)),\n CACHE_TTL.MEDIUM\n );\n }\n\n async createGlobalSetting(request: CreateGlobalSettingDto): Promise<GlobalSettingDto> {\n try {\n createSettingSchema.parse(request);\n } catch (error) {\n throw new ValidationError('Invalid global setting request', error);\n }\n\n const response = await this.post<GlobalSettingDto>(\n ENDPOINTS.SETTINGS.GLOBAL,\n request\n );\n\n await this.invalidateCache();\n return response;\n }\n\n async updateGlobalSetting(key: string, request: UpdateGlobalSettingDto): Promise<void> {\n await this.put(ENDPOINTS.SETTINGS.GLOBAL_BY_KEY(key), request);\n await this.invalidateCache();\n }\n\n async deleteGlobalSetting(key: string): Promise<void> {\n await this.delete(ENDPOINTS.SETTINGS.GLOBAL_BY_KEY(key));\n await this.invalidateCache();\n }\n\n // Audio Configuration\n async getAudioConfigurations(): Promise<AudioConfigurationDto[]> {\n const cacheKey = 'audio-configurations';\n return this.withCache(\n cacheKey,\n () => super.get<AudioConfigurationDto[]>(ENDPOINTS.SETTINGS.AUDIO),\n CACHE_TTL.MEDIUM\n );\n }\n\n async getAudioConfiguration(provider: string): Promise<AudioConfigurationDto> {\n const cacheKey = this.getCacheKey('audio-config', provider);\n return this.withCache(\n cacheKey,\n () => super.get<AudioConfigurationDto>(ENDPOINTS.SETTINGS.AUDIO_BY_PROVIDER(provider)),\n CACHE_TTL.MEDIUM\n );\n }\n\n async createAudioConfiguration(\n request: CreateAudioConfigurationDto\n ): Promise<AudioConfigurationDto> {\n try {\n audioConfigSchema.parse(request);\n } catch (error) {\n throw new ValidationError('Invalid audio configuration request', error);\n }\n\n const response = await this.post<AudioConfigurationDto>(\n ENDPOINTS.SETTINGS.AUDIO,\n request\n );\n\n await this.invalidateCache();\n return response;\n }\n\n async updateAudioConfiguration(\n provider: string,\n request: UpdateAudioConfigurationDto\n ): Promise<void> {\n await this.put(ENDPOINTS.SETTINGS.AUDIO_BY_PROVIDER(provider), request);\n await this.invalidateCache();\n }\n\n async deleteAudioConfiguration(provider: string): Promise<void> {\n await this.delete(ENDPOINTS.SETTINGS.AUDIO_BY_PROVIDER(provider));\n await this.invalidateCache();\n }\n\n // Router Configuration\n async getRouterConfiguration(): Promise<RouterConfigurationDto> {\n const cacheKey = 'router-configuration';\n return this.withCache(\n cacheKey,\n () => super.get<RouterConfigurationDto>(ENDPOINTS.SETTINGS.ROUTER),\n CACHE_TTL.SHORT\n );\n }\n\n async updateRouterConfiguration(request: UpdateRouterConfigurationDto): Promise<void> {\n await this.put(ENDPOINTS.SETTINGS.ROUTER, request);\n await this.invalidateCache();\n }\n\n // Convenience methods\n async getSetting(key: string): Promise<string> {\n const setting = await this.getGlobalSetting(key);\n return setting.value;\n }\n\n async setSetting(key: string, value: string, options?: {\n description?: string;\n dataType?: 'string' | 'number' | 'boolean' | 'json';\n category?: string;\n isSecret?: boolean;\n }): Promise<void> {\n try {\n await this.getGlobalSetting(key);\n // Setting exists, update it\n await this.updateGlobalSetting(key, { value });\n } catch (error) {\n // Setting doesn't exist, create it\n await this.createGlobalSetting({\n key,\n value,\n ...options,\n });\n }\n }\n\n async getSettingsByCategory(category: string): Promise<GlobalSettingDto[]> {\n const settings = await this.getGlobalSettings({ category });\n return settings;\n }\n\n // Stub methods\n async getSystemConfiguration(): Promise<SystemConfiguration> {\n // STUB: This endpoint needs to be implemented in the Admin API\n throw new NotImplementedError(\n 'getSystemConfiguration requires Admin API endpoint implementation. ' +\n 'Consider implementing GET /api/settings/system-configuration'\n );\n }\n\n async exportSettings(_format: 'json' | 'env'): Promise<Blob> {\n // STUB: This endpoint needs to be implemented in the Admin API\n throw new NotImplementedError(\n 'exportSettings requires Admin API endpoint implementation. ' +\n 'Consider implementing GET /api/settings/export'\n );\n }\n\n async importSettings(_file: File | Blob, _format: 'json' | 'env'): Promise<{\n imported: number;\n skipped: number;\n errors: string[];\n }> {\n // STUB: This endpoint needs to be implemented in the Admin API\n throw new NotImplementedError(\n 'importSettings requires Admin API endpoint implementation. ' +\n 'Consider implementing POST /api/settings/import'\n );\n }\n\n async validateConfiguration(): Promise<{\n isValid: boolean;\n errors: string[];\n warnings: string[];\n }> {\n // STUB: This endpoint needs to be implemented in the Admin API\n throw new NotImplementedError(\n 'validateConfiguration requires Admin API endpoint implementation. ' +\n 'Consider implementing POST /api/settings/validate'\n );\n }\n\n private async invalidateCache(): Promise<void> {\n if (!this.cache) return;\n await this.cache.clear();\n }\n}","import { BaseApiClient } from '../client/BaseApiClient';\nimport { ENDPOINTS, CACHE_TTL } from '../constants';\nimport {\n IpFilterDto,\n CreateIpFilterDto,\n UpdateIpFilterDto,\n IpFilterSettingsDto,\n UpdateIpFilterSettingsDto,\n IpCheckRequest,\n IpCheckResult,\n IpFilterFilters,\n IpFilterStatistics,\n BulkIpFilterRequest,\n BulkIpFilterResponse,\n IpFilterValidationResult,\n FilterType,\n} from '../models/ipFilter';\nimport { ValidationError, NotImplementedError } from '../utils/errors';\nimport { z } from 'zod';\n\nconst createFilterSchema = z.object({\n name: z.string().min(1).max(100),\n cidrRange: z.string().regex(\n /^(\\d{1,3}\\.){3}\\d{1,3}\\/\\d{1,2}$/,\n 'Invalid CIDR format (e.g., 192.168.1.0/24)'\n ),\n filterType: z.enum(['Allow', 'Deny']),\n isEnabled: z.boolean().optional(),\n description: z.string().max(500).optional(),\n});\n\nconst ipCheckSchema = z.object({\n ipAddress: z.string().ip(),\n endpoint: z.string().optional(),\n});\n\nexport class IpFilterService extends BaseApiClient {\n async create(request: CreateIpFilterDto): Promise<IpFilterDto> {\n try {\n createFilterSchema.parse(request);\n } catch (error) {\n throw new ValidationError('Invalid IP filter request', error);\n }\n\n const response = await this.post<IpFilterDto>(\n ENDPOINTS.IP_FILTERS.BASE,\n request\n );\n\n await this.invalidateCache();\n return response;\n }\n\n async list(filters?: IpFilterFilters): Promise<IpFilterDto[]> {\n const params = filters\n ? {\n filterType: filters.filterType,\n isEnabled: filters.isEnabled,\n nameContains: filters.nameContains,\n cidrContains: filters.cidrContains,\n lastMatchedAfter: filters.lastMatchedAfter,\n lastMatchedBefore: filters.lastMatchedBefore,\n minMatchCount: filters.minMatchCount,\n sortBy: filters.sortBy?.field,\n sortDirection: filters.sortBy?.direction,\n }\n : undefined;\n\n const cacheKey = this.getCacheKey('ip-filters', params);\n return this.withCache(\n cacheKey,\n () => super.get<IpFilterDto[]>(ENDPOINTS.IP_FILTERS.BASE, params),\n CACHE_TTL.SHORT\n );\n }\n\n async getById(id: number): Promise<IpFilterDto> {\n const cacheKey = this.getCacheKey('ip-filter', id);\n return this.withCache(\n cacheKey,\n () => super.get<IpFilterDto>(ENDPOINTS.IP_FILTERS.BY_ID(id)),\n CACHE_TTL.SHORT\n );\n }\n\n async getEnabled(): Promise<IpFilterDto[]> {\n const cacheKey = 'ip-filters-enabled';\n return this.withCache(\n cacheKey,\n () => super.get<IpFilterDto[]>(ENDPOINTS.IP_FILTERS.ENABLED),\n CACHE_TTL.SHORT\n );\n }\n\n async update(id: number, request: UpdateIpFilterDto): Promise<void> {\n await this.put(ENDPOINTS.IP_FILTERS.BY_ID(id), request);\n await this.invalidateCache();\n }\n\n async deleteById(id: number): Promise<void> {\n await super.delete(ENDPOINTS.IP_FILTERS.BY_ID(id));\n await this.invalidateCache();\n }\n\n async getSettings(): Promise<IpFilterSettingsDto> {\n const cacheKey = 'ip-filter-settings';\n return this.withCache(\n cacheKey,\n () => super.get<IpFilterSettingsDto>(ENDPOINTS.IP_FILTERS.SETTINGS),\n CACHE_TTL.SHORT\n );\n }\n\n async updateSettings(request: UpdateIpFilterSettingsDto): Promise<void> {\n await this.put(ENDPOINTS.IP_FILTERS.SETTINGS, request);\n await this.invalidateCache();\n }\n\n async checkIp(ipAddress: string, endpoint?: string): Promise<IpCheckResult> {\n try {\n ipCheckSchema.parse({ ipAddress, endpoint });\n } catch (error) {\n throw new ValidationError('Invalid IP check request', error);\n }\n\n const request: IpCheckRequest = { ipAddress, endpoint };\n return this.post<IpCheckResult>(ENDPOINTS.IP_FILTERS.CHECK, request);\n }\n\n async search(query: string): Promise<IpFilterDto[]> {\n const filters: IpFilterFilters = {\n nameContains: query,\n };\n return this.list(filters);\n }\n\n async enableFilter(id: number): Promise<void> {\n await this.update(id, { isEnabled: true });\n }\n\n async disableFilter(id: number): Promise<void> {\n await this.update(id, { isEnabled: false });\n }\n\n async createAllowFilter(name: string, cidrRange: string, description?: string): Promise<IpFilterDto> {\n return this.create({\n name,\n cidrRange,\n filterType: 'Allow',\n isEnabled: true,\n description,\n });\n }\n\n async createDenyFilter(name: string, cidrRange: string, description?: string): Promise<IpFilterDto> {\n return this.create({\n name,\n cidrRange,\n filterType: 'Deny',\n isEnabled: true,\n description,\n });\n }\n\n async getFiltersByType(filterType: FilterType): Promise<IpFilterDto[]> {\n return this.list({ filterType });\n }\n\n // Stub methods\n async getStatistics(): Promise<IpFilterStatistics> {\n // STUB: This endpoint needs to be implemented in the Admin API\n throw new NotImplementedError(\n 'getStatistics requires Admin API endpoint implementation. ' +\n 'Consider implementing GET /api/ipfilter/statistics'\n );\n }\n\n async bulkCreate(_request: BulkIpFilterRequest): Promise<BulkIpFilterResponse> {\n // STUB: This endpoint needs to be implemented in the Admin API\n throw new NotImplementedError(\n 'bulkCreate requires Admin API endpoint implementation. ' +\n 'Consider implementing POST /api/ipfilter/bulk'\n );\n }\n\n async importFilters(_file: File | Blob, _format: 'csv' | 'json'): Promise<BulkIpFilterResponse> {\n // STUB: This endpoint needs to be implemented in the Admin API\n throw new NotImplementedError(\n 'importFilters requires Admin API endpoint implementation. ' +\n 'Consider implementing POST /api/ipfilter/import'\n );\n }\n\n async exportFilters(_format: 'csv' | 'json', _filterType?: FilterType): Promise<Blob> {\n // STUB: This endpoint needs to be implemented in the Admin API\n throw new NotImplementedError(\n 'exportFilters requires Admin API endpoint implementation. ' +\n 'Consider implementing GET /api/ipfilter/export'\n );\n }\n\n async validateCidr(_cidrRange: string): Promise<IpFilterValidationResult> {\n // STUB: This endpoint needs to be implemented in the Admin API\n throw new NotImplementedError(\n 'validateCidr requires Admin API endpoint implementation. ' +\n