feat: refactor import routes and enhance backup state management with user ID

This commit is contained in:
shuaiplus
2026-03-15 03:44:38 +08:00
parent 8755b64f56
commit f749bbf7fd
3 changed files with 37 additions and 36 deletions
+22 -29
View File
@@ -119,7 +119,8 @@ type JwtUnsafeReason = 'missing' | 'default' | 'too_short';
const SEND_KEY_SALT = 'bitwarden-send'; const SEND_KEY_SALT = 'bitwarden-send';
const SEND_KEY_PURPOSE = 'send'; const SEND_KEY_PURPOSE = 'send';
const IMPORT_ROUTE = '/help/import-export'; const IMPORT_ROUTE = '/help/import-export';
const IMPORT_ROUTE_ALIASES = new Set(['/tools/import', '/tools/import-export', '/tools/import-data', '/import', '/import-export']); const IMPORT_ROUTE_PATHS = [IMPORT_ROUTE, '/tools/import', '/tools/import-export', '/tools/import-data', '/import', '/import-export'] as const;
const IMPORT_ROUTE_ALIASES = new Set(IMPORT_ROUTE_PATHS.filter((path) => path !== IMPORT_ROUTE));
const SETTINGS_HOME_ROUTE = '/settings'; const SETTINGS_HOME_ROUTE = '/settings';
const SETTINGS_ACCOUNT_ROUTE = '/settings/account'; const SETTINGS_ACCOUNT_ROUTE = '/settings/account';
@@ -1920,6 +1921,20 @@ export default function App() {
</Suspense> </Suspense>
); );
const renderImportPageRoute = () => (
<div className="stack">
{mobileLayout && (
<div className="mobile-settings-subhead">
<button type="button" className="btn btn-secondary small mobile-settings-back" onClick={() => navigate(SETTINGS_HOME_ROUTE)}>
<span className="btn-icon" aria-hidden="true">{"<"}</span>
{t('txt_back')}
</button>
</div>
)}
{importPageContent}
</div>
);
useEffect(() => { useEffect(() => {
if (phase === 'app' && location === '/' && !isPublicSendRoute) navigate('/vault'); if (phase === 'app' && location === '/' && !isPublicSendRoute) navigate('/vault');
}, [phase, location, isPublicSendRoute, navigate]); }, [phase, location, isPublicSendRoute, navigate]);
@@ -2366,34 +2381,11 @@ export default function App() {
</Suspense> </Suspense>
</div> </div>
</Route> </Route>
<Route path={IMPORT_ROUTE}> {IMPORT_ROUTE_PATHS.map((path) => (
<div className="stack"> <Route key={path} path={path}>
{mobileLayout && ( {renderImportPageRoute()}
<div className="mobile-settings-subhead"> </Route>
<button type="button" className="btn btn-secondary small mobile-settings-back" onClick={() => navigate(SETTINGS_HOME_ROUTE)}> ))}
<span className="btn-icon" aria-hidden="true">{"<"}</span>
{t('txt_back')}
</button>
</div>
)}
{importPageContent}
</div>
</Route>
<Route path="/tools/import">
{importPageContent}
</Route>
<Route path="/tools/import-export">
{importPageContent}
</Route>
<Route path="/tools/import-data">
{importPageContent}
</Route>
<Route path="/import">
{importPageContent}
</Route>
<Route path="/import-export">
{importPageContent}
</Route>
<Route path="/help"> <Route path="/help">
{profile?.role === 'admin' ? ( {profile?.role === 'admin' ? (
<div className="stack"> <div className="stack">
@@ -2407,6 +2399,7 @@ export default function App() {
)} )}
<Suspense fallback={<RouteContentFallback />}> <Suspense fallback={<RouteContentFallback />}>
<BackupCenterPage <BackupCenterPage
currentUserId={profile?.id || null}
onExport={handleBackupExportAction} onExport={handleBackupExportAction}
onImport={handleBackupImportAction} onImport={handleBackupImportAction}
onLoadSettings={handleLoadBackupSettingsAction} onLoadSettings={handleLoadBackupSettingsAction}
+4 -3
View File
@@ -28,6 +28,7 @@ import { BackupDestinationSidebar } from './backup-center/BackupDestinationSideb
import { BackupOperationsSidebar } from './backup-center/BackupOperationsSidebar'; import { BackupOperationsSidebar } from './backup-center/BackupOperationsSidebar';
interface BackupCenterPageProps { interface BackupCenterPageProps {
currentUserId: string | null;
onExport: () => Promise<void>; onExport: () => Promise<void>;
onImport: (file: File, replaceExisting?: boolean) => Promise<void>; onImport: (file: File, replaceExisting?: boolean) => Promise<void>;
onLoadSettings: () => Promise<AdminBackupSettings>; onLoadSettings: () => Promise<AdminBackupSettings>;
@@ -41,7 +42,7 @@ interface BackupCenterPageProps {
} }
export default function BackupCenterPage(props: BackupCenterPageProps) { export default function BackupCenterPage(props: BackupCenterPageProps) {
const persistedRemoteStateRef = useRef(loadPersistedRemoteBrowserState()); const persistedRemoteStateRef = useRef(loadPersistedRemoteBrowserState(props.currentUserId));
const persistedRemoteState = persistedRemoteStateRef.current; const persistedRemoteState = persistedRemoteStateRef.current;
const fileInputRef = useRef<HTMLInputElement | null>(null); const fileInputRef = useRef<HTMLInputElement | null>(null);
@@ -126,13 +127,13 @@ export default function BackupCenterPage(props: BackupCenterPageProps) {
}, []); }, []);
useEffect(() => { useEffect(() => {
persistRemoteBrowserState({ persistRemoteBrowserState(props.currentUserId, {
cache: remoteBrowserCache, cache: remoteBrowserCache,
pathByDestination: remoteBrowserPathByDestination, pathByDestination: remoteBrowserPathByDestination,
pageByKey: remoteBrowserPageByKey, pageByKey: remoteBrowserPageByKey,
selectedDestinationId, selectedDestinationId,
}); });
}, [remoteBrowserCache, remoteBrowserPageByKey, remoteBrowserPathByDestination, selectedDestinationId]); }, [props.currentUserId, remoteBrowserCache, remoteBrowserPageByKey, remoteBrowserPathByDestination, selectedDestinationId]);
useEffect(() => { useEffect(() => {
if (selectedDestination?.type === 'placeholder') { if (selectedDestination?.type === 'placeholder') {
+11 -4
View File
@@ -111,6 +111,13 @@ export function getRemoteBrowserCacheKey(destinationId: string, path: string = '
return `${destinationId}:${path}`; return `${destinationId}:${path}`;
} }
function getRemoteBrowserStorageKey(userId?: string | null): string {
const normalizedUserId = String(userId || '').trim();
return normalizedUserId
? `${REMOTE_BROWSER_STORAGE_KEY}:${normalizedUserId}`
: REMOTE_BROWSER_STORAGE_KEY;
}
function getRemoteBrowserStorage(): Storage | null { function getRemoteBrowserStorage(): Storage | null {
try { try {
if (typeof window !== 'undefined' && window.localStorage) { if (typeof window !== 'undefined' && window.localStorage) {
@@ -129,10 +136,10 @@ function getRemoteBrowserStorage(): Storage | null {
return null; return null;
} }
export function loadPersistedRemoteBrowserState(): PersistedRemoteBrowserState { export function loadPersistedRemoteBrowserState(userId?: string | null): PersistedRemoteBrowserState {
try { try {
const storage = getRemoteBrowserStorage(); const storage = getRemoteBrowserStorage();
const raw = storage?.getItem(REMOTE_BROWSER_STORAGE_KEY); const raw = storage?.getItem(getRemoteBrowserStorageKey(userId));
if (!raw) { if (!raw) {
return { return {
cache: {}, cache: {},
@@ -158,10 +165,10 @@ export function loadPersistedRemoteBrowserState(): PersistedRemoteBrowserState {
} }
} }
export function persistRemoteBrowserState(state: PersistedRemoteBrowserState): void { export function persistRemoteBrowserState(userId: string | null | undefined, state: PersistedRemoteBrowserState): void {
try { try {
const storage = getRemoteBrowserStorage(); const storage = getRemoteBrowserStorage();
storage?.setItem(REMOTE_BROWSER_STORAGE_KEY, JSON.stringify(state)); storage?.setItem(getRemoteBrowserStorageKey(userId), JSON.stringify(state));
} catch { } catch {
// Ignore cache persistence failures. // Ignore cache persistence failures.
} }