/*
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
}