/* Copyright (C) 2023-2026 QuantumNous This program is free software: you can redistribute it and/or modify it under the terms of the GNU Affero General Public License as published by the Free Software Foundation, either version 3 of the License, or (at your option) any later version. This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Affero General Public License for more details. You should have received a copy of the GNU Affero General Public License along with this program. If not, see . For commercial licensing, please contact support@quantumnous.com */ import { getGroups as getUserGroups } from '@/features/users/api' import { api, type ApiRequestConfig } from '@/lib/api' import type { AddChannelRequest, BatchDeleteParams, BatchSetTagParams, Channel, ChannelBalanceResponse, ChannelOpsResponse, ChannelTestResponse, CopyChannelParams, CopyChannelResponse, FetchModelsResponse, GetChannelResponse, GetChannelsParams, GetChannelsResponse, MultiKeyManageParams, MultiKeyStatusResponse, SearchChannelsParams, SearchChannelsResponse, TagOperationParams, } from './types' const channelActionConfig = ( config: ApiRequestConfig = {} ): ApiRequestConfig => ({ ...config, skipBusinessError: true, skipErrorHandler: true, }) export type CodexUsageResponse = { success: boolean message?: string upstream_status?: number data?: Record } export type CodexResetCreditsResponse = CodexUsageResponse export type CodexUsageResetResponse = CodexUsageResponse export type CodexCredentialRefreshResponse = { success: boolean message?: string data?: { expires_at?: string last_refresh?: string account_id?: string email?: string channel_id?: number channel_type?: number channel_name?: string } } // ============================================================================ // Base Channel CRUD Operations // ============================================================================ /** * Get paginated list of channels */ export async function getChannels( params: GetChannelsParams = {} ): Promise { const res = await api.get('/api/channel', { params }) return res.data } /** * Search channels with filters */ export async function searchChannels( params: SearchChannelsParams ): Promise { const res = await api.get('/api/channel/search', { params }) return res.data } /** * Get single channel by ID */ export async function getChannel(id: number): Promise { const res = await api.get(`/api/channel/${id}`) return res.data } /** * Get channel operations summary for administrators */ export async function getChannelOps(): Promise { const res = await api.get('/api/channel/ops', channelActionConfig()) return res.data } /** * Create new channel(s) * Supports single, batch, and multi-key modes */ export async function createChannel( data: AddChannelRequest ): Promise<{ success: boolean; message?: string }> { const res = await api.post('/api/channel', data, channelActionConfig()) return res.data } /** * Update existing channel */ export async function updateChannel( id: number, data: Partial ): Promise<{ success: boolean; message?: string; data?: Channel }> { const res = await api.put( '/api/channel/', { id, ...data }, channelActionConfig() ) return res.data } /** * Update channel enabled/disabled status. */ export async function updateChannelStatus( id: number, status: number ): Promise<{ success: boolean; message?: string; data?: boolean }> { const res = await api.post( `/api/channel/${id}/status`, { status }, channelActionConfig() ) return res.data } /** * Batch update channel enabled/disabled status. */ export async function batchUpdateChannelStatus( ids: number[], status: number ): Promise<{ success: boolean; message?: string; data?: number }> { const res = await api.post( '/api/channel/status/batch', { ids, status }, channelActionConfig() ) return res.data } /** * Delete single channel */ export async function deleteChannel( id: number ): Promise<{ success: boolean; message?: string }> { const res = await api.delete(`/api/channel/${id}`, channelActionConfig()) return res.data } /** * Batch delete channels */ export async function batchDeleteChannels( data: BatchDeleteParams ): Promise<{ success: boolean; message?: string; data?: number }> { const res = await api.post('/api/channel/batch', data, channelActionConfig()) return res.data } /** * Batch set tag for channels */ export async function batchSetChannelTag( data: BatchSetTagParams ): Promise<{ success: boolean; message?: string; data?: number }> { const res = await api.post( '/api/channel/batch/tag', data, channelActionConfig() ) return res.data } // ============================================================================ // Channel Operations // ============================================================================ /** * Test channel connectivity */ export async function testChannel( id: number, params?: { model?: string; endpoint_type?: string; stream?: boolean } ): Promise { const res = await api.get( `/api/channel/test/${id}`, channelActionConfig({ params }) ) return res.data } /** * Update channel balance */ export async function updateChannelBalance( id: number ): Promise { const res = await api.get( `/api/channel/update_balance/${id}`, channelActionConfig() ) return res.data } /** * Fetch available models from upstream provider */ export async function fetchUpstreamModels( id: number ): Promise { const res = await api.get( `/api/channel/fetch_models/${id}`, channelActionConfig() ) return res.data } /** * Copy/clone a channel */ export async function copyChannel( id: number, params: CopyChannelParams = {} ): Promise { const res = await api.post( `/api/channel/copy/${id}`, null, channelActionConfig({ params }) ) return res.data } /** * Fix channel abilities */ export async function fixChannelAbilities(): Promise<{ success: boolean message?: string data?: { success: number; fails: number } }> { const res = await api.post( '/api/channel/fix', undefined, channelActionConfig() ) return res.data } /** * Delete all disabled channels */ export async function deleteDisabledChannels(): Promise<{ success: boolean message?: string data?: number }> { const res = await api.delete('/api/channel/disabled', channelActionConfig()) return res.data } /** * Get channel key (requires 2FA verification) */ export async function getChannelKey( id: number, proofToken?: string ): Promise<{ success: boolean; message?: string; data?: { key: string } }> { const res = await api.post( `/api/channel/${id}/key`, undefined, channelActionConfig({ headers: proofToken ? { 'X-Security-Proof': proofToken } : undefined, }) ) return res.data } // ============================================================================ // Codex Channel Operations // ============================================================================ export async function refreshCodexCredential( channelId: number ): Promise { const res = await api.post( `/api/channel/${channelId}/codex/refresh`, {}, channelActionConfig() ) return res.data } export async function getCodexUsage( channelId: number ): Promise { const res = await api.get( `/api/channel/${channelId}/codex/usage`, channelActionConfig({ disableDuplicate: true }) ) return res.data } export async function getCodexResetCredits( channelId: number ): Promise { const res = await api.get( `/api/channel/${channelId}/codex/usage/reset-credits`, channelActionConfig({ disableDuplicate: true }) ) return res.data } export async function resetCodexUsage( channelId: number ): Promise { const res = await api.post( `/api/channel/${channelId}/codex/usage/reset`, {}, channelActionConfig({ disableDuplicate: true }) ) return res.data } // ============================================================================ // Multi-Key Management // ============================================================================ /** * Manage multi-key channel operations */ export async function manageMultiKeys( params: MultiKeyManageParams ): Promise { const res = await api.post( '/api/channel/multi_key/manage', params, channelActionConfig() ) return res.data } /** * Get key status for multi-key channel */ export async function getMultiKeyStatus( channelId: number, page = 1, pageSize = 50, status?: number ): Promise { return manageMultiKeys({ channel_id: channelId, action: 'get_key_status', page, page_size: pageSize, status, }) as Promise } /** * Enable a specific key in multi-key channel */ export async function enableMultiKey( channelId: number, keyIndex: number ): Promise<{ success: boolean; message?: string }> { return manageMultiKeys({ channel_id: channelId, action: 'enable_key', key_index: keyIndex, }) as Promise<{ success: boolean; message?: string }> } /** * Disable a specific key in multi-key channel */ export async function disableMultiKey( channelId: number, keyIndex: number ): Promise<{ success: boolean; message?: string }> { return manageMultiKeys({ channel_id: channelId, action: 'disable_key', key_index: keyIndex, }) as Promise<{ success: boolean; message?: string }> } /** * Delete a specific key in multi-key channel */ export async function deleteMultiKey( channelId: number, keyIndex: number ): Promise<{ success: boolean; message?: string }> { return manageMultiKeys({ channel_id: channelId, action: 'delete_key', key_index: keyIndex, }) as Promise<{ success: boolean; message?: string }> } /** * Enable all keys in multi-key channel */ export async function enableAllMultiKeys( channelId: number ): Promise<{ success: boolean; message?: string }> { return manageMultiKeys({ channel_id: channelId, action: 'enable_all_keys', }) as Promise<{ success: boolean; message?: string }> } /** * Disable all keys in multi-key channel */ export async function disableAllMultiKeys( channelId: number ): Promise<{ success: boolean; message?: string }> { return manageMultiKeys({ channel_id: channelId, action: 'disable_all_keys', }) as Promise<{ success: boolean; message?: string }> } /** * Delete all disabled keys in multi-key channel */ export async function deleteDisabledMultiKeys( channelId: number ): Promise<{ success: boolean; message?: string; data?: number }> { return manageMultiKeys({ channel_id: channelId, action: 'delete_disabled_keys', }) as Promise<{ success: boolean; message?: string; data?: number }> } // ============================================================================ // Tag Operations // ============================================================================ /** * Enable all channels with a specific tag */ export async function enableTagChannels( tag: string ): Promise<{ success: boolean; message?: string }> { const res = await api.post( '/api/channel/tag/enabled', { tag }, channelActionConfig() ) return res.data } /** * Disable all channels with a specific tag */ export async function disableTagChannels( tag: string ): Promise<{ success: boolean; message?: string }> { const res = await api.post( '/api/channel/tag/disabled', { tag }, channelActionConfig() ) return res.data } /** * Edit all channels with a specific tag */ export async function editTagChannels( params: TagOperationParams ): Promise<{ success: boolean; message?: string }> { const res = await api.put('/api/channel/tag', params, channelActionConfig()) return res.data } /** * Get models for a specific tag */ export async function getTagModels( tag: string ): Promise<{ success: boolean; message?: string; data?: string }> { const res = await api.get('/api/channel/tag/models', { params: { tag } }) return res.data } // ============================================================================ // Utility Functions // ============================================================================ /** * Fetch models from the current unsaved channel form configuration. */ export async function fetchModels(data: { base_url: string type: number key?: string channel_id?: number advanced_custom?: string header_override?: string proxy?: string }): Promise { const res = await api.post( '/api/channel/fetch_models', data, channelActionConfig() ) return res.data } /** * Delete an Ollama model from a channel */ export async function deleteOllamaModel(params: { channel_id: number model_name: string }): Promise<{ success: boolean; message?: string }> { const res = await api.delete( '/api/channel/ollama/delete', channelActionConfig({ data: params }) ) return res.data } /** * Test all enabled channels */ export async function testAllChannels(): Promise<{ success: boolean message?: string }> { const res = await api.get('/api/channel/test', channelActionConfig()) return res.data } /** * Update balance for all enabled channels */ export async function updateAllChannelsBalance(): Promise<{ success: boolean message?: string }> { const res = await api.get( '/api/channel/update_balance', channelActionConfig() ) return res.data } /** * Get all available models */ export async function getAllModels(): Promise<{ success: boolean message?: string data?: Array<{ id: string; [key: string]: unknown }> }> { const res = await api.get('/api/channel/models') return res.data } /** * Get all enabled models */ export async function getEnabledModels(): Promise<{ success: boolean message?: string data?: string[] }> { const res = await api.get('/api/channel/models_enabled') return res.data } // ============================================================================ // Ollama Utilities // ============================================================================ /** * Check Ollama version for a given channel */ export async function getOllamaVersion( channelId: number ): Promise<{ success: boolean; message?: string; data?: { version: string } }> { const res = await api.get(`/api/channel/ollama/version/${channelId}`) return res.data } // ============================================================================ // Group Management // ============================================================================ /** * Get all available groups (re-exported from users API for convenience) */ export const getGroups = getUserGroups // ============================================================================ // Prefill Groups (Model Groups) // ============================================================================ /** * Get prefill groups for quick model selection */ export async function getPrefillGroups( type: 'model' | 'group' = 'model' ): Promise<{ success: boolean message?: string data?: Array<{ id: number; name: string; items: string | string[] }> }> { const res = await api.get('/api/prefill_group', { params: { type } }) return res.data }