From 5ffffe49054d4b05b9fed60cead774d441b26010 Mon Sep 17 00:00:00 2001 From: Danijel Martinek Date: Tue, 5 May 2026 08:31:12 +0200 Subject: [PATCH] feat(marketing-pages): add Payload-backed pages + site-settings repos (constructor-injected config) --- .../repositories/payload-pages.repository.ts | 96 +++++++++++++++++++ .../payload-site-settings.repository.ts | 33 +++++++ 2 files changed, 129 insertions(+) create mode 100644 packages/marketing-pages/src/infrastructure/repositories/payload-pages.repository.ts create mode 100644 packages/marketing-pages/src/infrastructure/repositories/payload-site-settings.repository.ts diff --git a/packages/marketing-pages/src/infrastructure/repositories/payload-pages.repository.ts b/packages/marketing-pages/src/infrastructure/repositories/payload-pages.repository.ts new file mode 100644 index 0000000..f06743e --- /dev/null +++ b/packages/marketing-pages/src/infrastructure/repositories/payload-pages.repository.ts @@ -0,0 +1,96 @@ +import "reflect-metadata"; +import { injectable } from "inversify"; +import { getPayload } from "payload"; +import type { SanitizedConfig } from "payload"; + +import type { IPagesRepository } from "../../application/repositories/pages-repository.interface"; +import type { Page } from "../../entities/page"; + +type PayloadPageDoc = { + id: string | number; + title?: string | null; + slug?: string | null; + hero?: + | { + heading?: string | null; + subheading?: string | null; + image?: string | number | { id: string | number } | null; + } + | null; + layout?: unknown[] | null; + status?: string | null; + publishedAt?: string | null; + seo?: { title?: string | null; description?: string | null } | null; + createdAt?: string | null; + updatedAt?: string | null; +}; + +function mapDoc(doc: PayloadPageDoc): Page { + const imageId = + doc.hero && typeof doc.hero.image === "object" && doc.hero.image !== null + ? String(doc.hero.image.id) + : doc.hero?.image != null + ? String(doc.hero.image) + : undefined; + + return { + id: String(doc.id), + title: doc.title ?? "", + slug: doc.slug ?? "", + hero: { + heading: doc.hero?.heading ?? "", + subheading: doc.hero?.subheading ?? undefined, + imageId, + }, + layout: doc.layout ?? [], + status: doc.status === "published" ? "published" : "draft", + publishedAt: doc.publishedAt ? new Date(doc.publishedAt) : null, + seo: { + title: doc.seo?.title ?? "", + description: doc.seo?.description ?? undefined, + }, + createdAt: doc.createdAt ? new Date(doc.createdAt) : new Date(0), + updatedAt: doc.updatedAt ? new Date(doc.updatedAt) : new Date(0), + }; +} + +@injectable() +export class PayloadPagesRepository implements IPagesRepository { + private config: SanitizedConfig; + + constructor(config: SanitizedConfig) { + this.config = config; + } + + async getPageBySlug(slug: string): Promise { + const payload = await getPayload({ config: this.config }); + const result = await payload.find({ + collection: "pages", + where: { slug: { equals: slug } }, + limit: 1, + overrideAccess: false, + }); + const doc = result.docs[0] as PayloadPageDoc | undefined; + return doc ? mapDoc(doc) : undefined; + } + + async getPages(options?: { + status?: string; + limit?: number; + offset?: number; + }): Promise { + const payload = await getPayload({ config: this.config }); + const where: Record = {}; + if (options?.status) where.status = { equals: options.status }; + const result = await payload.find({ + collection: "pages", + where: where as never, + limit: options?.limit ?? 50, + page: options?.offset + ? Math.floor(options.offset / (options.limit ?? 50)) + 1 + : 1, + overrideAccess: false, + }); + return result.docs.map((d) => mapDoc(d as PayloadPageDoc)); + } +} diff --git a/packages/marketing-pages/src/infrastructure/repositories/payload-site-settings.repository.ts b/packages/marketing-pages/src/infrastructure/repositories/payload-site-settings.repository.ts new file mode 100644 index 0000000..f102bc9 --- /dev/null +++ b/packages/marketing-pages/src/infrastructure/repositories/payload-site-settings.repository.ts @@ -0,0 +1,33 @@ +import "reflect-metadata"; +import { injectable } from "inversify"; +import { getPayload } from "payload"; +import type { SanitizedConfig } from "payload"; + +import type { ISiteSettingsRepository } from "../../application/repositories/site-settings-repository.interface"; +import type { SiteSettings } from "../../entities/site-settings"; + +type PayloadSiteSettings = { + siteName?: string | null; + siteDescription?: string | null; +}; + +@injectable() +export class PayloadSiteSettingsRepository implements ISiteSettingsRepository { + private config: SanitizedConfig; + + constructor(config: SanitizedConfig) { + this.config = config; + } + + async getSiteSettings(): Promise { + const payload = await getPayload({ config: this.config }); + const doc = (await payload.findGlobal({ + slug: "site-settings", + overrideAccess: false, + })) as PayloadSiteSettings; + return { + siteName: doc.siteName ?? "My App", + siteDescription: doc.siteDescription ?? undefined, + }; + } +}