@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.
Files changed (165) hide show
  1. package/README.md +319 -0
  2. package/dist/backup-dashboard.css +1132 -0
  3. package/dist/client.d.ts +3 -0
  4. package/dist/client.js +3 -0
  5. package/dist/client.js.map +1 -0
  6. package/dist/collections/BackupSettings.d.ts +6 -0
  7. package/dist/collections/BackupSettings.js +91 -0
  8. package/dist/collections/BackupSettings.js.map +1 -0
  9. package/dist/collections/BackupTasks.d.ts +2 -0
  10. package/dist/collections/BackupTasks.js +56 -0
  11. package/dist/collections/BackupTasks.js.map +1 -0
  12. package/dist/components/BackupDashboard/BackupItemActions.client.d.ts +7 -0
  13. package/dist/components/BackupDashboard/BackupItemActions.client.js +90 -0
  14. package/dist/components/BackupDashboard/BackupItemActions.client.js.map +1 -0
  15. package/dist/components/BackupDashboard/BackupListCollapsible.client.d.ts +16 -0
  16. package/dist/components/BackupDashboard/BackupListCollapsible.client.js +597 -0
  17. package/dist/components/BackupDashboard/BackupListCollapsible.client.js.map +1 -0
  18. package/dist/components/BackupDashboard/BackupSettingsModal.client.d.ts +2 -0
  19. package/dist/components/BackupDashboard/BackupSettingsModal.client.js +939 -0
  20. package/dist/components/BackupDashboard/BackupSettingsModal.client.js.map +1 -0
  21. package/dist/components/BackupDashboard/CollectionBackupPreviewBody.client.d.ts +18 -0
  22. package/dist/components/BackupDashboard/CollectionBackupPreviewBody.client.js +267 -0
  23. package/dist/components/BackupDashboard/CollectionBackupPreviewBody.client.js.map +1 -0
  24. package/dist/components/BackupDashboard/ManualBackupDialog.client.d.ts +1 -0
  25. package/dist/components/BackupDashboard/ManualBackupDialog.client.js +276 -0
  26. package/dist/components/BackupDashboard/ManualBackupDialog.client.js.map +1 -0
  27. package/dist/components/BackupDashboard/RestoreBackupDialog.client.d.ts +6 -0
  28. package/dist/components/BackupDashboard/RestoreBackupDialog.client.js +551 -0
  29. package/dist/components/BackupDashboard/RestoreBackupDialog.client.js.map +1 -0
  30. package/dist/components/BackupDashboard/TaskActionButton.client.d.ts +29 -0
  31. package/dist/components/BackupDashboard/TaskActionButton.client.js +240 -0
  32. package/dist/components/BackupDashboard/TaskActionButton.client.js.map +1 -0
  33. package/dist/components/BackupDashboard/backupDashboardInlineCss.d.ts +1 -0
  34. package/dist/components/BackupDashboard/backupDashboardInlineCss.js +3 -0
  35. package/dist/components/BackupDashboard/backupDashboardInlineCss.js.map +1 -0
  36. package/dist/components/BackupDashboard/index.client.d.ts +8 -0
  37. package/dist/components/BackupDashboard/index.client.js +9 -0
  38. package/dist/components/BackupDashboard/index.client.js.map +1 -0
  39. package/dist/components/BackupDashboard/index.d.ts +8 -0
  40. package/dist/components/BackupDashboard/index.js +196 -0
  41. package/dist/components/BackupDashboard/index.js.map +1 -0
  42. package/dist/core/archive.d.ts +8 -0
  43. package/dist/core/archive.js +47 -0
  44. package/dist/core/archive.js.map +1 -0
  45. package/dist/core/backup.d.ts +41 -0
  46. package/dist/core/backup.js +188 -0
  47. package/dist/core/backup.js.map +1 -0
  48. package/dist/core/backupBlobIO.d.ts +54 -0
  49. package/dist/core/backupBlobIO.js +179 -0
  50. package/dist/core/backupBlobIO.js.map +1 -0
  51. package/dist/core/backupBlobTransfer.d.ts +29 -0
  52. package/dist/core/backupBlobTransfer.js +85 -0
  53. package/dist/core/backupBlobTransfer.js.map +1 -0
  54. package/dist/core/backupSettings.d.ts +35 -0
  55. package/dist/core/backupSettings.js +95 -0
  56. package/dist/core/backupSettings.js.map +1 -0
  57. package/dist/core/backupSourcePreview.d.ts +10 -0
  58. package/dist/core/backupSourcePreview.js +33 -0
  59. package/dist/core/backupSourcePreview.js.map +1 -0
  60. package/dist/core/blobTokenValidate.d.ts +14 -0
  61. package/dist/core/blobTokenValidate.js +75 -0
  62. package/dist/core/blobTokenValidate.js.map +1 -0
  63. package/dist/core/db.d.ts +24 -0
  64. package/dist/core/db.js +12 -0
  65. package/dist/core/db.js.map +1 -0
  66. package/dist/core/restore.d.ts +27 -0
  67. package/dist/core/restore.js +199 -0
  68. package/dist/core/restore.js.map +1 -0
  69. package/dist/core/restorePreview.d.ts +64 -0
  70. package/dist/core/restorePreview.js +266 -0
  71. package/dist/core/restorePreview.js.map +1 -0
  72. package/dist/core/taskProgress.d.ts +26 -0
  73. package/dist/core/taskProgress.js +73 -0
  74. package/dist/core/taskProgress.js.map +1 -0
  75. package/dist/core/vercelBackupCron.d.ts +8 -0
  76. package/dist/core/vercelBackupCron.js +38 -0
  77. package/dist/core/vercelBackupCron.js.map +1 -0
  78. package/dist/endpoints/createBackupEndpoints.d.ts +1 -0
  79. package/dist/endpoints/createBackupEndpoints.js +3 -0
  80. package/dist/endpoints/createBackupEndpoints.js.map +1 -0
  81. package/dist/endpoints/index.d.ts +3 -0
  82. package/dist/endpoints/index.js +34 -0
  83. package/dist/endpoints/index.js.map +1 -0
  84. package/dist/endpoints/paths/admin-backup-download.d.ts +8 -0
  85. package/dist/endpoints/paths/admin-backup-download.js +72 -0
  86. package/dist/endpoints/paths/admin-backup-download.js.map +1 -0
  87. package/dist/endpoints/paths/admin-delete.d.ts +3 -0
  88. package/dist/endpoints/paths/admin-delete.js +55 -0
  89. package/dist/endpoints/paths/admin-delete.js.map +1 -0
  90. package/dist/endpoints/paths/admin-manual.d.ts +3 -0
  91. package/dist/endpoints/paths/admin-manual.js +61 -0
  92. package/dist/endpoints/paths/admin-manual.js.map +1 -0
  93. package/dist/endpoints/paths/admin-preview.d.ts +6 -0
  94. package/dist/endpoints/paths/admin-preview.js +102 -0
  95. package/dist/endpoints/paths/admin-preview.js.map +1 -0
  96. package/dist/endpoints/paths/admin-restore.d.ts +3 -0
  97. package/dist/endpoints/paths/admin-restore.js +74 -0
  98. package/dist/endpoints/paths/admin-restore.js.map +1 -0
  99. package/dist/endpoints/paths/admin-seed.d.ts +3 -0
  100. package/dist/endpoints/paths/admin-seed.js +47 -0
  101. package/dist/endpoints/paths/admin-seed.js.map +1 -0
  102. package/dist/endpoints/paths/admin-settings.d.ts +3 -0
  103. package/dist/endpoints/paths/admin-settings.js +230 -0
  104. package/dist/endpoints/paths/admin-settings.js.map +1 -0
  105. package/dist/endpoints/paths/admin-task.d.ts +3 -0
  106. package/dist/endpoints/paths/admin-task.js +34 -0
  107. package/dist/endpoints/paths/admin-task.js.map +1 -0
  108. package/dist/endpoints/paths/admin-validate-blob-token.d.ts +8 -0
  109. package/dist/endpoints/paths/admin-validate-blob-token.js +26 -0
  110. package/dist/endpoints/paths/admin-validate-blob-token.js.map +1 -0
  111. package/dist/endpoints/paths/cron-list.d.ts +2 -0
  112. package/dist/endpoints/paths/cron-list.js +33 -0
  113. package/dist/endpoints/paths/cron-list.js.map +1 -0
  114. package/dist/endpoints/paths/cron-restore.d.ts +2 -0
  115. package/dist/endpoints/paths/cron-restore.js +61 -0
  116. package/dist/endpoints/paths/cron-restore.js.map +1 -0
  117. package/dist/endpoints/paths/cron-run.d.ts +3 -0
  118. package/dist/endpoints/paths/cron-run.js +45 -0
  119. package/dist/endpoints/paths/cron-run.js.map +1 -0
  120. package/dist/endpoints/shared.d.ts +13 -0
  121. package/dist/endpoints/shared.js +58 -0
  122. package/dist/endpoints/shared.js.map +1 -0
  123. package/dist/exports/client.d.ts +1 -0
  124. package/dist/exports/client.js +3 -0
  125. package/dist/exports/client.js.map +1 -0
  126. package/dist/exports/rsc.d.ts +1 -0
  127. package/dist/exports/rsc.js +3 -0
  128. package/dist/exports/rsc.js.map +1 -0
  129. package/dist/index.d.ts +18 -0
  130. package/dist/index.js +14 -0
  131. package/dist/index.js.map +1 -0
  132. package/dist/plugin.d.ts +3 -0
  133. package/dist/plugin.js +82 -0
  134. package/dist/plugin.js.map +1 -0
  135. package/dist/publicApiPaths.d.ts +19 -0
  136. package/dist/publicApiPaths.js +20 -0
  137. package/dist/publicApiPaths.js.map +1 -0
  138. package/dist/types.d.ts +21 -0
  139. package/dist/types.js +3 -0
  140. package/dist/types.js.map +1 -0
  141. package/dist/utils/backupSelection.d.ts +6 -0
  142. package/dist/utils/backupSelection.js +16 -0
  143. package/dist/utils/backupSelection.js.map +1 -0
  144. package/dist/utils/blobName.d.ts +32 -0
  145. package/dist/utils/blobName.js +99 -0
  146. package/dist/utils/blobName.js.map +1 -0
  147. package/dist/utils/dashboardRoleAccess.d.ts +23 -0
  148. package/dist/utils/dashboardRoleAccess.js +64 -0
  149. package/dist/utils/dashboardRoleAccess.js.map +1 -0
  150. package/dist/utils/dialogBackdrop.d.ts +9 -0
  151. package/dist/utils/dialogBackdrop.js +17 -0
  152. package/dist/utils/dialogBackdrop.js.map +1 -0
  153. package/dist/utils/formatBytes.d.ts +2 -0
  154. package/dist/utils/formatBytes.js +20 -0
  155. package/dist/utils/formatBytes.js.map +1 -0
  156. package/dist/utils/hostname.d.ts +2 -0
  157. package/dist/utils/hostname.js +17 -0
  158. package/dist/utils/hostname.js.map +1 -0
  159. package/dist/utils/index.d.ts +5 -0
  160. package/dist/utils/index.js +7 -0
  161. package/dist/utils/index.js.map +1 -0
  162. package/dist/utils/maskBlobToken.d.ts +11 -0
  163. package/dist/utils/maskBlobToken.js +42 -0
  164. package/dist/utils/maskBlobToken.js.map +1 -0
  165. package/package.json +140 -0
