import { useMutation, useQuery, useQueryClient } from '@tanstack/react-query'; import { graphqlRequest } from '@/gql/graphql'; import { toast } from 'sonner'; import i18n from '@/lib/i18n'; import { useErrorHandler } from '@/hooks/use-error-handler'; import { getTokenFromStorage } from '@/stores/authStore'; // GraphQL queries and mutations const SYSTEM_VERSION_QUERY = ` query SystemVersion { systemVersion { version commit buildTime goVersion platform uptime } } `; export const CHECK_FOR_UPDATE_QUERY = ` query CheckForUpdate { checkForUpdate { currentVersion latestVersion hasUpdate releaseUrl } } `; const BRAND_SETTINGS_QUERY = ` query BrandSettings { brandSettings { brandName brandLogo } } `; const STORAGE_POLICY_QUERY = ` query StoragePolicy { storagePolicy { storeChunks storeRequestBody storeResponseBody cleanupOptions { resourceType enabled cleanupDays } } } `; const UPDATE_BRAND_SETTINGS_MUTATION = ` mutation UpdateBrandSettings($input: UpdateBrandSettingsInput!) { updateBrandSettings(input: $input) } `; const UPDATE_STORAGE_POLICY_MUTATION = ` mutation UpdateStoragePolicy($input: UpdateStoragePolicyInput!) { updateStoragePolicy(input: $input) } `; const RETRY_POLICY_QUERY = ` query RetryPolicy { retryPolicy { maxChannelRetries maxSingleChannelRetries retryDelayMs loadBalancerStrategy enabled autoDisableChannel { enabled statuses { status times } } } } `; const UPDATE_RETRY_POLICY_MUTATION = ` mutation UpdateRetryPolicy($input: UpdateRetryPolicyInput!) { updateRetryPolicy(input: $input) } `; const DEFAULT_DATA_STORAGE_QUERY = ` query DefaultDataStorageID { defaultDataStorageID } `; const UPDATE_DEFAULT_DATA_STORAGE_MUTATION = ` mutation UpdateDefaultDataStorage($input: UpdateDefaultDataStorageInput!) { updateDefaultDataStorage(input: $input) } `; const ONBOARDING_INFO_QUERY = ` query OnboardingInfo { onboardingInfo { onboarded completedAt systemModelSetting { onboarded completedAt } autoDisableChannel { onboarded completedAt } } } `; const COMPLETE_ONBOARDING_MUTATION = ` mutation CompleteOnboarding($input: CompleteOnboardingInput!) { completeOnboarding(input: $input) } `; const COMPLETE_SYSTEM_MODEL_SETTING_ONBOARDING_MUTATION = ` mutation CompleteSystemModelSettingOnboarding($input: CompleteSystemModelSettingOnboardingInput!) { completeSystemModelSettingOnboarding(input: $input) } `; const COMPLETE_AUTO_DISABLE_CHANNEL_ONBOARDING_MUTATION = ` mutation CompleteAutoDisableChannelOnboarding($input: CompleteAutoDisableChannelOnboardingInput!) { completeAutoDisableChannelOnboarding(input: $input) } `; // Types export interface BrandSettings { brandName?: string; brandLogo?: string; } export interface SystemGeneralSettings { currencyCode: string; timezone: string; } export interface UpdateSystemGeneralSettingsInput { currencyCode?: string; timezone?: string; } export interface StoragePolicy { storeChunks: boolean; storeRequestBody: boolean; storeResponseBody: boolean; cleanupOptions: CleanupOption[]; } export interface CleanupOption { resourceType: string; enabled: boolean; cleanupDays: number; } export interface UpdateBrandSettingsInput { brandName?: string; brandLogo?: string; } export interface UpdateStoragePolicyInput { storeChunks?: boolean; storeRequestBody?: boolean; storeResponseBody?: boolean; cleanupOptions?: CleanupOptionInput[]; } export interface CleanupOptionInput { resourceType: string; enabled: boolean; cleanupDays: number; } export interface AutoDisableChannelStatus { status: number; times: number; } export interface AutoDisableChannel { enabled: boolean; statuses: AutoDisableChannelStatus[]; } export interface RetryPolicy { maxChannelRetries: number; maxSingleChannelRetries: number; retryDelayMs: number; loadBalancerStrategy: string; enabled: boolean; autoDisableChannel: AutoDisableChannel; } export interface AutoDisableChannelStatusInput { status: number; times: number; } export interface AutoDisableChannelInput { enabled?: boolean; statuses?: AutoDisableChannelStatusInput[]; } export interface RetryPolicyInput { maxChannelRetries?: number; maxSingleChannelRetries?: number; retryDelayMs?: number; loadBalancerStrategy?: string; enabled?: boolean; autoDisableChannel?: AutoDisableChannelInput; } export interface UpdateDefaultDataStorageInput { dataStorageID: string; } export interface SystemModelSettingOnboarding { onboarded: boolean; completedAt?: string; } export interface AutoDisableChannelOnboarding { onboarded: boolean; completedAt?: string; } export interface OnboardingInfo { onboarded: boolean; completedAt?: string; systemModelSetting?: SystemModelSettingOnboarding; autoDisableChannel?: AutoDisableChannelOnboarding; } export interface CompleteOnboardingInput { dummy?: string; } export interface CompleteSystemModelSettingOnboardingInput { dummy?: string; } export interface CompleteAutoDisableChannelOnboardingInput { dummy?: string; } export interface SystemVersion { version: string; commit: string; buildTime: string; goVersion: string; platform: string; uptime: string; } export interface VersionCheck { currentVersion: string; latestVersion: string; hasUpdate: boolean; releaseUrl: string; } // Hooks export function useBrandSettings() { const { handleError } = useErrorHandler(); return useQuery({ queryKey: ['brandSettings'], queryFn: async () => { try { const data = await graphqlRequest<{ brandSettings: BrandSettings }>(BRAND_SETTINGS_QUERY); return data.brandSettings; } catch (error) { handleError(error, i18n.t('common.errors.internalServerError')); throw error; } }, }); } export function useStoragePolicy() { const { handleError } = useErrorHandler(); return useQuery({ queryKey: ['storagePolicy'], queryFn: async () => { try { const data = await graphqlRequest<{ storagePolicy: StoragePolicy }>(STORAGE_POLICY_QUERY); return data.storagePolicy; } catch (error) { handleError(error, i18n.t('common.errors.internalServerError')); throw error; } }, }); } export function useUpdateBrandSettings() { const queryClient = useQueryClient(); return useMutation({ mutationFn: async (input: UpdateBrandSettingsInput) => { const data = await graphqlRequest<{ updateBrandSettings: boolean }>(UPDATE_BRAND_SETTINGS_MUTATION, { input }); return data.updateBrandSettings; }, onSuccess: () => { queryClient.invalidateQueries({ queryKey: ['brandSettings'] }); toast.success(i18n.t('common.success.systemUpdated')); }, onError: () => { toast.error(i18n.t('common.errors.systemUpdateFailed')); }, }); } export function useUpdateStoragePolicy() { const queryClient = useQueryClient(); return useMutation({ mutationFn: async (input: UpdateStoragePolicyInput) => { const data = await graphqlRequest<{ updateStoragePolicy: boolean }>(UPDATE_STORAGE_POLICY_MUTATION, { input }); return data.updateStoragePolicy; }, onSuccess: () => { queryClient.invalidateQueries({ queryKey: ['storagePolicy'] }); toast.success(i18n.t('common.success.systemUpdated')); }, onError: () => { toast.error(i18n.t('common.errors.systemUpdateFailed')); }, }); } export function useRetryPolicy() { const { handleError } = useErrorHandler(); return useQuery({ queryKey: ['retryPolicy'], queryFn: async () => { try { const data = await graphqlRequest<{ retryPolicy: RetryPolicy }>(RETRY_POLICY_QUERY); return data.retryPolicy; } catch (error) { handleError(error, i18n.t('common.errors.internalServerError')); throw error; } }, }); } export function useUpdateRetryPolicy() { const queryClient = useQueryClient(); return useMutation({ mutationFn: async (input: RetryPolicyInput) => { const data = await graphqlRequest<{ updateRetryPolicy: boolean }>(UPDATE_RETRY_POLICY_MUTATION, { input }); return data.updateRetryPolicy; }, onSuccess: () => { queryClient.invalidateQueries({ queryKey: ['retryPolicy'] }); toast.success(i18n.t('common.success.systemUpdated')); }, onError: () => { toast.error(i18n.t('common.errors.systemUpdateFailed')); }, }); } export function useDefaultDataStorageID() { const { handleError } = useErrorHandler(); return useQuery({ queryKey: ['defaultDataStorageID'], queryFn: async () => { try { const data = await graphqlRequest<{ defaultDataStorageID: string | null }>(DEFAULT_DATA_STORAGE_QUERY); return data.defaultDataStorageID; } catch (error) { handleError(error, i18n.t('common.errors.internalServerError')); throw error; } }, }); } export function useUpdateDefaultDataStorage() { const queryClient = useQueryClient(); return useMutation({ mutationFn: async (input: UpdateDefaultDataStorageInput) => { const data = await graphqlRequest<{ updateDefaultDataStorage: boolean }>(UPDATE_DEFAULT_DATA_STORAGE_MUTATION, { input }); return data.updateDefaultDataStorage; }, onSuccess: () => { queryClient.invalidateQueries({ queryKey: ['defaultDataStorageID'] }); toast.success(i18n.t('common.success.systemUpdated')); }, onError: () => { toast.error(i18n.t('common.errors.systemUpdateFailed')); }, }); } export function useOnboardingInfo() { return useQuery({ queryKey: ['onboardingInfo'], queryFn: async () => { try { const data = await graphqlRequest<{ onboardingInfo: OnboardingInfo | null }>(ONBOARDING_INFO_QUERY); return data.onboardingInfo; } catch (error) { return { onboarded: true, completedAt: new Date().toISOString(), }; } }, }); } export function useCompleteOnboarding() { const queryClient = useQueryClient(); return useMutation({ mutationFn: async (input?: CompleteOnboardingInput) => { const data = await graphqlRequest<{ completeOnboarding: boolean }>(COMPLETE_ONBOARDING_MUTATION, { input: input || {} }); return data.completeOnboarding; }, onSuccess: () => { queryClient.invalidateQueries({ queryKey: ['onboardingInfo'] }); }, onError: () => { toast.error(i18n.t('common.errors.onboardingFailed')); }, }); } export function useCompleteSystemModelSettingOnboarding() { const queryClient = useQueryClient(); return useMutation({ mutationFn: async (input?: CompleteSystemModelSettingOnboardingInput) => { const data = await graphqlRequest<{ completeSystemModelSettingOnboarding: boolean }>( COMPLETE_SYSTEM_MODEL_SETTING_ONBOARDING_MUTATION, { input: input || {} } ); return data.completeSystemModelSettingOnboarding; }, onSuccess: () => { queryClient.invalidateQueries({ queryKey: ['onboardingInfo'] }); }, onError: () => { toast.error(i18n.t('common.errors.onboardingFailed')); }, }); } export function useCompleteAutoDisableChannelOnboarding() { const queryClient = useQueryClient(); return useMutation({ mutationFn: async (input?: CompleteAutoDisableChannelOnboardingInput) => { const data = await graphqlRequest<{ completeAutoDisableChannelOnboarding: boolean }>( COMPLETE_AUTO_DISABLE_CHANNEL_ONBOARDING_MUTATION, { input: input || {} } ); return data.completeAutoDisableChannelOnboarding; }, onSuccess: () => { queryClient.invalidateQueries({ queryKey: ['onboardingInfo'] }); }, onError: () => { toast.error(i18n.t('common.errors.onboardingFailed')); }, }); } export function useSystemVersion() { return useQuery({ queryKey: ['systemVersion'], queryFn: async () => { const data = await graphqlRequest<{ systemVersion: SystemVersion }>(SYSTEM_VERSION_QUERY); return data.systemVersion; }, }); } export function useCheckForUpdate() { return useQuery({ queryKey: ['checkForUpdate'], queryFn: async () => { const data = await graphqlRequest<{ checkForUpdate: VersionCheck }>(CHECK_FOR_UPDATE_QUERY); return data.checkForUpdate; }, retry: false, staleTime: 60 * 60 * 1000, // 1 hour }); } // Model Settings const MODEL_SETTINGS_QUERY = ` query ModelSettings { systemModelSettings { fallbackToChannelsOnModelNotFound queryAllChannelModels } } `; const UPDATE_MODEL_SETTINGS_MUTATION = ` mutation UpdateModelSettings($input: UpdateSystemModelSettingsInput!) { updateSystemModelSettings(input: $input) } `; const CHANNEL_SETTINGS_QUERY = ` query SystemChannelSettings { systemChannelSettings { probe { enabled frequency } } } `; const UPDATE_CHANNEL_SETTINGS_MUTATION = ` mutation UpdateChannelSettings($input: UpdateSystemChannelSettingsInput!) { updateSystemChannelSettings(input: $input) } `; const SYSTEM_GENERAL_SETTINGS_QUERY = ` query SystemGeneralSettings { systemGeneralSettings { currencyCode timezone } } `; const UPDATE_SYSTEM_GENERAL_SETTINGS_MUTATION = ` mutation UpdateSystemGeneralSettings($input: UpdateSystemGeneralSettingsInput!) { updateSystemGeneralSettings(input: $input) } `; export interface ModelSettings { fallbackToChannelsOnModelNotFound: boolean; queryAllChannelModels: boolean; } export interface UpdateModelSettingsInput { fallbackToChannelsOnModelNotFound?: boolean; queryAllChannelModels?: boolean; } export function useModelSettings() { const { handleError } = useErrorHandler(); return useQuery({ queryKey: ['modelSettings'], queryFn: async () => { try { const data = await graphqlRequest<{ systemModelSettings: ModelSettings }>(MODEL_SETTINGS_QUERY); return data.systemModelSettings; } catch (error) { handleError(error, i18n.t('common.errors.internalServerError')); throw error; } }, }); } export function useUpdateModelSettings() { const queryClient = useQueryClient(); return useMutation({ mutationFn: async (input: UpdateModelSettingsInput) => { const data = await graphqlRequest<{ updateSystemModelSettings: boolean }>(UPDATE_MODEL_SETTINGS_MUTATION, { input }); return data.updateSystemModelSettings; }, onSuccess: () => { queryClient.invalidateQueries({ queryKey: ['modelSettings'] }); toast.success(i18n.t('common.success.systemUpdated')); }, onError: () => { toast.error(i18n.t('common.errors.systemUpdateFailed')); }, }); } export type ProbeFrequency = 'ONE_MINUTE' | 'FIVE_MINUTES' | 'THIRTY_MINUTES' | 'ONE_HOUR'; export interface ChannelProbeSetting { enabled: boolean; frequency: ProbeFrequency; } export interface ChannelSetting { probe: ChannelProbeSetting; } export interface UpdateChannelProbeSettingInput { enabled?: boolean; frequency?: ProbeFrequency; } export interface UpdateSystemChannelSettingsInput { probe?: UpdateChannelProbeSettingInput; } export function useChannelSetting() { const { handleError } = useErrorHandler(); return useQuery({ queryKey: ['channelSetting'], queryFn: async () => { try { const data = await graphqlRequest<{ systemChannelSettings: ChannelSetting }>(CHANNEL_SETTINGS_QUERY); return data.systemChannelSettings; } catch (error) { handleError(error, i18n.t('common.errors.internalServerError')); throw error; } }, }); } export function useUpdateChannelSetting() { const queryClient = useQueryClient(); return useMutation({ mutationFn: async (input: UpdateSystemChannelSettingsInput) => { const data = await graphqlRequest<{ updateSystemChannelSettings: boolean }>(UPDATE_CHANNEL_SETTINGS_MUTATION, { input }); return data.updateSystemChannelSettings; }, onSuccess: () => { queryClient.invalidateQueries({ queryKey: ['channelSetting'] }); queryClient.invalidateQueries({ queryKey: ['channelProbeData'] }); toast.success(i18n.t('common.success.systemUpdated')); }, onError: () => { toast.error(i18n.t('common.errors.systemUpdateFailed')); }, }); } export function useGeneralSettings() { const { handleError } = useErrorHandler(); return useQuery({ queryKey: ['generalSettings'], queryFn: async () => { try { const data = await graphqlRequest<{ systemGeneralSettings: SystemGeneralSettings }>(SYSTEM_GENERAL_SETTINGS_QUERY); return data.systemGeneralSettings; } catch (error) { handleError(error, i18n.t('common.errors.internalServerError')); throw error; } }, }); } export function useUpdateGeneralSettings() { const queryClient = useQueryClient(); return useMutation({ mutationFn: async (input: UpdateSystemGeneralSettingsInput) => { const data = await graphqlRequest<{ updateSystemGeneralSettings: boolean }>(UPDATE_SYSTEM_GENERAL_SETTINGS_MUTATION, { input }); return data.updateSystemGeneralSettings; }, onSuccess: () => { queryClient.invalidateQueries({ queryKey: ['generalSettings'] }); toast.success(i18n.t('common.success.systemUpdated')); }, onError: () => { toast.error(i18n.t('common.errors.systemUpdateFailed')); }, }); } // Backup and Restore const BACKUP_MUTATION = ` mutation Backup($input: BackupOptionsInput!) { backup(input: $input) { success data message } } `; const RESTORE_MUTATION = ` mutation Restore($file: Upload!, $input: RestoreOptionsInput!) { restore(file: $file, input: $input) { success message } } `; export interface BackupOptionsInput { includeChannels: boolean; includeModelPrices: boolean; includeModels: boolean; includeAPIKeys: boolean; } export interface BackupPayload { success: boolean; data?: string; message?: string; } export interface RestoreOptionsInput { includeChannels: boolean; includeModelPrices: boolean; includeModels: boolean; includeAPIKeys: boolean; channelConflictStrategy: 'skip' | 'overwrite' | 'error'; modelConflictStrategy: 'skip' | 'overwrite' | 'error'; modelPriceConflictStrategy: 'skip' | 'overwrite' | 'error'; apiKeyConflictStrategy: 'skip' | 'overwrite' | 'error'; } export interface RestorePayload { success: boolean; message?: string; } export function useBackup() { return useMutation({ mutationFn: async (input: BackupOptionsInput) => { const data = await graphqlRequest<{ backup: BackupPayload }>(BACKUP_MUTATION, { input }); return data.backup; }, onSuccess: (data) => { if (data.success && data.data) { const blob = new Blob([data.data], { type: 'application/json' }); const url = URL.createObjectURL(blob); const a = document.createElement('a'); a.href = url; const timestamp = new Date().toISOString().replace(/[:.]/g, '-'); a.download = `axonhub-backup-${timestamp}.json`; document.body.appendChild(a); a.click(); document.body.removeChild(a); URL.revokeObjectURL(url); toast.success(data.message || i18n.t('system.backup.success')); } else { toast.error(data.message || i18n.t('system.backup.failed')); } }, onError: () => { toast.error(i18n.t('system.backup.failed')); }, }); } export function useRestore() { const queryClient = useQueryClient(); return useMutation({ mutationFn: async ({ file, input }: { file: File; input: RestoreOptionsInput }) => { const formData = new FormData(); formData.append('operations', JSON.stringify({ query: RESTORE_MUTATION, variables: { file: null, input } })); formData.append('map', JSON.stringify({ '0': ['variables.file'] })); formData.append('0', file); const token = getTokenFromStorage(); const response = await fetch('/admin/graphql', { method: 'POST', headers: { 'Authorization': token ? `Bearer ${token}` : '', }, body: formData, }); const result = await response.json(); if (result.errors) { throw new Error(result.errors[0].message); } return result.data.restore as RestorePayload; }, onSuccess: (data) => { if (data.success) { queryClient.invalidateQueries(); toast.success(data.message || i18n.t('system.restore.success')); } else { toast.error(data.message || i18n.t('system.restore.failed')); } }, onError: (error: Error) => { toast.error(error.message || i18n.t('system.restore.failed')); }, }); } // Auto Backup Settings const AUTO_BACKUP_SETTINGS_QUERY = ` query AutoBackupSettings { autoBackupSettings { enabled frequency dataStorageID includeChannels includeModels includeAPIKeys includeModelPrices retentionDays lastBackupAt lastBackupError } } `; const UPDATE_AUTO_BACKUP_SETTINGS_MUTATION = ` mutation UpdateAutoBackupSettings($input: UpdateAutoBackupSettingsInput!) { updateAutoBackupSettings(input: $input) } `; const TRIGGER_AUTO_BACKUP_MUTATION = ` mutation TriggerAutoBackup { triggerAutoBackup { success message } } `; export type BackupFrequency = 'daily' | 'weekly' | 'monthly'; export interface AutoBackupSettings { enabled: boolean; frequency: BackupFrequency; dataStorageID: number; includeChannels: boolean; includeModels: boolean; includeAPIKeys: boolean; includeModelPrices: boolean; retentionDays: number; lastBackupAt?: string; lastBackupError?: string; } export interface UpdateAutoBackupSettingsInput { enabled?: boolean; frequency?: BackupFrequency; dataStorageID?: number; includeChannels?: boolean; includeModels?: boolean; includeAPIKeys?: boolean; includeModelPrices?: boolean; retentionDays?: number; } export function useAutoBackupSettings() { const { handleError } = useErrorHandler(); return useQuery({ queryKey: ['autoBackupSettings'], queryFn: async () => { try { const data = await graphqlRequest<{ autoBackupSettings: AutoBackupSettings }>(AUTO_BACKUP_SETTINGS_QUERY); return data.autoBackupSettings; } catch (error) { handleError(error, i18n.t('common.errors.internalServerError')); throw error; } }, }); } export function useUpdateAutoBackupSettings() { const queryClient = useQueryClient(); return useMutation({ mutationFn: async (input: UpdateAutoBackupSettingsInput) => { const data = await graphqlRequest<{ updateAutoBackupSettings: boolean }>(UPDATE_AUTO_BACKUP_SETTINGS_MUTATION, { input }); return data.updateAutoBackupSettings; }, onSuccess: () => { queryClient.invalidateQueries({ queryKey: ['autoBackupSettings'] }); toast.success(i18n.t('common.success.systemUpdated')); }, onError: () => { toast.error(i18n.t('common.errors.systemUpdateFailed')); }, }); } export function useTriggerAutoBackup() { const queryClient = useQueryClient(); return useMutation({ mutationFn: async () => { const data = await graphqlRequest<{ triggerAutoBackup: { success: boolean; message?: string } }>( TRIGGER_AUTO_BACKUP_MUTATION ); return data.triggerAutoBackup; }, onSuccess: (data) => { queryClient.invalidateQueries({ queryKey: ['autoBackupSettings'] }); if (data.success) { toast.success(i18n.t('system.autoBackup.triggerSuccess')); } else { toast.error(data.message || i18n.t('system.autoBackup.triggerFailed')); } }, onError: () => { toast.error(i18n.t('system.autoBackup.triggerFailed')); }, }); }