| 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';
|
|
|
|
|
| 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)
|
| }
|
| `;
|
|
|
|
|
| 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;
|
| }
|
|
|
|
|
| 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,
|
| });
|
| }
|
|
|
|
|
| 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'));
|
| },
|
| });
|
| }
|
|
|
|
|
| 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'));
|
| },
|
| });
|
| }
|
|
|
|
|
| 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'));
|
| },
|
| });
|
| }
|
|
|