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_PURPOSE = 'send';
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_ACCOUNT_ROUTE = '/settings/account';
@@ -1920,6 +1921,20 @@ export default function App() {
</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(() => {
if (phase === 'app' && location === '/' && !isPublicSendRoute) navigate('/vault');
}, [phase, location, isPublicSendRoute, navigate]);
@@ -2366,34 +2381,11 @@ export default function App() {
</Suspense>
</div>
</Route>
<Route path={IMPORT_ROUTE}>
<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>
</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>
{IMPORT_ROUTE_PATHS.map((path) => (
<Route key={path} path={path}>
{renderImportPageRoute()}
</Route>
))}
<Route path="/help">
{profile?.role === 'admin' ? (
<div className="stack">
@@ -2407,6 +2399,7 @@ export default function App() {
)}
<Suspense fallback={<RouteContentFallback />}>
<BackupCenterPage
currentUserId={profile?.id || null}
onExport={handleBackupExportAction}
onImport={handleBackupImportAction}
onLoadSettings={handleLoadBackupSettingsAction}
+4 -3
View File
@@ -28,6 +28,7 @@ import { BackupDestinationSidebar } from './backup-center/BackupDestinationSideb
import { BackupOperationsSidebar } from './backup-center/BackupOperationsSidebar';
interface BackupCenterPageProps {
currentUserId: string | null;
onExport: () => Promise<void>;
onImport: (file: File, replaceExisting?: boolean) => Promise<void>;
onLoadSettings: () => Promise<AdminBackupSettings>;
@@ -41,7 +42,7 @@ interface BackupCenterPageProps {
}
export default function BackupCenterPage(props: BackupCenterPageProps) {
const persistedRemoteStateRef = useRef(loadPersistedRemoteBrowserState());
const persistedRemoteStateRef = useRef(loadPersistedRemoteBrowserState(props.currentUserId));
const persistedRemoteState = persistedRemoteStateRef.current;
const fileInputRef = useRef<HTMLInputElement | null>(null);
@@ -126,13 +127,13 @@ export default function BackupCenterPage(props: BackupCenterPageProps) {
}, []);
useEffect(() => {
persistRemoteBrowserState({
persistRemoteBrowserState(props.currentUserId, {
cache: remoteBrowserCache,
pathByDestination: remoteBrowserPathByDestination,
pageByKey: remoteBrowserPageByKey,
selectedDestinationId,
});
}, [remoteBrowserCache, remoteBrowserPageByKey, remoteBrowserPathByDestination, selectedDestinationId]);
}, [props.currentUserId, remoteBrowserCache, remoteBrowserPageByKey, remoteBrowserPathByDestination, selectedDestinationId]);
useEffect(() => {
if (selectedDestination?.type === 'placeholder') {
+11 -4
View File
@@ -111,6 +111,13 @@ export function getRemoteBrowserCacheKey(destinationId: string, path: string = '
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 {
try {
if (typeof window !== 'undefined' && window.localStorage) {
@@ -129,10 +136,10 @@ function getRemoteBrowserStorage(): Storage | null {
return null;
}
export function loadPersistedRemoteBrowserState(): PersistedRemoteBrowserState {
export function loadPersistedRemoteBrowserState(userId?: string | null): PersistedRemoteBrowserState {
try {
const storage = getRemoteBrowserStorage();
const raw = storage?.getItem(REMOTE_BROWSER_STORAGE_KEY);
const raw = storage?.getItem(getRemoteBrowserStorageKey(userId));
if (!raw) {
return {
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 {
const storage = getRemoteBrowserStorage();
storage?.setItem(REMOTE_BROWSER_STORAGE_KEY, JSON.stringify(state));
storage?.setItem(getRemoteBrowserStorageKey(userId), JSON.stringify(state));
} catch {
// Ignore cache persistence failures.
}