@trieb.work/payload-plugin-backup-mongodb 0.1.5 → 0.2.0
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 +260 -90
- package/dist/backup-dashboard.css +18 -1
- package/dist/collections/BackupTasks.js +8 -1
- package/dist/collections/BackupTasks.js.map +1 -1
- package/dist/components/BackupDashboard/BackupListCollapsible.client.js.map +1 -1
- package/dist/components/BackupDashboard/BackupSettingsModal.client.js +203 -115
- package/dist/components/BackupDashboard/BackupSettingsModal.client.js.map +1 -1
- package/dist/components/BackupDashboard/CollectionBackupPreviewBody.client.js.map +1 -1
- package/dist/components/BackupDashboard/RestoreBackupDialog.client.js +1 -1
- package/dist/components/BackupDashboard/RestoreBackupDialog.client.js.map +1 -1
- package/dist/components/BackupDashboard/TaskActionButton.client.d.ts +1 -1
- package/dist/components/BackupDashboard/TaskActionButton.client.js +59 -26
- package/dist/components/BackupDashboard/TaskActionButton.client.js.map +1 -1
- package/dist/components/BackupDashboard/backupDashboardInlineCss.js +1 -1
- package/dist/components/BackupDashboard/backupDashboardInlineCss.js.map +1 -1
- package/dist/components/BackupDashboard/index.js +27 -3
- package/dist/components/BackupDashboard/index.js.map +1 -1
- package/dist/core/archive.js.map +1 -1
- package/dist/core/backup.d.ts +4 -3
- package/dist/core/backup.js +50 -43
- package/dist/core/backup.js.map +1 -1
- package/dist/core/backupArchiveRead.d.ts +15 -0
- package/dist/core/backupArchiveRead.js +30 -0
- package/dist/core/backupArchiveRead.js.map +1 -0
- package/dist/core/backupBlobIO.js.map +1 -1
- package/dist/core/backupSettings.js +5 -0
- package/dist/core/backupSettings.js.map +1 -1
- package/dist/core/blobTokenValidate.js.map +1 -1
- package/dist/core/restore.d.ts +5 -2
- package/dist/core/restore.js +133 -89
- package/dist/core/restore.js.map +1 -1
- package/dist/core/restorePreview.d.ts +12 -3
- package/dist/core/restorePreview.js +17 -13
- package/dist/core/restorePreview.js.map +1 -1
- package/dist/core/restoreResult.d.ts +34 -0
- package/dist/core/restoreResult.js +45 -0
- package/dist/core/restoreResult.js.map +1 -0
- package/dist/core/storage/config.d.ts +31 -0
- package/dist/core/storage/config.js +60 -0
- package/dist/core/storage/config.js.map +1 -0
- package/dist/core/storage/index.d.ts +23 -0
- package/dist/core/storage/index.js +25 -0
- package/dist/core/storage/index.js.map +1 -0
- package/dist/core/storage/s3.d.ts +35 -0
- package/dist/core/storage/s3.js +200 -0
- package/dist/core/storage/s3.js.map +1 -0
- package/dist/core/storage/types.d.ts +60 -0
- package/dist/core/storage/types.js +6 -0
- package/dist/core/storage/types.js.map +1 -0
- package/dist/core/storage/vercelBlob.d.ts +25 -0
- package/dist/core/storage/vercelBlob.js +53 -0
- package/dist/core/storage/vercelBlob.js.map +1 -0
- package/dist/core/taskProgress.d.ts +10 -4
- package/dist/core/taskProgress.js +11 -2
- package/dist/core/taskProgress.js.map +1 -1
- package/dist/core/vercelBackupCron.js.map +1 -1
- package/dist/endpoints/index.js +0 -5
- package/dist/endpoints/index.js.map +1 -1
- package/dist/endpoints/paths/admin-backup-download.js +8 -7
- package/dist/endpoints/paths/admin-backup-download.js.map +1 -1
- package/dist/endpoints/paths/admin-delete.js +11 -5
- package/dist/endpoints/paths/admin-delete.js.map +1 -1
- package/dist/endpoints/paths/admin-manual.js +2 -1
- package/dist/endpoints/paths/admin-manual.js.map +1 -1
- package/dist/endpoints/paths/admin-preview.js +9 -2
- package/dist/endpoints/paths/admin-preview.js.map +1 -1
- package/dist/endpoints/paths/admin-restore.js +7 -4
- package/dist/endpoints/paths/admin-restore.js.map +1 -1
- package/dist/endpoints/paths/admin-settings.js +35 -2
- package/dist/endpoints/paths/admin-settings.js.map +1 -1
- package/dist/endpoints/paths/cron-list.js +2 -1
- package/dist/endpoints/paths/cron-list.js.map +1 -1
- package/dist/endpoints/paths/cron-restore.js +14 -4
- package/dist/endpoints/paths/cron-restore.js.map +1 -1
- package/dist/endpoints/paths/cron-run.js +2 -1
- package/dist/endpoints/paths/cron-run.js.map +1 -1
- package/dist/endpoints/shared.d.ts +0 -1
- package/dist/endpoints/shared.js +0 -6
- package/dist/endpoints/shared.js.map +1 -1
- package/dist/index.d.ts +5 -3
- package/dist/index.js +3 -2
- package/dist/index.js.map +1 -1
- package/dist/plugin.js.map +1 -1
- package/dist/publicApiPaths.d.ts +0 -1
- package/dist/publicApiPaths.js +0 -1
- package/dist/publicApiPaths.js.map +1 -1
- package/dist/types.d.ts +0 -5
- package/dist/types.js.map +1 -1
- package/dist/utils/blobName.js.map +1 -1
- package/dist/utils/dashboardRoleAccess.js.map +1 -1
- package/dist/utils/dialogBackdrop.js.map +1 -1
- package/dist/utils/hostname.js.map +1 -1
- package/dist/utils/maskBlobToken.js.map +1 -1
- package/package.json +25 -4
- package/dist/endpoints/paths/admin-seed.d.ts +0 -3
- package/dist/endpoints/paths/admin-seed.js +0 -47
- package/dist/endpoints/paths/admin-seed.js.map +0 -1
package/README.md
CHANGED
|
@@ -1,59 +1,172 @@
|
|
|
1
|
-
> Experimental. Originated in the
|
|
1
|
+
> Experimental. Originated in the
|
|
2
|
+
> [Payblocks](https://www.shadcnblocks.com/payload-cms) template, now maintained
|
|
3
|
+
> as a standalone package. Running in production at 20+ projects from TRWK
|
|
4
|
+
> agency. Feedback and PRs welcome.
|
|
2
5
|
|
|
3
6
|
# @trieb.work/payload-plugin-backup-mongodb
|
|
4
7
|
|
|
5
8
|
[](https://www.npmjs.com/package/@trieb.work/payload-plugin-backup-mongodb)
|
|
6
9
|
|
|
7
|
-
A **Payload CMS v3** plugin for **MongoDB only** — not Postgres, SQLite, or
|
|
10
|
+
A **Payload CMS v3** plugin for **MongoDB only** — not Postgres, SQLite, or
|
|
11
|
+
other database adapters. It handles Mongo + media backup, restore, and scheduled
|
|
12
|
+
retention with zero meta-database and a built-in admin UI. Backups live in
|
|
13
|
+
**Vercel Blob Storage** (default) or **AWS S3 / S3-compatible stores** (R2,
|
|
14
|
+
MinIO, etc.), so a fresh install can list and restore any prior backup without
|
|
15
|
+
bootstrapping a database first.
|
|
8
16
|
|
|
9
17
|

|
|
10
18
|
|
|
11
|
-
A first-class **Backups** section lives right below the Payload dashboard:
|
|
12
|
-
sorted by creation time, see at a glance which host and
|
|
13
|
-
|
|
19
|
+
A first-class **Backups** section lives right below the Payload dashboard:
|
|
20
|
+
browse every archive sorted by creation time, see at a glance which host and
|
|
21
|
+
database it belongs to, whether media is bundled and how big it is, then
|
|
22
|
+
download, restore or delete any backup with one click. Read more on how it
|
|
23
|
+
originated at
|
|
24
|
+
[TRWK> Case Study](https://trwk.de/case-studies/payload-plugin-mongodb-backup-restore).
|
|
14
25
|
|
|
15
26
|
---
|
|
16
27
|
|
|
17
28
|
## Highlights
|
|
18
29
|
|
|
19
|
-
- **No meta database.** Every backup is self-describing in its blob name
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
- **
|
|
23
|
-
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
- **
|
|
27
|
-
|
|
28
|
-
-
|
|
29
|
-
|
|
30
|
+
- **No meta database.** Every backup is self-describing in its blob name
|
|
31
|
+
(`type---db---host---{collectionCount}-{timestampMs}.{ext}`). A brand new
|
|
32
|
+
project can list and restore any backup straight from blob storage.
|
|
33
|
+
- **Scheduled (cron) and on-demand backups.** Wire
|
|
34
|
+
`/api/backup-mongodb/cron/run` to Vercel Cron or any HTTP scheduler. Retention
|
|
35
|
+
is configurable (N most recent cron backups are kept; older ones are pruned
|
|
36
|
+
automatically).
|
|
37
|
+
- **Full database coverage.** Dumps **every** MongoDB collection via the Payload
|
|
38
|
+
mongoose adapter — including hidden system collections like `users`,
|
|
39
|
+
`payload-preferences`, `payload-migrations`. Individual collections can be
|
|
40
|
+
excluded per backup from the UI or via API.
|
|
41
|
+
- **Optional media bundling.** Cron or manual backups can include Payload
|
|
42
|
+
`media` blobs in a `.tar.gz` archive alongside the MongoDB dump.
|
|
43
|
+
- **Restore with filters.** Restore any backup with an optional collection
|
|
44
|
+
blacklist and `mergeData` upsert mode. Partial restores keep the running admin
|
|
45
|
+
session / tasks collection intact.
|
|
46
|
+
- **Native admin dashboard.** Adds a `BackupDashboard` widget to the Payload
|
|
47
|
+
admin (`afterDashboard`): list / sort / filter / search backups, trigger
|
|
48
|
+
manual backups or restores, configure retention and storage, and live-poll
|
|
49
|
+
long-running tasks.
|
|
50
|
+
- **Payload REST, not Next.js route files.** All endpoints are registered as
|
|
51
|
+
Payload custom endpoints and served by the default `/api/[...slug]` handler —
|
|
52
|
+
you do not add `app/.../route.ts` files for this plugin.
|
|
53
|
+
- **Pluggable blob storage.** Uses `BLOB_READ_WRITE_TOKEN` by default (the same
|
|
54
|
+
store you often use with `@payloadcms/storage-vercel-blob`), or point backups
|
|
55
|
+
at a dedicated Vercel Blob store in the admin. Alternatively set
|
|
56
|
+
`BACKUP_STORAGE=s3` and use AWS S3, Cloudflare R2, MinIO, etc. Both **public**
|
|
57
|
+
and **private** access stores are supported; a validation step detects which
|
|
58
|
+
modes the store accepts.
|
|
59
|
+
- **Resumable long-running tasks.** Manual backups and restores are tracked in a
|
|
60
|
+
hidden `backup-tasks` collection (TTL, 30 min). The UI polls progress with a
|
|
61
|
+
short-lived `pollSecret` so long jobs stay observable even across reloads.
|
|
62
|
+
- **Tested.** Vitest covers archive, backup, restore, task progress, blob I/O,
|
|
63
|
+
endpoint auth, cron parsing, and blob-name helpers.
|
|
30
64
|
|
|
31
65
|
---
|
|
32
66
|
|
|
33
67
|
## Requirements
|
|
34
68
|
|
|
69
|
+
- **Node.js 20.9+** (Node 18 is no longer supported).
|
|
35
70
|
- Payload **v3+** (same major as your other `@payloadcms/*` packages).
|
|
36
71
|
- MongoDB with Payload’s mongoose adapter (`@payloadcms/db-mongodb`).
|
|
37
72
|
- MongoDB server (any version supported by that adapter).
|
|
38
|
-
- A
|
|
73
|
+
- A backup target:
|
|
74
|
+
- **Vercel Blob:** `BLOB_READ_WRITE_TOKEN` (the default target). Vercel
|
|
75
|
+
hosting is **not** required — any Node runtime that can reach Vercel Blob
|
|
76
|
+
works.
|
|
77
|
+
- **S3:** `BACKUP_STORAGE=s3` plus `BACKUP_S3_BUCKET` (and optionally
|
|
78
|
+
credentials). Requires Node 20+ for the optional AWS SDK peer packages. See
|
|
79
|
+
[Choosing a backup target](#choosing-a-backup-target-vercel-blob-or-s3).
|
|
39
80
|
- Next.js **15+** and React **19+** (the usual Payload 3 + App Router stack).
|
|
40
81
|
|
|
41
82
|
### Environment variables
|
|
42
83
|
|
|
43
|
-
| Variable | Required
|
|
44
|
-
| ------------------------------ |
|
|
45
|
-
| `MONGODB_URI` | yes
|
|
46
|
-
| `
|
|
47
|
-
| `
|
|
48
|
-
| `
|
|
49
|
-
| `
|
|
50
|
-
| `
|
|
84
|
+
| Variable | Required | Purpose |
|
|
85
|
+
| ------------------------------ | --------------- | ---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
|
|
86
|
+
| `MONGODB_URI` | yes | MongoDB connection string (used by Payload and to label backups with the DB name). |
|
|
87
|
+
| `BACKUP_STORAGE` | optional | Backup target: `vercel-blob` (default) or `s3`. See [Choosing a backup target](#choosing-a-backup-target-vercel-blob-or-s3). |
|
|
88
|
+
| `BLOB_READ_WRITE_TOKEN` | for Vercel Blob | Default Vercel Blob store for backups and media. Required when `BACKUP_STORAGE=vercel-blob` (the default). Can be overridden in admin settings. |
|
|
89
|
+
| `CRON_SECRET` | for cron | Bearer token for every `/api/backup-mongodb/cron/*` call. Vercel Cron can supply this. |
|
|
90
|
+
| `NEXT_PUBLIC_SERVER_URL` | optional | Used to label backups with the current host. Falls back to `VERCEL_URL` when set. |
|
|
91
|
+
| `BACKUPS_TO_KEEP` | optional | Default retention for cron backups if the settings document has not been edited. Default `10`. |
|
|
92
|
+
| `PAYLOAD_BACKUP_ALLOWED_ROLES` | optional | Comma-separated role slugs that may see the **Backups** dashboard (case-insensitive). Use `*` to allow any authenticated user. When unset, the plugin falls back to requiring a `role` with slug `admin`, or allows everyone when the users collection has no `roles` field. Overridden by the `access` plugin option. |
|
|
93
|
+
|
|
94
|
+
---
|
|
95
|
+
|
|
96
|
+
## Choosing a backup target (Vercel Blob or S3)
|
|
97
|
+
|
|
98
|
+
Backups can be stored in **Vercel Blob** (default) or **AWS S3**. The S3 target
|
|
99
|
+
also works with any S3-compatible store — **Cloudflare R2**, **MinIO**, etc. —
|
|
100
|
+
because they share the same API; only the configuration differs, never the
|
|
101
|
+
plugin code.
|
|
102
|
+
|
|
103
|
+
Select the target with `BACKUP_STORAGE`. When unset it stays `vercel-blob`, so
|
|
104
|
+
existing deployments are unaffected.
|
|
105
|
+
|
|
106
|
+
### Using S3
|
|
107
|
+
|
|
108
|
+
S3 is enabled when `BACKUP_STORAGE=s3`. Install the AWS SDK (declared as
|
|
109
|
+
optional peer dependencies, so Vercel-only installs don't pull it in):
|
|
110
|
+
|
|
111
|
+
```bash
|
|
112
|
+
pnpm add @aws-sdk/client-s3 @aws-sdk/lib-storage @aws-sdk/s3-request-presigner
|
|
113
|
+
```
|
|
114
|
+
|
|
115
|
+
| Variable | Required | Purpose |
|
|
116
|
+
| ----------------------------- | -------- | --------------------------------------------------------------------------------------------------------------------- |
|
|
117
|
+
| `BACKUP_STORAGE` | yes | Set to `s3`. |
|
|
118
|
+
| `BACKUP_S3_BUCKET` | yes | Target bucket name. |
|
|
119
|
+
| `BACKUP_S3_REGION` | optional | Region. Falls back to `AWS_REGION`, then `us-east-1`. |
|
|
120
|
+
| `BACKUP_S3_ENDPOINT` | optional | Custom endpoint for S3-compatible stores (R2, MinIO). Omit for AWS S3. |
|
|
121
|
+
| `BACKUP_S3_FORCE_PATH_STYLE` | optional | Path-style addressing. Defaults to `true` when an endpoint is set (needed for MinIO and some R2 setups). |
|
|
122
|
+
| `BACKUP_S3_PREFIX` | optional | Key namespace prepended to every object (for sharing a bucket). Backups still list/restore by their `backups/…` name. |
|
|
123
|
+
| `BACKUP_S3_ACCESS_KEY_ID` | optional | Access key. Falls back to `AWS_ACCESS_KEY_ID`. Omit to use the AWS default credential chain (IAM roles, etc.). |
|
|
124
|
+
| `BACKUP_S3_SECRET_ACCESS_KEY` | optional | Secret key. Falls back to `AWS_SECRET_ACCESS_KEY`. |
|
|
125
|
+
| `BACKUP_S3_SESSION_TOKEN` | optional | Session token for temporary credentials. Falls back to `AWS_SESSION_TOKEN`. |
|
|
126
|
+
|
|
127
|
+
Credentials are optional: when omitted, the AWS SDK's default provider chain is
|
|
128
|
+
used (environment, shared config, or instance/role credentials — recommended on
|
|
129
|
+
AWS).
|
|
130
|
+
|
|
131
|
+
**AWS S3**
|
|
132
|
+
|
|
133
|
+
```bash
|
|
134
|
+
BACKUP_STORAGE=s3
|
|
135
|
+
BACKUP_S3_BUCKET=my-payload-backups
|
|
136
|
+
BACKUP_S3_REGION=eu-central-1
|
|
137
|
+
# Credentials via IAM role (recommended) or:
|
|
138
|
+
# BACKUP_S3_ACCESS_KEY_ID=...
|
|
139
|
+
# BACKUP_S3_SECRET_ACCESS_KEY=...
|
|
140
|
+
```
|
|
141
|
+
|
|
142
|
+
**Cloudflare R2**
|
|
143
|
+
|
|
144
|
+
```bash
|
|
145
|
+
BACKUP_STORAGE=s3
|
|
146
|
+
BACKUP_S3_BUCKET=my-payload-backups
|
|
147
|
+
BACKUP_S3_REGION=auto
|
|
148
|
+
BACKUP_S3_ENDPOINT=https://<account-id>.r2.cloudflarestorage.com
|
|
149
|
+
BACKUP_S3_FORCE_PATH_STYLE=true
|
|
150
|
+
BACKUP_S3_ACCESS_KEY_ID=<r2-access-key-id>
|
|
151
|
+
BACKUP_S3_SECRET_ACCESS_KEY=<r2-secret-access-key>
|
|
152
|
+
```
|
|
153
|
+
|
|
154
|
+
Backups are stored under the same `backups/…` keys (see
|
|
155
|
+
[Blob naming](#blob-naming)), so listing and restore work identically across
|
|
156
|
+
targets. Downloads stream through the plugin's authenticated endpoint, so the
|
|
157
|
+
bucket does **not** need to be public.
|
|
158
|
+
|
|
159
|
+
> **Note on media:** the "include media" option bundles files from your Payload
|
|
160
|
+
> **Vercel Blob upload store** (`BLOB_READ_WRITE_TOKEN`). The configurable
|
|
161
|
+
> target above governs where backup **archives** live only; media is always read
|
|
162
|
+
> from and restored to the upload store, even when `BACKUP_STORAGE=s3`.
|
|
51
163
|
|
|
52
164
|
---
|
|
53
165
|
|
|
54
166
|
## Installation and getting started
|
|
55
167
|
|
|
56
|
-
Use this flow when **adding the plugin to an existing Payload 3 + Next.js app**
|
|
168
|
+
Use this flow when **adding the plugin to an existing Payload 3 + Next.js app**
|
|
169
|
+
that already has MongoDB and a blob/S3 store configured.
|
|
57
170
|
|
|
58
171
|
### 1. Add the package
|
|
59
172
|
|
|
@@ -63,7 +176,9 @@ pnpm add @trieb.work/payload-plugin-backup-mongodb
|
|
|
63
176
|
# yarn add @trieb.work/payload-plugin-backup-mongodb
|
|
64
177
|
```
|
|
65
178
|
|
|
66
|
-
Peer dependencies: **`@payloadcms/db-mongodb`**, **`payload`**,
|
|
179
|
+
Peer dependencies: **`@payloadcms/db-mongodb`**, **`payload`**,
|
|
180
|
+
**`@payloadcms/ui`**, **`next`**, and **`react`**. (all expected in a Payload
|
|
181
|
+
3 + MongoDB + Next.js app).
|
|
67
182
|
|
|
68
183
|
### 2. Register the plugin
|
|
69
184
|
|
|
@@ -82,7 +197,10 @@ export default buildConfig({
|
|
|
82
197
|
})
|
|
83
198
|
```
|
|
84
199
|
|
|
85
|
-
The plugin will register the backup collections, mount the dashboard, and add
|
|
200
|
+
The plugin will register the backup collections, mount the dashboard, and add
|
|
201
|
+
`/api/backup-mongodb/*` endpoints. Import paths resolve to a **published or
|
|
202
|
+
linked** `dist/` build; after local changes to the package, run `pnpm build` in
|
|
203
|
+
the plugin repo (or reinstall) so `dist/` is up to date.
|
|
86
204
|
|
|
87
205
|
### 3. Regenerate types and the import map
|
|
88
206
|
|
|
@@ -95,7 +213,8 @@ pnpm payload generate:importmap
|
|
|
95
213
|
|
|
96
214
|
### 4. (Optional) Schedule cron backups
|
|
97
215
|
|
|
98
|
-
**Vercel:** add a cron in `vercel.json`. Path must match your API; default is
|
|
216
|
+
**Vercel:** add a cron in `vercel.json`. Path must match your API; default is
|
|
217
|
+
under `/api/…`:
|
|
99
218
|
|
|
100
219
|
```json
|
|
101
220
|
{
|
|
@@ -108,7 +227,9 @@ pnpm payload generate:importmap
|
|
|
108
227
|
}
|
|
109
228
|
```
|
|
110
229
|
|
|
111
|
-
Vercel Cron sends header `Authorization: Bearer $CRON_SECRET`. **Other hosts**
|
|
230
|
+
Vercel Cron sends header `Authorization: Bearer $CRON_SECRET`. **Other hosts**
|
|
231
|
+
(k8s, GitHub Actions, etc.): `GET` the same URL on your schedule with that same
|
|
232
|
+
authorization header.
|
|
112
233
|
|
|
113
234
|
### Checklist
|
|
114
235
|
|
|
@@ -116,33 +237,49 @@ Vercel Cron sends header `Authorization: Bearer $CRON_SECRET`. **Other hosts** (
|
|
|
116
237
|
- [ ] `backupMongodbPlugin` in `plugins` array
|
|
117
238
|
- [ ] `generate:types` and `generate:importmap` run
|
|
118
239
|
- [ ] `MONGODB_URI` and `BLOB_READ_WRITE_TOKEN` set in the environment
|
|
119
|
-
- [ ] (Optional) `CRON_SECRET` and cron job (vercel.json) if you want scheduled
|
|
240
|
+
- [ ] (Optional) `CRON_SECRET` and cron job (vercel.json) if you want scheduled
|
|
241
|
+
backups
|
|
120
242
|
|
|
121
|
-
Start your dev server, open `/admin`, sign in as an admin, and you should see
|
|
243
|
+
Start your dev server, open `/admin`, sign in as an admin, and you should see
|
|
244
|
+
**Backups** below the dashboard.
|
|
122
245
|
|
|
123
246
|
---
|
|
124
247
|
|
|
125
248
|
## Admin UI
|
|
126
249
|
|
|
127
|
-
After the first login as an admin user, a **Backups** section appears below the
|
|
128
|
-
dashboard. Day-to-day tasks — create, download, restore, delete,
|
|
250
|
+
After the first login as an admin user, a **Backups** section appears below the
|
|
251
|
+
default Payload dashboard. Day-to-day tasks — create, download, restore, delete,
|
|
252
|
+
schedule — all happen in the UI below.
|
|
129
253
|
|
|
130
254
|
**Who can see it?** The visibility rules, in precedence order:
|
|
131
255
|
|
|
132
|
-
1. If the plugin is registered with an `access` function, it wins (see
|
|
133
|
-
|
|
134
|
-
|
|
256
|
+
1. If the plugin is registered with an `access` function, it wins (see
|
|
257
|
+
[Plugin options](#plugin-options)).
|
|
258
|
+
2. Otherwise, if `PAYLOAD_BACKUP_ALLOWED_ROLES` is set, it is used as a
|
|
259
|
+
comma-separated allow-list of role slugs (case-insensitive). Use `*` for "any
|
|
260
|
+
authenticated user". Example:
|
|
261
|
+
`PAYLOAD_BACKUP_ALLOWED_ROLES=admin,superadmin`.
|
|
262
|
+
3. Otherwise, the historical default applies: users with a `role` whose slug is
|
|
263
|
+
`admin` see the dashboard; projects that don't use a `roles` field at all get
|
|
264
|
+
the dashboard for any authenticated user.
|
|
135
265
|
|
|
136
266
|
### Scheduled (cron) vs on-demand (manual) backups
|
|
137
267
|
|
|
138
|
-
- **Scheduled / cron** backups follow shared settings: retention, media toggle,
|
|
139
|
-
|
|
268
|
+
- **Scheduled / cron** backups follow shared settings: retention, media toggle,
|
|
269
|
+
and collections to skip. Older cron archives are pruned when the keep-count is
|
|
270
|
+
exceeded. They appear in the list as **CRON BACKUP** (or similar).
|
|
271
|
+
- **Manual / on-demand** backups are configured **per run** — per-collection
|
|
272
|
+
toggles, optional media in a `.tar.gz` archive. Manual backups are not
|
|
273
|
+
auto-pruned; use them as checkpoints before migrations.
|
|
140
274
|
|
|
141
275
|

|
|
142
276
|
|
|
143
277
|
### Selective restore with per-collection preview
|
|
144
278
|
|
|
145
|
-
**Restore** opens a preview of the archive before any write. You can restore
|
|
279
|
+
**Restore** opens a preview of the archive before any write. You can restore
|
|
280
|
+
everything, cherry-pick collections, or skip media from `.tar.gz` when you only
|
|
281
|
+
need database rows. Restoring from a different host or database is supported;
|
|
282
|
+
filters help when cloning production into staging.
|
|
146
283
|
|
|
147
284
|

|
|
148
285
|
|
|
@@ -152,10 +289,18 @@ Retention, storage token, and cron collection skip list live in one modal:
|
|
|
152
289
|
|
|
153
290
|

|
|
154
291
|
|
|
155
|
-
- **Schedule:** when `vercel.json` is present, the plugin can show a
|
|
156
|
-
-
|
|
157
|
-
|
|
158
|
-
- **
|
|
292
|
+
- **Schedule:** when `vercel.json` is present, the plugin can show a
|
|
293
|
+
human-readable cron description (via
|
|
294
|
+
[cronstrue](https://github.com/bradymholt/cronstrue)).
|
|
295
|
+
- **Retention:** how many **cron** archives to keep; manual backups are not
|
|
296
|
+
pruned by this.
|
|
297
|
+
- **Dedicated backup storage (optional):** a separate Vercel Blob read/write
|
|
298
|
+
token (when `BACKUP_STORAGE=vercel-blob`) so backups can live in a different
|
|
299
|
+
store than media. The UI can validate the token and optionally copy existing
|
|
300
|
+
`backups/*` objects to the new store. For S3, configure the target bucket via
|
|
301
|
+
environment variables.
|
|
302
|
+
- **Collection selection for cron:** defaults to all collections, with
|
|
303
|
+
per-collection opt-out.
|
|
159
304
|
|
|
160
305
|
---
|
|
161
306
|
|
|
@@ -172,12 +317,6 @@ type BackupPluginOptions = {
|
|
|
172
317
|
/** Default cron retention when no value is stored in settings. Falls back to `BACKUPS_TO_KEEP` or `10`. */
|
|
173
318
|
backupsToKeep?: number
|
|
174
319
|
|
|
175
|
-
/**
|
|
176
|
-
* If set, registers `POST /api/backup-mongodb/admin/seed` (demo dump + public seed media where applicable).
|
|
177
|
-
* Omit in production unless you need it.
|
|
178
|
-
*/
|
|
179
|
-
seedDemoDumpUrl?: string
|
|
180
|
-
|
|
181
320
|
/**
|
|
182
321
|
* Custom access check for admin routes and the dashboard. Overrides the
|
|
183
322
|
* `PAYLOAD_BACKUP_ALLOWED_ROLES` env var when provided.
|
|
@@ -191,24 +330,14 @@ type BackupPluginOptions = {
|
|
|
191
330
|
}
|
|
192
331
|
```
|
|
193
332
|
|
|
194
|
-
Example with custom access and seed URL (typical for starters):
|
|
195
|
-
|
|
196
|
-
```ts
|
|
197
|
-
backupMongodbPlugin({
|
|
198
|
-
access: (user) =>
|
|
199
|
-
Array.isArray((user as { roles?: { slug?: string }[] })?.roles) &&
|
|
200
|
-
(user as { roles: { slug?: string }[] }).roles.some(
|
|
201
|
-
(r) => r?.slug === 'admin' || r?.slug === 'superadmin',
|
|
202
|
-
),
|
|
203
|
-
seedDemoDumpUrl: 'https://example.com/seed/demo-db.json',
|
|
204
|
-
})
|
|
205
|
-
```
|
|
206
|
-
|
|
207
333
|
---
|
|
208
334
|
|
|
209
335
|
## HTTP API (Payload REST)
|
|
210
336
|
|
|
211
|
-
All routes are served by Payload’s `/api/[...slug]` handler under
|
|
337
|
+
All routes are served by Payload’s `/api/[...slug]` handler under
|
|
338
|
+
**`/api/backup-mongodb/…`**. The `backup-mongodb` prefix avoids clashing with a
|
|
339
|
+
collection slug. The admin UI uses the same URLs; use the exported
|
|
340
|
+
`backupPluginPublicApiPaths` helper in client code.
|
|
212
341
|
|
|
213
342
|
### Cron / external (Bearer `CRON_SECRET`)
|
|
214
343
|
|
|
@@ -221,18 +350,17 @@ Authorization: Bearer <CRON_SECRET>
|
|
|
221
350
|
|
|
222
351
|
### Admin (session cookie, or `pollSecret` for `/task/:id` where applicable)
|
|
223
352
|
|
|
224
|
-
| Method | Path
|
|
225
|
-
| -------------- |
|
|
226
|
-
| `POST` | `/api/backup-mongodb/admin/manual`
|
|
227
|
-
| `POST` | `/api/backup-mongodb/admin/restore`
|
|
228
|
-
| `POST` | `/api/backup-mongodb/admin/backup-preview`
|
|
229
|
-
| `POST` | `/api/backup-mongodb/admin/restore-preview`
|
|
230
|
-
| `POST` | `/api/backup-mongodb/admin/delete`
|
|
231
|
-
| `GET` | `/api/backup-mongodb/admin/backup-download`
|
|
232
|
-
| `GET` | `/api/backup-mongodb/admin/task/:id`
|
|
233
|
-
| `GET` / `POST` | `/api/backup-mongodb/admin/settings`
|
|
234
|
-
| `POST` | `/api/backup-mongodb/admin/validate-blob-token`
|
|
235
|
-
| `POST` | `/api/backup-mongodb/admin/seed` — if `seedDemoDumpUrl` is set |
|
|
353
|
+
| Method | Path |
|
|
354
|
+
| -------------- | ----------------------------------------------- |
|
|
355
|
+
| `POST` | `/api/backup-mongodb/admin/manual` |
|
|
356
|
+
| `POST` | `/api/backup-mongodb/admin/restore` |
|
|
357
|
+
| `POST` | `/api/backup-mongodb/admin/backup-preview` |
|
|
358
|
+
| `POST` | `/api/backup-mongodb/admin/restore-preview` |
|
|
359
|
+
| `POST` | `/api/backup-mongodb/admin/delete` |
|
|
360
|
+
| `GET` | `/api/backup-mongodb/admin/backup-download` |
|
|
361
|
+
| `GET` | `/api/backup-mongodb/admin/task/:id` |
|
|
362
|
+
| `GET` / `POST` | `/api/backup-mongodb/admin/settings` |
|
|
363
|
+
| `POST` | `/api/backup-mongodb/admin/validate-blob-token` |
|
|
236
364
|
|
|
237
365
|
---
|
|
238
366
|
|
|
@@ -241,7 +369,11 @@ Authorization: Bearer <CRON_SECRET>
|
|
|
241
369
|
For scripts, hooks, and tests:
|
|
242
370
|
|
|
243
371
|
```ts
|
|
244
|
-
import {
|
|
372
|
+
import {
|
|
373
|
+
createBackup,
|
|
374
|
+
listBackups,
|
|
375
|
+
restoreBackup,
|
|
376
|
+
} from '@trieb.work/payload-plugin-backup-mongodb'
|
|
245
377
|
|
|
246
378
|
// Example: manual backup from a one-off script
|
|
247
379
|
await createBackup(payload, { cron: false, includeMedia: true })
|
|
@@ -266,16 +398,31 @@ backups/{type}---{dbName}---{hostname}---{collectionCount}-{timestampMs}.{json|t
|
|
|
266
398
|
```
|
|
267
399
|
|
|
268
400
|
- `type`: `cron` or `manual` (and legacy-style labels where applicable).
|
|
269
|
-
- `dbName` / `hostname`: derived from your MongoDB URL and public server URL;
|
|
270
|
-
-
|
|
401
|
+
- `dbName` / `hostname`: derived from your MongoDB URL and public server URL;
|
|
402
|
+
URL-encoded in the name.
|
|
403
|
+
- `collectionCount` + `timestampMs`: sort key for listing; legacy blob names are
|
|
404
|
+
still supported.
|
|
271
405
|
|
|
272
406
|
### Public vs private stores
|
|
273
407
|
|
|
274
|
-
A validation step probes the bucket and records whether the store behaves as
|
|
408
|
+
A validation step probes the bucket and records whether the store behaves as
|
|
409
|
+
public or private. New uploads use the right access mode. Restore and preview
|
|
410
|
+
flows handle both.
|
|
275
411
|
|
|
276
412
|
### Overriding the backup store
|
|
277
413
|
|
|
278
|
-
To keep backups in a **different** Vercel Blob project than media
|
|
414
|
+
To keep backups in a **different** Vercel Blob project than media (when
|
|
415
|
+
`BACKUP_STORAGE=vercel-blob`), open **Backup settings** in the admin, paste a
|
|
416
|
+
dedicated `BLOB_READ_WRITE_TOKEN`, validate, and optionally migrate existing
|
|
417
|
+
`backups/*` objects to the new store before switching. For S3, the target is
|
|
418
|
+
controlled entirely by environment variables.
|
|
419
|
+
|
|
420
|
+
### Removed demo seed API (intentional)
|
|
421
|
+
|
|
422
|
+
Earlier releases exposed an optional demo seed route (`seedDemoDumpUrl`,
|
|
423
|
+
`/backup-mongodb/admin/seed`, and the `restoreSeedMedia` export). That was
|
|
424
|
+
removed on purpose: a backup plugin should not ship a public “seed my database”
|
|
425
|
+
API. Use your own migration/seed tooling instead.
|
|
279
426
|
|
|
280
427
|
---
|
|
281
428
|
|
|
@@ -287,29 +434,51 @@ pnpm test:int
|
|
|
287
434
|
|
|
288
435
|
### E2E (Playwright)
|
|
289
436
|
|
|
290
|
-
End-to-end tests live in `tests/e2e/` and target the `dev/` Next + Payload app.
|
|
437
|
+
End-to-end tests live in `tests/e2e/` and target the `dev/` Next + Payload app.
|
|
438
|
+
**Locally**, start Mongo (or rely on the in-memory replica set when
|
|
439
|
+
`DATABASE_URL` / `MONGODB_URI` are unset), then either run `pnpm dev` and
|
|
440
|
+
`pnpm test:e2e` in another shell (Playwright reuses the server when not in
|
|
441
|
+
`CI`), or run only `pnpm test:e2e` so Playwright starts `pnpm dev` for you.
|
|
291
442
|
|
|
292
|
-
**CI** (`.github/workflows/e2e.yml`, same pattern as the reference `e2e.yml` in
|
|
443
|
+
**CI** (`.github/workflows/e2e.yml`, same pattern as the reference `e2e.yml` in
|
|
444
|
+
executive-search: MongoDB 7 service, `pnpm run build` then `pnpm run build:dev`,
|
|
445
|
+
then Playwright against `next start`). Optional secret
|
|
446
|
+
**`BLOB_READ_WRITE_TOKEN`** for fuller blob behaviour.
|
|
293
447
|
|
|
294
448
|
### Optional: deploy `dev/` to Vercel
|
|
295
449
|
|
|
296
|
-
`.github/workflows/deploy-dev-vercel.yml` runs when `dev/` (or related paths)
|
|
450
|
+
`.github/workflows/deploy-dev-vercel.yml` runs when `dev/` (or related paths)
|
|
451
|
+
change on `main`, or via **workflow_dispatch**, **only if** `VERCEL_TOKEN`,
|
|
452
|
+
`VERCEL_ORG_ID`, and `VERCEL_PROJECT_ID` are set. Configure the Vercel project
|
|
453
|
+
**Root Directory** to `dev` (`dev/vercel.json` wires install/build from the repo
|
|
454
|
+
root).
|
|
297
455
|
|
|
298
|
-
The repo includes a `dev/` Payload + Next app (MongoDB Memory Server when no URI
|
|
456
|
+
The repo includes a `dev/` Payload + Next app (MongoDB Memory Server when no URI
|
|
457
|
+
is set): `pnpm dev` → `http://localhost:3000/admin` (login in
|
|
458
|
+
`dev/helpers/credentials.ts`). In unit tests, external services like
|
|
459
|
+
`@vercel/blob` and `bson` are often mocked with `vi.mock()`.
|
|
299
460
|
|
|
300
461
|
---
|
|
301
462
|
|
|
302
463
|
## Publishing (npm)
|
|
303
464
|
|
|
304
|
-
Versioning uses [Changesets](https://github.com/changesets/changesets): add a
|
|
465
|
+
Versioning uses [Changesets](https://github.com/changesets/changesets): add a
|
|
466
|
+
file with `pnpm changeset`, open a PR to `main`, and merge. The **Release**
|
|
467
|
+
workflow opens a “version packages” PR or runs `pnpm run release` (`build` +
|
|
468
|
+
`changeset publish`) when the set of changesets is ready. PRs need a new
|
|
469
|
+
`.changeset/*.md` unless you add the **`no-changeset`** label (e.g. docs-only).
|
|
305
470
|
|
|
306
471
|
---
|
|
307
472
|
|
|
308
473
|
## Roadmap / ideas
|
|
309
474
|
|
|
310
|
-
- Multipart / very large backup uploads (Vercel Blob supports large objects; may
|
|
311
|
-
|
|
312
|
-
-
|
|
475
|
+
- Multipart / very large backup uploads (Vercel Blob supports large objects; may
|
|
476
|
+
need to switch past single-part limits). _(S3 uploads already use multipart.)_
|
|
477
|
+
- Additional storage adapters (filesystem, etc.). _(AWS S3 and S3-compatible
|
|
478
|
+
stores such as R2/MinIO are supported — see
|
|
479
|
+
[Choosing a backup target](#choosing-a-backup-target-vercel-blob-or-s3).)_
|
|
480
|
+
- Scheduler-agnostic display when not using Vercel (`vercel.json` is currently
|
|
481
|
+
used for the schedule summary where available).
|
|
313
482
|
- Streaming restore for very large databases.
|
|
314
483
|
- Configurable `backups/` prefix or bucket layout.
|
|
315
484
|
- add support for tenant plugin and partial backups per tenant
|
|
@@ -322,4 +491,5 @@ MIT
|
|
|
322
491
|
|
|
323
492
|
---
|
|
324
493
|
|
|
325
|
-
Built and maintained by [TRWK>](https://trwk.de), formerly
|
|
494
|
+
Built and maintained by [TRWK>](https://trwk.de), formerly
|
|
495
|
+
[trieb.work](https://trieb.work).
|
|
@@ -595,14 +595,31 @@
|
|
|
595
595
|
.backup-task-status-slot {
|
|
596
596
|
display: flex;
|
|
597
597
|
flex-direction: row;
|
|
598
|
-
align-items:
|
|
598
|
+
align-items: flex-start;
|
|
599
599
|
gap: 6px;
|
|
600
600
|
}
|
|
601
|
+
.backup-task-status-slot .backup-task-status__details {
|
|
602
|
+
display: flex;
|
|
603
|
+
flex-direction: column;
|
|
604
|
+
gap: 3px;
|
|
605
|
+
min-width: 0;
|
|
606
|
+
}
|
|
601
607
|
.backup-task-status-slot .backup-task-status__message {
|
|
602
608
|
font-size: 12px;
|
|
603
609
|
line-height: 1.4;
|
|
604
610
|
color: var(--theme-elevation-700);
|
|
605
611
|
}
|
|
612
|
+
.backup-task-status-slot .backup-task-status__warnings {
|
|
613
|
+
font-size: 12px;
|
|
614
|
+
line-height: 1.4;
|
|
615
|
+
color: var(--theme-warning-600, #b7791f);
|
|
616
|
+
white-space: pre-wrap;
|
|
617
|
+
}
|
|
618
|
+
.backup-task-status-slot .backup-task-status__error {
|
|
619
|
+
font-size: 12px;
|
|
620
|
+
line-height: 1.4;
|
|
621
|
+
color: var(--theme-error-500, #c0392b);
|
|
622
|
+
}
|
|
606
623
|
|
|
607
624
|
.restore-preview__blob-token-row {
|
|
608
625
|
display: flex;
|
|
@@ -16,7 +16,6 @@ export const BackupTasksCollection = {
|
|
|
16
16
|
options: [
|
|
17
17
|
'backup',
|
|
18
18
|
'restore',
|
|
19
|
-
'seed',
|
|
20
19
|
'delete',
|
|
21
20
|
'blobTransfer'
|
|
22
21
|
],
|
|
@@ -29,6 +28,7 @@ export const BackupTasksCollection = {
|
|
|
29
28
|
'queued',
|
|
30
29
|
'running',
|
|
31
30
|
'completed',
|
|
31
|
+
'completed_with_warnings',
|
|
32
32
|
'failed'
|
|
33
33
|
],
|
|
34
34
|
required: true
|
|
@@ -42,6 +42,13 @@ export const BackupTasksCollection = {
|
|
|
42
42
|
name: 'error',
|
|
43
43
|
type: 'text'
|
|
44
44
|
},
|
|
45
|
+
{
|
|
46
|
+
name: 'warnings',
|
|
47
|
+
type: 'textarea',
|
|
48
|
+
admin: {
|
|
49
|
+
hidden: true
|
|
50
|
+
}
|
|
51
|
+
},
|
|
45
52
|
{
|
|
46
53
|
name: 'pollSecret',
|
|
47
54
|
type: 'text',
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"sources":["../../src/collections/BackupTasks.ts"],"sourcesContent":["import type { CollectionConfig } from 'payload'\n\nexport const BackupTasksCollection: CollectionConfig = {\n slug: 'backup-tasks',\n access: {\n create: () => false,\n delete: () => false,\n read: () => false,\n update: () => false,\n },\n admin: {\n hidden: true,\n },\n fields: [\n {\n name: 'kind',\n type: 'select',\n options: ['backup', 'restore', '
|
|
1
|
+
{"version":3,"sources":["../../src/collections/BackupTasks.ts"],"sourcesContent":["import type { CollectionConfig } from 'payload'\n\nexport const BackupTasksCollection: CollectionConfig = {\n slug: 'backup-tasks',\n access: {\n create: () => false,\n delete: () => false,\n read: () => false,\n update: () => false,\n },\n admin: {\n hidden: true,\n },\n fields: [\n {\n name: 'kind',\n type: 'select',\n options: ['backup', 'restore', 'delete', 'blobTransfer'],\n required: true,\n },\n {\n name: 'status',\n type: 'select',\n options: ['queued', 'running', 'completed', 'completed_with_warnings', 'failed'],\n required: true,\n },\n {\n name: 'message',\n type: 'text',\n required: true,\n },\n {\n name: 'error',\n type: 'text',\n },\n {\n name: 'warnings',\n type: 'textarea',\n admin: {\n hidden: true,\n },\n },\n {\n name: 'pollSecret',\n type: 'text',\n admin: {\n hidden: true,\n },\n },\n ],\n timestamps: true,\n}\n"],"names":["BackupTasksCollection","slug","access","create","delete","read","update","admin","hidden","fields","name","type","options","required","timestamps"],"mappings":"AAEA,OAAO,MAAMA,wBAA0C;IACrDC,MAAM;IACNC,QAAQ;QACNC,QAAQ,IAAM;QACdC,QAAQ,IAAM;QACdC,MAAM,IAAM;QACZC,QAAQ,IAAM;IAChB;IACAC,OAAO;QACLC,QAAQ;IACV;IACAC,QAAQ;QACN;YACEC,MAAM;YACNC,MAAM;YACNC,SAAS;gBAAC;gBAAU;gBAAW;gBAAU;aAAe;YACxDC,UAAU;QACZ;QACA;YACEH,MAAM;YACNC,MAAM;YACNC,SAAS;gBAAC;gBAAU;gBAAW;gBAAa;gBAA2B;aAAS;YAChFC,UAAU;QACZ;QACA;YACEH,MAAM;YACNC,MAAM;YACNE,UAAU;QACZ;QACA;YACEH,MAAM;YACNC,MAAM;QACR;QACA;YACED,MAAM;YACNC,MAAM;YACNJ,OAAO;gBACLC,QAAQ;YACV;QACF;QACA;YACEE,MAAM;YACNC,MAAM;YACNJ,OAAO;gBACLC,QAAQ;YACV;QACF;KACD;IACDM,YAAY;AACd,EAAC"}
|