Drafts are on so a post can be written across sittings without saving being publishing. afterChange and afterDelete revalidate every path a post appears on. Blog pages are ISR because CI builds with no database, so without these a published post would not appear until the revalidate window expired — up to an hour of a writer concluding that publishing is broken. Payload runs in the same process as Next, so these are direct revalidatePath calls with no webhook and no shared secret. Media writes to an absolute /app/media matching the compose mount; a mismatch would write into the container filesystem, where the next redeploy silently discards it. Alt text is required rather than optional. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_017YmbBhr8s7GusjDE12hrZM
32 lines
989 B
TypeScript
32 lines
989 B
TypeScript
import type { CollectionConfig } from 'payload';
|
|
|
|
/**
|
|
* Uploads land on a Docker named volume mounted at /app/media. The path is
|
|
* absolute because Payload 3 requires it, and it must match the payload_media
|
|
* mount in docker-compose.portainer.yml exactly — a mismatch writes into the
|
|
* container's own filesystem, where the next redeploy silently discards it.
|
|
*/
|
|
export const Media: CollectionConfig = {
|
|
slug: 'media',
|
|
access: { read: () => true },
|
|
upload: {
|
|
staticDir: '/app/media',
|
|
mimeTypes: ['image/*'],
|
|
imageSizes: [
|
|
{ name: 'thumbnail', width: 400 },
|
|
{ name: 'hero', width: 1200 },
|
|
],
|
|
adminThumbnail: 'thumbnail',
|
|
},
|
|
fields: [
|
|
{
|
|
name: 'alt',
|
|
type: 'text',
|
|
required: true,
|
|
// Required rather than optional: a decorative-by-default image is an
|
|
// accessibility regression on a site parents use under time pressure.
|
|
admin: { description: 'Describe the image for screen readers.' },
|
|
},
|
|
],
|
|
};
|