feat: conditional gcs setup

This commit is contained in:
Benno Tielen 2026-03-10 08:59:38 +01:00
parent 4c07349f9d
commit 6bc4e6e8df
4 changed files with 22 additions and 13 deletions

View file

@ -1,4 +1,5 @@
DATABASE_URI=mongodb://127.0.0.1/dreikoenige
PAYLOAD_SECRET=YOUR_SECRET_HERE
GOOGLE_BUCKET=google_storage_bucket
RESEND_API_KEY=some_api_key
GOOGLE_BUCKET=google_storage_bucket #can be omitted
RESEND_API_KEY=some_api_key
PUBLIC_URL=http://localhost:3000

View file

@ -46,6 +46,15 @@ Create and run a container, then set your `DATABASE_URI` to the Postgres URL. En
Alternatively, you can enable the Postgres service in `docker-compose.yml` (currently commented out) and use that as your local DB.
## Google Cloud Storage
Media and document uploads use `@payloadcms/storage-gcs`. The plugin is always registered but conditionally enabled via the `GOOGLE_BUCKET` environment variable:
- **`GOOGLE_BUCKET` set** — files are uploaded to GCS and served from `https://storage.googleapis.com/<bucket>/`.
- **`GOOGLE_BUCKET` unset** — the plugin is disabled and Payload falls back to local file storage. The `alwaysInsertFields` flag keeps the schema identical in both cases, so `payload-types.ts` and `importMap.js` stay stable across environments.
No additional configuration is needed for local development — simply omit `GOOGLE_BUCKET` from your `.env`.
## Migrations (Payload)
Create a new migration:

View file

@ -259,7 +259,6 @@ export interface Church {
*/
export interface Document {
id: string;
prefix?: string | null;
updatedAt: string;
createdAt: string;
url?: string | null;
@ -285,7 +284,6 @@ export interface Media {
publicWithoutName: boolean;
consent: boolean;
};
prefix?: string | null;
updatedAt: string;
createdAt: string;
url?: string | null;
@ -1654,7 +1652,6 @@ export interface MagazineSelect<T extends boolean = true> {
* via the `definition` "documents_select".
*/
export interface DocumentsSelect<T extends boolean = true> {
prefix?: T;
updatedAt?: T;
createdAt?: T;
url?: T;
@ -1681,7 +1678,6 @@ export interface MediaSelect<T extends boolean = true> {
publicWithoutName?: T;
consent?: T;
};
prefix?: T;
updatedAt?: T;
createdAt?: T;
url?: T;

View file

@ -10,7 +10,7 @@ import {
AlignFeature,
UnorderedListFeature,
LinkFeature,
HTMLConverterFeature, FixedToolbarFeature,
FixedToolbarFeature,
} from '@payloadcms/richtext-lexical'
import path from 'path'
import { buildConfig } from 'payload'
@ -141,22 +141,25 @@ export default buildConfig({
sharp,
plugins: [
gcsStorage({
enabled: !!process.env.GOOGLE_BUCKET,
alwaysInsertFields: true,
collections: {
media: {
disablePayloadAccessControl: true,
prefix: 'media/',
generateFileURL: args => `https://storage.googleapis.com/${process.env.GOOGLE_BUCKET}/media/${args.filename}`
generateFileURL: (args) =>
`https://storage.googleapis.com/${process.env.GOOGLE_BUCKET}/media/${args.filename}`,
},
documents: {
disablePayloadAccessControl: true,
prefix: 'documents/',
generateFileURL: args => `https://storage.googleapis.com/${process.env.GOOGLE_BUCKET}/documents/${args.filename}`
generateFileURL: (args) =>
`https://storage.googleapis.com/${process.env.GOOGLE_BUCKET}/documents/${args.filename}`,
},
},
bucket: process.env.GOOGLE_BUCKET || "",
bucket: process.env.GOOGLE_BUCKET || '',
options: {},
acl: undefined
})
acl: undefined,
}),
],
})