194 lines
5.4 KiB
JavaScript
194 lines
5.4 KiB
JavaScript
import { useCallback, useEffect } from 'react';
|
|
import apiClient from '../api/client';
|
|
import { getHealth } from '../api/health';
|
|
|
|
const HEALTH_POLL_INTERVAL_MS = 30000;
|
|
|
|
export default function useAppAuthLifecycle({
|
|
ensureCanOperate,
|
|
clearRadarSearchResults,
|
|
radarSearchRequestSeqRef,
|
|
prevLicenseOkRef,
|
|
aoeLayerRef,
|
|
activeLayersRef,
|
|
radarPreviewLayersRef,
|
|
setHasRadarSearched,
|
|
setCurrentUser,
|
|
setAuthChecked,
|
|
setIsGlobalLocked,
|
|
setPendingTaskIds,
|
|
setLicenseLoading,
|
|
setLicenseStatus,
|
|
setHealthLoading,
|
|
setHealthError,
|
|
setHealthStatus,
|
|
setLicenseFileName,
|
|
setLicenseUploadStatus,
|
|
setAoiLayer,
|
|
setAllData,
|
|
setRadarPagination,
|
|
}) {
|
|
const fetchCurrentUser = useCallback(async () => {
|
|
try {
|
|
const response = await apiClient.get('/auth/me');
|
|
setCurrentUser(response.data || null);
|
|
} catch {
|
|
setCurrentUser(null);
|
|
} finally {
|
|
setAuthChecked(true);
|
|
}
|
|
}, [setCurrentUser, setAuthChecked]);
|
|
|
|
const handleLoginSuccess = useCallback(async () => {
|
|
await fetchCurrentUser();
|
|
}, [fetchCurrentUser]);
|
|
|
|
const handleLogout = useCallback(async () => {
|
|
try {
|
|
await apiClient.post('/auth/logout');
|
|
} catch (error) {
|
|
console.error('Logout failed:', error);
|
|
} finally {
|
|
radarSearchRequestSeqRef.current += 1;
|
|
setHasRadarSearched(false);
|
|
clearRadarSearchResults();
|
|
setCurrentUser(null);
|
|
setIsGlobalLocked(false);
|
|
setPendingTaskIds([]);
|
|
prevLicenseOkRef.current = false;
|
|
setAuthChecked(true);
|
|
}
|
|
}, [
|
|
clearRadarSearchResults,
|
|
radarSearchRequestSeqRef,
|
|
setHasRadarSearched,
|
|
setCurrentUser,
|
|
setIsGlobalLocked,
|
|
setPendingTaskIds,
|
|
prevLicenseOkRef,
|
|
setAuthChecked,
|
|
]);
|
|
|
|
const fetchLicenseStatus = useCallback(async () => {
|
|
try {
|
|
setLicenseLoading(true);
|
|
const response = await apiClient.get('/license/status');
|
|
const data = response.data || {};
|
|
if (!data.ok && !data.reason) {
|
|
data.reason = '未授权';
|
|
}
|
|
setLicenseStatus(data);
|
|
} catch (error) {
|
|
setLicenseStatus({
|
|
ok: false,
|
|
reason: error.response?.data?.detail || '无法获取授权状态',
|
|
});
|
|
} finally {
|
|
setLicenseLoading(false);
|
|
}
|
|
}, [setLicenseLoading, setLicenseStatus]);
|
|
|
|
const fetchHealthStatus = useCallback(async (options = {}) => {
|
|
const { refresh = false, silent = false } = options;
|
|
try {
|
|
if (!silent) {
|
|
setHealthLoading(true);
|
|
}
|
|
setHealthError('');
|
|
const data = await getHealth(refresh ? { refresh: true } : {});
|
|
setHealthStatus(data || null);
|
|
} catch (error) {
|
|
setHealthError(error.response?.data?.detail || '运维自检失败');
|
|
setHealthStatus(null);
|
|
} finally {
|
|
if (!silent) {
|
|
setHealthLoading(false);
|
|
}
|
|
}
|
|
}, [setHealthLoading, setHealthError, setHealthStatus]);
|
|
|
|
const handleLicenseUpload = useCallback(async (file) => {
|
|
if (!file) return;
|
|
if (!ensureCanOperate()) return;
|
|
try {
|
|
setLicenseFileName(file.name);
|
|
setLicenseUploadStatus({ type: 'info', message: '正在上传授权文件...' });
|
|
const form = new FormData();
|
|
form.append('file', file);
|
|
const response = await apiClient.post('/license/upload', form);
|
|
setLicenseUploadStatus({ type: 'success', message: response.data?.message || '授权文件已上传' });
|
|
await fetchLicenseStatus();
|
|
} catch (error) {
|
|
setLicenseUploadStatus({ type: 'error', message: error.response?.data?.detail || '授权文件上传失败' });
|
|
}
|
|
}, [ensureCanOperate, setLicenseFileName, setLicenseUploadStatus, fetchLicenseStatus]);
|
|
|
|
useEffect(() => {
|
|
const interceptorId = apiClient.interceptors.response.use(
|
|
(response) => response,
|
|
(error) => {
|
|
if (error?.response?.status === 401) {
|
|
radarSearchRequestSeqRef.current += 1;
|
|
setHasRadarSearched(false);
|
|
if (aoeLayerRef.current) {
|
|
aoeLayerRef.current.remove();
|
|
aoeLayerRef.current = null;
|
|
}
|
|
setAoiLayer(null);
|
|
Object.values(activeLayersRef.current).forEach((layer) => layer.remove());
|
|
activeLayersRef.current = {};
|
|
Object.values(radarPreviewLayersRef.current).forEach((layer) => layer.remove());
|
|
radarPreviewLayersRef.current = {};
|
|
setAllData([]);
|
|
setRadarPagination((prev) => ({
|
|
...prev,
|
|
total: 0,
|
|
offset: 0,
|
|
hasMore: false,
|
|
}));
|
|
setCurrentUser(null);
|
|
setAuthChecked(true);
|
|
prevLicenseOkRef.current = false;
|
|
}
|
|
return Promise.reject(error);
|
|
}
|
|
);
|
|
return () => {
|
|
apiClient.interceptors.response.eject(interceptorId);
|
|
};
|
|
}, [
|
|
radarSearchRequestSeqRef,
|
|
setHasRadarSearched,
|
|
aoeLayerRef,
|
|
setAoiLayer,
|
|
activeLayersRef,
|
|
radarPreviewLayersRef,
|
|
setAllData,
|
|
setRadarPagination,
|
|
setCurrentUser,
|
|
setAuthChecked,
|
|
prevLicenseOkRef,
|
|
]);
|
|
|
|
useEffect(() => {
|
|
fetchCurrentUser();
|
|
fetchLicenseStatus();
|
|
}, [fetchCurrentUser, fetchLicenseStatus]);
|
|
|
|
useEffect(() => {
|
|
void fetchHealthStatus();
|
|
const interval = setInterval(() => {
|
|
void fetchHealthStatus({ silent: true });
|
|
}, HEALTH_POLL_INTERVAL_MS);
|
|
return () => clearInterval(interval);
|
|
}, [fetchHealthStatus]);
|
|
|
|
return {
|
|
handleLoginSuccess,
|
|
handleLogout,
|
|
fetchLicenseStatus,
|
|
fetchHealthStatus,
|
|
handleLicenseUpload,
|
|
};
|
|
}
|