@trieb.work/payload-plugin-backup-mongodb 0.1.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +319 -0
- package/dist/backup-dashboard.css +1132 -0
- package/dist/client.d.ts +3 -0
- package/dist/client.js +3 -0
- package/dist/client.js.map +1 -0
- package/dist/collections/BackupSettings.d.ts +6 -0
- package/dist/collections/BackupSettings.js +91 -0
- package/dist/collections/BackupSettings.js.map +1 -0
- package/dist/collections/BackupTasks.d.ts +2 -0
- package/dist/collections/BackupTasks.js +56 -0
- package/dist/collections/BackupTasks.js.map +1 -0
- package/dist/components/BackupDashboard/BackupItemActions.client.d.ts +7 -0
- package/dist/components/BackupDashboard/BackupItemActions.client.js +90 -0
- package/dist/components/BackupDashboard/BackupItemActions.client.js.map +1 -0
- package/dist/components/BackupDashboard/BackupListCollapsible.client.d.ts +16 -0
- package/dist/components/BackupDashboard/BackupListCollapsible.client.js +597 -0
- package/dist/components/BackupDashboard/BackupListCollapsible.client.js.map +1 -0
- package/dist/components/BackupDashboard/BackupSettingsModal.client.d.ts +2 -0
- package/dist/components/BackupDashboard/BackupSettingsModal.client.js +939 -0
- package/dist/components/BackupDashboard/BackupSettingsModal.client.js.map +1 -0
- package/dist/components/BackupDashboard/CollectionBackupPreviewBody.client.d.ts +18 -0
- package/dist/components/BackupDashboard/CollectionBackupPreviewBody.client.js +267 -0
- package/dist/components/BackupDashboard/CollectionBackupPreviewBody.client.js.map +1 -0
- package/dist/components/BackupDashboard/ManualBackupDialog.client.d.ts +1 -0
- package/dist/components/BackupDashboard/ManualBackupDialog.client.js +276 -0
- package/dist/components/BackupDashboard/ManualBackupDialog.client.js.map +1 -0
- package/dist/components/BackupDashboard/RestoreBackupDialog.client.d.ts +6 -0
- package/dist/components/BackupDashboard/RestoreBackupDialog.client.js +551 -0
- package/dist/components/BackupDashboard/RestoreBackupDialog.client.js.map +1 -0
- package/dist/components/BackupDashboard/TaskActionButton.client.d.ts +29 -0
- package/dist/components/BackupDashboard/TaskActionButton.client.js +240 -0
- package/dist/components/BackupDashboard/TaskActionButton.client.js.map +1 -0
- package/dist/components/BackupDashboard/backupDashboardInlineCss.d.ts +1 -0
- package/dist/components/BackupDashboard/backupDashboardInlineCss.js +3 -0
- package/dist/components/BackupDashboard/backupDashboardInlineCss.js.map +1 -0
- package/dist/components/BackupDashboard/index.client.d.ts +8 -0
- package/dist/components/BackupDashboard/index.client.js +9 -0
- package/dist/components/BackupDashboard/index.client.js.map +1 -0
- package/dist/components/BackupDashboard/index.d.ts +8 -0
- package/dist/components/BackupDashboard/index.js +196 -0
- package/dist/components/BackupDashboard/index.js.map +1 -0
- package/dist/core/archive.d.ts +8 -0
- package/dist/core/archive.js +47 -0
- package/dist/core/archive.js.map +1 -0
- package/dist/core/backup.d.ts +41 -0
- package/dist/core/backup.js +188 -0
- package/dist/core/backup.js.map +1 -0
- package/dist/core/backupBlobIO.d.ts +54 -0
- package/dist/core/backupBlobIO.js +179 -0
- package/dist/core/backupBlobIO.js.map +1 -0
- package/dist/core/backupBlobTransfer.d.ts +29 -0
- package/dist/core/backupBlobTransfer.js +85 -0
- package/dist/core/backupBlobTransfer.js.map +1 -0
- package/dist/core/backupSettings.d.ts +35 -0
- package/dist/core/backupSettings.js +95 -0
- package/dist/core/backupSettings.js.map +1 -0
- package/dist/core/backupSourcePreview.d.ts +10 -0
- package/dist/core/backupSourcePreview.js +33 -0
- package/dist/core/backupSourcePreview.js.map +1 -0
- package/dist/core/blobTokenValidate.d.ts +14 -0
- package/dist/core/blobTokenValidate.js +75 -0
- package/dist/core/blobTokenValidate.js.map +1 -0
- package/dist/core/db.d.ts +24 -0
- package/dist/core/db.js +12 -0
- package/dist/core/db.js.map +1 -0
- package/dist/core/restore.d.ts +27 -0
- package/dist/core/restore.js +199 -0
- package/dist/core/restore.js.map +1 -0
- package/dist/core/restorePreview.d.ts +64 -0
- package/dist/core/restorePreview.js +266 -0
- package/dist/core/restorePreview.js.map +1 -0
- package/dist/core/taskProgress.d.ts +26 -0
- package/dist/core/taskProgress.js +73 -0
- package/dist/core/taskProgress.js.map +1 -0
- package/dist/core/vercelBackupCron.d.ts +8 -0
- package/dist/core/vercelBackupCron.js +38 -0
- package/dist/core/vercelBackupCron.js.map +1 -0
- package/dist/endpoints/createBackupEndpoints.d.ts +1 -0
- package/dist/endpoints/createBackupEndpoints.js +3 -0
- package/dist/endpoints/createBackupEndpoints.js.map +1 -0
- package/dist/endpoints/index.d.ts +3 -0
- package/dist/endpoints/index.js +34 -0
- package/dist/endpoints/index.js.map +1 -0
- package/dist/endpoints/paths/admin-backup-download.d.ts +8 -0
- package/dist/endpoints/paths/admin-backup-download.js +72 -0
- package/dist/endpoints/paths/admin-backup-download.js.map +1 -0
- package/dist/endpoints/paths/admin-delete.d.ts +3 -0
- package/dist/endpoints/paths/admin-delete.js +55 -0
- package/dist/endpoints/paths/admin-delete.js.map +1 -0
- package/dist/endpoints/paths/admin-manual.d.ts +3 -0
- package/dist/endpoints/paths/admin-manual.js +61 -0
- package/dist/endpoints/paths/admin-manual.js.map +1 -0
- package/dist/endpoints/paths/admin-preview.d.ts +6 -0
- package/dist/endpoints/paths/admin-preview.js +102 -0
- package/dist/endpoints/paths/admin-preview.js.map +1 -0
- package/dist/endpoints/paths/admin-restore.d.ts +3 -0
- package/dist/endpoints/paths/admin-restore.js +74 -0
- package/dist/endpoints/paths/admin-restore.js.map +1 -0
- package/dist/endpoints/paths/admin-seed.d.ts +3 -0
- package/dist/endpoints/paths/admin-seed.js +47 -0
- package/dist/endpoints/paths/admin-seed.js.map +1 -0
- package/dist/endpoints/paths/admin-settings.d.ts +3 -0
- package/dist/endpoints/paths/admin-settings.js +230 -0
- package/dist/endpoints/paths/admin-settings.js.map +1 -0
- package/dist/endpoints/paths/admin-task.d.ts +3 -0
- package/dist/endpoints/paths/admin-task.js +34 -0
- package/dist/endpoints/paths/admin-task.js.map +1 -0
- package/dist/endpoints/paths/admin-validate-blob-token.d.ts +8 -0
- package/dist/endpoints/paths/admin-validate-blob-token.js +26 -0
- package/dist/endpoints/paths/admin-validate-blob-token.js.map +1 -0
- package/dist/endpoints/paths/cron-list.d.ts +2 -0
- package/dist/endpoints/paths/cron-list.js +33 -0
- package/dist/endpoints/paths/cron-list.js.map +1 -0
- package/dist/endpoints/paths/cron-restore.d.ts +2 -0
- package/dist/endpoints/paths/cron-restore.js +61 -0
- package/dist/endpoints/paths/cron-restore.js.map +1 -0
- package/dist/endpoints/paths/cron-run.d.ts +3 -0
- package/dist/endpoints/paths/cron-run.js +45 -0
- package/dist/endpoints/paths/cron-run.js.map +1 -0
- package/dist/endpoints/shared.d.ts +13 -0
- package/dist/endpoints/shared.js +58 -0
- package/dist/endpoints/shared.js.map +1 -0
- package/dist/exports/client.d.ts +1 -0
- package/dist/exports/client.js +3 -0
- package/dist/exports/client.js.map +1 -0
- package/dist/exports/rsc.d.ts +1 -0
- package/dist/exports/rsc.js +3 -0
- package/dist/exports/rsc.js.map +1 -0
- package/dist/index.d.ts +18 -0
- package/dist/index.js +14 -0
- package/dist/index.js.map +1 -0
- package/dist/plugin.d.ts +3 -0
- package/dist/plugin.js +82 -0
- package/dist/plugin.js.map +1 -0
- package/dist/publicApiPaths.d.ts +19 -0
- package/dist/publicApiPaths.js +20 -0
- package/dist/publicApiPaths.js.map +1 -0
- package/dist/types.d.ts +21 -0
- package/dist/types.js +3 -0
- package/dist/types.js.map +1 -0
- package/dist/utils/backupSelection.d.ts +6 -0
- package/dist/utils/backupSelection.js +16 -0
- package/dist/utils/backupSelection.js.map +1 -0
- package/dist/utils/blobName.d.ts +32 -0
- package/dist/utils/blobName.js +99 -0
- package/dist/utils/blobName.js.map +1 -0
- package/dist/utils/dashboardRoleAccess.d.ts +23 -0
- package/dist/utils/dashboardRoleAccess.js +64 -0
- package/dist/utils/dashboardRoleAccess.js.map +1 -0
- package/dist/utils/dialogBackdrop.d.ts +9 -0
- package/dist/utils/dialogBackdrop.js +17 -0
- package/dist/utils/dialogBackdrop.js.map +1 -0
- package/dist/utils/formatBytes.d.ts +2 -0
- package/dist/utils/formatBytes.js +20 -0
- package/dist/utils/formatBytes.js.map +1 -0
- package/dist/utils/hostname.d.ts +2 -0
- package/dist/utils/hostname.js +17 -0
- package/dist/utils/hostname.js.map +1 -0
- package/dist/utils/index.d.ts +5 -0
- package/dist/utils/index.js +7 -0
- package/dist/utils/index.js.map +1 -0
- package/dist/utils/maskBlobToken.d.ts +11 -0
- package/dist/utils/maskBlobToken.js +42 -0
- package/dist/utils/maskBlobToken.js.map +1 -0
- package/package.json +140 -0
|
@@ -0,0 +1,551 @@
|
|
|
1
|
+
'use client';
|
|
2
|
+
import { jsx as _jsx, jsxs as _jsxs, Fragment as _Fragment } from "react/jsx-runtime";
|
|
3
|
+
import { Button } from '@payloadcms/ui';
|
|
4
|
+
import { useRouter } from 'next/navigation';
|
|
5
|
+
import { useCallback, useEffect, useMemo, useRef, useState } from 'react';
|
|
6
|
+
import { backupPluginPublicApiPaths } from '../../publicApiPaths';
|
|
7
|
+
import { closeNativeDialogOnBackdropPointer } from '../../utils/dialogBackdrop';
|
|
8
|
+
import { TaskActionButton } from './TaskActionButton.client';
|
|
9
|
+
function hiddenPills(reasons) {
|
|
10
|
+
const out = [];
|
|
11
|
+
if (reasons.includes('collection-config')) {
|
|
12
|
+
out.push('Hidden in admin');
|
|
13
|
+
}
|
|
14
|
+
return out;
|
|
15
|
+
}
|
|
16
|
+
function groupDocTotal(g) {
|
|
17
|
+
return (g.main?.docCount ?? 0) + (g.versions?.docCount ?? 0);
|
|
18
|
+
}
|
|
19
|
+
function isRestoreRowVisuallyEmpty(g, preview) {
|
|
20
|
+
const t = groupDocTotal(g);
|
|
21
|
+
const isMedia = g.groupId === 'media';
|
|
22
|
+
const hasBlobs = preview.mediaBlobCount > 0;
|
|
23
|
+
return t === 0 && !(isMedia && hasBlobs);
|
|
24
|
+
}
|
|
25
|
+
function isRestoreCollectionCheckboxDisabled(g, preview) {
|
|
26
|
+
return isRestoreRowVisuallyEmpty(g, preview);
|
|
27
|
+
}
|
|
28
|
+
function isRestoreSignInAgainGroup(groupId) {
|
|
29
|
+
return groupId === 'users' || groupId === 'roles' || groupId === 'payload-preferences';
|
|
30
|
+
}
|
|
31
|
+
export const RestoreBackupDialog = ({ downloadUrl, pathname })=>{
|
|
32
|
+
const router = useRouter();
|
|
33
|
+
const dialogRef = useRef(null);
|
|
34
|
+
const postSuccessCloseTimerRef = useRef(null);
|
|
35
|
+
const [preview, setPreview] = useState(null);
|
|
36
|
+
const [phase, setPhase] = useState('idle');
|
|
37
|
+
const [errorMessage, setErrorMessage] = useState(null);
|
|
38
|
+
const [selected, setSelected] = useState({});
|
|
39
|
+
const [restoreAllCollections, setRestoreAllCollections] = useState(false);
|
|
40
|
+
const [restoreArchiveMedia, setRestoreArchiveMedia] = useState(true);
|
|
41
|
+
const resetState = useCallback(()=>{
|
|
42
|
+
setPreview(null);
|
|
43
|
+
setPhase('idle');
|
|
44
|
+
setErrorMessage(null);
|
|
45
|
+
setSelected({});
|
|
46
|
+
setRestoreAllCollections(false);
|
|
47
|
+
setRestoreArchiveMedia(true);
|
|
48
|
+
}, []);
|
|
49
|
+
useEffect(()=>{
|
|
50
|
+
const el = dialogRef.current;
|
|
51
|
+
if (!el) {
|
|
52
|
+
return;
|
|
53
|
+
}
|
|
54
|
+
const onClose = ()=>{
|
|
55
|
+
if (postSuccessCloseTimerRef.current) {
|
|
56
|
+
clearTimeout(postSuccessCloseTimerRef.current);
|
|
57
|
+
postSuccessCloseTimerRef.current = null;
|
|
58
|
+
}
|
|
59
|
+
resetState();
|
|
60
|
+
};
|
|
61
|
+
el.addEventListener('close', onClose);
|
|
62
|
+
return ()=>el.removeEventListener('close', onClose);
|
|
63
|
+
}, [
|
|
64
|
+
resetState
|
|
65
|
+
]);
|
|
66
|
+
useEffect(()=>{
|
|
67
|
+
return ()=>{
|
|
68
|
+
if (postSuccessCloseTimerRef.current) {
|
|
69
|
+
clearTimeout(postSuccessCloseTimerRef.current);
|
|
70
|
+
}
|
|
71
|
+
};
|
|
72
|
+
}, []);
|
|
73
|
+
useEffect(()=>{
|
|
74
|
+
if (!preview) {
|
|
75
|
+
return;
|
|
76
|
+
}
|
|
77
|
+
setSelected(Object.fromEntries(preview.groups.map((g)=>{
|
|
78
|
+
const t = groupDocTotal(g);
|
|
79
|
+
const isMedia = g.groupId === 'media';
|
|
80
|
+
const hasBlobs = preview.mediaBlobCount > 0;
|
|
81
|
+
if (t === 0 && !(isMedia && hasBlobs)) {
|
|
82
|
+
return [
|
|
83
|
+
g.groupId,
|
|
84
|
+
false
|
|
85
|
+
];
|
|
86
|
+
}
|
|
87
|
+
return [
|
|
88
|
+
g.groupId,
|
|
89
|
+
true
|
|
90
|
+
];
|
|
91
|
+
})));
|
|
92
|
+
setRestoreAllCollections(false);
|
|
93
|
+
setRestoreArchiveMedia(true);
|
|
94
|
+
}, [
|
|
95
|
+
preview
|
|
96
|
+
]);
|
|
97
|
+
const loadPreview = useCallback(async ()=>{
|
|
98
|
+
setPhase('loading');
|
|
99
|
+
setErrorMessage(null);
|
|
100
|
+
setPreview(null);
|
|
101
|
+
try {
|
|
102
|
+
const locale = typeof document !== 'undefined' ? (document.documentElement.lang || 'en').slice(0, 2) : 'en';
|
|
103
|
+
const response = await fetch(backupPluginPublicApiPaths.adminRestorePreview, {
|
|
104
|
+
body: JSON.stringify({
|
|
105
|
+
locale,
|
|
106
|
+
pathname,
|
|
107
|
+
url: downloadUrl
|
|
108
|
+
}),
|
|
109
|
+
headers: {
|
|
110
|
+
'Content-Type': 'application/json'
|
|
111
|
+
},
|
|
112
|
+
method: 'POST'
|
|
113
|
+
});
|
|
114
|
+
const data = await response.json();
|
|
115
|
+
if (!response.ok) {
|
|
116
|
+
throw new Error(data.error || 'Could not analyse backup');
|
|
117
|
+
}
|
|
118
|
+
setPreview(data);
|
|
119
|
+
setPhase('ready');
|
|
120
|
+
} catch (e) {
|
|
121
|
+
setPhase('error');
|
|
122
|
+
setErrorMessage(e instanceof Error ? e.message : 'Could not analyse backup');
|
|
123
|
+
}
|
|
124
|
+
}, [
|
|
125
|
+
downloadUrl,
|
|
126
|
+
pathname
|
|
127
|
+
]);
|
|
128
|
+
const openDialog = ()=>{
|
|
129
|
+
setPhase('loading');
|
|
130
|
+
setErrorMessage(null);
|
|
131
|
+
setPreview(null);
|
|
132
|
+
setSelected({});
|
|
133
|
+
setRestoreAllCollections(false);
|
|
134
|
+
setRestoreArchiveMedia(true);
|
|
135
|
+
dialogRef.current?.showModal();
|
|
136
|
+
void loadPreview();
|
|
137
|
+
};
|
|
138
|
+
const skipCollections = useMemo(()=>{
|
|
139
|
+
if (!preview) {
|
|
140
|
+
return [];
|
|
141
|
+
}
|
|
142
|
+
if (restoreAllCollections) {
|
|
143
|
+
return [];
|
|
144
|
+
}
|
|
145
|
+
return preview.groups.filter((g)=>selected[g.groupId] === false).flatMap((g)=>g.mongoNames);
|
|
146
|
+
}, [
|
|
147
|
+
preview,
|
|
148
|
+
restoreAllCollections,
|
|
149
|
+
selected
|
|
150
|
+
]);
|
|
151
|
+
const willAutoLogout = useMemo(()=>{
|
|
152
|
+
if (!preview) {
|
|
153
|
+
return false;
|
|
154
|
+
}
|
|
155
|
+
return preview.groups.some((g)=>(restoreAllCollections || selected[g.groupId] !== false) && g.affectsAuthSession);
|
|
156
|
+
}, [
|
|
157
|
+
preview,
|
|
158
|
+
restoreAllCollections,
|
|
159
|
+
selected
|
|
160
|
+
]);
|
|
161
|
+
const needsAuthLockoutConfirm = useCallback(()=>{
|
|
162
|
+
if (!preview) {
|
|
163
|
+
return false;
|
|
164
|
+
}
|
|
165
|
+
return preview.groups.some((g)=>isRestoreSignInAgainGroup(g.groupId) && (restoreAllCollections || selected[g.groupId] !== false));
|
|
166
|
+
}, [
|
|
167
|
+
preview,
|
|
168
|
+
restoreAllCollections,
|
|
169
|
+
selected
|
|
170
|
+
]);
|
|
171
|
+
const hasArchiveMedia = preview ? preview.mediaBlobCount > 0 : false;
|
|
172
|
+
const mediaGroupIncluded = preview ? restoreAllCollections || selected['media'] !== false : false;
|
|
173
|
+
const hasMediaBlobOption = Boolean(preview && preview.mediaBlobCount > 0 && mediaGroupIncluded);
|
|
174
|
+
const restoreBody = useMemo(()=>({
|
|
175
|
+
pathname,
|
|
176
|
+
restoreArchiveMedia: hasArchiveMedia ? restoreArchiveMedia : true,
|
|
177
|
+
skipCollections,
|
|
178
|
+
url: downloadUrl
|
|
179
|
+
}), [
|
|
180
|
+
downloadUrl,
|
|
181
|
+
hasArchiveMedia,
|
|
182
|
+
pathname,
|
|
183
|
+
restoreArchiveMedia,
|
|
184
|
+
skipCollections
|
|
185
|
+
]);
|
|
186
|
+
const closeModalAfterRestoreSuccess = useCallback(()=>{
|
|
187
|
+
if (postSuccessCloseTimerRef.current) {
|
|
188
|
+
clearTimeout(postSuccessCloseTimerRef.current);
|
|
189
|
+
}
|
|
190
|
+
postSuccessCloseTimerRef.current = setTimeout(()=>{
|
|
191
|
+
postSuccessCloseTimerRef.current = null;
|
|
192
|
+
router.refresh();
|
|
193
|
+
dialogRef.current?.close();
|
|
194
|
+
}, 1000);
|
|
195
|
+
}, [
|
|
196
|
+
router
|
|
197
|
+
]);
|
|
198
|
+
return /*#__PURE__*/ _jsxs(_Fragment, {
|
|
199
|
+
children: [
|
|
200
|
+
/*#__PURE__*/ _jsx(Button, {
|
|
201
|
+
buttonStyle: "secondary",
|
|
202
|
+
onClick: openDialog,
|
|
203
|
+
size: "small",
|
|
204
|
+
children: "Restore"
|
|
205
|
+
}),
|
|
206
|
+
/*#__PURE__*/ _jsxs("dialog", {
|
|
207
|
+
className: "backup-confirm-dialog backup-confirm-dialog--restore",
|
|
208
|
+
onMouseDown: (e)=>closeNativeDialogOnBackdropPointer(e, dialogRef),
|
|
209
|
+
ref: dialogRef,
|
|
210
|
+
children: [
|
|
211
|
+
/*#__PURE__*/ _jsx("p", {
|
|
212
|
+
className: "backup-confirm-dialog__title",
|
|
213
|
+
children: "Restore this backup?"
|
|
214
|
+
}),
|
|
215
|
+
/*#__PURE__*/ _jsxs("div", {
|
|
216
|
+
className: "backup-confirm-dialog__body restore-preview",
|
|
217
|
+
children: [
|
|
218
|
+
phase === 'loading' && /*#__PURE__*/ _jsxs("div", {
|
|
219
|
+
"aria-live": "polite",
|
|
220
|
+
className: "restore-preview__loading-block",
|
|
221
|
+
children: [
|
|
222
|
+
/*#__PURE__*/ _jsx("p", {
|
|
223
|
+
className: "restore-preview__status",
|
|
224
|
+
children: "Downloading and analysing backup…"
|
|
225
|
+
}),
|
|
226
|
+
/*#__PURE__*/ _jsx("div", {
|
|
227
|
+
className: "restore-preview__loading-line"
|
|
228
|
+
}),
|
|
229
|
+
/*#__PURE__*/ _jsx("div", {
|
|
230
|
+
className: "restore-preview__loading-line restore-preview__loading-line--short"
|
|
231
|
+
}),
|
|
232
|
+
/*#__PURE__*/ _jsx("div", {
|
|
233
|
+
className: "restore-preview__loading-line"
|
|
234
|
+
})
|
|
235
|
+
]
|
|
236
|
+
}),
|
|
237
|
+
phase === 'error' && /*#__PURE__*/ _jsxs("div", {
|
|
238
|
+
className: "restore-preview__error",
|
|
239
|
+
children: [
|
|
240
|
+
/*#__PURE__*/ _jsx("p", {
|
|
241
|
+
children: errorMessage
|
|
242
|
+
}),
|
|
243
|
+
/*#__PURE__*/ _jsx("p", {
|
|
244
|
+
className: "restore-preview__error-hint",
|
|
245
|
+
children: "You can still start restore below. If this backup replaces users or sessions, sign out manually afterwards if you are not redirected automatically."
|
|
246
|
+
}),
|
|
247
|
+
/*#__PURE__*/ _jsx(Button, {
|
|
248
|
+
buttonStyle: "secondary",
|
|
249
|
+
onClick: ()=>void loadPreview(),
|
|
250
|
+
size: "small",
|
|
251
|
+
children: "Retry analysis"
|
|
252
|
+
})
|
|
253
|
+
]
|
|
254
|
+
}),
|
|
255
|
+
phase === 'ready' && preview && /*#__PURE__*/ _jsxs(_Fragment, {
|
|
256
|
+
children: [
|
|
257
|
+
/*#__PURE__*/ _jsx("p", {
|
|
258
|
+
className: "restore-preview__sticky-heading",
|
|
259
|
+
children: "Collection selection"
|
|
260
|
+
}),
|
|
261
|
+
/*#__PURE__*/ _jsx("p", {
|
|
262
|
+
className: "restore-preview__intro",
|
|
263
|
+
children: "Choose which collections to restore. Version history is grouped with its collection. Collections with no documents in this backup still appear (greyed); you can leave them unchecked."
|
|
264
|
+
}),
|
|
265
|
+
preview.groups.length === 0 && /*#__PURE__*/ _jsx("p", {
|
|
266
|
+
className: "restore-preview__empty",
|
|
267
|
+
children: "No collections found to list."
|
|
268
|
+
}),
|
|
269
|
+
restoreAllCollections ? /*#__PURE__*/ _jsxs("div", {
|
|
270
|
+
className: "restore-preview__all-block",
|
|
271
|
+
children: [
|
|
272
|
+
/*#__PURE__*/ _jsxs("label", {
|
|
273
|
+
className: "restore-preview__all-block-head",
|
|
274
|
+
children: [
|
|
275
|
+
/*#__PURE__*/ _jsx("input", {
|
|
276
|
+
"aria-label": "Restore all collections",
|
|
277
|
+
checked: true,
|
|
278
|
+
className: "checkbox-input__input restore-preview__group-check",
|
|
279
|
+
onChange: ()=>setRestoreAllCollections((prev)=>{
|
|
280
|
+
const next = !prev;
|
|
281
|
+
if (next && preview) {
|
|
282
|
+
setSelected(Object.fromEntries(preview.groups.map((g)=>[
|
|
283
|
+
g.groupId,
|
|
284
|
+
true
|
|
285
|
+
])));
|
|
286
|
+
}
|
|
287
|
+
return next;
|
|
288
|
+
}),
|
|
289
|
+
type: "checkbox"
|
|
290
|
+
}),
|
|
291
|
+
/*#__PURE__*/ _jsx("span", {
|
|
292
|
+
className: "restore-preview__media-label",
|
|
293
|
+
children: "Restore all collections"
|
|
294
|
+
})
|
|
295
|
+
]
|
|
296
|
+
}),
|
|
297
|
+
preview.groups.length > 0 ? /*#__PURE__*/ _jsx("ul", {
|
|
298
|
+
"aria-label": "Collections in backup",
|
|
299
|
+
className: "restore-preview__all-list",
|
|
300
|
+
children: preview.groups.map((g)=>{
|
|
301
|
+
const isMedia = g.groupId === 'media';
|
|
302
|
+
const rowEmpty = isRestoreRowVisuallyEmpty(g, preview);
|
|
303
|
+
const versionDocs = g.versions?.docCount ?? 0;
|
|
304
|
+
const docTotal = groupDocTotal(g);
|
|
305
|
+
const signInAgain = isRestoreSignInAgainGroup(g.groupId);
|
|
306
|
+
let countLabel;
|
|
307
|
+
if (rowEmpty) {
|
|
308
|
+
countLabel = 'Nothing to restore';
|
|
309
|
+
} else if (docTotal === 0 && isMedia && preview.mediaBlobCount > 0) {
|
|
310
|
+
countLabel = `0 docs · ${preview.mediaBlobCount} media file${preview.mediaBlobCount === 1 ? '' : 's'} in archive`;
|
|
311
|
+
} else {
|
|
312
|
+
countLabel = `${docTotal} doc${docTotal === 1 ? '' : 's'} to restore`;
|
|
313
|
+
if (versionDocs > 0) {
|
|
314
|
+
countLabel += ` (incl. ${versionDocs} version${versionDocs === 1 ? '' : 's'})`;
|
|
315
|
+
}
|
|
316
|
+
}
|
|
317
|
+
return /*#__PURE__*/ _jsxs("li", {
|
|
318
|
+
className: "restore-preview__all-item",
|
|
319
|
+
children: [
|
|
320
|
+
/*#__PURE__*/ _jsxs("span", {
|
|
321
|
+
className: "restore-preview__all-name",
|
|
322
|
+
children: [
|
|
323
|
+
g.displayTitle,
|
|
324
|
+
signInAgain ? /*#__PURE__*/ _jsxs(_Fragment, {
|
|
325
|
+
children: [
|
|
326
|
+
' ',
|
|
327
|
+
/*#__PURE__*/ _jsx("span", {
|
|
328
|
+
className: "restore-preview__pill restore-preview__pill--signin",
|
|
329
|
+
children: "Sign-in again"
|
|
330
|
+
})
|
|
331
|
+
]
|
|
332
|
+
}) : null
|
|
333
|
+
]
|
|
334
|
+
}),
|
|
335
|
+
/*#__PURE__*/ _jsx("span", {
|
|
336
|
+
className: "restore-preview__all-count",
|
|
337
|
+
children: countLabel
|
|
338
|
+
})
|
|
339
|
+
]
|
|
340
|
+
}, g.groupId);
|
|
341
|
+
})
|
|
342
|
+
}) : null,
|
|
343
|
+
hasMediaBlobOption ? /*#__PURE__*/ _jsxs("label", {
|
|
344
|
+
className: "restore-preview__media-block restore-preview__media-block--nested",
|
|
345
|
+
children: [
|
|
346
|
+
/*#__PURE__*/ _jsx("input", {
|
|
347
|
+
"aria-label": `Restore media files from backup (${preview.mediaBlobCount} files in archive)`,
|
|
348
|
+
checked: restoreArchiveMedia,
|
|
349
|
+
className: "checkbox-input__input restore-preview__group-check",
|
|
350
|
+
onChange: ()=>setRestoreArchiveMedia((v)=>!v),
|
|
351
|
+
type: "checkbox"
|
|
352
|
+
}),
|
|
353
|
+
/*#__PURE__*/ _jsxs("span", {
|
|
354
|
+
className: "restore-preview__media-label",
|
|
355
|
+
children: [
|
|
356
|
+
"Restore media files from backup? (",
|
|
357
|
+
preview.mediaBlobCount,
|
|
358
|
+
" file",
|
|
359
|
+
preview.mediaBlobCount === 1 ? '' : 's',
|
|
360
|
+
" in archive)"
|
|
361
|
+
]
|
|
362
|
+
})
|
|
363
|
+
]
|
|
364
|
+
}) : null
|
|
365
|
+
]
|
|
366
|
+
}) : /*#__PURE__*/ _jsxs(_Fragment, {
|
|
367
|
+
children: [
|
|
368
|
+
/*#__PURE__*/ _jsxs("label", {
|
|
369
|
+
className: "restore-preview__media-block",
|
|
370
|
+
children: [
|
|
371
|
+
/*#__PURE__*/ _jsx("input", {
|
|
372
|
+
"aria-label": "Restore all collections",
|
|
373
|
+
checked: false,
|
|
374
|
+
className: "checkbox-input__input restore-preview__group-check",
|
|
375
|
+
onChange: ()=>setRestoreAllCollections((prev)=>{
|
|
376
|
+
const next = !prev;
|
|
377
|
+
if (next && preview) {
|
|
378
|
+
setSelected(Object.fromEntries(preview.groups.map((g)=>[
|
|
379
|
+
g.groupId,
|
|
380
|
+
true
|
|
381
|
+
])));
|
|
382
|
+
}
|
|
383
|
+
return next;
|
|
384
|
+
}),
|
|
385
|
+
type: "checkbox"
|
|
386
|
+
}),
|
|
387
|
+
/*#__PURE__*/ _jsx("span", {
|
|
388
|
+
className: "restore-preview__media-label",
|
|
389
|
+
children: "Restore all collections"
|
|
390
|
+
})
|
|
391
|
+
]
|
|
392
|
+
}),
|
|
393
|
+
/*#__PURE__*/ _jsx("ul", {
|
|
394
|
+
"aria-label": "Collections in backup",
|
|
395
|
+
className: "restore-preview__list",
|
|
396
|
+
children: preview.groups.map((g)=>{
|
|
397
|
+
const adminPills = hiddenPills(g.adminHiddenReasons);
|
|
398
|
+
const isMedia = g.groupId === 'media';
|
|
399
|
+
const rowEmpty = isRestoreRowVisuallyEmpty(g, preview);
|
|
400
|
+
const checkboxDisabled = isRestoreCollectionCheckboxDisabled(g, preview);
|
|
401
|
+
const docTotal = groupDocTotal(g);
|
|
402
|
+
return /*#__PURE__*/ _jsxs("li", {
|
|
403
|
+
className: `restore-preview__group${rowEmpty ? 'restore-preview__group--empty' : ''}`,
|
|
404
|
+
children: [
|
|
405
|
+
/*#__PURE__*/ _jsxs("div", {
|
|
406
|
+
className: "restore-preview__group-head",
|
|
407
|
+
children: [
|
|
408
|
+
/*#__PURE__*/ _jsxs("label", {
|
|
409
|
+
className: "restore-preview__group-label",
|
|
410
|
+
children: [
|
|
411
|
+
/*#__PURE__*/ _jsx("input", {
|
|
412
|
+
"aria-label": `Restore collection ${g.displayTitle}`,
|
|
413
|
+
checked: selected[g.groupId] !== false,
|
|
414
|
+
className: "checkbox-input__input restore-preview__group-check",
|
|
415
|
+
disabled: checkboxDisabled,
|
|
416
|
+
onChange: ()=>{
|
|
417
|
+
setSelected((prev)=>{
|
|
418
|
+
const isOn = prev[g.groupId] !== false;
|
|
419
|
+
return {
|
|
420
|
+
...prev,
|
|
421
|
+
[g.groupId]: !isOn
|
|
422
|
+
};
|
|
423
|
+
});
|
|
424
|
+
},
|
|
425
|
+
type: "checkbox"
|
|
426
|
+
}),
|
|
427
|
+
/*#__PURE__*/ _jsx("span", {
|
|
428
|
+
className: "restore-preview__group-title",
|
|
429
|
+
children: g.displayTitle
|
|
430
|
+
})
|
|
431
|
+
]
|
|
432
|
+
}),
|
|
433
|
+
isRestoreSignInAgainGroup(g.groupId) && /*#__PURE__*/ _jsx("span", {
|
|
434
|
+
className: "restore-preview__pill restore-preview__pill--signin",
|
|
435
|
+
children: "Requires sign-in again"
|
|
436
|
+
})
|
|
437
|
+
]
|
|
438
|
+
}),
|
|
439
|
+
/*#__PURE__*/ _jsxs("div", {
|
|
440
|
+
className: "restore-preview__sublines",
|
|
441
|
+
children: [
|
|
442
|
+
rowEmpty ? /*#__PURE__*/ _jsx("div", {
|
|
443
|
+
className: "restore-preview__subline restore-preview__subline--empty",
|
|
444
|
+
children: "0 docs, nothing to restore"
|
|
445
|
+
}) : docTotal === 0 && isMedia && preview.mediaBlobCount > 0 ? /*#__PURE__*/ _jsx("div", {
|
|
446
|
+
className: "restore-preview__subline",
|
|
447
|
+
children: "0 collection docs in backup"
|
|
448
|
+
}) : /*#__PURE__*/ _jsxs(_Fragment, {
|
|
449
|
+
children: [
|
|
450
|
+
g.main ? /*#__PURE__*/ _jsxs("div", {
|
|
451
|
+
className: "restore-preview__subline",
|
|
452
|
+
children: [
|
|
453
|
+
g.main.docCount,
|
|
454
|
+
" doc",
|
|
455
|
+
g.main.docCount === 1 ? '' : 's',
|
|
456
|
+
" to restore"
|
|
457
|
+
]
|
|
458
|
+
}) : null,
|
|
459
|
+
g.versions ? /*#__PURE__*/ _jsxs("div", {
|
|
460
|
+
className: "restore-preview__subline",
|
|
461
|
+
children: [
|
|
462
|
+
g.versions.docCount,
|
|
463
|
+
" version doc",
|
|
464
|
+
g.versions.docCount === 1 ? '' : 's',
|
|
465
|
+
" to restore"
|
|
466
|
+
]
|
|
467
|
+
}) : null
|
|
468
|
+
]
|
|
469
|
+
}),
|
|
470
|
+
isMedia && selected['media'] !== false && preview.mediaBlobCount === 0 && docTotal > 0 && /*#__PURE__*/ _jsx("div", {
|
|
471
|
+
className: "restore-preview__note restore-preview__note--media",
|
|
472
|
+
children: "No media files in this backup archive — only collection data will be restored for media."
|
|
473
|
+
}),
|
|
474
|
+
isMedia && hasMediaBlobOption && /*#__PURE__*/ _jsxs("label", {
|
|
475
|
+
className: "restore-preview__media-block restore-preview__media-block--nested",
|
|
476
|
+
children: [
|
|
477
|
+
/*#__PURE__*/ _jsx("input", {
|
|
478
|
+
"aria-label": `Restore media files from backup (${preview.mediaBlobCount} files in archive)`,
|
|
479
|
+
checked: restoreArchiveMedia,
|
|
480
|
+
className: "checkbox-input__input restore-preview__group-check",
|
|
481
|
+
onChange: ()=>setRestoreArchiveMedia((v)=>!v),
|
|
482
|
+
type: "checkbox"
|
|
483
|
+
}),
|
|
484
|
+
/*#__PURE__*/ _jsxs("span", {
|
|
485
|
+
className: "restore-preview__media-label",
|
|
486
|
+
children: [
|
|
487
|
+
"Restore media files from backup? (",
|
|
488
|
+
preview.mediaBlobCount,
|
|
489
|
+
" file",
|
|
490
|
+
preview.mediaBlobCount === 1 ? '' : 's',
|
|
491
|
+
" in archive)"
|
|
492
|
+
]
|
|
493
|
+
})
|
|
494
|
+
]
|
|
495
|
+
}),
|
|
496
|
+
adminPills.length > 0 && /*#__PURE__*/ _jsx("div", {
|
|
497
|
+
className: "restore-preview__pills",
|
|
498
|
+
children: adminPills.map((t)=>/*#__PURE__*/ _jsx("span", {
|
|
499
|
+
className: "restore-preview__pill",
|
|
500
|
+
children: t
|
|
501
|
+
}, t))
|
|
502
|
+
})
|
|
503
|
+
]
|
|
504
|
+
})
|
|
505
|
+
]
|
|
506
|
+
}, g.groupId);
|
|
507
|
+
})
|
|
508
|
+
})
|
|
509
|
+
]
|
|
510
|
+
})
|
|
511
|
+
]
|
|
512
|
+
})
|
|
513
|
+
]
|
|
514
|
+
}),
|
|
515
|
+
/*#__PURE__*/ _jsxs("div", {
|
|
516
|
+
className: "backup-confirm-dialog__actions",
|
|
517
|
+
children: [
|
|
518
|
+
/*#__PURE__*/ _jsx(TaskActionButton, {
|
|
519
|
+
body: restoreBody,
|
|
520
|
+
buttonStyle: "error",
|
|
521
|
+
completeLabel: "Restore started",
|
|
522
|
+
dangerConfirm: {
|
|
523
|
+
body: 'You have selected Users, Roles, and/or Payload preferences. Restoring these collections replaces login accounts, role definitions, and/or UI session data. You can be locked out until you sign in again with the restored credentials. After a successful restore you will be signed out automatically.',
|
|
524
|
+
cancelLabel: 'Go back',
|
|
525
|
+
confirmLabel: 'I understand — restore anyway',
|
|
526
|
+
title: 'Danger: lock-out risk',
|
|
527
|
+
when: needsAuthLockoutConfirm
|
|
528
|
+
},
|
|
529
|
+
endpoint: backupPluginPublicApiPaths.adminRestore,
|
|
530
|
+
idleDisabled: phase !== 'ready' && phase !== 'error',
|
|
531
|
+
idleLabel: "Yes, restore",
|
|
532
|
+
kind: "restore",
|
|
533
|
+
onComplete: closeModalAfterRestoreSuccess,
|
|
534
|
+
pendingLabel: "Restoring...",
|
|
535
|
+
redirectOnComplete: willAutoLogout ? '/admin/logout' : undefined
|
|
536
|
+
}),
|
|
537
|
+
/*#__PURE__*/ _jsx(Button, {
|
|
538
|
+
buttonStyle: "secondary",
|
|
539
|
+
onClick: ()=>dialogRef.current?.close(),
|
|
540
|
+
size: "small",
|
|
541
|
+
children: "Cancel"
|
|
542
|
+
})
|
|
543
|
+
]
|
|
544
|
+
})
|
|
545
|
+
]
|
|
546
|
+
})
|
|
547
|
+
]
|
|
548
|
+
});
|
|
549
|
+
};
|
|
550
|
+
|
|
551
|
+
//# sourceMappingURL=RestoreBackupDialog.client.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"sources":["../../../src/components/BackupDashboard/RestoreBackupDialog.client.tsx"],"sourcesContent":["'use client'\n\nimport { Button } from '@payloadcms/ui'\nimport { useRouter } from 'next/navigation'\nimport { useCallback, useEffect, useMemo, useRef, useState } from 'react'\n\nimport type { RestorePreviewResponse } from '../../core/restorePreview'\n\nimport { backupPluginPublicApiPaths } from '../../publicApiPaths'\nimport { closeNativeDialogOnBackdropPointer } from '../../utils/dialogBackdrop'\nimport { TaskActionButton } from './TaskActionButton.client'\n\ntype RestoreBackupDialogProps = {\n downloadUrl: string\n pathname: string\n}\n\nfunction hiddenPills(\n reasons: RestorePreviewResponse['groups'][number]['adminHiddenReasons'],\n): string[] {\n const out: string[] = []\n if (reasons.includes('collection-config')) {\n out.push('Hidden in admin')\n }\n return out\n}\n\nfunction groupDocTotal(g: RestorePreviewResponse['groups'][number]): number {\n return (g.main?.docCount ?? 0) + (g.versions?.docCount ?? 0)\n}\n\nfunction isRestoreRowVisuallyEmpty(\n g: RestorePreviewResponse['groups'][number],\n preview: RestorePreviewResponse,\n): boolean {\n const t = groupDocTotal(g)\n const isMedia = g.groupId === 'media'\n const hasBlobs = preview.mediaBlobCount > 0\n return t === 0 && !(isMedia && hasBlobs)\n}\n\nfunction isRestoreCollectionCheckboxDisabled(\n g: RestorePreviewResponse['groups'][number],\n preview: RestorePreviewResponse,\n): boolean {\n return isRestoreRowVisuallyEmpty(g, preview)\n}\n\nfunction isRestoreSignInAgainGroup(groupId: string): boolean {\n return groupId === 'users' || groupId === 'roles' || groupId === 'payload-preferences'\n}\n\nexport const RestoreBackupDialog: React.FC<RestoreBackupDialogProps> = ({\n downloadUrl,\n pathname,\n}) => {\n const router = useRouter()\n const dialogRef = useRef<HTMLDialogElement>(null)\n const postSuccessCloseTimerRef = useRef<null | ReturnType<typeof setTimeout>>(null)\n const [preview, setPreview] = useState<null | RestorePreviewResponse>(null)\n const [phase, setPhase] = useState<'error' | 'idle' | 'loading' | 'ready'>('idle')\n const [errorMessage, setErrorMessage] = useState<null | string>(null)\n const [selected, setSelected] = useState<Record<string, boolean>>({})\n const [restoreAllCollections, setRestoreAllCollections] = useState(false)\n const [restoreArchiveMedia, setRestoreArchiveMedia] = useState(true)\n\n const resetState = useCallback(() => {\n setPreview(null)\n setPhase('idle')\n setErrorMessage(null)\n setSelected({})\n setRestoreAllCollections(false)\n setRestoreArchiveMedia(true)\n }, [])\n\n useEffect(() => {\n const el = dialogRef.current\n if (!el) {\n return\n }\n const onClose = () => {\n if (postSuccessCloseTimerRef.current) {\n clearTimeout(postSuccessCloseTimerRef.current)\n postSuccessCloseTimerRef.current = null\n }\n resetState()\n }\n el.addEventListener('close', onClose)\n return () => el.removeEventListener('close', onClose)\n }, [resetState])\n\n useEffect(() => {\n return () => {\n if (postSuccessCloseTimerRef.current) {\n clearTimeout(postSuccessCloseTimerRef.current)\n }\n }\n }, [])\n\n useEffect(() => {\n if (!preview) {\n return\n }\n setSelected(\n Object.fromEntries(\n preview.groups.map((g) => {\n const t = groupDocTotal(g)\n const isMedia = g.groupId === 'media'\n const hasBlobs = preview.mediaBlobCount > 0\n if (t === 0 && !(isMedia && hasBlobs)) {\n return [g.groupId, false]\n }\n return [g.groupId, true]\n }),\n ),\n )\n setRestoreAllCollections(false)\n setRestoreArchiveMedia(true)\n }, [preview])\n\n const loadPreview = useCallback(async () => {\n setPhase('loading')\n setErrorMessage(null)\n setPreview(null)\n try {\n const locale =\n typeof document !== 'undefined' ? (document.documentElement.lang || 'en').slice(0, 2) : 'en'\n const response = await fetch(backupPluginPublicApiPaths.adminRestorePreview, {\n body: JSON.stringify({\n locale,\n pathname,\n url: downloadUrl,\n }),\n headers: { 'Content-Type': 'application/json' },\n method: 'POST',\n })\n const data = (await response.json()) as { error?: string } & RestorePreviewResponse\n if (!response.ok) {\n throw new Error(data.error || 'Could not analyse backup')\n }\n setPreview(data)\n setPhase('ready')\n } catch (e) {\n setPhase('error')\n setErrorMessage(e instanceof Error ? e.message : 'Could not analyse backup')\n }\n }, [downloadUrl, pathname])\n\n const openDialog = () => {\n setPhase('loading')\n setErrorMessage(null)\n setPreview(null)\n setSelected({})\n setRestoreAllCollections(false)\n setRestoreArchiveMedia(true)\n dialogRef.current?.showModal()\n void loadPreview()\n }\n\n const skipCollections = useMemo(() => {\n if (!preview) {\n return []\n }\n if (restoreAllCollections) {\n return []\n }\n return preview.groups.filter((g) => selected[g.groupId] === false).flatMap((g) => g.mongoNames)\n }, [preview, restoreAllCollections, selected])\n\n const willAutoLogout = useMemo(() => {\n if (!preview) {\n return false\n }\n return preview.groups.some(\n (g) => (restoreAllCollections || selected[g.groupId] !== false) && g.affectsAuthSession,\n )\n }, [preview, restoreAllCollections, selected])\n\n const needsAuthLockoutConfirm = useCallback(() => {\n if (!preview) {\n return false\n }\n return preview.groups.some(\n (g) =>\n isRestoreSignInAgainGroup(g.groupId) &&\n (restoreAllCollections || selected[g.groupId] !== false),\n )\n }, [preview, restoreAllCollections, selected])\n\n const hasArchiveMedia = preview ? preview.mediaBlobCount > 0 : false\n const mediaGroupIncluded = preview ? restoreAllCollections || selected['media'] !== false : false\n const hasMediaBlobOption = Boolean(preview && preview.mediaBlobCount > 0 && mediaGroupIncluded)\n\n const restoreBody = useMemo(\n () => ({\n pathname,\n restoreArchiveMedia: hasArchiveMedia ? restoreArchiveMedia : true,\n skipCollections,\n url: downloadUrl,\n }),\n [downloadUrl, hasArchiveMedia, pathname, restoreArchiveMedia, skipCollections],\n )\n\n const closeModalAfterRestoreSuccess = useCallback(() => {\n if (postSuccessCloseTimerRef.current) {\n clearTimeout(postSuccessCloseTimerRef.current)\n }\n postSuccessCloseTimerRef.current = setTimeout(() => {\n postSuccessCloseTimerRef.current = null\n router.refresh()\n dialogRef.current?.close()\n }, 1000)\n }, [router])\n\n return (\n <>\n <Button buttonStyle=\"secondary\" onClick={openDialog} size=\"small\">\n Restore\n </Button>\n\n {/* Native <dialog>: backdrop dismiss; element not in jsx-a11y interactive list */}\n {/* eslint-disable-next-line jsx-a11y/no-noninteractive-element-interactions */}\n <dialog\n className=\"backup-confirm-dialog backup-confirm-dialog--restore\"\n onMouseDown={(e) => closeNativeDialogOnBackdropPointer(e, dialogRef)}\n ref={dialogRef}\n >\n <p className=\"backup-confirm-dialog__title\">Restore this backup?</p>\n\n <div className=\"backup-confirm-dialog__body restore-preview\">\n {phase === 'loading' && (\n <div aria-live=\"polite\" className=\"restore-preview__loading-block\">\n <p className=\"restore-preview__status\">Downloading and analysing backup…</p>\n <div className=\"restore-preview__loading-line\" />\n <div className=\"restore-preview__loading-line restore-preview__loading-line--short\" />\n <div className=\"restore-preview__loading-line\" />\n </div>\n )}\n\n {phase === 'error' && (\n <div className=\"restore-preview__error\">\n <p>{errorMessage}</p>\n <p className=\"restore-preview__error-hint\">\n You can still start restore below. If this backup replaces users or sessions, sign\n out manually afterwards if you are not redirected automatically.\n </p>\n <Button buttonStyle=\"secondary\" onClick={() => void loadPreview()} size=\"small\">\n Retry analysis\n </Button>\n </div>\n )}\n\n {phase === 'ready' && preview && (\n <>\n <p className=\"restore-preview__sticky-heading\">Collection selection</p>\n <p className=\"restore-preview__intro\">\n Choose which collections to restore. Version history is grouped with its collection.\n Collections with no documents in this backup still appear (greyed); you can leave\n them unchecked.\n </p>\n\n {preview.groups.length === 0 && (\n <p className=\"restore-preview__empty\">No collections found to list.</p>\n )}\n\n {restoreAllCollections ? (\n <div className=\"restore-preview__all-block\">\n <label className=\"restore-preview__all-block-head\">\n <input\n aria-label=\"Restore all collections\"\n checked\n className=\"checkbox-input__input restore-preview__group-check\"\n onChange={() =>\n setRestoreAllCollections((prev) => {\n const next = !prev\n if (next && preview) {\n setSelected(\n Object.fromEntries(preview.groups.map((g) => [g.groupId, true])),\n )\n }\n return next\n })\n }\n type=\"checkbox\"\n />\n <span className=\"restore-preview__media-label\">Restore all collections</span>\n </label>\n {preview.groups.length > 0 ? (\n <ul aria-label=\"Collections in backup\" className=\"restore-preview__all-list\">\n {preview.groups.map((g) => {\n const isMedia = g.groupId === 'media'\n const rowEmpty = isRestoreRowVisuallyEmpty(g, preview)\n const versionDocs = g.versions?.docCount ?? 0\n const docTotal = groupDocTotal(g)\n const signInAgain = isRestoreSignInAgainGroup(g.groupId)\n\n let countLabel: string\n if (rowEmpty) {\n countLabel = 'Nothing to restore'\n } else if (docTotal === 0 && isMedia && preview.mediaBlobCount > 0) {\n countLabel = `0 docs · ${preview.mediaBlobCount} media file${preview.mediaBlobCount === 1 ? '' : 's'} in archive`\n } else {\n countLabel = `${docTotal} doc${docTotal === 1 ? '' : 's'} to restore`\n if (versionDocs > 0) {\n countLabel += ` (incl. ${versionDocs} version${versionDocs === 1 ? '' : 's'})`\n }\n }\n\n return (\n <li className=\"restore-preview__all-item\" key={g.groupId}>\n <span className=\"restore-preview__all-name\">\n {g.displayTitle}\n {signInAgain ? (\n <>\n {' '}\n <span className=\"restore-preview__pill restore-preview__pill--signin\">\n Sign-in again\n </span>\n </>\n ) : null}\n </span>\n <span className=\"restore-preview__all-count\">{countLabel}</span>\n </li>\n )\n })}\n </ul>\n ) : null}\n {hasMediaBlobOption ? (\n <label className=\"restore-preview__media-block restore-preview__media-block--nested\">\n <input\n aria-label={`Restore media files from backup (${preview.mediaBlobCount} files in archive)`}\n checked={restoreArchiveMedia}\n className=\"checkbox-input__input restore-preview__group-check\"\n onChange={() => setRestoreArchiveMedia((v) => !v)}\n type=\"checkbox\"\n />\n <span className=\"restore-preview__media-label\">\n Restore media files from backup? ({preview.mediaBlobCount} file\n {preview.mediaBlobCount === 1 ? '' : 's'} in archive)\n </span>\n </label>\n ) : null}\n </div>\n ) : (\n <>\n <label className=\"restore-preview__media-block\">\n <input\n aria-label=\"Restore all collections\"\n checked={false}\n className=\"checkbox-input__input restore-preview__group-check\"\n onChange={() =>\n setRestoreAllCollections((prev) => {\n const next = !prev\n if (next && preview) {\n setSelected(\n Object.fromEntries(preview.groups.map((g) => [g.groupId, true])),\n )\n }\n return next\n })\n }\n type=\"checkbox\"\n />\n <span className=\"restore-preview__media-label\">Restore all collections</span>\n </label>\n\n <ul aria-label=\"Collections in backup\" className=\"restore-preview__list\">\n {preview.groups.map((g) => {\n const adminPills = hiddenPills(g.adminHiddenReasons)\n const isMedia = g.groupId === 'media'\n const rowEmpty = isRestoreRowVisuallyEmpty(g, preview)\n const checkboxDisabled = isRestoreCollectionCheckboxDisabled(g, preview)\n const docTotal = groupDocTotal(g)\n\n return (\n <li\n className={`restore-preview__group${rowEmpty ? 'restore-preview__group--empty' : ''}`}\n key={g.groupId}\n >\n <div className=\"restore-preview__group-head\">\n <label className=\"restore-preview__group-label\">\n <input\n aria-label={`Restore collection ${g.displayTitle}`}\n checked={selected[g.groupId] !== false}\n className=\"checkbox-input__input restore-preview__group-check\"\n disabled={checkboxDisabled}\n onChange={() => {\n setSelected((prev) => {\n const isOn = prev[g.groupId] !== false\n return { ...prev, [g.groupId]: !isOn }\n })\n }}\n type=\"checkbox\"\n />\n <span className=\"restore-preview__group-title\">{g.displayTitle}</span>\n </label>\n {isRestoreSignInAgainGroup(g.groupId) && (\n <span className=\"restore-preview__pill restore-preview__pill--signin\">\n Requires sign-in again\n </span>\n )}\n </div>\n <div className=\"restore-preview__sublines\">\n {rowEmpty ? (\n <div className=\"restore-preview__subline restore-preview__subline--empty\">\n 0 docs, nothing to restore\n </div>\n ) : docTotal === 0 && isMedia && preview.mediaBlobCount > 0 ? (\n <div className=\"restore-preview__subline\">\n 0 collection docs in backup\n </div>\n ) : (\n <>\n {g.main ? (\n <div className=\"restore-preview__subline\">\n {g.main.docCount} doc{g.main.docCount === 1 ? '' : 's'} to\n restore\n </div>\n ) : null}\n {g.versions ? (\n <div className=\"restore-preview__subline\">\n {g.versions.docCount} version doc\n {g.versions.docCount === 1 ? '' : 's'} to restore\n </div>\n ) : null}\n </>\n )}\n {isMedia &&\n selected['media'] !== false &&\n preview.mediaBlobCount === 0 &&\n docTotal > 0 && (\n <div className=\"restore-preview__note restore-preview__note--media\">\n No media files in this backup archive — only collection data will\n be restored for media.\n </div>\n )}\n {isMedia && hasMediaBlobOption && (\n <label className=\"restore-preview__media-block restore-preview__media-block--nested\">\n <input\n aria-label={`Restore media files from backup (${preview.mediaBlobCount} files in archive)`}\n checked={restoreArchiveMedia}\n className=\"checkbox-input__input restore-preview__group-check\"\n onChange={() => setRestoreArchiveMedia((v) => !v)}\n type=\"checkbox\"\n />\n <span className=\"restore-preview__media-label\">\n Restore media files from backup? ({preview.mediaBlobCount} file\n {preview.mediaBlobCount === 1 ? '' : 's'} in archive)\n </span>\n </label>\n )}\n {adminPills.length > 0 && (\n <div className=\"restore-preview__pills\">\n {adminPills.map((t) => (\n <span className=\"restore-preview__pill\" key={t}>\n {t}\n </span>\n ))}\n </div>\n )}\n </div>\n </li>\n )\n })}\n </ul>\n </>\n )}\n </>\n )}\n </div>\n\n <div className=\"backup-confirm-dialog__actions\">\n <TaskActionButton\n body={restoreBody}\n buttonStyle=\"error\"\n completeLabel=\"Restore started\"\n dangerConfirm={{\n body: 'You have selected Users, Roles, and/or Payload preferences. Restoring these collections replaces login accounts, role definitions, and/or UI session data. You can be locked out until you sign in again with the restored credentials. After a successful restore you will be signed out automatically.',\n cancelLabel: 'Go back',\n confirmLabel: 'I understand — restore anyway',\n title: 'Danger: lock-out risk',\n when: needsAuthLockoutConfirm,\n }}\n endpoint={backupPluginPublicApiPaths.adminRestore}\n idleDisabled={phase !== 'ready' && phase !== 'error'}\n idleLabel=\"Yes, restore\"\n kind=\"restore\"\n onComplete={closeModalAfterRestoreSuccess}\n pendingLabel=\"Restoring...\"\n redirectOnComplete={willAutoLogout ? '/admin/logout' : undefined}\n />\n <Button buttonStyle=\"secondary\" onClick={() => dialogRef.current?.close()} size=\"small\">\n Cancel\n </Button>\n </div>\n </dialog>\n </>\n )\n}\n"],"names":["Button","useRouter","useCallback","useEffect","useMemo","useRef","useState","backupPluginPublicApiPaths","closeNativeDialogOnBackdropPointer","TaskActionButton","hiddenPills","reasons","out","includes","push","groupDocTotal","g","main","docCount","versions","isRestoreRowVisuallyEmpty","preview","t","isMedia","groupId","hasBlobs","mediaBlobCount","isRestoreCollectionCheckboxDisabled","isRestoreSignInAgainGroup","RestoreBackupDialog","downloadUrl","pathname","router","dialogRef","postSuccessCloseTimerRef","setPreview","phase","setPhase","errorMessage","setErrorMessage","selected","setSelected","restoreAllCollections","setRestoreAllCollections","restoreArchiveMedia","setRestoreArchiveMedia","resetState","el","current","onClose","clearTimeout","addEventListener","removeEventListener","Object","fromEntries","groups","map","loadPreview","locale","document","documentElement","lang","slice","response","fetch","adminRestorePreview","body","JSON","stringify","url","headers","method","data","json","ok","Error","error","e","message","openDialog","showModal","skipCollections","filter","flatMap","mongoNames","willAutoLogout","some","affectsAuthSession","needsAuthLockoutConfirm","hasArchiveMedia","mediaGroupIncluded","hasMediaBlobOption","Boolean","restoreBody","closeModalAfterRestoreSuccess","setTimeout","refresh","close","buttonStyle","onClick","size","dialog","className","onMouseDown","ref","p","div","aria-live","length","label","input","aria-label","checked","onChange","prev","next","type","span","ul","rowEmpty","versionDocs","docTotal","signInAgain","countLabel","li","displayTitle","v","adminPills","adminHiddenReasons","checkboxDisabled","disabled","isOn","completeLabel","dangerConfirm","cancelLabel","confirmLabel","title","when","endpoint","adminRestore","idleDisabled","idleLabel","kind","onComplete","pendingLabel","redirectOnComplete","undefined"],"mappings":"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"}
|