Skip to content

feat: native AWS S3 backup target (alongside Vercel Blob)#15

Open
triebdev wants to merge 13 commits into
mainfrom
feat/native-s3-backup-target
Open

feat: native AWS S3 backup target (alongside Vercel Blob)#15
triebdev wants to merge 13 commits into
mainfrom
feat/native-s3-backup-target

Conversation

@triebdev

Copy link
Copy Markdown
Collaborator

Summary

Adds AWS S3 as a selectable backup target next to the existing Vercel Blob.
The S3 implementation also covers any S3-compatible store (Cloudflare R2, MinIO)
with no provider-specific code — only configuration differs. Vercel Blob remains the
default, so existing deployments are unaffected.

What changed

  • Storage adapter layer (src/core/storage/): a provider-agnostic BackupStorageAdapter
    interface, env-based config + target selector, a VercelBlobStorage adapter (wraps the
    existing @vercel/blob calls unchanged), an S3BackupStorage adapter (multipart upload,
    paginated list, presigned downloads, optional key prefix), and a factory/resolver.
  • Core + endpoints route through the adapter; the Vercel-token 503/access gates are now
    target-aware (isBackupStorageConfigured) so the S3 path passes. Restore on S3 works via
    the presigned download URL. The Vercel path is behaviorally unchanged.
  • Admin UI: the dashboard shows a Storage pill and the settings modal shows the active
    target (with a non-secret S3 summary). When S3 is active, the Vercel-only "dedicated token"
    section is hidden.
  • Docs/tests/deps: README "Choosing a backup target" section (AWS + R2 examples),
    dev/.env.example updated, AWS SDK added as optional peer deps (loaded lazily; Vercel-only
    installs unaffected), and tests for the S3 adapter + config resolution.

Configuration

Select the target with BACKUP_STORAGE (vercel-blob default, or s3). For S3:

Variable Required Notes
BACKUP_S3_BUCKET yes Target bucket
BACKUP_S3_REGION optional Falls back to AWS_REGION, then us-east-1
BACKUP_S3_ENDPOINT optional For R2/MinIO; omit for AWS
BACKUP_S3_FORCE_PATH_STYLE optional Defaults true when an endpoint is set
BACKUP_S3_PREFIX optional Key namespace for sharing a bucket
BACKUP_S3_ACCESS_KEY_ID / BACKUP_S3_SECRET_ACCESS_KEY / BACKUP_S3_SESSION_TOKEN optional Omit to use the AWS default credential chain (IAM roles, env, shared config)

S3 credentials are env-only (no UI field), keeping secrets out of the database.

Backward compatibility

No breaking changes. With BACKUP_STORAGE unset, behavior is identical to before (Vercel Blob
via BLOB_READ_WRITE_TOKEN, including the optional dedicated-token override in admin settings).

Testing

  • New unit/integration tests for the S3 adapter (mocking the AWS SDK) and config resolution.
  • Full suite green; verified end-to-end against a local MinIO container (create / list /
    download / restore / delete).

triebdev added 11 commits June 12, 2026 15:54
Declare @aws-sdk/client-s3, @aws-sdk/lib-storage and @aws-sdk/s3-request-presigner as
optional peer dependencies (and dev dependencies for the build/tests). They are loaded
lazily only when the S3 target is used, so Vercel-only installs are unaffected.
Introduce BackupStorageAdapter plus the object/ref/validation types describing the
operations the plugin performs on backup archives (list, put, del, read, stream, validate).
This is the seam both the Vercel Blob and S3 targets implement.
Add BACKUP_STORAGE selector (vercel-blob default, s3 opt-in) and BACKUP_S3_* parsing with
sensible defaults (region, forcePathStyle when an endpoint is set, optional key prefix, AWS
default-credential-chain fallback). Includes getBackupStorageKind, isS3Configured, loadS3Config.
Wrap the existing @vercel/blob helpers behind the new adapter interface without changing
behaviour, so the historical default target keeps working exactly as before.
Implement the S3 target with multipart upload, paginated list, presigned downloads and
key-prefix support. The AWS SDK is imported dynamically so it stays optional. The same
implementation serves any S3-compatible store (Cloudflare R2, MinIO) — only the
endpoint/forcePathStyle config differs.
resolveBackupStorage builds the adapter for the active target; isBackupStorageConfigured lets
endpoints return 503 early (Vercel needs a token, S3 needs a bucket). Re-export the public
storage surface.
…apter

listBackups and createBackup (archive upload + cron pruning) now go through the resolved
adapter instead of calling @vercel/blob directly. The Vercel path is unchanged; the S3 path is
selected via config. Media bundling still reads the Payload upload store as before.
…wnload via adapter

Replace the Vercel-token 503/access gates with isBackupStorageConfigured so the S3 target
passes, and route delete/download through the adapter. Restore works for S3 via the presigned
download URL.
…ntrols for S3

Expose the active target (and a non-secret S3 summary) from the settings endpoint, show a
Storage pill on the dashboard, and add a Storage target section to the settings modal. When S3
is active the Vercel-only dedicated-token section is hidden and the dashboard lists S3 backups.
Target selection stays config-driven (BACKUP_STORAGE).
Unit-test config resolution (selector precedence, defaults, credential fallbacks) and the S3
adapter (multipart put, paginated list mapping, presigned URL, prefix round-trip, delete,
validate) by mocking the AWS SDK the same way existing suites mock @vercel/blob.
Add a 'Choosing a backup target' README section with AWS and R2 examples, document the new env
vars in dev/.env.example, ignore real .env files (keeping .example templates tracked), and add
the changeset.
@triebdev
triebdev requested a review from JannikZed June 12, 2026 10:33
@socket-security

socket-security Bot commented Jun 12, 2026

Copy link
Copy Markdown

Review the following changes in direct dependencies. Learn more about Socket for GitHub.

Diff Package Supply Chain
Security
Vulnerability Quality Maintenance License
Added@​aws-sdk/​lib-storage@​3.1067.01001008598100
Added@​aws-sdk/​client-s3@​3.1067.09910010098100
Added@​aws-sdk/​s3-request-presigner@​3.1067.010010010098100
Added@​playwright/​test@​1.61.010010010099100

View full report

Remove the seedDemoDumpUrl option, admin/seed endpoint, and restoreSeedMedia helper. Update
README to reflect Vercel Blob + S3 target support, clarify storage configuration in settings,
and remove seed-related API documentation. Adjust backup/restore to route media I/O through
the storage adapter instead of direct Vercel Blob calls.
@github-actions

Copy link
Copy Markdown
Contributor

Coverage Report

Coverage after merging feat/native-s3-backup-target into main will be:

Hit/ Total Coverage
🌿 Branches 574 / 809 70.95% 😊
🔢 Functions 161 / 204 78.92% 😊
📝 Lines 857 / 1125 76.18% 😊

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants