feat: add Docker support with deployment scripts and database backup functionality

This commit is contained in:
syntaxbullet
2026-07-09 13:29:41 +02:00
parent 5a08350b6a
commit 1216a37049
12 changed files with 418 additions and 167 deletions

View File

@@ -6,13 +6,11 @@ import {
lexicalEditor,
} from '@payloadcms/richtext-lexical'
import path from 'path'
import { fileURLToPath } from 'url'
import { anyone } from '../access/anyone'
import { authenticated } from '../access/authenticated'
const filename = fileURLToPath(import.meta.url)
const dirname = path.dirname(filename)
const mediaStaticDir = path.resolve(process.cwd(), 'public/media')
export const Media: CollectionConfig = {
slug: 'media',
@@ -48,7 +46,7 @@ export const Media: CollectionConfig = {
},
upload: {
// Upload to the public/media directory in Next.js making them publicly accessible even outside of Payload
staticDir: path.resolve(dirname, '../../public/media'),
staticDir: mediaStaticDir,
adminThumbnail: 'thumbnail',
focalPoint: true,
imageSizes: [

View File

@@ -6,10 +6,28 @@ type MediaLike = {
alt?: string | null
}
const payloadFilePrefix = '/api/media/file/'
export function normalizeMediaUrl(url: string | null | undefined) {
if (!url) return ''
if (url.startsWith(payloadFilePrefix)) return `/media/${url.slice(payloadFilePrefix.length)}`
try {
const parsed = new URL(url)
if (parsed.pathname.startsWith(payloadFilePrefix)) {
return `/media/${parsed.pathname.slice(payloadFilePrefix.length)}`
}
} catch {
// Relative URLs are handled above.
}
return url
}
export function mediaUrl(media: unknown, fallback: string) {
if (!media || typeof media !== 'object') return fallback
const doc = media as MediaLike
return doc.url || (doc.filename ? `/media/${doc.filename}` : fallback)
return normalizeMediaUrl(doc.url) || (doc.filename ? `/media/${doc.filename}` : fallback)
}
export function mediaAlt(media: unknown, fallback: string) {

View File

@@ -1,4 +1,5 @@
import type { Footer, Header, Media } from '@/payload-types'
import { normalizeMediaUrl } from '@/spa/cmsMediaField'
import { isLoginIntentHref } from '@/spa/loginIntent'
export type SpaIconName = 'instagram' | 'linkedin' | 'facebook'
@@ -164,7 +165,7 @@ export const defaultFooterData: SpaFooterData = {
const isMedia = (value: unknown): value is Media => Boolean(value && typeof value === 'object' && 'url' in value)
const mediaToSpa = (value: unknown, fallback?: SpaMedia): SpaMedia | undefined => {
if (isMedia(value)) return { url: value.url || fallback?.url, alt: value.alt || fallback?.alt }
if (isMedia(value)) return { url: normalizeMediaUrl(value.url) || fallback?.url, alt: value.alt || fallback?.alt }
return fallback
}