import { lazy, Suspense } from 'preact/compat'; import { useEffect } from 'preact/hooks'; import { Link, Route, Switch } from 'wouter'; import { ArrowUpDown, Cloud, LogOut, Settings as SettingsIcon, Shield, ShieldUser } from 'lucide-preact'; import type { ImportAttachmentFile, ImportResultSummary } from '@/components/ImportPage'; import type { AdminBackupImportResponse, AdminBackupRunResponse, AdminBackupSettings, RemoteBackupBrowserResponse } from '@/lib/api/backup'; import type { CiphersImportPayload } from '@/lib/api/vault'; import { t } from '@/lib/i18n'; import type { AdminInvite, AdminUser, AuthorizedDevice, Cipher, Folder as VaultFolder, Profile, Send, SendDraft, SessionState, VaultDraft } from '@/lib/types'; import type { ExportRequest } from '@/lib/export-formats'; const SendsPage = lazy(() => import('@/components/SendsPage')); const TotpCodesPage = lazy(() => import('@/components/TotpCodesPage')); const VaultPage = lazy(() => import('@/components/VaultPage')); const SettingsPage = lazy(() => import('@/components/SettingsPage')); const SecurityDevicesPage = lazy(() => import('@/components/SecurityDevicesPage')); const AdminPage = lazy(() => import('@/components/AdminPage')); const BackupCenterPage = lazy(() => import('@/components/BackupCenterPage')); const ImportPage = lazy(() => import('@/components/ImportPage')); function RouteContentFallback() { return
{t('txt_loading_nodewarden')}
; } function LegacyBackupRedirect(props: { onNavigate: (path: string) => void }) { useEffect(() => { props.onNavigate('/backup'); }, [props]); return null; } export interface AppMainRoutesProps { profile: Profile | null; session: SessionState | null; mobileLayout: boolean; mobileSidebarToggleKey: number; importRoute: string; settingsHomeRoute: string; settingsAccountRoute: string; decryptedCiphers: Cipher[]; decryptedFolders: VaultFolder[]; decryptedSends: Send[]; ciphersLoading: boolean; foldersLoading: boolean; sendsLoading: boolean; users: AdminUser[]; invites: AdminInvite[]; totpEnabled: boolean; lockTimeoutMinutes: 0 | 1 | 5 | 15 | 30; authorizedDevices: AuthorizedDevice[]; authorizedDevicesLoading: boolean; onNavigate: (path: string) => void; onLogout: () => void; onNotify: (type: 'success' | 'error' | 'warning', text: string) => void; onImport: ( payload: CiphersImportPayload, options: { folderMode: 'original' | 'none' | 'target'; targetFolderId: string | null }, attachments?: ImportAttachmentFile[] ) => Promise; onImportEncryptedRaw: ( payload: CiphersImportPayload, options: { folderMode: 'original' | 'none' | 'target'; targetFolderId: string | null }, attachments?: ImportAttachmentFile[] ) => Promise; onExport: (request: ExportRequest) => Promise; onCreateVaultItem: (draft: VaultDraft, attachments?: File[]) => Promise; onUpdateVaultItem: (cipher: Cipher, draft: VaultDraft, options?: { addFiles?: File[]; removeAttachmentIds?: string[] }) => Promise; onDeleteVaultItem: (cipher: Cipher) => Promise; onArchiveVaultItem: (cipher: Cipher) => Promise; onUnarchiveVaultItem: (cipher: Cipher) => Promise; onBulkDeleteVaultItems: (ids: string[]) => Promise; onBulkPermanentDeleteVaultItems: (ids: string[]) => Promise; onBulkRestoreVaultItems: (ids: string[]) => Promise; onBulkArchiveVaultItems: (ids: string[]) => Promise; onBulkUnarchiveVaultItems: (ids: string[]) => Promise; onBulkMoveVaultItems: (ids: string[], folderId: string | null) => Promise; onVerifyMasterPassword: (email: string, password: string) => Promise; onCreateFolder: (name: string) => Promise; onRenameFolder: (folderId: string, name: string) => Promise; onDeleteFolder: (folderId: string) => Promise; onBulkDeleteFolders: (folderIds: string[]) => Promise; onDownloadVaultAttachment: (cipher: Cipher, attachmentId: string) => Promise; downloadingAttachmentKey: string; attachmentDownloadPercent: number | null; uploadingAttachmentName: string; attachmentUploadPercent: number | null; onRefreshVault: () => Promise; onCreateSend: (draft: SendDraft, autoCopyLink: boolean) => Promise; onUpdateSend: (send: Send, draft: SendDraft, autoCopyLink: boolean) => Promise; onDeleteSend: (send: Send) => Promise; onBulkDeleteSends: (ids: string[]) => Promise; uploadingSendFileName: string; sendUploadPercent: number | null; onChangePassword: (currentPassword: string, nextPassword: string, nextPassword2: string) => Promise; onSavePasswordHint: (masterPasswordHint: string) => Promise; onEnableTotp: (secret: string, token: string) => Promise; onOpenDisableTotp: () => void; onGetRecoveryCode: (masterPassword: string) => Promise; onGetApiKey: (masterPassword: string) => Promise; onRotateApiKey: (masterPassword: string) => Promise; onLockTimeoutChange: (minutes: 0 | 1 | 5 | 15 | 30) => void; onRefreshAuthorizedDevices: () => Promise; onRenameAuthorizedDevice: (device: AuthorizedDevice, name: string) => Promise; onRevokeDeviceTrust: (device: AuthorizedDevice) => void; onRemoveDevice: (device: AuthorizedDevice) => void; onRevokeAllDeviceTrust: () => void; onRemoveAllDevices: () => void; onCreateInvite: (hours: number) => Promise; onRefreshAdmin: () => void; onDeleteAllInvites: () => Promise; onToggleUserStatus: (userId: string, status: 'active' | 'banned') => Promise; onDeleteUser: (userId: string) => Promise; onRevokeInvite: (code: string) => Promise; onExportBackup: (includeAttachments?: boolean) => Promise; onImportBackup: (file: File, replaceExisting?: boolean) => Promise; onImportBackupAllowingChecksumMismatch: (file: File, replaceExisting?: boolean) => Promise; onLoadBackupSettings: () => Promise; onSaveBackupSettings: (settings: AdminBackupSettings) => Promise; onRunRemoteBackup: (destinationId?: string | null) => Promise; onListRemoteBackups: (destinationId: string, path: string) => Promise; onDownloadRemoteBackup: (destinationId: string, path: string, onProgress?: (percent: number | null) => void) => Promise; onInspectRemoteBackup: (destinationId: string, path: string) => Promise<{ object: 'backup-remote-integrity'; destinationId: string; path: string; fileName: string; integrity: { hasChecksumPrefix: boolean; expectedPrefix: string | null; actualPrefix: string; matches: boolean } }>; onDeleteRemoteBackup: (destinationId: string, path: string) => Promise; onRestoreRemoteBackup: (destinationId: string, path: string, replaceExisting?: boolean) => Promise; onRestoreRemoteBackupAllowingChecksumMismatch: (destinationId: string, path: string, replaceExisting?: boolean) => Promise; } export default function AppMainRoutes(props: AppMainRoutesProps) { const importRoutePaths = [props.importRoute, '/tools/import', '/tools/import-export', '/tools/import-data', '/import', '/import-export'] as const; const importPageContent = ( }> ); const renderImportPageRoute = () => (
{props.mobileLayout && (
)} {importPageContent}
); return ( }> }> }> {props.profile && (
{props.mobileLayout && (
)} }>
)}
{props.profile && (
{t('nav_account_settings')} {t('nav_device_management')} {t('nav_import_export')} {props.profile.role === 'admin' && ( {t('nav_admin_panel')} )} {props.profile.role === 'admin' && ( {t('nav_backup_strategy')} )}
)}
{props.mobileLayout && (
)} }> void props.onRefreshAuthorizedDevices()} onRenameDevice={props.onRenameAuthorizedDevice} onRevokeTrust={props.onRevokeDeviceTrust} onRemoveDevice={props.onRemoveDevice} onRevokeAll={props.onRevokeAllDeviceTrust} onRemoveAll={props.onRemoveAllDevices} />
{props.mobileLayout && (
)} }>
{importRoutePaths.map((path) => ( {renderImportPageRoute()} ))} {props.profile?.role === 'admin' ? (
{props.mobileLayout && (
)} }>
) : null}
); }