package/README.md ADDED
@@ -0,0 +1,319 @@
1
+ > Experimental. Originated in the [Payblocks](https://github.com/trieb-work/payblocks) template, now maintained as a standalone package. Feedback and PRs welcome.
2
+
3
+ # @trieb.work/payload-plugin-backup-mongodb
4
+
5
+ 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
+
7
+ ![Backup dashboard embedded in the Payload admin panel](./docs/screenshots/dashboard.png)
8
+
9
+ A first-class **Backups** section lives right below the Payload dashboard: browse every archive
10
+ 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.
12
+
13
+ ---
14
+
15
+ ## Highlights
16
+
17
+ - **No meta database.** Every backup is self-describing in its blob name (`type---db---host---{collectionCount}-{timestampMs}.{ext}`). A brand new project can list and restore any backup straight from blob storage.
18
+ - **Scheduled (cron) and on-demand backups.** Wire `/api/backup-mongodb/cron/run` to Vercel Cron or any HTTP scheduler. Retention is configurable (N most recent cron backups are kept; older ones are pruned automatically).
19
+ - **Full database coverage.** Dumps **every** MongoDB collection via the Payload mongoose adapter — including hidden system collections like `users`, `payload-preferences`, `payload-migrations`. Individual collections can be excluded per backup from the UI or via API.
20
+ - **Optional media bundling.** Cron or manual backups can include Payload `media` blobs in a `.tar.gz` archive alongside the MongoDB dump.
21
+ - **Restore with filters.** Restore any backup with an optional collection blacklist and `mergeData` upsert mode. Partial restores keep the running admin session / tasks collection intact.
22
+ - **Native admin dashboard.** Adds a `BackupDashboard` widget to the Payload admin (`afterDashboard`): list / sort / filter / search backups, trigger manual backups or restores, configure retention and storage, and live-poll long-running tasks.
23
+ - **Payload REST, not Next.js route files.** All endpoints are registered as Payload custom endpoints and served by the default `/api/[...slug]` handler — you do not add `app/.../route.ts` files for this plugin.
24
+ - **Pluggable blob storage.** Uses `BLOB_READ_WRITE_TOKEN` by default (the same store you often use with `@payloadcms/storage-vercel-blob`), or point backups at a dedicated Vercel Blob store in the admin. Both **public** and **private** access stores are supported; a validation step detects which modes the store accepts.
25
+ - **Resumable long-running tasks.** Manual backups, restores, and seed runs are tracked in a hidden `backup-tasks` collection (TTL, 30 min). The UI polls progress with a short-lived `pollSecret` so long jobs stay observable even across reloads.
26
+ - **Demo/seed support.** Optional `seedDemoDumpUrl` registers a one-click seed endpoint for templates and starters.
27
+ - **Tested.** Vitest covers archive, backup, restore, task progress, blob I/O, endpoint auth, cron parsing, and blob-name helpers.
28
+
29
+ ---
30
+
31
+ ## Requirements
32
+
33
+ - Payload **v3+** (same major as your other `@payloadcms/*` packages).
34
+ - MongoDB with Payload’s mongoose adapter (`@payloadcms/db-mongodb`).
35
+ - MongoDB server (any version supported by that adapter).
36
+ - A Vercel Blob read/write token (`BLOB_READ_WRITE_TOKEN`). Vercel hosting is **not** required — any Node runtime that can reach Vercel Blob works.
37
+ - Next.js **15+** and React **19+** (the usual Payload 3 + App Router stack).
38
+
39
+ ### Environment variables
40
+
41
+ | Variable | Required | Purpose |
42
+ | ------------------------------ | -------- | ---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
43
+ | `MONGODB_URI` | yes | MongoDB connection string (used by Payload and to label backups with the DB name). |
44
+ | `BLOB_READ_WRITE_TOKEN` | yes | Default Vercel Blob store for backups and media. Can be overridden in admin settings. |
45
+ | `CRON_SECRET` | for cron | Bearer token for every `/api/backup-mongodb/cron/*` call. Vercel Cron can supply this. |
46
+ | `NEXT_PUBLIC_SERVER_URL` | optional | Used to label backups with the current host. Falls back to `VERCEL_URL` when set. |
47
+ | `BACKUPS_TO_KEEP` | optional | Default retention for cron backups if the settings document has not been edited. Default `10`. |
48
+ | `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. |
49
+
50
+ ---
51
+
52
+ ## Installation and getting started
53
+
54
+ Use this flow when **adding the plugin to an existing Payload 3 + Next.js app** that already has MongoDB and (typically) Vercel Blob configured.
55
+
56
+ ### 1. Add the package
57
+
58
+ ```bash
59
+ pnpm add @trieb.work/payload-plugin-backup-mongodb
60
+ # npm install @trieb.work/payload-plugin-backup-mongodb
61
+ # yarn add @trieb.work/payload-plugin-backup-mongodb
62
+ ```
63
+
64
+ Peer dependencies: **`@payloadcms/db-mongodb`**, **`payload`**, **`@payloadcms/ui`**, **`next`**, and **`react`**. (all expected in a Payload 3 + MongoDB + Next.js app).
65
+
66
+ ### 2. Register the plugin
67
+
68
+ ```ts
69
+ // payload.config.ts
70
+ import { buildConfig } from 'payload'
71
+ import { backupMongodbPlugin } from '@trieb.work/payload-plugin-backup-mongodb'
72
+
73
+ export default buildConfig({
74
+ // your db adapter, collections, etc.
75
+ plugins: [
76
+ backupMongodbPlugin({
77
+ // all options optional — see "Plugin options"
78
+ }),
79
+ ],
80
+ })
81
+ ```
82
+
83
+ The plugin will register the backup collections, mount the dashboard, and add `/api/backup-mongodb/*` endpoints. Import paths resolve to a **published or linked** `dist/` build; after local changes to the package, run `pnpm build` in the plugin repo (or reinstall) so `dist/` is up to date.
84
+
85
+ ### 3. Regenerate types and the import map
86
+
87
+ So Payload picks up the new admin components and collections:
88
+
89
+ ```bash
90
+ pnpm payload generate:types
91
+ pnpm payload generate:importmap
92
+ ```
93
+
94
+ ### 4. (Optional) Schedule cron backups
95
+
96
+ **Vercel:** add a cron in `vercel.json`. Path must match your API; default is under `/api/…`:
97
+
98
+ ```json
99
+ {
100
+ "crons": [
101
+ {
102
+ "path": "/api/backup-mongodb/cron/run",
103
+ "schedule": "0 3 * * *"
104
+ }
105
+ ]
106
+ }
107
+ ```
108
+
109
+ Vercel Cron sends header `Authorization: Bearer $CRON_SECRET`. **Other hosts** (k8s, GitHub Actions, etc.): `GET` the same URL on your schedule with that same authorization header.
110
+
111
+ ### Checklist
112
+
113
+ - [ ] Package installed
114
+ - [ ] `backupMongodbPlugin` in `plugins` array
115
+ - [ ] `generate:types` and `generate:importmap` run
116
+ - [ ] `MONGODB_URI` and `BLOB_READ_WRITE_TOKEN` set in the environment
117
+ - [ ] (Optional) `CRON_SECRET` and cron job (vercel.json) if you want scheduled backups
118
+
119
+ Start your dev server, open `/admin`, sign in as an admin, and you should see **Backups** below the dashboard.
120
+
121
+ ---
122
+
123
+ ## Admin UI
124
+
125
+ After the first login as an admin user, a **Backups** section appears below the default Payload
126
+ dashboard. Day-to-day tasks — create, download, restore, delete, schedule — all happen in the UI below.
127
+
128
+ **Who can see it?** The visibility rules, in precedence order:
129
+
130
+ 1. If the plugin is registered with an `access` function, it wins (see [Plugin options](#plugin-options)).
131
+ 2. Otherwise, if `PAYLOAD_BACKUP_ALLOWED_ROLES` is set, it is used as a comma-separated allow-list of role slugs (case-insensitive). Use `*` for "any authenticated user". Example: `PAYLOAD_BACKUP_ALLOWED_ROLES=admin,superadmin`.
132
+ 3. Otherwise, the historical default applies: users with a `role` whose slug is `admin` see the dashboard; projects that don't use a `roles` field at all get the dashboard for any authenticated user.
133
+
134
+ ### Scheduled (cron) vs on-demand (manual) backups
135
+
136
+ - **Scheduled / cron** backups follow shared settings: retention, media toggle, and collections to skip. Older cron archives are pruned when the keep-count is exceeded. They appear in the list as **CRON BACKUP** (or similar).
137
+ - **Manual / on-demand** backups are configured **per run** — per-collection toggles, optional media in a `.tar.gz` archive. Manual backups are not auto-pruned; use them as checkpoints before migrations.
138
+
139
+ ![Manual backup dialog with per-collection selection and optional media bundling](./docs/screenshots/manual-backup.png)
140
+
141
+ ### Selective restore with per-collection preview
142
+
143
+ **Restore** opens a preview of the archive before any write. You can restore everything, cherry-pick collections, or skip media from `.tar.gz` when you only need database rows. Restoring from a different host or database is supported; filters help when cloning production into staging.
144
+
145
+ ![Restore dialog previewing document counts per collection](./docs/screenshots/restore.png)
146
+
147
+ ### Scheduled backup settings
148
+
149
+ Retention, storage token, and cron collection skip list live in one modal:
150
+
151
+ ![Scheduled backup settings: cron schedule, retention, blob token, collection skip list](./docs/screenshots/settings.png)
152
+
153
+ - **Schedule:** when `vercel.json` is present, the plugin can show a human-readable cron description (via [cronstrue](https://github.com/bradymholt/cronstrue)).
154
+ - **Retention:** how many **cron** archives to keep; manual backups are not pruned by this.
155
+ - **Dedicated backup storage (optional):** a separate Vercel Blob read/write token so backups can live in a different store than media. The UI can validate the token and optionally copy existing `backups/*` objects to the new store.
156
+ - **Collection selection for cron:** defaults to all collections, with per-collection opt-out.
157
+
158
+ ---
159
+
160
+ ## Plugin options
161
+
162
+ ```ts
163
+ type BackupPluginOptions = {
164
+ /**
165
+ * Set to `false` to disable the plugin (no collections, endpoints, or admin UI).
166
+ * Omit or leave unset to keep the plugin active.
167
+ */
168
+ enabled?: boolean
169
+
170
+ /** Default cron retention when no value is stored in settings. Falls back to `BACKUPS_TO_KEEP` or `10`. */
171
+ backupsToKeep?: number
172
+
173
+ /**
174
+ * If set, registers `POST /api/backup-mongodb/admin/seed` (demo dump + public seed media where applicable).
175
+ * Omit in production unless you need it.
176
+ */
177
+ seedDemoDumpUrl?: string
178
+
179
+ /**
180
+ * Custom access check for admin routes and the dashboard. Overrides the
181
+ * `PAYLOAD_BACKUP_ALLOWED_ROLES` env var when provided.
182
+ *
183
+ * Default (env-based): see the `PAYLOAD_BACKUP_ALLOWED_ROLES` entry in the
184
+ * environment variables table. When neither is configured, the check falls back
185
+ * to a role with slug `admin`, or allows any authenticated user in projects
186
+ * without a `roles` field.
187
+ */
188
+ access?: (user: Record<string, unknown> | null) => boolean
189
+ }
190
+ ```
191
+
192
+ Example with custom access and seed URL (typical for starters):
193
+
194
+ ```ts
195
+ backupMongodbPlugin({
196
+ access: (user) =>
197
+ Array.isArray((user as { roles?: { slug?: string }[] })?.roles) &&
198
+ (user as { roles: { slug?: string }[] }).roles.some(
199
+ (r) => r?.slug === 'admin' || r?.slug === 'superadmin',
200
+ ),
201
+ seedDemoDumpUrl: 'https://example.com/seed/demo-db.json',
202
+ })
203
+ ```
204
+
205
+ ---
206
+
207
+ ## HTTP API (Payload REST)
208
+
209
+ All routes are served by Payload’s `/api/[...slug]` handler under **`/api/backup-mongodb/…`**. The `backup-mongodb` prefix avoids clashing with a collection slug. The admin UI uses the same URLs; use the exported `backupPluginPublicApiPaths` helper in client code.
210
+
211
+ ### Cron / external (Bearer `CRON_SECRET`)
212
+
213
+ ```http
214
+ GET /api/backup-mongodb/cron/run # enqueue a cron backup
215
+ GET /api/backup-mongodb/cron/list # list backups
216
+ POST /api/backup-mongodb/cron/restore # body: { "url": "https://…" }
217
+ Authorization: Bearer <CRON_SECRET>
218
+ ```
219
+
220
+ ### Admin (session cookie, or `pollSecret` for `/task/:id` where applicable)
221
+
222
+ | Method | Path |
223
+ | -------------- | -------------------------------------------------------------- |
224
+ | `POST` | `/api/backup-mongodb/admin/manual` |
225
+ | `POST` | `/api/backup-mongodb/admin/restore` |
226
+ | `POST` | `/api/backup-mongodb/admin/backup-preview` |
227
+ | `POST` | `/api/backup-mongodb/admin/restore-preview` |
228
+ | `POST` | `/api/backup-mongodb/admin/delete` |
229
+ | `GET` | `/api/backup-mongodb/admin/backup-download` |
230
+ | `GET` | `/api/backup-mongodb/admin/task/:id` |
231
+ | `GET` / `POST` | `/api/backup-mongodb/admin/settings` |
232
+ | `POST` | `/api/backup-mongodb/admin/validate-blob-token` |
233
+ | `POST` | `/api/backup-mongodb/admin/seed` — if `seedDemoDumpUrl` is set |
234
+
235
+ ---
236
+
237
+ ## Programmatic API
238
+
239
+ For scripts, hooks, and tests:
240
+
241
+ ```ts
242
+ import { createBackup, listBackups, restoreBackup } from '@trieb.work/payload-plugin-backup-mongodb'
243
+
244
+ // Example: manual backup from a one-off script
245
+ await createBackup(payload, { cron: false, includeMedia: true })
246
+
247
+ // Example: restore from a URL, skipping `users`
248
+ await restoreBackup(payload, downloadUrl, ['users'], false)
249
+
250
+ // Example: list backup archives (token from backup-settings + env, same as create/restore)
251
+ const blobs = await listBackups(payload)
252
+ ```
253
+
254
+ ---
255
+
256
+ ## Blob storage model
257
+
258
+ ### Blob naming
259
+
260
+ ```
261
+
262
+ backups/{type}---{dbName}---{hostname}---{collectionCount}-{timestampMs}.{json|tar.gz}
263
+
264
+ ```
265
+
266
+ - `type`: `cron` or `manual` (and legacy-style labels where applicable).
267
+ - `dbName` / `hostname`: derived from your MongoDB URL and public server URL; URL-encoded in the name.
268
+ - `collectionCount` + `timestampMs`: sort key for listing; legacy blob names are still supported.
269
+
270
+ ### Public vs private stores
271
+
272
+ A validation step probes the bucket and records whether the store behaves as public or private. New uploads use the right access mode. Restore and preview flows handle both.
273
+
274
+ ### Overriding the backup store
275
+
276
+ To keep backups in a **different** Vercel Blob project than media, open **Backup settings** in the admin, paste a dedicated `BLOB_READ_WRITE_TOKEN`, validate, and optionally migrate existing `backups/*` objects to the new store before switching.
277
+
278
+ ---
279
+
280
+ ## Running the tests
281
+
282
+ ```bash
283
+ pnpm test:int
284
+ ```
285
+
286
+ ### E2E (Playwright)
287
+
288
+ End-to-end tests live in `tests/e2e/` and target the `dev/` Next + Payload app. **Locally**, start Mongo (or rely on the in-memory replica set when `DATABASE_URL` / `MONGODB_URI` are unset), then either run `pnpm dev` and `pnpm test:e2e` in another shell (Playwright reuses the server when not in `CI`), or run only `pnpm test:e2e` so Playwright starts `pnpm dev` for you.
289
+
290
+ **CI** (`.github/workflows/e2e.yml`, same pattern as the reference `e2e.yml` in executive-search: MongoDB 7 service, `pnpm run build` then `pnpm run build:dev`, then Playwright against `next start`). Optional secret **`BLOB_READ_WRITE_TOKEN`** for fuller blob behaviour.
291
+
292
+ ### Optional: deploy `dev/` to Vercel
293
+
294
+ `.github/workflows/deploy-dev-vercel.yml` runs when `dev/` (or related paths) change on `main`, or via **workflow_dispatch**, **only if** `VERCEL_TOKEN`, `VERCEL_ORG_ID`, and `VERCEL_PROJECT_ID` are set. Configure the Vercel project **Root Directory** to `dev` (`dev/vercel.json` wires install/build from the repo root).
295
+
296
+ The repo includes a `dev/` Payload + Next app (MongoDB Memory Server when no URI is set): `pnpm dev` → `http://localhost:3000/admin` (login in `dev/helpers/credentials.ts`). In unit tests, external services like `@vercel/blob` and `bson` are often mocked with `vi.mock()`.
297
+
298
+ ---
299
+
300
+ ## Publishing (npm)
301
+
302
+ Versioning uses [Changesets](https://github.com/changesets/changesets), like [Payblocks](https://github.com/trieb-work/payblocks): 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
+
304
+ ---
305
+
306
+ ## Roadmap / ideas
307
+
308
+ - Multipart / very large backup uploads (Vercel Blob supports large objects; may need to switch past single-part limits).
309
+ - Additional storage adapters (S3, R2, filesystem, etc.).
310
+ - Scheduler-agnostic display when not using Vercel (`vercel.json` is currently used for the schedule summary where available).
311
+ - Streaming restore for very large databases.
312
+ - More E2E coverage around a demo project.
313
+ - Configurable `backups/` prefix or bucket layout.
314
+
315
+ ---
316
+
317
+ ## License
318
+
319
+ MIT