Remove application license gate
This commit is contained in:
+5
-31
@@ -3802,29 +3802,6 @@ input[type="checkbox"] {
|
||||
min-width: 0;
|
||||
}
|
||||
|
||||
.status-license-chip {
|
||||
display: inline-flex;
|
||||
align-items: center;
|
||||
padding: 3px 7px;
|
||||
border-radius: 999px;
|
||||
border: 1px solid var(--color-border);
|
||||
background: var(--color-panel-muted);
|
||||
color: var(--color-text-secondary);
|
||||
line-height: 1.2;
|
||||
}
|
||||
|
||||
.status-license-chip.ok {
|
||||
border-color: rgba(22, 163, 74, 0.24);
|
||||
background: rgba(22, 163, 74, 0.08);
|
||||
color: #166534;
|
||||
}
|
||||
|
||||
.status-license-chip.fail {
|
||||
border-color: rgba(220, 38, 38, 0.24);
|
||||
background: rgba(220, 38, 38, 0.08);
|
||||
color: #991b1b;
|
||||
}
|
||||
|
||||
.status-actions {
|
||||
display: flex;
|
||||
justify-content: flex-end;
|
||||
@@ -3891,12 +3868,6 @@ input[type="checkbox"] {
|
||||
white-space: nowrap;
|
||||
}
|
||||
|
||||
.status-license {
|
||||
font-size: 0.75em;
|
||||
color: var(--color-text-muted);
|
||||
white-space: nowrap;
|
||||
}
|
||||
|
||||
.status-task-bar {
|
||||
width: 120px;
|
||||
height: 6px;
|
||||
@@ -6194,12 +6165,15 @@ input[type="checkbox"] {
|
||||
|
||||
.production-workspace-shell .dinsar-production-shell,
|
||||
.production-workspace-shell .dinsar-products-page {
|
||||
width: 100%;
|
||||
max-width: none;
|
||||
margin-left: 0;
|
||||
margin-right: 0;
|
||||
box-sizing: border-box;
|
||||
}
|
||||
|
||||
.production-workspace-shell .dinsar-production-shell {
|
||||
.production-workspace-shell .dinsar-production-shell,
|
||||
.production-workspace-shell .dinsar-products-page {
|
||||
padding: 0;
|
||||
}
|
||||
|
||||
@@ -7090,7 +7064,7 @@ input[type="checkbox"] {
|
||||
}
|
||||
|
||||
.dinsar-products-catalog-section .dinsar-catalog-workspace {
|
||||
grid-template-columns: minmax(360px, 420px) minmax(0, 1fr);
|
||||
grid-template-columns: minmax(380px, 0.36fr) minmax(0, 1fr);
|
||||
}
|
||||
|
||||
.sbas-products-page {
|
||||
|
||||
+7
-37
@@ -184,22 +184,12 @@ function App() {
|
||||
// --- Zustand stores ---
|
||||
const {
|
||||
currentUser, setCurrentUser, authChecked, setAuthChecked,
|
||||
licenseStatus, setLicenseStatus, licenseLoading, setLicenseLoading,
|
||||
licenseUploadStatus, setLicenseUploadStatus, licenseFileName, setLicenseFileName,
|
||||
healthStatus, setHealthStatus, healthLoading, setHealthLoading, healthError, setHealthError,
|
||||
} = useAuthStore(useShallow((state) => ({
|
||||
currentUser: state.currentUser,
|
||||
setCurrentUser: state.setCurrentUser,
|
||||
authChecked: state.authChecked,
|
||||
setAuthChecked: state.setAuthChecked,
|
||||
licenseStatus: state.licenseStatus,
|
||||
setLicenseStatus: state.setLicenseStatus,
|
||||
licenseLoading: state.licenseLoading,
|
||||
setLicenseLoading: state.setLicenseLoading,
|
||||
licenseUploadStatus: state.licenseUploadStatus,
|
||||
setLicenseUploadStatus: state.setLicenseUploadStatus,
|
||||
licenseFileName: state.licenseFileName,
|
||||
setLicenseFileName: state.setLicenseFileName,
|
||||
healthStatus: state.healthStatus,
|
||||
setHealthStatus: state.setHealthStatus,
|
||||
healthLoading: state.healthLoading,
|
||||
@@ -405,13 +395,12 @@ function App() {
|
||||
const floodPairPreviewLayerRef = useRef(null);
|
||||
const floodVectorLayersRef = useRef({});
|
||||
const mapRegionLayerRef = useRef(null);
|
||||
const prevLicenseOkRef = useRef(false);
|
||||
const prevAppReadyRef = useRef(false);
|
||||
const initializeAppDataRef = useRef(null);
|
||||
const addLogRef = useRef(addLog);
|
||||
const handleTaskCompletionRef = useRef(null);
|
||||
const updateLayerTooltipRef = useRef(null);
|
||||
const radarSearchRequestSeqRef = useRef(0);
|
||||
const licenseFileRef = useRef(null);
|
||||
const foundPairsRef = useRef(foundPairs);
|
||||
const hazardLayersRef = useRef({});
|
||||
const dinsarResultLayersRef = useRef({});
|
||||
@@ -712,14 +701,10 @@ function App() {
|
||||
const {
|
||||
handleLoginSuccess,
|
||||
handleLogout,
|
||||
fetchLicenseStatus,
|
||||
fetchHealthStatus,
|
||||
handleLicenseUpload,
|
||||
} = useAppAuthLifecycle({
|
||||
ensureCanOperate,
|
||||
clearRadarSearchResults,
|
||||
radarSearchRequestSeqRef,
|
||||
prevLicenseOkRef,
|
||||
aoeLayerRef,
|
||||
activeLayersRef,
|
||||
radarPreviewLayersRef,
|
||||
@@ -727,13 +712,9 @@ function App() {
|
||||
setCurrentUser,
|
||||
setAuthChecked,
|
||||
setPendingTaskIds,
|
||||
setLicenseLoading,
|
||||
setLicenseStatus,
|
||||
setHealthLoading,
|
||||
setHealthError,
|
||||
setHealthStatus,
|
||||
setLicenseFileName,
|
||||
setLicenseUploadStatus,
|
||||
setAoiLayer,
|
||||
setAllData,
|
||||
setRadarPagination,
|
||||
@@ -838,14 +819,14 @@ function App() {
|
||||
}, [baseLayerKey, currentUser?.id]);
|
||||
|
||||
useEffect(() => {
|
||||
if (licenseLoading || !authChecked) return;
|
||||
const isOk = !!licenseStatus?.ok && !!currentUser;
|
||||
const prevOk = prevLicenseOkRef.current;
|
||||
if (isOk && !prevOk) {
|
||||
if (!authChecked) return;
|
||||
const isReady = !!currentUser;
|
||||
const wasReady = prevAppReadyRef.current;
|
||||
if (isReady && !wasReady) {
|
||||
initializeAppDataRef.current?.();
|
||||
}
|
||||
prevLicenseOkRef.current = isOk;
|
||||
}, [licenseLoading, licenseStatus?.ok, authChecked, currentUser]);
|
||||
prevAppReadyRef.current = isReady;
|
||||
}, [authChecked, currentUser]);
|
||||
|
||||
const {
|
||||
fetchDinsarResults,
|
||||
@@ -917,7 +898,6 @@ function App() {
|
||||
handleCancelActiveTasks,
|
||||
} = useGlobalTaskControl({
|
||||
currentUser,
|
||||
licenseOk: !!licenseStatus?.ok,
|
||||
activeTasks,
|
||||
setActiveTasks,
|
||||
setRuntimeSummary,
|
||||
@@ -2002,7 +1982,6 @@ function App() {
|
||||
const selectedPairsCount = foundPairs.filter(p => p.isSelected).length;
|
||||
const hasEnoughRadarScenesForPlanning = radarImagingDates.length >= 2;
|
||||
|
||||
const licenseOk = !!licenseStatus?.ok;
|
||||
const avgTaskProgress = activeTasks.length
|
||||
? Math.round(activeTasks.reduce((sum, t) => sum + (t.progress || 0), 0) / activeTasks.length)
|
||||
: 0;
|
||||
@@ -2118,7 +2097,6 @@ function App() {
|
||||
activeTasks={activeTasks}
|
||||
avgTaskProgress={avgTaskProgress}
|
||||
runtimeSummary={runtimeSummary}
|
||||
licenseStatus={licenseStatus}
|
||||
healthStatus={healthStatus}
|
||||
healthLoading={healthLoading}
|
||||
healthError={healthError}
|
||||
@@ -2136,7 +2114,6 @@ function App() {
|
||||
currentUser={currentUser}
|
||||
language={language}
|
||||
apiEndpoint={apiClient.defaults.baseURL}
|
||||
licenseOk={licenseOk}
|
||||
foundPairs={foundPairs}
|
||||
dinsarTotal={dinsarPagination.total}
|
||||
selectedPairsCount={selectedPairsCount}
|
||||
@@ -2192,14 +2169,7 @@ function App() {
|
||||
onPairingAoiModeChange={handlePairingAoiModeChange}
|
||||
onPairingProvinceChange={handlePairingProvinceChange}
|
||||
onPairingCityChange={handlePairingCityChange}
|
||||
licenseLoading={licenseLoading}
|
||||
licenseStatus={licenseStatus}
|
||||
isAdmin={isAdmin}
|
||||
licenseFileRef={licenseFileRef}
|
||||
onUploadFile={handleLicenseUpload}
|
||||
onRefreshLicenseStatus={fetchLicenseStatus}
|
||||
licenseFileName={licenseFileName}
|
||||
licenseUploadStatus={licenseUploadStatus}
|
||||
activeTasks={activeTasks}
|
||||
runtimeSummary={runtimeSummary}
|
||||
showCancelTask={showCancelTask}
|
||||
|
||||
@@ -12,9 +12,6 @@ const ACTION_LABELS = {
|
||||
logout: '退出登录',
|
||||
user_created: '创建用户',
|
||||
user_updated: '更新用户',
|
||||
license_uploaded: '上传授权文件',
|
||||
license_upload_failed: '上传授权失败',
|
||||
license_refreshed: '刷新授权状态',
|
||||
task_queued: '任务入队',
|
||||
batch_created: '创建任务批次',
|
||||
batch_marked_complete: '批次标记完成',
|
||||
|
||||
@@ -27,21 +27,8 @@ const LoginPage = ({ onLoginSuccess }) => {
|
||||
const [password, setPassword] = useState('');
|
||||
const [loading, setLoading] = useState(false);
|
||||
const [message, setMessage] = useState('');
|
||||
const [licenseStatus, setLicenseStatus] = useState(null);
|
||||
const [activeSlide, setActiveSlide] = useState(0);
|
||||
|
||||
useEffect(() => {
|
||||
const checkLicense = async () => {
|
||||
try {
|
||||
const response = await apiClient.get('/license/status');
|
||||
setLicenseStatus(response.data || null);
|
||||
} catch {
|
||||
setLicenseStatus({ ok: false, reason: '无法获取授权状态' });
|
||||
}
|
||||
};
|
||||
checkLicense();
|
||||
}, []);
|
||||
|
||||
useEffect(() => {
|
||||
const timer = window.setInterval(() => {
|
||||
setActiveSlide((current) => (current + 1) % loginSlides.length);
|
||||
@@ -85,12 +72,6 @@ const LoginPage = ({ onLoginSuccess }) => {
|
||||
<p className="login-subtitle">内网受控访问,请使用管理员分配的账号登录。</p>
|
||||
</div>
|
||||
|
||||
{licenseStatus && !licenseStatus.ok && (
|
||||
<div className="login-alert warn">
|
||||
当前授权状态异常:{licenseStatus.reason || '未授权'}
|
||||
</div>
|
||||
)}
|
||||
|
||||
<form onSubmit={handleSubmit} className="login-form-grid">
|
||||
<label className="login-field">
|
||||
<span>用户名</span>
|
||||
|
||||
@@ -1,6 +1,5 @@
|
||||
export { default as apiClient } from './client';
|
||||
export * as authApi from './auth';
|
||||
export * as licenseApi from './license';
|
||||
export * as tasksApi from './tasks';
|
||||
export * as healthApi from './health';
|
||||
export * as radarApi from './radar';
|
||||
|
||||
@@ -1,5 +0,0 @@
|
||||
import apiClient from './client';
|
||||
|
||||
export const getLicenseStatus = () => apiClient.get('/license/status').then(r => r.data);
|
||||
export const uploadLicense = (formData) => apiClient.post('/license/upload', formData).then(r => r.data);
|
||||
export const refreshLicense = () => apiClient.post('/license/refresh').then(r => r.data);
|
||||
@@ -1,106 +0,0 @@
|
||||
export default function LicenseOverlay({
|
||||
licenseLoading,
|
||||
licenseStatus,
|
||||
isAdmin,
|
||||
licenseFileRef,
|
||||
onUploadFile,
|
||||
onRefreshStatus,
|
||||
licenseFileName,
|
||||
licenseUploadStatus,
|
||||
}) {
|
||||
if (!licenseLoading && licenseStatus?.ok) {
|
||||
return null;
|
||||
}
|
||||
|
||||
return (
|
||||
<div style={{
|
||||
position: 'fixed',
|
||||
top: 0,
|
||||
left: 0,
|
||||
width: '100%',
|
||||
height: '100%',
|
||||
background: 'rgba(15, 23, 42, 0.82)',
|
||||
zIndex: 2000,
|
||||
display: 'flex',
|
||||
alignItems: 'center',
|
||||
justifyContent: 'center',
|
||||
}}>
|
||||
<div style={{
|
||||
width: 'min(560px, 90%)',
|
||||
background: '#ffffff',
|
||||
borderRadius: '12px',
|
||||
padding: '24px',
|
||||
boxShadow: '0 20px 60px rgba(15, 23, 42, 0.35)',
|
||||
}}>
|
||||
<h3 style={{ marginTop: 0 }}>
|
||||
{licenseLoading ? '正在验证授权...' : '系统未授权'}
|
||||
</h3>
|
||||
{!licenseLoading && (
|
||||
<>
|
||||
<p style={{ color: '#475569', marginBottom: '12px' }}>
|
||||
失败原因:{licenseStatus?.reason || '授权无效或已过期,请联系管理员。'}
|
||||
</p>
|
||||
{isAdmin ? (
|
||||
<div style={{ display: 'flex', flexDirection: 'column', gap: '8px', marginBottom: '12px' }}>
|
||||
<input
|
||||
type="file"
|
||||
ref={licenseFileRef}
|
||||
accept=".lic"
|
||||
onChange={(e) => onUploadFile(e.target.files?.[0])}
|
||||
style={{ display: 'none' }}
|
||||
/>
|
||||
<div style={{ display: 'flex', gap: '10px', alignItems: 'center', flexWrap: 'wrap' }}>
|
||||
<button
|
||||
className="primary-btn"
|
||||
onClick={() => licenseFileRef.current && licenseFileRef.current.click()}
|
||||
style={{ padding: '8px 14px' }}
|
||||
>
|
||||
选择授权文件
|
||||
</button>
|
||||
<button
|
||||
className="secondary-btn"
|
||||
onClick={onRefreshStatus}
|
||||
style={{ padding: '8px 14px' }}
|
||||
>
|
||||
刷新授权状态
|
||||
</button>
|
||||
<span style={{ fontSize: '0.85em', color: '#64748b' }}>
|
||||
{licenseFileName ? `已选择: ${licenseFileName}` : '未选择文件'}
|
||||
</span>
|
||||
</div>
|
||||
{licenseUploadStatus?.message && (
|
||||
<div style={{
|
||||
fontSize: '0.85em',
|
||||
color:
|
||||
licenseUploadStatus.type === 'error'
|
||||
? '#dc2626'
|
||||
: licenseUploadStatus.type === 'success'
|
||||
? '#16a34a'
|
||||
: '#475569',
|
||||
}}>
|
||||
{licenseUploadStatus.message}
|
||||
</div>
|
||||
)}
|
||||
</div>
|
||||
) : (
|
||||
<div style={{ marginBottom: '12px', color: '#b45309', background: '#fffbeb', border: '1px solid #fcd34d', padding: '8px 10px', borderRadius: '6px' }}>
|
||||
当前账号无上传授权权限,请联系管理员处理授权文件。
|
||||
</div>
|
||||
)}
|
||||
<div style={{ marginTop: '10px', background: '#f8fafc', padding: '10px 12px', borderRadius: '8px', border: '1px solid #e2e8f0' }}>
|
||||
<div style={{ fontWeight: 600, marginBottom: '6px', color: '#334155' }}>授权使用说明</div>
|
||||
<ol style={{ margin: 0, paddingLeft: '18px', color: '#475569', fontSize: '0.85em' }}>
|
||||
<li>确认已在服务器 .env 中配置 LICENSE_SECRET 与 LICENSE_PUBLIC_KEY。</li>
|
||||
<li>上传有效的 .lic 授权文件(与当前机器指纹匹配)。</li>
|
||||
<li>点击“刷新授权状态”确认授权已生效。</li>
|
||||
</ol>
|
||||
</div>
|
||||
</>
|
||||
)}
|
||||
{licenseLoading && (
|
||||
<p style={{ color: '#64748b' }}>请稍候,正在与授权文件进行校验。</p>
|
||||
)}
|
||||
</div>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
@@ -1,6 +1,5 @@
|
||||
import { Suspense, lazy } from 'react';
|
||||
import { useShallow } from 'zustand/react/shallow';
|
||||
import LicenseOverlay from '../LicenseOverlay';
|
||||
import { useDinsarStore, usePairingStore, useUiStore } from '../../store';
|
||||
import { useI18n } from '../../i18n/I18nContext';
|
||||
import { formatYmd } from '../../utils/appUiHelpers';
|
||||
@@ -17,14 +16,7 @@ export default function AppOverlays({
|
||||
onPairingAoiModeChange,
|
||||
onPairingProvinceChange,
|
||||
onPairingCityChange,
|
||||
licenseLoading,
|
||||
licenseStatus,
|
||||
isAdmin,
|
||||
licenseFileRef,
|
||||
onUploadFile,
|
||||
onRefreshLicenseStatus,
|
||||
licenseFileName,
|
||||
licenseUploadStatus,
|
||||
activeTasks,
|
||||
runtimeSummary,
|
||||
showCancelTask,
|
||||
@@ -75,17 +67,6 @@ export default function AppOverlays({
|
||||
</Suspense>
|
||||
)}
|
||||
|
||||
<LicenseOverlay
|
||||
licenseLoading={licenseLoading}
|
||||
licenseStatus={licenseStatus}
|
||||
isAdmin={isAdmin}
|
||||
licenseFileRef={licenseFileRef}
|
||||
onUploadFile={onUploadFile}
|
||||
onRefreshStatus={onRefreshLicenseStatus}
|
||||
licenseFileName={licenseFileName}
|
||||
licenseUploadStatus={licenseUploadStatus}
|
||||
/>
|
||||
|
||||
{showDataInfo && (
|
||||
<Suspense fallback={<ModalLoadingFallback message="正在加载数据详情..." />}>
|
||||
<LazyDataInfoModal
|
||||
|
||||
@@ -41,7 +41,6 @@ export default function AppSidePanel({
|
||||
currentUser,
|
||||
language,
|
||||
apiEndpoint,
|
||||
licenseOk,
|
||||
foundPairs,
|
||||
dinsarTotal,
|
||||
selectedPairsCount,
|
||||
@@ -236,7 +235,7 @@ export default function AppSidePanel({
|
||||
apiEndpoint={apiEndpoint}
|
||||
onTaskStart={taskPanel.onTaskStart}
|
||||
readOnly={isReadOnlyUser}
|
||||
enabled={!!currentUser && licenseOk}
|
||||
enabled={!!currentUser}
|
||||
/>
|
||||
</Suspense>
|
||||
</div>
|
||||
|
||||
@@ -1,6 +1,5 @@
|
||||
import { memo } from 'react';
|
||||
import defaultLogoUrl from '../../logo.jpg';
|
||||
import { formatUtc } from '../../utils/appUiHelpers';
|
||||
|
||||
const ORGANIZATION_NAME = import.meta.env.VITE_APP_ORG_NAME || '黑龙江省自然资源卫星应用技术中心';
|
||||
const SYSTEM_NAME = import.meta.env.VITE_APP_SYSTEM_NAME || '雷达数据生产管理系统';
|
||||
@@ -8,17 +7,14 @@ const SYSTEM_TAGLINE = import.meta.env.VITE_APP_SYSTEM_TAGLINE || '科研工程
|
||||
const LOGO_URL = import.meta.env.VITE_APP_LOGO_URL || defaultLogoUrl;
|
||||
|
||||
function AppStatusHeader({
|
||||
language,
|
||||
currentUser,
|
||||
isAdmin,
|
||||
isReadOnlyUser,
|
||||
activeTasks,
|
||||
avgTaskProgress,
|
||||
runtimeSummary,
|
||||
licenseStatus,
|
||||
onLogout,
|
||||
}) {
|
||||
const licenseOk = !!licenseStatus?.ok;
|
||||
const worker = runtimeSummary?.worker || {};
|
||||
const jobs = runtimeSummary?.jobs || {};
|
||||
const scan = runtimeSummary?.scan || {};
|
||||
@@ -68,14 +64,6 @@ function AppStatusHeader({
|
||||
|
||||
<div className="status-system-meta" aria-label="系统状态摘要">
|
||||
<span>{SYSTEM_TAGLINE}</span>
|
||||
<span className={`status-license-chip ${licenseOk ? 'ok' : 'fail'}`}>
|
||||
{licenseOk ? '已授权' : '未授权'}
|
||||
</span>
|
||||
{licenseStatus?.expires_at && (
|
||||
<span className="status-license">
|
||||
授权至 {formatUtc(licenseStatus.expires_at, language)}
|
||||
</span>
|
||||
)}
|
||||
</div>
|
||||
|
||||
<div className="status-actions">
|
||||
|
||||
@@ -38,10 +38,8 @@ const requestWithTransientRetry = async (
|
||||
};
|
||||
|
||||
export default function useAppAuthLifecycle({
|
||||
ensureCanOperate,
|
||||
clearRadarSearchResults,
|
||||
radarSearchRequestSeqRef,
|
||||
prevLicenseOkRef,
|
||||
aoeLayerRef,
|
||||
activeLayersRef,
|
||||
radarPreviewLayersRef,
|
||||
@@ -49,13 +47,9 @@ export default function useAppAuthLifecycle({
|
||||
setCurrentUser,
|
||||
setAuthChecked,
|
||||
setPendingTaskIds,
|
||||
setLicenseLoading,
|
||||
setLicenseStatus,
|
||||
setHealthLoading,
|
||||
setHealthError,
|
||||
setHealthStatus,
|
||||
setLicenseFileName,
|
||||
setLicenseUploadStatus,
|
||||
setAoiLayer,
|
||||
setAllData,
|
||||
setRadarPagination,
|
||||
@@ -113,7 +107,6 @@ export default function useAppAuthLifecycle({
|
||||
clearRadarSearchResults();
|
||||
setCurrentUser(null);
|
||||
setPendingTaskIds([]);
|
||||
prevLicenseOkRef.current = false;
|
||||
setAuthChecked(true);
|
||||
}
|
||||
}, [
|
||||
@@ -122,29 +115,9 @@ export default function useAppAuthLifecycle({
|
||||
setHasRadarSearched,
|
||||
setCurrentUser,
|
||||
setPendingTaskIds,
|
||||
prevLicenseOkRef,
|
||||
setAuthChecked,
|
||||
]);
|
||||
|
||||
const fetchLicenseStatus = useCallback(async () => {
|
||||
try {
|
||||
setLicenseLoading(true);
|
||||
const response = await requestWithTransientRetry(() => 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 {
|
||||
@@ -167,22 +140,6 @@ export default function useAppAuthLifecycle({
|
||||
}
|
||||
}, [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,
|
||||
@@ -210,7 +167,6 @@ export default function useAppAuthLifecycle({
|
||||
}));
|
||||
setCurrentUser(null);
|
||||
setAuthChecked(true);
|
||||
prevLicenseOkRef.current = false;
|
||||
}
|
||||
return Promise.reject(error);
|
||||
}
|
||||
@@ -229,13 +185,11 @@ export default function useAppAuthLifecycle({
|
||||
setRadarPagination,
|
||||
setCurrentUser,
|
||||
setAuthChecked,
|
||||
prevLicenseOkRef,
|
||||
]);
|
||||
|
||||
useEffect(() => {
|
||||
fetchCurrentUser();
|
||||
fetchLicenseStatus();
|
||||
}, [fetchCurrentUser, fetchLicenseStatus]);
|
||||
}, [fetchCurrentUser]);
|
||||
|
||||
useEffect(() => {
|
||||
const startupTimer = setTimeout(() => {
|
||||
@@ -253,8 +207,6 @@ export default function useAppAuthLifecycle({
|
||||
return {
|
||||
handleLoginSuccess,
|
||||
handleLogout,
|
||||
fetchLicenseStatus,
|
||||
fetchHealthStatus,
|
||||
handleLicenseUpload,
|
||||
};
|
||||
}
|
||||
|
||||
@@ -7,7 +7,6 @@ const TERMINAL_TASK_STATUSES = new Set(['COMPLETED', 'PARTIAL_SUCCESS', 'FAILED'
|
||||
|
||||
export default function useGlobalTaskControl({
|
||||
currentUser,
|
||||
licenseOk,
|
||||
activeTasks,
|
||||
setActiveTasks,
|
||||
setRuntimeSummary,
|
||||
@@ -139,7 +138,7 @@ export default function useGlobalTaskControl({
|
||||
}, [handleTasksUpdate, normalizeRuntimeSummary]);
|
||||
|
||||
useEffect(() => {
|
||||
if (!currentUser || !licenseOk) return;
|
||||
if (!currentUser) return;
|
||||
|
||||
// Initial fetch
|
||||
syncActiveTasks();
|
||||
@@ -179,7 +178,7 @@ export default function useGlobalTaskControl({
|
||||
if (es) es.close();
|
||||
if (fallbackInterval) clearInterval(fallbackInterval);
|
||||
};
|
||||
}, [currentUser, licenseOk, syncActiveTasks, handleTasksUpdate]);
|
||||
}, [currentUser, syncActiveTasks, handleTasksUpdate]);
|
||||
|
||||
const handleCancelActiveTasks = useCallback(() => {
|
||||
if (!cancelTaskPwd || activeTasks.length === 0) return;
|
||||
|
||||
@@ -3,12 +3,9 @@
|
||||
{ zh: '请稍候,系统正在验证会话。', en: 'Please wait, verifying your session.' },
|
||||
{ zh: '雷达数据生产管理系统', en: 'Radar Data Production Management System' },
|
||||
{ zh: '科研工程模式', en: 'Research Engineering Mode' },
|
||||
{ zh: '已授权', en: 'Licensed' },
|
||||
{ zh: '未授权', en: 'Unlicensed' },
|
||||
{ zh: '管理员', en: 'Admin' },
|
||||
{ zh: '只读账号', en: 'Read-only account' },
|
||||
{ zh: '任务', en: 'Tasks' },
|
||||
{ zh: '授权至', en: 'License until' },
|
||||
{ zh: '刷新自检状态', en: 'Refresh health status' },
|
||||
{ zh: '自检中...', en: 'Checking...' },
|
||||
{ zh: '刷新自检', en: 'Refresh Health' },
|
||||
|
||||
@@ -7,19 +7,11 @@ const s = (set, key) => (v) =>
|
||||
export const useAuthStore = create((set) => ({
|
||||
currentUser: null,
|
||||
authChecked: false,
|
||||
licenseStatus: { ok: false, reason: '', expires_at: null },
|
||||
licenseLoading: true,
|
||||
licenseUploadStatus: { type: '', message: '' },
|
||||
licenseFileName: '',
|
||||
healthStatus: null,
|
||||
healthLoading: false,
|
||||
healthError: '',
|
||||
setCurrentUser: s(set, 'currentUser'),
|
||||
setAuthChecked: s(set, 'authChecked'),
|
||||
setLicenseStatus: s(set, 'licenseStatus'),
|
||||
setLicenseLoading: s(set, 'licenseLoading'),
|
||||
setLicenseUploadStatus: s(set, 'licenseUploadStatus'),
|
||||
setLicenseFileName: s(set, 'licenseFileName'),
|
||||
setHealthStatus: s(set, 'healthStatus'),
|
||||
setHealthLoading: s(set, 'healthLoading'),
|
||||
setHealthError: s(set, 'healthError'),
|
||||
|
||||
Reference in New Issue
Block a user