@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,102 @@
|
|
|
1
|
+
import { getResolvedCronBackupSettings, resolveBackupArchiveRead, resolveBackupBlobAccess } from '../../core/backupSettings';
|
|
2
|
+
import { getBackupSourcePreviewForManual } from '../../core/backupSourcePreview';
|
|
3
|
+
import { getRestorePreviewForAdminRestore } from '../../core/restorePreview';
|
|
4
|
+
import { jsonError, readRequestJson, requireBackupAdmin } from '../shared';
|
|
5
|
+
/**
|
|
6
|
+
* Admin backup source preview and restore archive preview (both POST).
|
|
7
|
+
*/ export function createAdminPreviewEndpoints(options) {
|
|
8
|
+
return [
|
|
9
|
+
{
|
|
10
|
+
handler: async (req)=>{
|
|
11
|
+
const auth = await requireBackupAdmin(req, options);
|
|
12
|
+
if (auth instanceof Response) {
|
|
13
|
+
return auth;
|
|
14
|
+
}
|
|
15
|
+
const { payload } = req;
|
|
16
|
+
const body = await readRequestJson(req);
|
|
17
|
+
const locale = typeof body?.locale === 'string' ? body.locale : undefined;
|
|
18
|
+
const preferredLocales = locale ? [
|
|
19
|
+
locale,
|
|
20
|
+
'de',
|
|
21
|
+
'en'
|
|
22
|
+
] : [
|
|
23
|
+
'de',
|
|
24
|
+
'en'
|
|
25
|
+
];
|
|
26
|
+
try {
|
|
27
|
+
const preview = await getBackupSourcePreviewForManual(payload, {
|
|
28
|
+
preferredLocales
|
|
29
|
+
});
|
|
30
|
+
return Response.json(preview);
|
|
31
|
+
} catch (error) {
|
|
32
|
+
const message = error instanceof Error ? error.message : 'Preview failed';
|
|
33
|
+
payload.logger.error({
|
|
34
|
+
err: error
|
|
35
|
+
}, '[backup-endpoint] Backup source preview failed');
|
|
36
|
+
return Response.json({
|
|
37
|
+
error: message
|
|
38
|
+
}, {
|
|
39
|
+
status: 422
|
|
40
|
+
});
|
|
41
|
+
}
|
|
42
|
+
},
|
|
43
|
+
method: 'post',
|
|
44
|
+
path: '/backup-mongodb/admin/backup-preview'
|
|
45
|
+
},
|
|
46
|
+
{
|
|
47
|
+
handler: async (req)=>{
|
|
48
|
+
const auth = await requireBackupAdmin(req, options);
|
|
49
|
+
if (auth instanceof Response) {
|
|
50
|
+
return auth;
|
|
51
|
+
}
|
|
52
|
+
const { payload } = req;
|
|
53
|
+
const body = await readRequestJson(req);
|
|
54
|
+
const url = body?.url;
|
|
55
|
+
if (!url || typeof url !== 'string') {
|
|
56
|
+
return jsonError('Missing url', 400);
|
|
57
|
+
}
|
|
58
|
+
try {
|
|
59
|
+
new URL(url);
|
|
60
|
+
} catch {
|
|
61
|
+
return jsonError('Invalid url', 400);
|
|
62
|
+
}
|
|
63
|
+
const settings = await getResolvedCronBackupSettings(payload);
|
|
64
|
+
const backupRead = resolveBackupArchiveRead(settings, body?.pathname);
|
|
65
|
+
if (resolveBackupBlobAccess(settings) === 'private' && !backupRead) {
|
|
66
|
+
return jsonError('Missing pathname (required for dedicated backup blob store)', 400);
|
|
67
|
+
}
|
|
68
|
+
const locale = typeof body?.locale === 'string' ? body.locale : undefined;
|
|
69
|
+
const preferredLocales = locale ? [
|
|
70
|
+
locale,
|
|
71
|
+
'de',
|
|
72
|
+
'en'
|
|
73
|
+
] : [
|
|
74
|
+
'de',
|
|
75
|
+
'en'
|
|
76
|
+
];
|
|
77
|
+
try {
|
|
78
|
+
const preview = await getRestorePreviewForAdminRestore(payload, url, {
|
|
79
|
+
backupRead: backupRead ?? undefined,
|
|
80
|
+
preferredLocales
|
|
81
|
+
});
|
|
82
|
+
return Response.json(preview);
|
|
83
|
+
} catch (error) {
|
|
84
|
+
const message = error instanceof Error ? error.message : 'Preview failed';
|
|
85
|
+
payload.logger.error({
|
|
86
|
+
err: error,
|
|
87
|
+
url
|
|
88
|
+
}, '[backup-endpoint] Restore preview failed');
|
|
89
|
+
return Response.json({
|
|
90
|
+
error: message
|
|
91
|
+
}, {
|
|
92
|
+
status: 422
|
|
93
|
+
});
|
|
94
|
+
}
|
|
95
|
+
},
|
|
96
|
+
method: 'post',
|
|
97
|
+
path: '/backup-mongodb/admin/restore-preview'
|
|
98
|
+
}
|
|
99
|
+
];
|
|
100
|
+
}
|
|
101
|
+
|
|
102
|
+
//# sourceMappingURL=admin-preview.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"sources":["../../../src/endpoints/paths/admin-preview.ts"],"sourcesContent":["import type { Endpoint } from 'payload'\n\nimport type { BackupPluginOptions } from '../../types'\n\nimport {\n getResolvedCronBackupSettings,\n resolveBackupArchiveRead,\n resolveBackupBlobAccess,\n} from '../../core/backupSettings'\nimport { getBackupSourcePreviewForManual } from '../../core/backupSourcePreview'\nimport { getRestorePreviewForAdminRestore } from '../../core/restorePreview'\nimport { jsonError, readRequestJson, requireBackupAdmin } from '../shared'\n\n/**\n * Admin backup source preview and restore archive preview (both POST).\n */\nexport function createAdminPreviewEndpoints(options: BackupPluginOptions): Endpoint[] {\n return [\n {\n handler: async (req) => {\n const auth = await requireBackupAdmin(req, options)\n if (auth instanceof Response) {\n return auth\n }\n\n const { payload } = req\n const body = (await readRequestJson(req)) as { locale?: string }\n const locale = typeof body?.locale === 'string' ? body.locale : undefined\n const preferredLocales = locale ? [locale, 'de', 'en'] : ['de', 'en']\n\n try {\n const preview = await getBackupSourcePreviewForManual(payload, { preferredLocales })\n return Response.json(preview)\n } catch (error) {\n const message = error instanceof Error ? error.message : 'Preview failed'\n payload.logger.error({ err: error }, '[backup-endpoint] Backup source preview failed')\n return Response.json({ error: message }, { status: 422 })\n }\n },\n method: 'post',\n path: '/backup-mongodb/admin/backup-preview',\n },\n {\n handler: async (req) => {\n const auth = await requireBackupAdmin(req, options)\n if (auth instanceof Response) {\n return auth\n }\n\n const { payload } = req\n const body = (await readRequestJson(req)) as {\n locale?: string\n pathname?: string\n url?: string\n }\n const url = body?.url\n\n if (!url || typeof url !== 'string') {\n return jsonError('Missing url', 400)\n }\n\n try {\n new URL(url)\n } catch {\n return jsonError('Invalid url', 400)\n }\n\n const settings = await getResolvedCronBackupSettings(payload)\n const backupRead = resolveBackupArchiveRead(settings, body?.pathname)\n if (resolveBackupBlobAccess(settings) === 'private' && !backupRead) {\n return jsonError('Missing pathname (required for dedicated backup blob store)', 400)\n }\n\n const locale = typeof body?.locale === 'string' ? body.locale : undefined\n const preferredLocales = locale ? [locale, 'de', 'en'] : ['de', 'en']\n\n try {\n const preview = await getRestorePreviewForAdminRestore(payload, url, {\n backupRead: backupRead ?? undefined,\n preferredLocales,\n })\n return Response.json(preview)\n } catch (error) {\n const message = error instanceof Error ? error.message : 'Preview failed'\n payload.logger.error({ err: error, url }, '[backup-endpoint] Restore preview failed')\n return Response.json({ error: message }, { status: 422 })\n }\n },\n method: 'post',\n path: '/backup-mongodb/admin/restore-preview',\n },\n ]\n}\n"],"names":["getResolvedCronBackupSettings","resolveBackupArchiveRead","resolveBackupBlobAccess","getBackupSourcePreviewForManual","getRestorePreviewForAdminRestore","jsonError","readRequestJson","requireBackupAdmin","createAdminPreviewEndpoints","options","handler","req","auth","Response","payload","body","locale","undefined","preferredLocales","preview","json","error","message","Error","logger","err","status","method","path","url","URL","settings","backupRead","pathname"],"mappings":"AAIA,SACEA,6BAA6B,EAC7BC,wBAAwB,EACxBC,uBAAuB,QAClB,4BAA2B;AAClC,SAASC,+BAA+B,QAAQ,iCAAgC;AAChF,SAASC,gCAAgC,QAAQ,4BAA2B;AAC5E,SAASC,SAAS,EAAEC,eAAe,EAAEC,kBAAkB,QAAQ,YAAW;AAE1E;;CAEC,GACD,OAAO,SAASC,4BAA4BC,OAA4B;IACtE,OAAO;QACL;YACEC,SAAS,OAAOC;gBACd,MAAMC,OAAO,MAAML,mBAAmBI,KAAKF;gBAC3C,IAAIG,gBAAgBC,UAAU;oBAC5B,OAAOD;gBACT;gBAEA,MAAM,EAAEE,OAAO,EAAE,GAAGH;gBACpB,MAAMI,OAAQ,MAAMT,gBAAgBK;gBACpC,MAAMK,SAAS,OAAOD,MAAMC,WAAW,WAAWD,KAAKC,MAAM,GAAGC;gBAChE,MAAMC,mBAAmBF,SAAS;oBAACA;oBAAQ;oBAAM;iBAAK,GAAG;oBAAC;oBAAM;iBAAK;gBAErE,IAAI;oBACF,MAAMG,UAAU,MAAMhB,gCAAgCW,SAAS;wBAAEI;oBAAiB;oBAClF,OAAOL,SAASO,IAAI,CAACD;gBACvB,EAAE,OAAOE,OAAO;oBACd,MAAMC,UAAUD,iBAAiBE,QAAQF,MAAMC,OAAO,GAAG;oBACzDR,QAAQU,MAAM,CAACH,KAAK,CAAC;wBAAEI,KAAKJ;oBAAM,GAAG;oBACrC,OAAOR,SAASO,IAAI,CAAC;wBAAEC,OAAOC;oBAAQ,GAAG;wBAAEI,QAAQ;oBAAI;gBACzD;YACF;YACAC,QAAQ;YACRC,MAAM;QACR;QACA;YACElB,SAAS,OAAOC;gBACd,MAAMC,OAAO,MAAML,mBAAmBI,KAAKF;gBAC3C,IAAIG,gBAAgBC,UAAU;oBAC5B,OAAOD;gBACT;gBAEA,MAAM,EAAEE,OAAO,EAAE,GAAGH;gBACpB,MAAMI,OAAQ,MAAMT,gBAAgBK;gBAKpC,MAAMkB,MAAMd,MAAMc;gBAElB,IAAI,CAACA,OAAO,OAAOA,QAAQ,UAAU;oBACnC,OAAOxB,UAAU,eAAe;gBAClC;gBAEA,IAAI;oBACF,IAAIyB,IAAID;gBACV,EAAE,OAAM;oBACN,OAAOxB,UAAU,eAAe;gBAClC;gBAEA,MAAM0B,WAAW,MAAM/B,8BAA8Bc;gBACrD,MAAMkB,aAAa/B,yBAAyB8B,UAAUhB,MAAMkB;gBAC5D,IAAI/B,wBAAwB6B,cAAc,aAAa,CAACC,YAAY;oBAClE,OAAO3B,UAAU,+DAA+D;gBAClF;gBAEA,MAAMW,SAAS,OAAOD,MAAMC,WAAW,WAAWD,KAAKC,MAAM,GAAGC;gBAChE,MAAMC,mBAAmBF,SAAS;oBAACA;oBAAQ;oBAAM;iBAAK,GAAG;oBAAC;oBAAM;iBAAK;gBAErE,IAAI;oBACF,MAAMG,UAAU,MAAMf,iCAAiCU,SAASe,KAAK;wBACnEG,YAAYA,cAAcf;wBAC1BC;oBACF;oBACA,OAAOL,SAASO,IAAI,CAACD;gBACvB,EAAE,OAAOE,OAAO;oBACd,MAAMC,UAAUD,iBAAiBE,QAAQF,MAAMC,OAAO,GAAG;oBACzDR,QAAQU,MAAM,CAACH,KAAK,CAAC;wBAAEI,KAAKJ;wBAAOQ;oBAAI,GAAG;oBAC1C,OAAOhB,SAASO,IAAI,CAAC;wBAAEC,OAAOC;oBAAQ,GAAG;wBAAEI,QAAQ;oBAAI;gBACzD;YACF;YACAC,QAAQ;YACRC,MAAM;QACR;KACD;AACH"}
|
|
@@ -0,0 +1,74 @@
|
|
|
1
|
+
import { after } from 'next/server';
|
|
2
|
+
import { getResolvedCronBackupSettings, resolveBackupArchiveRead, resolveBackupBlobAccess, resolveBackupBlobToken } from '../../core/backupSettings';
|
|
3
|
+
import { restoreBackup } from '../../core/restore';
|
|
4
|
+
import { completeBackupTask, createBackupTask, failBackupTask } from '../../core/taskProgress';
|
|
5
|
+
import { jsonError, readRequestJson, requireBackupAdmin } from '../shared';
|
|
6
|
+
export function createAdminRestoreEndpoint(options) {
|
|
7
|
+
return {
|
|
8
|
+
handler: async (req)=>{
|
|
9
|
+
const auth = await requireBackupAdmin(req, options);
|
|
10
|
+
if (auth instanceof Response) {
|
|
11
|
+
return auth;
|
|
12
|
+
}
|
|
13
|
+
const { payload } = req;
|
|
14
|
+
const settings = await getResolvedCronBackupSettings(payload);
|
|
15
|
+
const blobToken = resolveBackupBlobToken(settings);
|
|
16
|
+
const blobAccess = resolveBackupBlobAccess(settings);
|
|
17
|
+
if (!blobToken) {
|
|
18
|
+
return jsonError('Service unavailable', 503);
|
|
19
|
+
}
|
|
20
|
+
const body = await readRequestJson(req);
|
|
21
|
+
const pathname = body?.pathname;
|
|
22
|
+
const url = body?.url;
|
|
23
|
+
const clientSkipRaw = body?.skipCollections;
|
|
24
|
+
const clientSkip = Array.isArray(clientSkipRaw) ? clientSkipRaw.filter((x)=>typeof x === 'string' && x.length > 0 && x.length < 512) : [];
|
|
25
|
+
if (!url || typeof url !== 'string') {
|
|
26
|
+
return jsonError('Missing url', 400);
|
|
27
|
+
}
|
|
28
|
+
try {
|
|
29
|
+
new URL(url);
|
|
30
|
+
} catch {
|
|
31
|
+
return jsonError('Invalid url', 400);
|
|
32
|
+
}
|
|
33
|
+
const backupRead = resolveBackupArchiveRead(settings, pathname);
|
|
34
|
+
if (blobAccess === 'private' && !backupRead) {
|
|
35
|
+
return jsonError('Missing pathname (required for dedicated backup blob store)', 400);
|
|
36
|
+
}
|
|
37
|
+
const { pollSecret, taskId } = await createBackupTask(payload, 'restore', 'Restore queued');
|
|
38
|
+
payload.logger.info({
|
|
39
|
+
taskId,
|
|
40
|
+
url
|
|
41
|
+
}, '[backup-endpoint] Restore queued');
|
|
42
|
+
const collectionBlacklist = [
|
|
43
|
+
...new Set([
|
|
44
|
+
'backup-tasks',
|
|
45
|
+
...clientSkip
|
|
46
|
+
])
|
|
47
|
+
];
|
|
48
|
+
const restoreArchiveMedia = body?.restoreArchiveMedia !== false;
|
|
49
|
+
after(restoreBackup(payload, url, collectionBlacklist, false, taskId, {
|
|
50
|
+
backupRead: backupRead ?? undefined,
|
|
51
|
+
blobAccess,
|
|
52
|
+
blobToken,
|
|
53
|
+
restoreArchiveMedia
|
|
54
|
+
}).then(()=>completeBackupTask(payload, taskId, 'Restore completed')).catch(async (error)=>{
|
|
55
|
+
await failBackupTask(payload, taskId, error);
|
|
56
|
+
payload.logger.error({
|
|
57
|
+
err: error,
|
|
58
|
+
taskId,
|
|
59
|
+
url
|
|
60
|
+
}, '[backup-endpoint] Restore failed');
|
|
61
|
+
}));
|
|
62
|
+
return Response.json({
|
|
63
|
+
pollSecret,
|
|
64
|
+
taskId
|
|
65
|
+
}, {
|
|
66
|
+
status: 202
|
|
67
|
+
});
|
|
68
|
+
},
|
|
69
|
+
method: 'post',
|
|
70
|
+
path: '/backup-mongodb/admin/restore'
|
|
71
|
+
};
|
|
72
|
+
}
|
|
73
|
+
|
|
74
|
+
//# sourceMappingURL=admin-restore.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"sources":["../../../src/endpoints/paths/admin-restore.ts"],"sourcesContent":["import type { Endpoint } from 'payload'\n\nimport { after } from 'next/server'\n\nimport type { BackupPluginOptions } from '../../types'\n\nimport {\n getResolvedCronBackupSettings,\n resolveBackupArchiveRead,\n resolveBackupBlobAccess,\n resolveBackupBlobToken,\n} from '../../core/backupSettings'\nimport { restoreBackup } from '../../core/restore'\nimport { completeBackupTask, createBackupTask, failBackupTask } from '../../core/taskProgress'\nimport { jsonError, readRequestJson, requireBackupAdmin } from '../shared'\n\nexport function createAdminRestoreEndpoint(options: BackupPluginOptions): Endpoint {\n return {\n handler: async (req) => {\n const auth = await requireBackupAdmin(req, options)\n if (auth instanceof Response) {\n return auth\n }\n\n const { payload } = req\n const settings = await getResolvedCronBackupSettings(payload)\n const blobToken = resolveBackupBlobToken(settings)\n const blobAccess = resolveBackupBlobAccess(settings)\n if (!blobToken) {\n return jsonError('Service unavailable', 503)\n }\n\n const body = (await readRequestJson(req)) as Record<string, unknown>\n const pathname = body?.pathname\n const url = body?.url\n const clientSkipRaw = body?.skipCollections\n const clientSkip = Array.isArray(clientSkipRaw)\n ? clientSkipRaw.filter(\n (x: unknown): x is string => typeof x === 'string' && x.length > 0 && x.length < 512,\n )\n : []\n\n if (!url || typeof url !== 'string') {\n return jsonError('Missing url', 400)\n }\n try {\n new URL(url)\n } catch {\n return jsonError('Invalid url', 400)\n }\n\n const backupRead = resolveBackupArchiveRead(settings, pathname)\n if (blobAccess === 'private' && !backupRead) {\n return jsonError('Missing pathname (required for dedicated backup blob store)', 400)\n }\n\n const { pollSecret, taskId } = await createBackupTask(payload, 'restore', 'Restore queued')\n\n payload.logger.info({ taskId, url }, '[backup-endpoint] Restore queued')\n\n const collectionBlacklist = [...new Set(['backup-tasks', ...clientSkip])]\n const restoreArchiveMedia = body?.restoreArchiveMedia !== false\n\n after(\n restoreBackup(payload, url, collectionBlacklist, false, taskId, {\n backupRead: backupRead ?? undefined,\n blobAccess,\n blobToken,\n restoreArchiveMedia,\n })\n .then(() => completeBackupTask(payload, taskId, 'Restore completed'))\n .catch(async (error) => {\n await failBackupTask(payload, taskId, error)\n payload.logger.error({ err: error, taskId, url }, '[backup-endpoint] Restore failed')\n }),\n )\n\n return Response.json({ pollSecret, taskId }, { status: 202 })\n },\n method: 'post',\n path: '/backup-mongodb/admin/restore',\n }\n}\n"],"names":["after","getResolvedCronBackupSettings","resolveBackupArchiveRead","resolveBackupBlobAccess","resolveBackupBlobToken","restoreBackup","completeBackupTask","createBackupTask","failBackupTask","jsonError","readRequestJson","requireBackupAdmin","createAdminRestoreEndpoint","options","handler","req","auth","Response","payload","settings","blobToken","blobAccess","body","pathname","url","clientSkipRaw","skipCollections","clientSkip","Array","isArray","filter","x","length","URL","backupRead","pollSecret","taskId","logger","info","collectionBlacklist","Set","restoreArchiveMedia","undefined","then","catch","error","err","json","status","method","path"],"mappings":"AAEA,SAASA,KAAK,QAAQ,cAAa;AAInC,SACEC,6BAA6B,EAC7BC,wBAAwB,EACxBC,uBAAuB,EACvBC,sBAAsB,QACjB,4BAA2B;AAClC,SAASC,aAAa,QAAQ,qBAAoB;AAClD,SAASC,kBAAkB,EAAEC,gBAAgB,EAAEC,cAAc,QAAQ,0BAAyB;AAC9F,SAASC,SAAS,EAAEC,eAAe,EAAEC,kBAAkB,QAAQ,YAAW;AAE1E,OAAO,SAASC,2BAA2BC,OAA4B;IACrE,OAAO;QACLC,SAAS,OAAOC;YACd,MAAMC,OAAO,MAAML,mBAAmBI,KAAKF;YAC3C,IAAIG,gBAAgBC,UAAU;gBAC5B,OAAOD;YACT;YAEA,MAAM,EAAEE,OAAO,EAAE,GAAGH;YACpB,MAAMI,WAAW,MAAMlB,8BAA8BiB;YACrD,MAAME,YAAYhB,uBAAuBe;YACzC,MAAME,aAAalB,wBAAwBgB;YAC3C,IAAI,CAACC,WAAW;gBACd,OAAOX,UAAU,uBAAuB;YAC1C;YAEA,MAAMa,OAAQ,MAAMZ,gBAAgBK;YACpC,MAAMQ,WAAWD,MAAMC;YACvB,MAAMC,MAAMF,MAAME;YAClB,MAAMC,gBAAgBH,MAAMI;YAC5B,MAAMC,aAAaC,MAAMC,OAAO,CAACJ,iBAC7BA,cAAcK,MAAM,CAClB,CAACC,IAA4B,OAAOA,MAAM,YAAYA,EAAEC,MAAM,GAAG,KAAKD,EAAEC,MAAM,GAAG,OAEnF,EAAE;YAEN,IAAI,CAACR,OAAO,OAAOA,QAAQ,UAAU;gBACnC,OAAOf,UAAU,eAAe;YAClC;YACA,IAAI;gBACF,IAAIwB,IAAIT;YACV,EAAE,OAAM;gBACN,OAAOf,UAAU,eAAe;YAClC;YAEA,MAAMyB,aAAahC,yBAAyBiB,UAAUI;YACtD,IAAIF,eAAe,aAAa,CAACa,YAAY;gBAC3C,OAAOzB,UAAU,+DAA+D;YAClF;YAEA,MAAM,EAAE0B,UAAU,EAAEC,MAAM,EAAE,GAAG,MAAM7B,iBAAiBW,SAAS,WAAW;YAE1EA,QAAQmB,MAAM,CAACC,IAAI,CAAC;gBAAEF;gBAAQZ;YAAI,GAAG;YAErC,MAAMe,sBAAsB;mBAAI,IAAIC,IAAI;oBAAC;uBAAmBb;iBAAW;aAAE;YACzE,MAAMc,sBAAsBnB,MAAMmB,wBAAwB;YAE1DzC,MACEK,cAAca,SAASM,KAAKe,qBAAqB,OAAOH,QAAQ;gBAC9DF,YAAYA,cAAcQ;gBAC1BrB;gBACAD;gBACAqB;YACF,GACGE,IAAI,CAAC,IAAMrC,mBAAmBY,SAASkB,QAAQ,sBAC/CQ,KAAK,CAAC,OAAOC;gBACZ,MAAMrC,eAAeU,SAASkB,QAAQS;gBACtC3B,QAAQmB,MAAM,CAACQ,KAAK,CAAC;oBAAEC,KAAKD;oBAAOT;oBAAQZ;gBAAI,GAAG;YACpD;YAGJ,OAAOP,SAAS8B,IAAI,CAAC;gBAAEZ;gBAAYC;YAAO,GAAG;gBAAEY,QAAQ;YAAI;QAC7D;QACAC,QAAQ;QACRC,MAAM;IACR;AACF"}
|
|
@@ -0,0 +1,47 @@
|
|
|
1
|
+
import { after } from 'next/server';
|
|
2
|
+
import { restoreBackup, restoreSeedMedia } from '../../core/restore';
|
|
3
|
+
import { completeBackupTask, createBackupTask, failBackupTask } from '../../core/taskProgress';
|
|
4
|
+
import { requireBackupAdmin, requireBlobEnv } from '../shared';
|
|
5
|
+
export function createAdminSeedEndpoint(options) {
|
|
6
|
+
const seedUrl = options.seedDemoDumpUrl;
|
|
7
|
+
if (!seedUrl) {
|
|
8
|
+
return null;
|
|
9
|
+
}
|
|
10
|
+
return {
|
|
11
|
+
handler: async (req)=>{
|
|
12
|
+
const blobErr = requireBlobEnv();
|
|
13
|
+
if (blobErr) {
|
|
14
|
+
return blobErr;
|
|
15
|
+
}
|
|
16
|
+
const auth = await requireBackupAdmin(req, options);
|
|
17
|
+
if (auth instanceof Response) {
|
|
18
|
+
return auth;
|
|
19
|
+
}
|
|
20
|
+
const { payload } = req;
|
|
21
|
+
const { pollSecret, taskId } = await createBackupTask(payload, 'seed', 'Seed queued');
|
|
22
|
+
payload.logger.info({
|
|
23
|
+
taskId
|
|
24
|
+
}, '[backup-endpoint] Seed queued');
|
|
25
|
+
after(restoreSeedMedia(payload, taskId).then(()=>restoreBackup(payload, seedUrl, [
|
|
26
|
+
'users',
|
|
27
|
+
'roles'
|
|
28
|
+
], false, taskId)).then(()=>completeBackupTask(payload, taskId, 'Seed completed')).catch(async (error)=>{
|
|
29
|
+
await failBackupTask(payload, taskId, error);
|
|
30
|
+
payload.logger.error({
|
|
31
|
+
err: error,
|
|
32
|
+
taskId
|
|
33
|
+
}, '[backup-endpoint] Seed failed');
|
|
34
|
+
}));
|
|
35
|
+
return Response.json({
|
|
36
|
+
pollSecret,
|
|
37
|
+
taskId
|
|
38
|
+
}, {
|
|
39
|
+
status: 202
|
|
40
|
+
});
|
|
41
|
+
},
|
|
42
|
+
method: 'post',
|
|
43
|
+
path: '/backup-mongodb/admin/seed'
|
|
44
|
+
};
|
|
45
|
+
}
|
|
46
|
+
|
|
47
|
+
//# sourceMappingURL=admin-seed.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"sources":["../../../src/endpoints/paths/admin-seed.ts"],"sourcesContent":["import type { Endpoint } from 'payload'\n\nimport { after } from 'next/server'\n\nimport type { BackupPluginOptions } from '../../types'\n\nimport { restoreBackup, restoreSeedMedia } from '../../core/restore'\nimport { completeBackupTask, createBackupTask, failBackupTask } from '../../core/taskProgress'\nimport { requireBackupAdmin, requireBlobEnv } from '../shared'\n\nexport function createAdminSeedEndpoint(options: BackupPluginOptions): Endpoint | null {\n const seedUrl = options.seedDemoDumpUrl\n if (!seedUrl) {\n return null\n }\n\n return {\n handler: async (req) => {\n const blobErr = requireBlobEnv()\n if (blobErr) {\n return blobErr\n }\n\n const auth = await requireBackupAdmin(req, options)\n if (auth instanceof Response) {\n return auth\n }\n\n const { payload } = req\n const { pollSecret, taskId } = await createBackupTask(payload, 'seed', 'Seed queued')\n\n payload.logger.info({ taskId }, '[backup-endpoint] Seed queued')\n\n after(\n restoreSeedMedia(payload, taskId)\n .then(() => restoreBackup(payload, seedUrl, ['users', 'roles'], false, taskId))\n .then(() => completeBackupTask(payload, taskId, 'Seed completed'))\n .catch(async (error) => {\n await failBackupTask(payload, taskId, error)\n payload.logger.error({ err: error, taskId }, '[backup-endpoint] Seed failed')\n }),\n )\n\n return Response.json({ pollSecret, taskId }, { status: 202 })\n },\n method: 'post',\n path: '/backup-mongodb/admin/seed',\n }\n}\n"],"names":["after","restoreBackup","restoreSeedMedia","completeBackupTask","createBackupTask","failBackupTask","requireBackupAdmin","requireBlobEnv","createAdminSeedEndpoint","options","seedUrl","seedDemoDumpUrl","handler","req","blobErr","auth","Response","payload","pollSecret","taskId","logger","info","then","catch","error","err","json","status","method","path"],"mappings":"AAEA,SAASA,KAAK,QAAQ,cAAa;AAInC,SAASC,aAAa,EAAEC,gBAAgB,QAAQ,qBAAoB;AACpE,SAASC,kBAAkB,EAAEC,gBAAgB,EAAEC,cAAc,QAAQ,0BAAyB;AAC9F,SAASC,kBAAkB,EAAEC,cAAc,QAAQ,YAAW;AAE9D,OAAO,SAASC,wBAAwBC,OAA4B;IAClE,MAAMC,UAAUD,QAAQE,eAAe;IACvC,IAAI,CAACD,SAAS;QACZ,OAAO;IACT;IAEA,OAAO;QACLE,SAAS,OAAOC;YACd,MAAMC,UAAUP;YAChB,IAAIO,SAAS;gBACX,OAAOA;YACT;YAEA,MAAMC,OAAO,MAAMT,mBAAmBO,KAAKJ;YAC3C,IAAIM,gBAAgBC,UAAU;gBAC5B,OAAOD;YACT;YAEA,MAAM,EAAEE,OAAO,EAAE,GAAGJ;YACpB,MAAM,EAAEK,UAAU,EAAEC,MAAM,EAAE,GAAG,MAAMf,iBAAiBa,SAAS,QAAQ;YAEvEA,QAAQG,MAAM,CAACC,IAAI,CAAC;gBAAEF;YAAO,GAAG;YAEhCnB,MACEE,iBAAiBe,SAASE,QACvBG,IAAI,CAAC,IAAMrB,cAAcgB,SAASP,SAAS;oBAAC;oBAAS;iBAAQ,EAAE,OAAOS,SACtEG,IAAI,CAAC,IAAMnB,mBAAmBc,SAASE,QAAQ,mBAC/CI,KAAK,CAAC,OAAOC;gBACZ,MAAMnB,eAAeY,SAASE,QAAQK;gBACtCP,QAAQG,MAAM,CAACI,KAAK,CAAC;oBAAEC,KAAKD;oBAAOL;gBAAO,GAAG;YAC/C;YAGJ,OAAOH,SAASU,IAAI,CAAC;gBAAER;gBAAYC;YAAO,GAAG;gBAAEQ,QAAQ;YAAI;QAC7D;QACAC,QAAQ;QACRC,MAAM;IACR;AACF"}
|
|
@@ -0,0 +1,230 @@
|
|
|
1
|
+
import { list } from '@vercel/blob';
|
|
2
|
+
import { after } from 'next/server';
|
|
3
|
+
import { transferBackupBlobsToToken } from '../../core/backupBlobTransfer';
|
|
4
|
+
import { BACKUP_SETTINGS_SLUG, getResolvedCronBackupSettings, normalizeSkipMongoCollections, resolveBackupBlobAccess, resolveBackupBlobToken, toPayloadSkipRows } from '../../core/backupSettings';
|
|
5
|
+
import { validateBackupBlobToken } from '../../core/blobTokenValidate';
|
|
6
|
+
import { completeBackupTask, createBackupTask, failBackupTask, updateBackupTask } from '../../core/taskProgress';
|
|
7
|
+
import { describeCronSchedule, readVercelBackupCronFromRepo } from '../../core/vercelBackupCron';
|
|
8
|
+
import { maskBlobReadWriteToken, shouldPreserveBackupBlobTokenField } from '../../utils/maskBlobToken';
|
|
9
|
+
import { readRequestJson, requireBackupAdmin } from '../shared';
|
|
10
|
+
function clampBackupsToKeep(n) {
|
|
11
|
+
if (typeof n !== 'number' || !Number.isFinite(n)) {
|
|
12
|
+
return 10;
|
|
13
|
+
}
|
|
14
|
+
return Math.min(365, Math.max(1, Math.floor(n)));
|
|
15
|
+
}
|
|
16
|
+
function buildSettingsJson(stored, options, vercelCron, humanDescription, transfer) {
|
|
17
|
+
const t = transfer ?? {
|
|
18
|
+
deferred: false,
|
|
19
|
+
failed: 0,
|
|
20
|
+
performed: false,
|
|
21
|
+
skipped: 0,
|
|
22
|
+
total: 0,
|
|
23
|
+
transferred: 0
|
|
24
|
+
};
|
|
25
|
+
return {
|
|
26
|
+
id: stored.id,
|
|
27
|
+
backupBlobAccess: stored.backupBlobAccess,
|
|
28
|
+
backupBlobAccessEffective: resolveBackupBlobAccess(stored),
|
|
29
|
+
backupBlobTokenMasked: maskBlobReadWriteToken(stored.backupBlobReadWriteToken),
|
|
30
|
+
backupsToKeep: stored.backupsToKeep,
|
|
31
|
+
cron: vercelCron ? {
|
|
32
|
+
configFileRelative: vercelCron.configFileRelative,
|
|
33
|
+
humanDescription,
|
|
34
|
+
path: vercelCron.path,
|
|
35
|
+
schedule: vercelCron.schedule
|
|
36
|
+
} : null,
|
|
37
|
+
effectiveBackupsToKeep: typeof options.backupsToKeep === 'number' ? options.backupsToKeep : stored.backupsToKeep,
|
|
38
|
+
hasBackupBlobReadWriteToken: stored.backupBlobReadWriteToken.trim().length > 0,
|
|
39
|
+
includeMediaForCron: stored.includeMediaForCron,
|
|
40
|
+
pluginBackupsToKeepOverride: typeof options.backupsToKeep === 'number',
|
|
41
|
+
skipMongoCollections: stored.skipMongoCollections,
|
|
42
|
+
transfer: t
|
|
43
|
+
};
|
|
44
|
+
}
|
|
45
|
+
export function createAdminSettingsEndpoints(options) {
|
|
46
|
+
return [
|
|
47
|
+
{
|
|
48
|
+
handler: async (req)=>{
|
|
49
|
+
const auth = await requireBackupAdmin(req, options);
|
|
50
|
+
if (auth instanceof Response) {
|
|
51
|
+
return auth;
|
|
52
|
+
}
|
|
53
|
+
const { payload } = req;
|
|
54
|
+
const stored = await getResolvedCronBackupSettings(payload);
|
|
55
|
+
const vercelCron = readVercelBackupCronFromRepo();
|
|
56
|
+
const humanDescription = vercelCron?.schedule != null ? describeCronSchedule(vercelCron.schedule) : null;
|
|
57
|
+
return Response.json(buildSettingsJson(stored, options, vercelCron, humanDescription, undefined));
|
|
58
|
+
},
|
|
59
|
+
method: 'get',
|
|
60
|
+
path: '/backup-mongodb/admin/settings'
|
|
61
|
+
},
|
|
62
|
+
{
|
|
63
|
+
handler: async (req)=>{
|
|
64
|
+
const auth = await requireBackupAdmin(req, options);
|
|
65
|
+
if (auth instanceof Response) {
|
|
66
|
+
return auth;
|
|
67
|
+
}
|
|
68
|
+
const { payload } = req;
|
|
69
|
+
const body = await readRequestJson(req);
|
|
70
|
+
const backupsToKeep = clampBackupsToKeep(body?.backupsToKeep);
|
|
71
|
+
const skipMongoCollections = normalizeSkipMongoCollections(body?.skipMongoCollections);
|
|
72
|
+
const includeMediaForCron = body?.includeMediaForCron === true;
|
|
73
|
+
const transferBackupBlobs = typeof body?.transferBackupBlobs === 'boolean' ? body.transferBackupBlobs : false;
|
|
74
|
+
const deleteBackupBlobsFromSource = typeof body?.deleteBackupBlobsFromSource === 'boolean' ? body.deleteBackupBlobsFromSource : false;
|
|
75
|
+
const requestedRaw = typeof body?.backupBlobReadWriteToken === 'string' ? body.backupBlobReadWriteToken : '';
|
|
76
|
+
const requestedTrimmed = requestedRaw.trim();
|
|
77
|
+
let stored = await getResolvedCronBackupSettings(payload);
|
|
78
|
+
const previousAccess = resolveBackupBlobAccess(stored);
|
|
79
|
+
const previousBackupBlobToken = stored.backupBlobReadWriteToken.trim();
|
|
80
|
+
const hadStoredToken = previousBackupBlobToken.length > 0;
|
|
81
|
+
const preserveTokenField = shouldPreserveBackupBlobTokenField(requestedTrimmed, hadStoredToken);
|
|
82
|
+
const tokenForDb = preserveTokenField ? previousBackupBlobToken : requestedTrimmed;
|
|
83
|
+
// Access detection: re-probe when the token actually changes; preserve existing otherwise;
|
|
84
|
+
// clear when the override is removed (falls back to heuristic on the default env token).
|
|
85
|
+
let backupBlobAccessForDb = stored.backupBlobAccess;
|
|
86
|
+
if (!preserveTokenField) {
|
|
87
|
+
if (tokenForDb.length === 0) {
|
|
88
|
+
backupBlobAccessForDb = null;
|
|
89
|
+
} else {
|
|
90
|
+
const validation = await validateBackupBlobToken(tokenForDb);
|
|
91
|
+
if (!validation.ok) {
|
|
92
|
+
return Response.json({
|
|
93
|
+
error: `Blob token rejected: ${validation.error ?? 'unknown error'}`
|
|
94
|
+
}, {
|
|
95
|
+
status: 422
|
|
96
|
+
});
|
|
97
|
+
}
|
|
98
|
+
backupBlobAccessForDb = validation.access ?? null;
|
|
99
|
+
}
|
|
100
|
+
}
|
|
101
|
+
if (!stored.id) {
|
|
102
|
+
await payload.create({
|
|
103
|
+
collection: BACKUP_SETTINGS_SLUG,
|
|
104
|
+
data: {
|
|
105
|
+
backupBlobAccess: backupBlobAccessForDb,
|
|
106
|
+
backupBlobReadWriteToken: tokenForDb,
|
|
107
|
+
backupsToKeep,
|
|
108
|
+
includeMediaForCron,
|
|
109
|
+
skipMongoCollections: toPayloadSkipRows(skipMongoCollections)
|
|
110
|
+
},
|
|
111
|
+
overrideAccess: true
|
|
112
|
+
});
|
|
113
|
+
} else {
|
|
114
|
+
await payload.update({
|
|
115
|
+
id: stored.id,
|
|
116
|
+
collection: BACKUP_SETTINGS_SLUG,
|
|
117
|
+
data: {
|
|
118
|
+
backupBlobAccess: backupBlobAccessForDb,
|
|
119
|
+
backupBlobReadWriteToken: tokenForDb,
|
|
120
|
+
backupsToKeep,
|
|
121
|
+
includeMediaForCron,
|
|
122
|
+
skipMongoCollections: toPayloadSkipRows(skipMongoCollections)
|
|
123
|
+
},
|
|
124
|
+
overrideAccess: true
|
|
125
|
+
});
|
|
126
|
+
}
|
|
127
|
+
stored = await getResolvedCronBackupSettings(payload);
|
|
128
|
+
if (!stored.id) {
|
|
129
|
+
return Response.json({
|
|
130
|
+
error: 'Could not persist backup settings'
|
|
131
|
+
}, {
|
|
132
|
+
status: 500
|
|
133
|
+
});
|
|
134
|
+
}
|
|
135
|
+
const vercelCron = readVercelBackupCronFromRepo();
|
|
136
|
+
const humanDescription = vercelCron?.schedule != null ? describeCronSchedule(vercelCron.schedule) : null;
|
|
137
|
+
const newBlobToken = stored.backupBlobReadWriteToken.trim();
|
|
138
|
+
// Read blobs from the *previous* store when rotating tokens; otherwise first-time setup
|
|
139
|
+
// reads from BLOB_READ_WRITE_TOKEN (default Vercel store).
|
|
140
|
+
const sourceTokenForTransfer = previousBackupBlobToken.length > 0 ? previousBackupBlobToken : (process.env.BLOB_READ_WRITE_TOKEN || '').trim();
|
|
141
|
+
const shouldTransferToNewBlobToken = newBlobToken.length > 0 && sourceTokenForTransfer.length > 0 && newBlobToken !== sourceTokenForTransfer;
|
|
142
|
+
if (!transferBackupBlobs || !shouldTransferToNewBlobToken) {
|
|
143
|
+
return Response.json(buildSettingsJson(stored, options, vercelCron, humanDescription, {
|
|
144
|
+
deferred: false,
|
|
145
|
+
failed: 0,
|
|
146
|
+
performed: false,
|
|
147
|
+
skipped: 0,
|
|
148
|
+
total: 0,
|
|
149
|
+
transferred: 0
|
|
150
|
+
}));
|
|
151
|
+
}
|
|
152
|
+
const targetToken = resolveBackupBlobToken(stored);
|
|
153
|
+
const targetAccess = resolveBackupBlobAccess(stored);
|
|
154
|
+
const { blobs } = await list({
|
|
155
|
+
limit: 1000,
|
|
156
|
+
prefix: 'backups/',
|
|
157
|
+
token: sourceTokenForTransfer
|
|
158
|
+
});
|
|
159
|
+
const total = blobs.length;
|
|
160
|
+
if (total === 0) {
|
|
161
|
+
const summary = await transferBackupBlobsToToken(payload, sourceTokenForTransfer, targetToken, {
|
|
162
|
+
deleteFromSource: deleteBackupBlobsFromSource,
|
|
163
|
+
sourceAccessHint: previousAccess,
|
|
164
|
+
targetAccess
|
|
165
|
+
});
|
|
166
|
+
return Response.json(buildSettingsJson(stored, options, vercelCron, humanDescription, {
|
|
167
|
+
deferred: false,
|
|
168
|
+
failed: summary.failed,
|
|
169
|
+
performed: true,
|
|
170
|
+
skipped: summary.skipped,
|
|
171
|
+
total: summary.total,
|
|
172
|
+
transferred: summary.transferred
|
|
173
|
+
}));
|
|
174
|
+
}
|
|
175
|
+
const { pollSecret, taskId } = await createBackupTask(payload, 'blobTransfer', JSON.stringify({
|
|
176
|
+
failed: 0,
|
|
177
|
+
pathname: '',
|
|
178
|
+
total,
|
|
179
|
+
transferred: 0
|
|
180
|
+
}));
|
|
181
|
+
after((async ()=>{
|
|
182
|
+
try {
|
|
183
|
+
await updateBackupTask(payload, taskId, {
|
|
184
|
+
message: JSON.stringify({
|
|
185
|
+
failed: 0,
|
|
186
|
+
pathname: '',
|
|
187
|
+
total,
|
|
188
|
+
transferred: 0
|
|
189
|
+
}),
|
|
190
|
+
status: 'running'
|
|
191
|
+
});
|
|
192
|
+
const summary = await transferBackupBlobsToToken(payload, sourceTokenForTransfer, targetToken, {
|
|
193
|
+
deleteFromSource: deleteBackupBlobsFromSource,
|
|
194
|
+
sourceAccessHint: previousAccess,
|
|
195
|
+
targetAccess,
|
|
196
|
+
taskId
|
|
197
|
+
});
|
|
198
|
+
await completeBackupTask(payload, taskId, JSON.stringify({
|
|
199
|
+
failed: summary.failed,
|
|
200
|
+
pathname: '',
|
|
201
|
+
phase: 'done',
|
|
202
|
+
total: summary.total,
|
|
203
|
+
transferred: summary.transferred
|
|
204
|
+
}));
|
|
205
|
+
} catch (e) {
|
|
206
|
+
await failBackupTask(payload, taskId, e);
|
|
207
|
+
payload.logger.error({
|
|
208
|
+
err: e,
|
|
209
|
+
taskId
|
|
210
|
+
}, '[backup-settings] Blob transfer task failed');
|
|
211
|
+
}
|
|
212
|
+
})());
|
|
213
|
+
return Response.json(buildSettingsJson(stored, options, vercelCron, humanDescription, {
|
|
214
|
+
deferred: true,
|
|
215
|
+
failed: 0,
|
|
216
|
+
performed: true,
|
|
217
|
+
pollSecret,
|
|
218
|
+
skipped: 0,
|
|
219
|
+
taskId,
|
|
220
|
+
total,
|
|
221
|
+
transferred: 0
|
|
222
|
+
}));
|
|
223
|
+
},
|
|
224
|
+
method: 'patch',
|
|
225
|
+
path: '/backup-mongodb/admin/settings'
|
|
226
|
+
}
|
|
227
|
+
];
|
|
228
|
+
}
|
|
229
|
+
|
|
230
|
+
//# sourceMappingURL=admin-settings.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"sources":["../../../src/endpoints/paths/admin-settings.ts"],"sourcesContent":["import type { Endpoint } from 'payload'\n\nimport { list } from '@vercel/blob'\nimport { after } from 'next/server'\n\nimport type { BackupPluginOptions } from '../../types'\n\nimport { transferBackupBlobsToToken } from '../../core/backupBlobTransfer'\nimport {\n BACKUP_SETTINGS_SLUG,\n getResolvedCronBackupSettings,\n normalizeSkipMongoCollections,\n resolveBackupBlobAccess,\n resolveBackupBlobToken,\n toPayloadSkipRows,\n} from '../../core/backupSettings'\nimport { validateBackupBlobToken } from '../../core/blobTokenValidate'\nimport {\n completeBackupTask,\n createBackupTask,\n failBackupTask,\n updateBackupTask,\n} from '../../core/taskProgress'\nimport { describeCronSchedule, readVercelBackupCronFromRepo } from '../../core/vercelBackupCron'\nimport {\n maskBlobReadWriteToken,\n shouldPreserveBackupBlobTokenField,\n} from '../../utils/maskBlobToken'\nimport { readRequestJson, requireBackupAdmin } from '../shared'\n\nfunction clampBackupsToKeep(n: unknown): number {\n if (typeof n !== 'number' || !Number.isFinite(n)) {\n return 10\n }\n return Math.min(365, Math.max(1, Math.floor(n)))\n}\n\nfunction buildSettingsJson(\n stored: Awaited<ReturnType<typeof getResolvedCronBackupSettings>>,\n options: BackupPluginOptions,\n vercelCron: ReturnType<typeof readVercelBackupCronFromRepo>,\n humanDescription: null | string,\n transfer:\n | {\n deferred: boolean\n failed: number\n performed: boolean\n pollSecret?: string\n skipped: number\n taskId?: string\n total: number\n transferred: number\n }\n | undefined,\n) {\n const t = transfer ?? {\n deferred: false,\n failed: 0,\n performed: false,\n skipped: 0,\n total: 0,\n transferred: 0,\n }\n return {\n id: stored.id,\n backupBlobAccess: stored.backupBlobAccess,\n backupBlobAccessEffective: resolveBackupBlobAccess(stored),\n backupBlobTokenMasked: maskBlobReadWriteToken(stored.backupBlobReadWriteToken),\n backupsToKeep: stored.backupsToKeep,\n cron: vercelCron\n ? {\n configFileRelative: vercelCron.configFileRelative,\n humanDescription,\n path: vercelCron.path,\n schedule: vercelCron.schedule,\n }\n : null,\n effectiveBackupsToKeep:\n typeof options.backupsToKeep === 'number' ? options.backupsToKeep : stored.backupsToKeep,\n hasBackupBlobReadWriteToken: stored.backupBlobReadWriteToken.trim().length > 0,\n includeMediaForCron: stored.includeMediaForCron,\n pluginBackupsToKeepOverride: typeof options.backupsToKeep === 'number',\n skipMongoCollections: stored.skipMongoCollections,\n transfer: t,\n }\n}\n\nexport function createAdminSettingsEndpoints(options: BackupPluginOptions): Endpoint[] {\n return [\n {\n handler: async (req) => {\n const auth = await requireBackupAdmin(req, options)\n if (auth instanceof Response) {\n return auth\n }\n\n const { payload } = req\n const stored = await getResolvedCronBackupSettings(payload)\n const vercelCron = readVercelBackupCronFromRepo()\n const humanDescription =\n vercelCron?.schedule != null ? describeCronSchedule(vercelCron.schedule) : null\n\n return Response.json(\n buildSettingsJson(stored, options, vercelCron, humanDescription, undefined),\n )\n },\n method: 'get',\n path: '/backup-mongodb/admin/settings',\n },\n {\n handler: async (req) => {\n const auth = await requireBackupAdmin(req, options)\n if (auth instanceof Response) {\n return auth\n }\n\n const { payload } = req\n const body = (await readRequestJson(req)) as Record<string, unknown>\n const backupsToKeep = clampBackupsToKeep(body?.backupsToKeep)\n const skipMongoCollections = normalizeSkipMongoCollections(body?.skipMongoCollections)\n const includeMediaForCron = body?.includeMediaForCron === true\n const transferBackupBlobs =\n typeof body?.transferBackupBlobs === 'boolean' ? body.transferBackupBlobs : false\n const deleteBackupBlobsFromSource =\n typeof body?.deleteBackupBlobsFromSource === 'boolean'\n ? body.deleteBackupBlobsFromSource\n : false\n const requestedRaw =\n typeof body?.backupBlobReadWriteToken === 'string' ? body.backupBlobReadWriteToken : ''\n const requestedTrimmed = requestedRaw.trim()\n\n let stored = await getResolvedCronBackupSettings(payload)\n const previousAccess = resolveBackupBlobAccess(stored)\n const previousBackupBlobToken = stored.backupBlobReadWriteToken.trim()\n const hadStoredToken = previousBackupBlobToken.length > 0\n const preserveTokenField = shouldPreserveBackupBlobTokenField(\n requestedTrimmed,\n hadStoredToken,\n )\n const tokenForDb = preserveTokenField ? previousBackupBlobToken : requestedTrimmed\n\n // Access detection: re-probe when the token actually changes; preserve existing otherwise;\n // clear when the override is removed (falls back to heuristic on the default env token).\n let backupBlobAccessForDb: 'private' | 'public' | null = stored.backupBlobAccess\n if (!preserveTokenField) {\n if (tokenForDb.length === 0) {\n backupBlobAccessForDb = null\n } else {\n const validation = await validateBackupBlobToken(tokenForDb)\n if (!validation.ok) {\n return Response.json(\n { error: `Blob token rejected: ${validation.error ?? 'unknown error'}` },\n { status: 422 },\n )\n }\n backupBlobAccessForDb = validation.access ?? null\n }\n }\n\n if (!stored.id) {\n await payload.create({\n collection: BACKUP_SETTINGS_SLUG,\n data: {\n backupBlobAccess: backupBlobAccessForDb,\n backupBlobReadWriteToken: tokenForDb,\n backupsToKeep,\n includeMediaForCron,\n skipMongoCollections: toPayloadSkipRows(skipMongoCollections),\n },\n overrideAccess: true,\n })\n } else {\n await payload.update({\n id: stored.id,\n collection: BACKUP_SETTINGS_SLUG,\n data: {\n backupBlobAccess: backupBlobAccessForDb,\n backupBlobReadWriteToken: tokenForDb,\n backupsToKeep,\n includeMediaForCron,\n skipMongoCollections: toPayloadSkipRows(skipMongoCollections),\n },\n overrideAccess: true,\n })\n }\n\n stored = await getResolvedCronBackupSettings(payload)\n if (!stored.id) {\n return Response.json({ error: 'Could not persist backup settings' }, { status: 500 })\n }\n\n const vercelCron = readVercelBackupCronFromRepo()\n const humanDescription =\n vercelCron?.schedule != null ? describeCronSchedule(vercelCron.schedule) : null\n\n const newBlobToken = stored.backupBlobReadWriteToken.trim()\n // Read blobs from the *previous* store when rotating tokens; otherwise first-time setup\n // reads from BLOB_READ_WRITE_TOKEN (default Vercel store).\n const sourceTokenForTransfer =\n previousBackupBlobToken.length > 0\n ? previousBackupBlobToken\n : (process.env.BLOB_READ_WRITE_TOKEN || '').trim()\n\n const shouldTransferToNewBlobToken =\n newBlobToken.length > 0 &&\n sourceTokenForTransfer.length > 0 &&\n newBlobToken !== sourceTokenForTransfer\n\n if (!transferBackupBlobs || !shouldTransferToNewBlobToken) {\n return Response.json(\n buildSettingsJson(stored, options, vercelCron, humanDescription, {\n deferred: false,\n failed: 0,\n performed: false,\n skipped: 0,\n total: 0,\n transferred: 0,\n }),\n )\n }\n\n const targetToken = resolveBackupBlobToken(stored)\n const targetAccess = resolveBackupBlobAccess(stored)\n const { blobs } = await list({\n limit: 1000,\n prefix: 'backups/',\n token: sourceTokenForTransfer,\n })\n const total = blobs.length\n\n if (total === 0) {\n const summary = await transferBackupBlobsToToken(\n payload,\n sourceTokenForTransfer,\n targetToken,\n {\n deleteFromSource: deleteBackupBlobsFromSource,\n sourceAccessHint: previousAccess,\n targetAccess,\n },\n )\n return Response.json(\n buildSettingsJson(stored, options, vercelCron, humanDescription, {\n deferred: false,\n failed: summary.failed,\n performed: true,\n skipped: summary.skipped,\n total: summary.total,\n transferred: summary.transferred,\n }),\n )\n }\n\n const { pollSecret, taskId } = await createBackupTask(\n payload,\n 'blobTransfer',\n JSON.stringify({ failed: 0, pathname: '', total, transferred: 0 }),\n )\n\n after(\n (async () => {\n try {\n await updateBackupTask(payload, taskId, {\n message: JSON.stringify({ failed: 0, pathname: '', total, transferred: 0 }),\n status: 'running',\n })\n const summary = await transferBackupBlobsToToken(\n payload,\n sourceTokenForTransfer,\n targetToken,\n {\n deleteFromSource: deleteBackupBlobsFromSource,\n sourceAccessHint: previousAccess,\n targetAccess,\n taskId,\n },\n )\n await completeBackupTask(\n payload,\n taskId,\n JSON.stringify({\n failed: summary.failed,\n pathname: '',\n phase: 'done',\n total: summary.total,\n transferred: summary.transferred,\n }),\n )\n } catch (e) {\n await failBackupTask(payload, taskId, e)\n payload.logger.error(\n { err: e, taskId },\n '[backup-settings] Blob transfer task failed',\n )\n }\n })(),\n )\n\n return Response.json(\n buildSettingsJson(stored, options, vercelCron, humanDescription, {\n deferred: true,\n failed: 0,\n performed: true,\n pollSecret,\n skipped: 0,\n taskId,\n total,\n transferred: 0,\n }),\n )\n },\n method: 'patch',\n path: '/backup-mongodb/admin/settings',\n },\n ]\n}\n"],"names":["list","after","transferBackupBlobsToToken","BACKUP_SETTINGS_SLUG","getResolvedCronBackupSettings","normalizeSkipMongoCollections","resolveBackupBlobAccess","resolveBackupBlobToken","toPayloadSkipRows","validateBackupBlobToken","completeBackupTask","createBackupTask","failBackupTask","updateBackupTask","describeCronSchedule","readVercelBackupCronFromRepo","maskBlobReadWriteToken","shouldPreserveBackupBlobTokenField","readRequestJson","requireBackupAdmin","clampBackupsToKeep","n","Number","isFinite","Math","min","max","floor","buildSettingsJson","stored","options","vercelCron","humanDescription","transfer","t","deferred","failed","performed","skipped","total","transferred","id","backupBlobAccess","backupBlobAccessEffective","backupBlobTokenMasked","backupBlobReadWriteToken","backupsToKeep","cron","configFileRelative","path","schedule","effectiveBackupsToKeep","hasBackupBlobReadWriteToken","trim","length","includeMediaForCron","pluginBackupsToKeepOverride","skipMongoCollections","createAdminSettingsEndpoints","handler","req","auth","Response","payload","json","undefined","method","body","transferBackupBlobs","deleteBackupBlobsFromSource","requestedRaw","requestedTrimmed","previousAccess","previousBackupBlobToken","hadStoredToken","preserveTokenField","tokenForDb","backupBlobAccessForDb","validation","ok","error","status","access","create","collection","data","overrideAccess","update","newBlobToken","sourceTokenForTransfer","process","env","BLOB_READ_WRITE_TOKEN","shouldTransferToNewBlobToken","targetToken","targetAccess","blobs","limit","prefix","token","summary","deleteFromSource","sourceAccessHint","pollSecret","taskId","JSON","stringify","pathname","message","phase","e","logger","err"],"mappings":"AAEA,SAASA,IAAI,QAAQ,eAAc;AACnC,SAASC,KAAK,QAAQ,cAAa;AAInC,SAASC,0BAA0B,QAAQ,gCAA+B;AAC1E,SACEC,oBAAoB,EACpBC,6BAA6B,EAC7BC,6BAA6B,EAC7BC,uBAAuB,EACvBC,sBAAsB,EACtBC,iBAAiB,QACZ,4BAA2B;AAClC,SAASC,uBAAuB,QAAQ,+BAA8B;AACtE,SACEC,kBAAkB,EAClBC,gBAAgB,EAChBC,cAAc,EACdC,gBAAgB,QACX,0BAAyB;AAChC,SAASC,oBAAoB,EAAEC,4BAA4B,QAAQ,8BAA6B;AAChG,SACEC,sBAAsB,EACtBC,kCAAkC,QAC7B,4BAA2B;AAClC,SAASC,eAAe,EAAEC,kBAAkB,QAAQ,YAAW;AAE/D,SAASC,mBAAmBC,CAAU;IACpC,IAAI,OAAOA,MAAM,YAAY,CAACC,OAAOC,QAAQ,CAACF,IAAI;QAChD,OAAO;IACT;IACA,OAAOG,KAAKC,GAAG,CAAC,KAAKD,KAAKE,GAAG,CAAC,GAAGF,KAAKG,KAAK,CAACN;AAC9C;AAEA,SAASO,kBACPC,MAAiE,EACjEC,OAA4B,EAC5BC,UAA2D,EAC3DC,gBAA+B,EAC/BC,QAWa;IAEb,MAAMC,IAAID,YAAY;QACpBE,UAAU;QACVC,QAAQ;QACRC,WAAW;QACXC,SAAS;QACTC,OAAO;QACPC,aAAa;IACf;IACA,OAAO;QACLC,IAAIZ,OAAOY,EAAE;QACbC,kBAAkBb,OAAOa,gBAAgB;QACzCC,2BAA2BrC,wBAAwBuB;QACnDe,uBAAuB5B,uBAAuBa,OAAOgB,wBAAwB;QAC7EC,eAAejB,OAAOiB,aAAa;QACnCC,MAAMhB,aACF;YACEiB,oBAAoBjB,WAAWiB,kBAAkB;YACjDhB;YACAiB,MAAMlB,WAAWkB,IAAI;YACrBC,UAAUnB,WAAWmB,QAAQ;QAC/B,IACA;QACJC,wBACE,OAAOrB,QAAQgB,aAAa,KAAK,WAAWhB,QAAQgB,aAAa,GAAGjB,OAAOiB,aAAa;QAC1FM,6BAA6BvB,OAAOgB,wBAAwB,CAACQ,IAAI,GAAGC,MAAM,GAAG;QAC7EC,qBAAqB1B,OAAO0B,mBAAmB;QAC/CC,6BAA6B,OAAO1B,QAAQgB,aAAa,KAAK;QAC9DW,sBAAsB5B,OAAO4B,oBAAoB;QACjDxB,UAAUC;IACZ;AACF;AAEA,OAAO,SAASwB,6BAA6B5B,OAA4B;IACvE,OAAO;QACL;YACE6B,SAAS,OAAOC;gBACd,MAAMC,OAAO,MAAM1C,mBAAmByC,KAAK9B;gBAC3C,IAAI+B,gBAAgBC,UAAU;oBAC5B,OAAOD;gBACT;gBAEA,MAAM,EAAEE,OAAO,EAAE,GAAGH;gBACpB,MAAM/B,SAAS,MAAMzB,8BAA8B2D;gBACnD,MAAMhC,aAAahB;gBACnB,MAAMiB,mBACJD,YAAYmB,YAAY,OAAOpC,qBAAqBiB,WAAWmB,QAAQ,IAAI;gBAE7E,OAAOY,SAASE,IAAI,CAClBpC,kBAAkBC,QAAQC,SAASC,YAAYC,kBAAkBiC;YAErE;YACAC,QAAQ;YACRjB,MAAM;QACR;QACA;YACEU,SAAS,OAAOC;gBACd,MAAMC,OAAO,MAAM1C,mBAAmByC,KAAK9B;gBAC3C,IAAI+B,gBAAgBC,UAAU;oBAC5B,OAAOD;gBACT;gBAEA,MAAM,EAAEE,OAAO,EAAE,GAAGH;gBACpB,MAAMO,OAAQ,MAAMjD,gBAAgB0C;gBACpC,MAAMd,gBAAgB1B,mBAAmB+C,MAAMrB;gBAC/C,MAAMW,uBAAuBpD,8BAA8B8D,MAAMV;gBACjE,MAAMF,sBAAsBY,MAAMZ,wBAAwB;gBAC1D,MAAMa,sBACJ,OAAOD,MAAMC,wBAAwB,YAAYD,KAAKC,mBAAmB,GAAG;gBAC9E,MAAMC,8BACJ,OAAOF,MAAME,gCAAgC,YACzCF,KAAKE,2BAA2B,GAChC;gBACN,MAAMC,eACJ,OAAOH,MAAMtB,6BAA6B,WAAWsB,KAAKtB,wBAAwB,GAAG;gBACvF,MAAM0B,mBAAmBD,aAAajB,IAAI;gBAE1C,IAAIxB,SAAS,MAAMzB,8BAA8B2D;gBACjD,MAAMS,iBAAiBlE,wBAAwBuB;gBAC/C,MAAM4C,0BAA0B5C,OAAOgB,wBAAwB,CAACQ,IAAI;gBACpE,MAAMqB,iBAAiBD,wBAAwBnB,MAAM,GAAG;gBACxD,MAAMqB,qBAAqB1D,mCACzBsD,kBACAG;gBAEF,MAAME,aAAaD,qBAAqBF,0BAA0BF;gBAElE,2FAA2F;gBAC3F,yFAAyF;gBACzF,IAAIM,wBAAqDhD,OAAOa,gBAAgB;gBAChF,IAAI,CAACiC,oBAAoB;oBACvB,IAAIC,WAAWtB,MAAM,KAAK,GAAG;wBAC3BuB,wBAAwB;oBAC1B,OAAO;wBACL,MAAMC,aAAa,MAAMrE,wBAAwBmE;wBACjD,IAAI,CAACE,WAAWC,EAAE,EAAE;4BAClB,OAAOjB,SAASE,IAAI,CAClB;gCAAEgB,OAAO,CAAC,qBAAqB,EAAEF,WAAWE,KAAK,IAAI,iBAAiB;4BAAC,GACvE;gCAAEC,QAAQ;4BAAI;wBAElB;wBACAJ,wBAAwBC,WAAWI,MAAM,IAAI;oBAC/C;gBACF;gBAEA,IAAI,CAACrD,OAAOY,EAAE,EAAE;oBACd,MAAMsB,QAAQoB,MAAM,CAAC;wBACnBC,YAAYjF;wBACZkF,MAAM;4BACJ3C,kBAAkBmC;4BAClBhC,0BAA0B+B;4BAC1B9B;4BACAS;4BACAE,sBAAsBjD,kBAAkBiD;wBAC1C;wBACA6B,gBAAgB;oBAClB;gBACF,OAAO;oBACL,MAAMvB,QAAQwB,MAAM,CAAC;wBACnB9C,IAAIZ,OAAOY,EAAE;wBACb2C,YAAYjF;wBACZkF,MAAM;4BACJ3C,kBAAkBmC;4BAClBhC,0BAA0B+B;4BAC1B9B;4BACAS;4BACAE,sBAAsBjD,kBAAkBiD;wBAC1C;wBACA6B,gBAAgB;oBAClB;gBACF;gBAEAzD,SAAS,MAAMzB,8BAA8B2D;gBAC7C,IAAI,CAAClC,OAAOY,EAAE,EAAE;oBACd,OAAOqB,SAASE,IAAI,CAAC;wBAAEgB,OAAO;oBAAoC,GAAG;wBAAEC,QAAQ;oBAAI;gBACrF;gBAEA,MAAMlD,aAAahB;gBACnB,MAAMiB,mBACJD,YAAYmB,YAAY,OAAOpC,qBAAqBiB,WAAWmB,QAAQ,IAAI;gBAE7E,MAAMsC,eAAe3D,OAAOgB,wBAAwB,CAACQ,IAAI;gBACzD,wFAAwF;gBACxF,2DAA2D;gBAC3D,MAAMoC,yBACJhB,wBAAwBnB,MAAM,GAAG,IAC7BmB,0BACA,AAACiB,CAAAA,QAAQC,GAAG,CAACC,qBAAqB,IAAI,EAAC,EAAGvC,IAAI;gBAEpD,MAAMwC,+BACJL,aAAalC,MAAM,GAAG,KACtBmC,uBAAuBnC,MAAM,GAAG,KAChCkC,iBAAiBC;gBAEnB,IAAI,CAACrB,uBAAuB,CAACyB,8BAA8B;oBACzD,OAAO/B,SAASE,IAAI,CAClBpC,kBAAkBC,QAAQC,SAASC,YAAYC,kBAAkB;wBAC/DG,UAAU;wBACVC,QAAQ;wBACRC,WAAW;wBACXC,SAAS;wBACTC,OAAO;wBACPC,aAAa;oBACf;gBAEJ;gBAEA,MAAMsD,cAAcvF,uBAAuBsB;gBAC3C,MAAMkE,eAAezF,wBAAwBuB;gBAC7C,MAAM,EAAEmE,KAAK,EAAE,GAAG,MAAMhG,KAAK;oBAC3BiG,OAAO;oBACPC,QAAQ;oBACRC,OAAOV;gBACT;gBACA,MAAMlD,QAAQyD,MAAM1C,MAAM;gBAE1B,IAAIf,UAAU,GAAG;oBACf,MAAM6D,UAAU,MAAMlG,2BACpB6D,SACA0B,wBACAK,aACA;wBACEO,kBAAkBhC;wBAClBiC,kBAAkB9B;wBAClBuB;oBACF;oBAEF,OAAOjC,SAASE,IAAI,CAClBpC,kBAAkBC,QAAQC,SAASC,YAAYC,kBAAkB;wBAC/DG,UAAU;wBACVC,QAAQgE,QAAQhE,MAAM;wBACtBC,WAAW;wBACXC,SAAS8D,QAAQ9D,OAAO;wBACxBC,OAAO6D,QAAQ7D,KAAK;wBACpBC,aAAa4D,QAAQ5D,WAAW;oBAClC;gBAEJ;gBAEA,MAAM,EAAE+D,UAAU,EAAEC,MAAM,EAAE,GAAG,MAAM7F,iBACnCoD,SACA,gBACA0C,KAAKC,SAAS,CAAC;oBAAEtE,QAAQ;oBAAGuE,UAAU;oBAAIpE;oBAAOC,aAAa;gBAAE;gBAGlEvC,MACE,AAAC,CAAA;oBACC,IAAI;wBACF,MAAMY,iBAAiBkD,SAASyC,QAAQ;4BACtCI,SAASH,KAAKC,SAAS,CAAC;gCAAEtE,QAAQ;gCAAGuE,UAAU;gCAAIpE;gCAAOC,aAAa;4BAAE;4BACzEyC,QAAQ;wBACV;wBACA,MAAMmB,UAAU,MAAMlG,2BACpB6D,SACA0B,wBACAK,aACA;4BACEO,kBAAkBhC;4BAClBiC,kBAAkB9B;4BAClBuB;4BACAS;wBACF;wBAEF,MAAM9F,mBACJqD,SACAyC,QACAC,KAAKC,SAAS,CAAC;4BACbtE,QAAQgE,QAAQhE,MAAM;4BACtBuE,UAAU;4BACVE,OAAO;4BACPtE,OAAO6D,QAAQ7D,KAAK;4BACpBC,aAAa4D,QAAQ5D,WAAW;wBAClC;oBAEJ,EAAE,OAAOsE,GAAG;wBACV,MAAMlG,eAAemD,SAASyC,QAAQM;wBACtC/C,QAAQgD,MAAM,CAAC/B,KAAK,CAClB;4BAAEgC,KAAKF;4BAAGN;wBAAO,GACjB;oBAEJ;gBACF,CAAA;gBAGF,OAAO1C,SAASE,IAAI,CAClBpC,kBAAkBC,QAAQC,SAASC,YAAYC,kBAAkB;oBAC/DG,UAAU;oBACVC,QAAQ;oBACRC,WAAW;oBACXkE;oBACAjE,SAAS;oBACTkE;oBACAjE;oBACAC,aAAa;gBACf;YAEJ;YACA0B,QAAQ;YACRjB,MAAM;QACR;KACD;AACH"}
|
|
@@ -0,0 +1,34 @@
|
|
|
1
|
+
import { getBackupTask, pollSecretsMatch, stripPollSecretForClient } from '../../core/taskProgress';
|
|
2
|
+
import { getAuthorizedBackupAdmin, jsonError } from '../shared';
|
|
3
|
+
export function createAdminTaskEndpoint(options) {
|
|
4
|
+
return {
|
|
5
|
+
handler: async (req)=>{
|
|
6
|
+
const { payload } = req;
|
|
7
|
+
const id = req.routeParams?.id;
|
|
8
|
+
if (typeof id !== 'string' || !id) {
|
|
9
|
+
return jsonError('Not found', 404);
|
|
10
|
+
}
|
|
11
|
+
const task = await getBackupTask(payload, id);
|
|
12
|
+
if (!task) {
|
|
13
|
+
return jsonError('Not found', 404);
|
|
14
|
+
}
|
|
15
|
+
const url = new URL(req.url);
|
|
16
|
+
const bearer = req.headers.get('authorization')?.match(/^Bearer\s+(\S+)\s*$/i)?.[1];
|
|
17
|
+
const querySecret = url.searchParams.get('pollSecret');
|
|
18
|
+
const provided = bearer ?? querySecret ?? '';
|
|
19
|
+
const storedSecret = typeof task.pollSecret === 'string' ? task.pollSecret : null;
|
|
20
|
+
const pollOk = storedSecret && provided.length > 0 && pollSecretsMatch(provided, storedSecret);
|
|
21
|
+
if (!pollOk) {
|
|
22
|
+
const user = await getAuthorizedBackupAdmin(req, options);
|
|
23
|
+
if (!user) {
|
|
24
|
+
return jsonError('Unauthorized', 401);
|
|
25
|
+
}
|
|
26
|
+
}
|
|
27
|
+
return Response.json(stripPollSecretForClient(task));
|
|
28
|
+
},
|
|
29
|
+
method: 'get',
|
|
30
|
+
path: '/backup-mongodb/admin/task/:id'
|
|
31
|
+
};
|
|
32
|
+
}
|
|
33
|
+
|
|
34
|
+
//# sourceMappingURL=admin-task.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"sources":["../../../src/endpoints/paths/admin-task.ts"],"sourcesContent":["import type { Endpoint } from 'payload'\n\nimport type { BackupPluginOptions } from '../../types'\n\nimport { getBackupTask, pollSecretsMatch, stripPollSecretForClient } from '../../core/taskProgress'\nimport { getAuthorizedBackupAdmin, jsonError } from '../shared'\n\nexport function createAdminTaskEndpoint(options: BackupPluginOptions): Endpoint {\n return {\n handler: async (req) => {\n const { payload } = req\n const id = req.routeParams?.id\n if (typeof id !== 'string' || !id) {\n return jsonError('Not found', 404)\n }\n\n const task = await getBackupTask(payload, id)\n\n if (!task) {\n return jsonError('Not found', 404)\n }\n\n const url = new URL((req as unknown as Request).url)\n const bearer = req.headers.get('authorization')?.match(/^Bearer\\s+(\\S+)\\s*$/i)?.[1]\n const querySecret = url.searchParams.get('pollSecret')\n const provided = bearer ?? querySecret ?? ''\n\n const storedSecret = typeof task.pollSecret === 'string' ? task.pollSecret : null\n const pollOk = storedSecret && provided.length > 0 && pollSecretsMatch(provided, storedSecret)\n\n if (!pollOk) {\n const user = await getAuthorizedBackupAdmin(req, options)\n if (!user) {\n return jsonError('Unauthorized', 401)\n }\n }\n\n return Response.json(stripPollSecretForClient(task))\n },\n method: 'get',\n path: '/backup-mongodb/admin/task/:id',\n }\n}\n"],"names":["getBackupTask","pollSecretsMatch","stripPollSecretForClient","getAuthorizedBackupAdmin","jsonError","createAdminTaskEndpoint","options","handler","req","payload","id","routeParams","task","url","URL","bearer","headers","get","match","querySecret","searchParams","provided","storedSecret","pollSecret","pollOk","length","user","Response","json","method","path"],"mappings":"AAIA,SAASA,aAAa,EAAEC,gBAAgB,EAAEC,wBAAwB,QAAQ,0BAAyB;AACnG,SAASC,wBAAwB,EAAEC,SAAS,QAAQ,YAAW;AAE/D,OAAO,SAASC,wBAAwBC,OAA4B;IAClE,OAAO;QACLC,SAAS,OAAOC;YACd,MAAM,EAAEC,OAAO,EAAE,GAAGD;YACpB,MAAME,KAAKF,IAAIG,WAAW,EAAED;YAC5B,IAAI,OAAOA,OAAO,YAAY,CAACA,IAAI;gBACjC,OAAON,UAAU,aAAa;YAChC;YAEA,MAAMQ,OAAO,MAAMZ,cAAcS,SAASC;YAE1C,IAAI,CAACE,MAAM;gBACT,OAAOR,UAAU,aAAa;YAChC;YAEA,MAAMS,MAAM,IAAIC,IAAI,AAACN,IAA2BK,GAAG;YACnD,MAAME,SAASP,IAAIQ,OAAO,CAACC,GAAG,CAAC,kBAAkBC,MAAM,yBAAyB,CAAC,EAAE;YACnF,MAAMC,cAAcN,IAAIO,YAAY,CAACH,GAAG,CAAC;YACzC,MAAMI,WAAWN,UAAUI,eAAe;YAE1C,MAAMG,eAAe,OAAOV,KAAKW,UAAU,KAAK,WAAWX,KAAKW,UAAU,GAAG;YAC7E,MAAMC,SAASF,gBAAgBD,SAASI,MAAM,GAAG,KAAKxB,iBAAiBoB,UAAUC;YAEjF,IAAI,CAACE,QAAQ;gBACX,MAAME,OAAO,MAAMvB,yBAAyBK,KAAKF;gBACjD,IAAI,CAACoB,MAAM;oBACT,OAAOtB,UAAU,gBAAgB;gBACnC;YACF;YAEA,OAAOuB,SAASC,IAAI,CAAC1B,yBAAyBU;QAChD;QACAiB,QAAQ;QACRC,MAAM;IACR;AACF"}
|
|
@@ -0,0 +1,8 @@
|
|
|
1
|
+
import type { Endpoint } from 'payload';
|
|
2
|
+
import type { BackupPluginOptions } from '../../types';
|
|
3
|
+
/**
|
|
4
|
+
* Admin-only validation endpoint for a (candidate) Vercel Blob read/write token. Probes the store
|
|
5
|
+
* by uploading and deleting a tiny marker file and reports back whether the token is usable and
|
|
6
|
+
* which access level (public/private) the store accepts.
|
|
7
|
+
*/
|
|
8
|
+
export declare function createAdminValidateBlobTokenEndpoint(options: BackupPluginOptions): Endpoint;
|
|
@@ -0,0 +1,26 @@
|
|
|
1
|
+
import { validateBackupBlobToken } from '../../core/blobTokenValidate';
|
|
2
|
+
import { readRequestJson, requireBackupAdmin } from '../shared';
|
|
3
|
+
/**
|
|
4
|
+
* Admin-only validation endpoint for a (candidate) Vercel Blob read/write token. Probes the store
|
|
5
|
+
* by uploading and deleting a tiny marker file and reports back whether the token is usable and
|
|
6
|
+
* which access level (public/private) the store accepts.
|
|
7
|
+
*/ export function createAdminValidateBlobTokenEndpoint(options) {
|
|
8
|
+
return {
|
|
9
|
+
handler: async (req)=>{
|
|
10
|
+
const auth = await requireBackupAdmin(req, options);
|
|
11
|
+
if (auth instanceof Response) {
|
|
12
|
+
return auth;
|
|
13
|
+
}
|
|
14
|
+
const body = await readRequestJson(req);
|
|
15
|
+
const token = typeof body?.token === 'string' ? body.token : '';
|
|
16
|
+
const result = await validateBackupBlobToken(token);
|
|
17
|
+
return Response.json(result, {
|
|
18
|
+
status: result.ok ? 200 : 422
|
|
19
|
+
});
|
|
20
|
+
},
|
|
21
|
+
method: 'post',
|
|
22
|
+
path: '/backup-mongodb/admin/validate-blob-token'
|
|
23
|
+
};
|
|
24
|
+
}
|
|
25
|
+
|
|
26
|
+
//# sourceMappingURL=admin-validate-blob-token.js.map
|