mirror of
https://github.com/shuaiplus/nodewarden.git
synced 2026-08-05 06:50:10 +00:00
Merge branch 'main' of https://github.com/shuaiplus/nodewarden
This commit is contained in:
@@ -13,6 +13,7 @@ import {
|
|||||||
} from '@/lib/api/backup';
|
} from '@/lib/api/backup';
|
||||||
import {
|
import {
|
||||||
REMOTE_BROWSER_ITEMS_PER_PAGE,
|
REMOTE_BROWSER_ITEMS_PER_PAGE,
|
||||||
|
REMOTE_BROWSER_REFRESH_TTL_MS,
|
||||||
compareRemoteItems,
|
compareRemoteItems,
|
||||||
createDraftBackupSettings,
|
createDraftBackupSettings,
|
||||||
createDraftDestinationRecord,
|
createDraftDestinationRecord,
|
||||||
@@ -217,6 +218,7 @@ export default function BackupCenterPage(props: BackupCenterPageProps) {
|
|||||||
const [remoteBrowserCache, setRemoteBrowserCache] = useState<Record<string, RemoteBackupBrowserResponse>>(persistedRemoteState.cache);
|
const [remoteBrowserCache, setRemoteBrowserCache] = useState<Record<string, RemoteBackupBrowserResponse>>(persistedRemoteState.cache);
|
||||||
const [remoteBrowserPathByDestination, setRemoteBrowserPathByDestination] = useState<Record<string, string>>(persistedRemoteState.pathByDestination);
|
const [remoteBrowserPathByDestination, setRemoteBrowserPathByDestination] = useState<Record<string, string>>(persistedRemoteState.pathByDestination);
|
||||||
const [remoteBrowserPageByKey, setRemoteBrowserPageByKey] = useState<Record<string, number>>(persistedRemoteState.pageByKey);
|
const [remoteBrowserPageByKey, setRemoteBrowserPageByKey] = useState<Record<string, number>>(persistedRemoteState.pageByKey);
|
||||||
|
const [remoteBrowserRefreshedAt, setRemoteBrowserRefreshedAt] = useState<Record<string, number>>(persistedRemoteState.refreshedAt || {});
|
||||||
const [showAddChooser, setShowAddChooser] = useState(false);
|
const [showAddChooser, setShowAddChooser] = useState(false);
|
||||||
|
|
||||||
const visibleDestinations = getVisibleDestinations(settings);
|
const visibleDestinations = getVisibleDestinations(settings);
|
||||||
@@ -308,8 +310,22 @@ export default function BackupCenterPage(props: BackupCenterPageProps) {
|
|||||||
pathByDestination: remoteBrowserPathByDestination,
|
pathByDestination: remoteBrowserPathByDestination,
|
||||||
pageByKey: remoteBrowserPageByKey,
|
pageByKey: remoteBrowserPageByKey,
|
||||||
selectedDestinationId,
|
selectedDestinationId,
|
||||||
|
refreshedAt: remoteBrowserRefreshedAt,
|
||||||
});
|
});
|
||||||
}, [props.currentUserId, remoteBrowserCache, remoteBrowserPageByKey, remoteBrowserPathByDestination, selectedDestinationId]);
|
}, [props.currentUserId, remoteBrowserCache, remoteBrowserPageByKey, remoteBrowserPathByDestination, remoteBrowserRefreshedAt, selectedDestinationId]);
|
||||||
|
|
||||||
|
useEffect(() => {
|
||||||
|
if (!savedSelectedDestination) return;
|
||||||
|
const destinationId = savedSelectedDestination.id;
|
||||||
|
const path = remoteBrowserPathByDestination[destinationId] || '';
|
||||||
|
const cacheKey = getRemoteBrowserCacheKey(destinationId, path);
|
||||||
|
const lastRefreshed = remoteBrowserRefreshedAt[cacheKey] || 0;
|
||||||
|
const isStale = Date.now() - lastRefreshed > REMOTE_BROWSER_REFRESH_TTL_MS;
|
||||||
|
if (isStale) {
|
||||||
|
void loadRemoteBrowser(destinationId, path, { force: true });
|
||||||
|
}
|
||||||
|
// eslint-disable-next-line react-hooks/exhaustive-deps
|
||||||
|
}, [savedSelectedDestination?.id]);
|
||||||
|
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
if (!restoreProgress) {
|
if (!restoreProgress) {
|
||||||
@@ -398,6 +414,7 @@ export default function BackupCenterPage(props: BackupCenterPageProps) {
|
|||||||
};
|
};
|
||||||
setRemoteBrowserCache((current) => ({ ...current, [cacheKey]: nextBrowser }));
|
setRemoteBrowserCache((current) => ({ ...current, [cacheKey]: nextBrowser }));
|
||||||
setRemoteBrowserPageByKey((current) => ({ ...current, [cacheKey]: 1 }));
|
setRemoteBrowserPageByKey((current) => ({ ...current, [cacheKey]: 1 }));
|
||||||
|
setRemoteBrowserRefreshedAt((current) => ({ ...current, [cacheKey]: Date.now() }));
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
const message = error instanceof Error ? error.message : t('txt_backup_remote_load_failed');
|
const message = error instanceof Error ? error.message : t('txt_backup_remote_load_failed');
|
||||||
setLocalError(message);
|
setLocalError(message);
|
||||||
@@ -543,6 +560,7 @@ export default function BackupCenterPage(props: BackupCenterPageProps) {
|
|||||||
).cache);
|
).cache);
|
||||||
setRemoteBrowserPathByDestination((current) => Object.fromEntries(Object.entries(current).filter(([key]) => key !== destinationIdToDelete)));
|
setRemoteBrowserPathByDestination((current) => Object.fromEntries(Object.entries(current).filter(([key]) => key !== destinationIdToDelete)));
|
||||||
setRemoteBrowserPageByKey((current) => Object.fromEntries(Object.entries(current).filter(([key]) => !key.startsWith(`${destinationIdToDelete}:`))));
|
setRemoteBrowserPageByKey((current) => Object.fromEntries(Object.entries(current).filter(([key]) => !key.startsWith(`${destinationIdToDelete}:`))));
|
||||||
|
setRemoteBrowserRefreshedAt((current) => Object.fromEntries(Object.entries(current).filter(([key]) => !key.startsWith(`${destinationIdToDelete}:`))));
|
||||||
setSelectedDestinationId(nextSelected);
|
setSelectedDestinationId(nextSelected);
|
||||||
setConfirmDeleteDestinationOpen(false);
|
setConfirmDeleteDestinationOpen(false);
|
||||||
props.onNotify('success', t('txt_backup_destination_deleted'));
|
props.onNotify('success', t('txt_backup_destination_deleted'));
|
||||||
@@ -670,6 +688,7 @@ export default function BackupCenterPage(props: BackupCenterPageProps) {
|
|||||||
setRemoteBrowserCache((current) => Object.fromEntries(Object.entries(current).filter(([key]) => !key.startsWith(`${destinationIdToInvalidate}:`))));
|
setRemoteBrowserCache((current) => Object.fromEntries(Object.entries(current).filter(([key]) => !key.startsWith(`${destinationIdToInvalidate}:`))));
|
||||||
setRemoteBrowserPathByDestination((current) => Object.fromEntries(Object.entries(current).filter(([key]) => key !== destinationIdToInvalidate)));
|
setRemoteBrowserPathByDestination((current) => Object.fromEntries(Object.entries(current).filter(([key]) => key !== destinationIdToInvalidate)));
|
||||||
setRemoteBrowserPageByKey((current) => Object.fromEntries(Object.entries(current).filter(([key]) => !key.startsWith(`${destinationIdToInvalidate}:`))));
|
setRemoteBrowserPageByKey((current) => Object.fromEntries(Object.entries(current).filter(([key]) => !key.startsWith(`${destinationIdToInvalidate}:`))));
|
||||||
|
setRemoteBrowserRefreshedAt((current) => Object.fromEntries(Object.entries(current).filter(([key]) => !key.startsWith(`${destinationIdToInvalidate}:`))));
|
||||||
}
|
}
|
||||||
setSelectedDestinationId(nextSelected);
|
setSelectedDestinationId(nextSelected);
|
||||||
props.onNotify('success', t('txt_backup_settings_saved'));
|
props.onNotify('success', t('txt_backup_settings_saved'));
|
||||||
|
|||||||
@@ -14,10 +14,12 @@ export interface PersistedRemoteBrowserState {
|
|||||||
pathByDestination: Record<string, string>;
|
pathByDestination: Record<string, string>;
|
||||||
pageByKey: Record<string, number>;
|
pageByKey: Record<string, number>;
|
||||||
selectedDestinationId: string | null;
|
selectedDestinationId: string | null;
|
||||||
|
refreshedAt: Record<string, number>;
|
||||||
}
|
}
|
||||||
|
|
||||||
export const REMOTE_BROWSER_STORAGE_KEY = 'nodewarden.backup.remote-browser.v1';
|
export const REMOTE_BROWSER_STORAGE_KEY = 'nodewarden.backup.remote-browser.v1';
|
||||||
export const REMOTE_BROWSER_ITEMS_PER_PAGE = 10;
|
export const REMOTE_BROWSER_ITEMS_PER_PAGE = 10;
|
||||||
|
export const REMOTE_BROWSER_REFRESH_TTL_MS = 5 * 60 * 1000; // 5 minutes
|
||||||
|
|
||||||
export const COMMON_TIME_ZONES = [
|
export const COMMON_TIME_ZONES = [
|
||||||
'UTC',
|
'UTC',
|
||||||
@@ -148,6 +150,7 @@ export function loadPersistedRemoteBrowserState(userId?: string | null): Persist
|
|||||||
pathByDestination: {},
|
pathByDestination: {},
|
||||||
pageByKey: {},
|
pageByKey: {},
|
||||||
selectedDestinationId: null,
|
selectedDestinationId: null,
|
||||||
|
refreshedAt: {},
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
const parsed = JSON.parse(raw) as Partial<PersistedRemoteBrowserState>;
|
const parsed = JSON.parse(raw) as Partial<PersistedRemoteBrowserState>;
|
||||||
@@ -156,6 +159,7 @@ export function loadPersistedRemoteBrowserState(userId?: string | null): Persist
|
|||||||
pathByDestination: parsed.pathByDestination && typeof parsed.pathByDestination === 'object' ? parsed.pathByDestination : {},
|
pathByDestination: parsed.pathByDestination && typeof parsed.pathByDestination === 'object' ? parsed.pathByDestination : {},
|
||||||
pageByKey: parsed.pageByKey && typeof parsed.pageByKey === 'object' ? parsed.pageByKey : {},
|
pageByKey: parsed.pageByKey && typeof parsed.pageByKey === 'object' ? parsed.pageByKey : {},
|
||||||
selectedDestinationId: typeof parsed.selectedDestinationId === 'string' ? parsed.selectedDestinationId : null,
|
selectedDestinationId: typeof parsed.selectedDestinationId === 'string' ? parsed.selectedDestinationId : null,
|
||||||
|
refreshedAt: parsed.refreshedAt && typeof parsed.refreshedAt === 'object' ? parsed.refreshedAt as Record<string, number> : {},
|
||||||
};
|
};
|
||||||
} catch {
|
} catch {
|
||||||
return {
|
return {
|
||||||
@@ -163,6 +167,7 @@ export function loadPersistedRemoteBrowserState(userId?: string | null): Persist
|
|||||||
pathByDestination: {},
|
pathByDestination: {},
|
||||||
pageByKey: {},
|
pageByKey: {},
|
||||||
selectedDestinationId: null,
|
selectedDestinationId: null,
|
||||||
|
refreshedAt: {},
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
Reference in New Issue
Block a user