feat(auth-files): enhance error handling for invalid JSON and refactor file upload logic

This commit is contained in:
Supra4E8C
2026-03-07 16:06:23 +08:00
Unverified
parent ccf90f88dd
commit cb8208e0f7
3 changed files with 82 additions and 29 deletions
@@ -1,6 +1,6 @@
import { useCallback, useEffect, useRef, useState, type ChangeEvent, type RefObject } from 'react';
import { useTranslation } from 'react-i18next';
import { authFilesApi } from '@/services/api';
import { authFilesApi, isAuthFileInvalidJsonObjectError } from '@/services/api';
import { apiClient } from '@/services/api/client';
import { useNotificationStore } from '@/stores';
import type { AuthFileItem } from '@/types';
@@ -60,6 +60,16 @@ export function useAuthFilesData(options: UseAuthFilesDataOptions): UseAuthFiles
const fileInputRef = useRef<HTMLInputElement | null>(null);
const selectionCount = selectedFiles.size;
const resolveStatusUpdateErrorMessage = useCallback(
(err: unknown) => {
if (isAuthFileInvalidJsonObjectError(err)) {
return t('auth_files.prefix_proxy_invalid_json');
}
return err instanceof Error ? err.message : '';
},
[t]
);
const toggleSelect = useCallback((name: string) => {
setSelectedFiles((prev) => {
const next = new Set(prev);
@@ -337,10 +347,9 @@ export function useAuthFilesData(options: UseAuthFilesDataOptions): UseAuthFiles
setFiles((prev) => prev.map((f) => (f.name === name ? { ...f, disabled: nextDisabled } : f)));
try {
const res = await authFilesApi.setStatus(name, nextDisabled);
setFiles((prev) =>
prev.map((f) => (f.name === name ? { ...f, disabled: res.disabled } : f))
);
await authFilesApi.setStatus(name, nextDisabled);
await loadFiles();
void refreshKeyStats().catch(() => {});
showNotification(
enabled
? t('auth_files.status_enabled_success', { name })
@@ -348,7 +357,7 @@ export function useAuthFilesData(options: UseAuthFilesDataOptions): UseAuthFiles
'success'
);
} catch (err: unknown) {
const errorMessage = err instanceof Error ? err.message : '';
const errorMessage = resolveStatusUpdateErrorMessage(err);
setFiles((prev) =>
prev.map((f) => (f.name === name ? { ...f, disabled: previousDisabled } : f))
);
@@ -362,7 +371,7 @@ export function useAuthFilesData(options: UseAuthFilesDataOptions): UseAuthFiles
});
}
},
[showNotification, t]
[loadFiles, refreshKeyStats, resolveStatusUpdateErrorMessage, showNotification, t]
);
const batchSetStatus = useCallback(
@@ -372,6 +381,9 @@ export function useAuthFilesData(options: UseAuthFilesDataOptions): UseAuthFiles
const targetNames = new Set(uniqueNames);
const nextDisabled = !enabled;
const previousDisabled = new Map(
files.map((file) => [file.name, file.disabled === true] as const)
);
setFiles((prev) =>
prev.map((file) =>
@@ -385,31 +397,26 @@ export function useAuthFilesData(options: UseAuthFilesDataOptions): UseAuthFiles
let successCount = 0;
let failCount = 0;
const failedNames = new Set<string>();
const confirmedDisabled = new Map<string, boolean>();
results.forEach((result, index) => {
const name = uniqueNames[index];
results.forEach((result) => {
if (result.status === 'fulfilled') {
successCount++;
confirmedDisabled.set(name, result.value.disabled);
} else {
failCount++;
failedNames.add(name);
}
});
setFiles((prev) =>
prev.map((file) => {
if (failedNames.has(file.name)) {
return { ...file, disabled: !nextDisabled };
}
if (confirmedDisabled.has(file.name)) {
return { ...file, disabled: confirmedDisabled.get(file.name) };
}
return file;
})
);
if (successCount > 0) {
await loadFiles();
void refreshKeyStats().catch(() => {});
} else {
setFiles((prev) =>
prev.map((file) => {
if (!targetNames.has(file.name)) return file;
return { ...file, disabled: previousDisabled.get(file.name) === true };
})
);
}
if (failCount === 0) {
showNotification(t('auth_files.batch_status_success', { count: successCount }), 'success');
@@ -422,7 +429,7 @@ export function useAuthFilesData(options: UseAuthFilesDataOptions): UseAuthFiles
deselectAll();
},
[deselectAll, showNotification, t]
[deselectAll, files, loadFiles, refreshKeyStats, showNotification, t]
);
const batchDelete = useCallback(