@trieb.work/payload-plugin-backup-mongodb 0.1.2 → 0.1.4
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
CHANGED
|
@@ -1,14 +1,16 @@
|
|
|
1
|
-
> Experimental. Originated in the [Payblocks](https://
|
|
1
|
+
> Experimental. Originated in the [Payblocks](https://www.shadcnblocks.com/payload-cms) template, now maintained as a standalone package. Running in production at 20+ projects from TRWK agency. Feedback and PRs welcome.
|
|
2
2
|
|
|
3
3
|
# @trieb.work/payload-plugin-backup-mongodb
|
|
4
4
|
|
|
5
|
+
[](https://www.npmjs.com/package/@trieb.work/payload-plugin-backup-mongodb)
|
|
6
|
+
|
|
5
7
|
A **Payload CMS v3** plugin for **MongoDB only** — not Postgres, SQLite, or other database adapters. It handles Mongo + media backup, restore, and scheduled retention with zero meta-database and a built-in admin UI. Backups live directly in Vercel Blob Storage, so a fresh install can list and restore any prior backup without bootstrapping a database first.
|
|
6
8
|
|
|
7
9
|

|
|
8
10
|
|
|
9
11
|
A first-class **Backups** section lives right below the Payload dashboard: browse every archive
|
|
10
12
|
sorted by creation time, see at a glance which host and database it belongs to, whether media is
|
|
11
|
-
bundled and how big it is, then download, restore or delete any backup with one click.
|
|
13
|
+
bundled and how big it is, then download, restore or delete any backup with one click. Read more on how it originated at [TRWK> Case Study](https://trwk.de/case-studies/payload-plugin-mongodb-backup-restore).
|
|
12
14
|
|
|
13
15
|
---
|
|
14
16
|
|
|
@@ -299,7 +301,7 @@ The repo includes a `dev/` Payload + Next app (MongoDB Memory Server when no URI
|
|
|
299
301
|
|
|
300
302
|
## Publishing (npm)
|
|
301
303
|
|
|
302
|
-
Versioning uses [Changesets](https://github.com/changesets/changesets)
|
|
304
|
+
Versioning uses [Changesets](https://github.com/changesets/changesets): add a file with `pnpm changeset`, open a PR to `main`, and merge. The **Release** workflow opens a “version packages” PR or runs `pnpm run release` (`build` + `changeset publish`) when the set of changesets is ready. PRs need a new `.changeset/*.md` unless you add the **`no-changeset`** label (e.g. docs-only).
|
|
303
305
|
|
|
304
306
|
---
|
|
305
307
|
|
|
@@ -309,11 +311,15 @@ Versioning uses [Changesets](https://github.com/changesets/changesets), like [Pa
|
|
|
309
311
|
- Additional storage adapters (S3, R2, filesystem, etc.).
|
|
310
312
|
- Scheduler-agnostic display when not using Vercel (`vercel.json` is currently used for the schedule summary where available).
|
|
311
313
|
- Streaming restore for very large databases.
|
|
312
|
-
- More E2E coverage around a demo project.
|
|
313
314
|
- Configurable `backups/` prefix or bucket layout.
|
|
315
|
+
- add support for tenant plugin and partial backups per tenant
|
|
314
316
|
|
|
315
317
|
---
|
|
316
318
|
|
|
317
319
|
## License
|
|
318
320
|
|
|
319
321
|
MIT
|
|
322
|
+
|
|
323
|
+
---
|
|
324
|
+
|
|
325
|
+
Built and maintained by [TRWK>](https://trwk.de), formerly [trieb.work](https://trieb.work).
|
|
@@ -1,6 +1,13 @@
|
|
|
1
1
|
import type { I18n } from '@payloadcms/translations';
|
|
2
|
+
import type { Payload } from 'payload';
|
|
2
3
|
interface BackupDashboardProps {
|
|
3
4
|
i18n: I18n;
|
|
5
|
+
/**
|
|
6
|
+
* Payload instance injected by Payload's admin `RenderServerComponent` as a server prop
|
|
7
|
+
* for `afterDashboard` components. Avoids importing `@payload-config` from the plugin,
|
|
8
|
+
* which does not resolve when the plugin runs from `node_modules`.
|
|
9
|
+
*/
|
|
10
|
+
payload?: Payload;
|
|
4
11
|
/** Optional on admin server props; omitted does not mean logged out (see `defaultIsHidden`). */
|
|
5
12
|
user?: null | Record<string, unknown>;
|
|
6
13
|
}
|
|
@@ -1,6 +1,4 @@
|
|
|
1
1
|
import { jsx as _jsx, jsxs as _jsxs, Fragment as _Fragment } from "react/jsx-runtime";
|
|
2
|
-
import configPromise from '@payload-config';
|
|
3
|
-
import { getPayload } from 'payload';
|
|
4
2
|
import { listBackups, resolveBackupListToken } from '../../core/backup';
|
|
5
3
|
import { getBackupSortTimeMs, getCurrentDbName, getCurrentHostname, isUserAllowedByEnvRoles, transformBlobName } from '../../utils/index';
|
|
6
4
|
import { backupDashboardInlineCss } from './backupDashboardInlineCss';
|
|
@@ -22,7 +20,7 @@ function defaultIsHidden(user, access) {
|
|
|
22
20
|
// historical default (admin role, or "visible" when the project has no roles field).
|
|
23
21
|
return !isUserAllowedByEnvRoles(user);
|
|
24
22
|
}
|
|
25
|
-
export const BackupDashboard = async ({ i18n, user })=>{
|
|
23
|
+
export const BackupDashboard = async ({ i18n, payload, user })=>{
|
|
26
24
|
if (defaultIsHidden(user)) {
|
|
27
25
|
return null;
|
|
28
26
|
}
|
|
@@ -70,9 +68,37 @@ export const BackupDashboard = async ({ i18n, user })=>{
|
|
|
70
68
|
]
|
|
71
69
|
});
|
|
72
70
|
}
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
|
|
71
|
+
if (!payload) {
|
|
72
|
+
// Should not happen in a normal Payload admin render; guard for safety.
|
|
73
|
+
return /*#__PURE__*/ _jsxs(_Fragment, {
|
|
74
|
+
children: [
|
|
75
|
+
/*#__PURE__*/ _jsx("style", {
|
|
76
|
+
dangerouslySetInnerHTML: {
|
|
77
|
+
__html: backupDashboardInlineCss
|
|
78
|
+
}
|
|
79
|
+
}),
|
|
80
|
+
/*#__PURE__*/ _jsxs("div", {
|
|
81
|
+
className: "backup-dashboard",
|
|
82
|
+
children: [
|
|
83
|
+
/*#__PURE__*/ _jsxs("h2", {
|
|
84
|
+
children: [
|
|
85
|
+
"Backups ",
|
|
86
|
+
/*#__PURE__*/ _jsx("span", {
|
|
87
|
+
className: "experimental",
|
|
88
|
+
children: "(experimental)"
|
|
89
|
+
})
|
|
90
|
+
]
|
|
91
|
+
}),
|
|
92
|
+
/*#__PURE__*/ _jsx("p", {
|
|
93
|
+
className: "backup-dashboard__setup-hint",
|
|
94
|
+
role: "status",
|
|
95
|
+
children: "Backup dashboard could not initialise: Payload instance was not provided by the admin server props."
|
|
96
|
+
})
|
|
97
|
+
]
|
|
98
|
+
})
|
|
99
|
+
]
|
|
100
|
+
});
|
|
101
|
+
}
|
|
76
102
|
const backupBlobToken = await resolveBackupListToken(payload);
|
|
77
103
|
const hasBlobToken = backupBlobToken.trim().length > 0;
|
|
78
104
|
const blobs = hasBlobToken ? await listBackups(payload, {
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"sources":["../../../src/components/BackupDashboard/index.tsx"],"sourcesContent":["import type { I18n } from '@payloadcms/translations'\n\nimport configPromise from '@payload-config'\nimport { getPayload } from 'payload'\n\nimport type { BackupPluginOptions } from '../../types'\n\nimport { listBackups, resolveBackupListToken } from '../../core/backup'\nimport {\n getBackupSortTimeMs,\n getCurrentDbName,\n getCurrentHostname,\n isUserAllowedByEnvRoles,\n transformBlobName,\n} from '../../utils/index'\nimport { backupDashboardInlineCss } from './backupDashboardInlineCss'\nimport { BackupListCollapsible, BackupSettingsModal, ManualBackupDialog } from './index.client'\n\ninterface BackupDashboardProps {\n i18n: I18n\n /** Optional on admin server props; omitted does not mean logged out (see `defaultIsHidden`). */\n user?: null | Record<string, unknown>\n}\n\nfunction defaultIsHidden(\n user: null | Record<string, unknown> | undefined,\n access?: BackupPluginOptions['access'],\n): boolean {\n if (user === null) {\n return true\n }\n if (user === undefined) {\n if (access) {\n return true\n }\n return false\n }\n if (access) {\n return !access(user)\n }\n // Role allow-list via `PAYLOAD_BACKUP_ALLOWED_ROLES`. When unset, falls back to the\n // historical default (admin role, or \"visible\" when the project has no roles field).\n return !isUserAllowedByEnvRoles(user)\n}\n\nexport const BackupDashboard: React.FC<BackupDashboardProps> = async ({ i18n, user }) => {\n if (defaultIsHidden(user)) {\n return null\n }\n\n const hasMongoConnection =\n Boolean(process.env.MONGODB_URI?.trim()) || Boolean(process.env.DATABASE_URL?.trim())\n if (!hasMongoConnection) {\n return (\n <>\n <style dangerouslySetInnerHTML={{ __html: backupDashboardInlineCss }} />\n <div className=\"backup-dashboard\">\n <h2>\n Backups <span className=\"experimental\">(experimental)</span>\n </h2>\n <p className=\"backup-dashboard__setup-hint\" role=\"status\">\n Set <code className=\"backup-dashboard__setup-hint-code\">MONGODB_URI</code> or{' '}\n <code className=\"backup-dashboard__setup-hint-code\">DATABASE_URL</code> so Payload can\n connect to MongoDB. The in-memory dev database sets both automatically when neither is\n provided.\n </p>\n </div>\n </>\n )\n }\n\n const payload = await getPayload({ config: configPromise })\n const backupBlobToken = await resolveBackupListToken(payload)\n const hasBlobToken = backupBlobToken.trim().length > 0\n\n const blobs = hasBlobToken ? await listBackups(payload, { blobToken: backupBlobToken }) : []\n const sortedBlobs = [...blobs].sort((a, b) => {\n const ta = getBackupSortTimeMs(transformBlobName(a.pathname), new Date(a.uploadedAt))\n const tb = getBackupSortTimeMs(transformBlobName(b.pathname), new Date(b.uploadedAt))\n return tb - ta\n })\n const currentHostname = getCurrentHostname()\n const currentDbName = getCurrentDbName()\n\n const countOtherDb = sortedBlobs.filter((blob) => {\n const { dbName } = transformBlobName(blob.pathname)\n return currentDbName !== dbName\n }).length\n\n const countOtherHostname = sortedBlobs.filter((blob) => {\n const { hostname } = transformBlobName(blob.pathname)\n return currentHostname !== hostname\n }).length\n const lastBackup = sortedBlobs[0]\n\n return (\n <>\n <style\n dangerouslySetInnerHTML={{ __html: backupDashboardInlineCss }}\n data-payload-backup-mongodb=\"1\"\n />\n <div className=\"backup-dashboard\">\n <h2>\n Backups <span className=\"experimental\">(experimental)</span>\n </h2>\n\n {!hasBlobToken && (\n <p className=\"backup-dashboard__setup-hint\" role=\"status\">\n Add a Vercel Blob read/write token: set environment variable{' '}\n <code className=\"backup-dashboard__setup-hint-code\">BLOB_READ_WRITE_TOKEN</code>, or\n open <strong>Backup settings</strong> and paste a token. Until then, the list stays\n empty and cron or manual backups cannot run.\n </p>\n )}\n\n <div className=\"backup-dashboard__toolbar\">\n <div className=\"backup-dashboard__toolbar-meta\">\n <span className=\"backup-dashboard__toolbar-pill\">\n <span className=\"backup-dashboard__toolbar-key\">Total</span>\n {sortedBlobs.length} backup{sortedBlobs.length === 1 ? '' : 's'}\n </span>\n <span className=\"backup-dashboard__toolbar-pill\">\n <span className=\"backup-dashboard__toolbar-key\">Last backup</span>\n {lastBackup\n ? new Date(\n getBackupSortTimeMs(\n transformBlobName(lastBackup.pathname),\n new Date(lastBackup.uploadedAt),\n ),\n ).toLocaleString(i18n?.language || 'en')\n : 'No backups yet'}\n </span>\n </div>\n <div className=\"make-backup-actions\">\n <BackupSettingsModal />\n <ManualBackupDialog />\n </div>\n </div>\n\n <BackupListCollapsible\n blobs={sortedBlobs.map((b) => ({\n downloadUrl: b.downloadUrl,\n pathname: b.pathname,\n size: b.size,\n uploadedAt: new Date(b.uploadedAt).toISOString(),\n url: b.url,\n }))}\n countOtherDb={countOtherDb}\n countOtherHostname={countOtherHostname}\n currentDbName={currentDbName}\n currentHostname={currentHostname}\n i18nLanguage={i18n?.language || 'en'}\n />\n </div>\n </>\n )\n}\n\nexport default BackupDashboard\n"],"names":["configPromise","getPayload","listBackups","resolveBackupListToken","getBackupSortTimeMs","getCurrentDbName","getCurrentHostname","isUserAllowedByEnvRoles","transformBlobName","backupDashboardInlineCss","BackupListCollapsible","BackupSettingsModal","ManualBackupDialog","defaultIsHidden","user","access","undefined","BackupDashboard","i18n","hasMongoConnection","Boolean","process","env","MONGODB_URI","trim","DATABASE_URL","style","dangerouslySetInnerHTML","__html","div","className","h2","span","p","role","code","payload","config","backupBlobToken","hasBlobToken","length","blobs","blobToken","sortedBlobs","sort","a","b","ta","pathname","Date","uploadedAt","tb","currentHostname","currentDbName","countOtherDb","filter","blob","dbName","countOtherHostname","hostname","lastBackup","data-payload-backup-mongodb","strong","toLocaleString","language","map","downloadUrl","size","toISOString","url","i18nLanguage"],"mappings":"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"}
|
|
1
|
+
{"version":3,"sources":["../../../src/components/BackupDashboard/index.tsx"],"sourcesContent":["import type { I18n } from '@payloadcms/translations'\nimport type { Payload } from 'payload'\n\nimport type { BackupPluginOptions } from '../../types'\n\nimport { listBackups, resolveBackupListToken } from '../../core/backup'\nimport {\n getBackupSortTimeMs,\n getCurrentDbName,\n getCurrentHostname,\n isUserAllowedByEnvRoles,\n transformBlobName,\n} from '../../utils/index'\nimport { backupDashboardInlineCss } from './backupDashboardInlineCss'\nimport { BackupListCollapsible, BackupSettingsModal, ManualBackupDialog } from './index.client'\n\ninterface BackupDashboardProps {\n i18n: I18n\n /**\n * Payload instance injected by Payload's admin `RenderServerComponent` as a server prop\n * for `afterDashboard` components. Avoids importing `@payload-config` from the plugin,\n * which does not resolve when the plugin runs from `node_modules`.\n */\n payload?: Payload\n /** Optional on admin server props; omitted does not mean logged out (see `defaultIsHidden`). */\n user?: null | Record<string, unknown>\n}\n\nfunction defaultIsHidden(\n user: null | Record<string, unknown> | undefined,\n access?: BackupPluginOptions['access'],\n): boolean {\n if (user === null) {\n return true\n }\n if (user === undefined) {\n if (access) {\n return true\n }\n return false\n }\n if (access) {\n return !access(user)\n }\n // Role allow-list via `PAYLOAD_BACKUP_ALLOWED_ROLES`. When unset, falls back to the\n // historical default (admin role, or \"visible\" when the project has no roles field).\n return !isUserAllowedByEnvRoles(user)\n}\n\nexport const BackupDashboard: React.FC<BackupDashboardProps> = async ({ i18n, payload, user }) => {\n if (defaultIsHidden(user)) {\n return null\n }\n\n const hasMongoConnection =\n Boolean(process.env.MONGODB_URI?.trim()) || Boolean(process.env.DATABASE_URL?.trim())\n if (!hasMongoConnection) {\n return (\n <>\n <style dangerouslySetInnerHTML={{ __html: backupDashboardInlineCss }} />\n <div className=\"backup-dashboard\">\n <h2>\n Backups <span className=\"experimental\">(experimental)</span>\n </h2>\n <p className=\"backup-dashboard__setup-hint\" role=\"status\">\n Set <code className=\"backup-dashboard__setup-hint-code\">MONGODB_URI</code> or{' '}\n <code className=\"backup-dashboard__setup-hint-code\">DATABASE_URL</code> so Payload can\n connect to MongoDB. The in-memory dev database sets both automatically when neither is\n provided.\n </p>\n </div>\n </>\n )\n }\n\n if (!payload) {\n // Should not happen in a normal Payload admin render; guard for safety.\n return (\n <>\n <style dangerouslySetInnerHTML={{ __html: backupDashboardInlineCss }} />\n <div className=\"backup-dashboard\">\n <h2>\n Backups <span className=\"experimental\">(experimental)</span>\n </h2>\n <p className=\"backup-dashboard__setup-hint\" role=\"status\">\n Backup dashboard could not initialise: Payload instance was not provided by the admin\n server props.\n </p>\n </div>\n </>\n )\n }\n\n const backupBlobToken = await resolveBackupListToken(payload)\n const hasBlobToken = backupBlobToken.trim().length > 0\n\n const blobs = hasBlobToken ? await listBackups(payload, { blobToken: backupBlobToken }) : []\n const sortedBlobs = [...blobs].sort((a, b) => {\n const ta = getBackupSortTimeMs(transformBlobName(a.pathname), new Date(a.uploadedAt))\n const tb = getBackupSortTimeMs(transformBlobName(b.pathname), new Date(b.uploadedAt))\n return tb - ta\n })\n const currentHostname = getCurrentHostname()\n const currentDbName = getCurrentDbName()\n\n const countOtherDb = sortedBlobs.filter((blob) => {\n const { dbName } = transformBlobName(blob.pathname)\n return currentDbName !== dbName\n }).length\n\n const countOtherHostname = sortedBlobs.filter((blob) => {\n const { hostname } = transformBlobName(blob.pathname)\n return currentHostname !== hostname\n }).length\n const lastBackup = sortedBlobs[0]\n\n return (\n <>\n <style\n dangerouslySetInnerHTML={{ __html: backupDashboardInlineCss }}\n data-payload-backup-mongodb=\"1\"\n />\n <div className=\"backup-dashboard\">\n <h2>\n Backups <span className=\"experimental\">(experimental)</span>\n </h2>\n\n {!hasBlobToken && (\n <p className=\"backup-dashboard__setup-hint\" role=\"status\">\n Add a Vercel Blob read/write token: set environment variable{' '}\n <code className=\"backup-dashboard__setup-hint-code\">BLOB_READ_WRITE_TOKEN</code>, or\n open <strong>Backup settings</strong> and paste a token. Until then, the list stays\n empty and cron or manual backups cannot run.\n </p>\n )}\n\n <div className=\"backup-dashboard__toolbar\">\n <div className=\"backup-dashboard__toolbar-meta\">\n <span className=\"backup-dashboard__toolbar-pill\">\n <span className=\"backup-dashboard__toolbar-key\">Total</span>\n {sortedBlobs.length} backup{sortedBlobs.length === 1 ? '' : 's'}\n </span>\n <span className=\"backup-dashboard__toolbar-pill\">\n <span className=\"backup-dashboard__toolbar-key\">Last backup</span>\n {lastBackup\n ? new Date(\n getBackupSortTimeMs(\n transformBlobName(lastBackup.pathname),\n new Date(lastBackup.uploadedAt),\n ),\n ).toLocaleString(i18n?.language || 'en')\n : 'No backups yet'}\n </span>\n </div>\n <div className=\"make-backup-actions\">\n <BackupSettingsModal />\n <ManualBackupDialog />\n </div>\n </div>\n\n <BackupListCollapsible\n blobs={sortedBlobs.map((b) => ({\n downloadUrl: b.downloadUrl,\n pathname: b.pathname,\n size: b.size,\n uploadedAt: new Date(b.uploadedAt).toISOString(),\n url: b.url,\n }))}\n countOtherDb={countOtherDb}\n countOtherHostname={countOtherHostname}\n currentDbName={currentDbName}\n currentHostname={currentHostname}\n i18nLanguage={i18n?.language || 'en'}\n />\n </div>\n </>\n )\n}\n\nexport default BackupDashboard\n"],"names":["listBackups","resolveBackupListToken","getBackupSortTimeMs","getCurrentDbName","getCurrentHostname","isUserAllowedByEnvRoles","transformBlobName","backupDashboardInlineCss","BackupListCollapsible","BackupSettingsModal","ManualBackupDialog","defaultIsHidden","user","access","undefined","BackupDashboard","i18n","payload","hasMongoConnection","Boolean","process","env","MONGODB_URI","trim","DATABASE_URL","style","dangerouslySetInnerHTML","__html","div","className","h2","span","p","role","code","backupBlobToken","hasBlobToken","length","blobs","blobToken","sortedBlobs","sort","a","b","ta","pathname","Date","uploadedAt","tb","currentHostname","currentDbName","countOtherDb","filter","blob","dbName","countOtherHostname","hostname","lastBackup","data-payload-backup-mongodb","strong","toLocaleString","language","map","downloadUrl","size","toISOString","url","i18nLanguage"],"mappings":"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"}
|
package/package.json
CHANGED
|
@@ -1,7 +1,8 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@trieb.work/payload-plugin-backup-mongodb",
|
|
3
|
-
"version": "0.1.
|
|
3
|
+
"version": "0.1.4",
|
|
4
4
|
"description": "Payload CMS plugin for MongoDB backup and recovery via Vercel Blob Storage",
|
|
5
|
+
"homepage": "https://trwk.de/case-studies/payload-plugin-mongodb-backup-restore",
|
|
5
6
|
"repository": {
|
|
6
7
|
"type": "git",
|
|
7
8
|
"url": "https://github.com/trieb-work/payload-plugin-backup-mongodb.git"
|