Compare commits
6 Commits
24a19adf19
...
v2.3.22-rc
| Author | SHA1 | Date | |
|---|---|---|---|
| ce6436ab0a | |||
| de0089f068 | |||
| 263640bce5 | |||
| 1cc8fa4db4 | |||
| 1db7f3af6c | |||
| 909bad573b |
@@ -532,7 +532,7 @@ jobs:
|
|||||||
env:
|
env:
|
||||||
NEXT_PUBLIC_BASE_URL: ${{ needs.prepare.outputs.next_public_url }}
|
NEXT_PUBLIC_BASE_URL: ${{ needs.prepare.outputs.next_public_url }}
|
||||||
GATEKEEPER_PASSWORD: ${{ secrets.GATEKEEPER_PASSWORD || 'klz2026' }}
|
GATEKEEPER_PASSWORD: ${{ secrets.GATEKEEPER_PASSWORD || 'klz2026' }}
|
||||||
PAYLOAD_SECRET: ${{ secrets.PAYLOAD_SECRET || vars.PAYLOAD_SECRET }}
|
PAYLOAD_SECRET: ${{ secrets.PAYLOAD_SECRET || vars.PAYLOAD_SECRET || 'you-need-to-set-a-payload-secret' }}
|
||||||
PUPPETEER_EXECUTABLE_PATH: /usr/bin/chromium
|
PUPPETEER_EXECUTABLE_PATH: /usr/bin/chromium
|
||||||
run: pnpm run check:forms
|
run: pnpm run check:forms
|
||||||
|
|
||||||
|
|||||||
@@ -11,8 +11,38 @@ export async function GET(req: NextRequest, { params }: { params: Promise<{ slug
|
|||||||
try {
|
try {
|
||||||
const { slug } = await params;
|
const { slug } = await params;
|
||||||
|
|
||||||
// Hardcoded bypass for AGBs to use the original uploaded PDF.
|
// Handle AGBs specifically - either fetch from collection or use fallback file
|
||||||
if (slug === 'agbs') {
|
if (slug === 'agbs') {
|
||||||
|
const payload = await getPayload({ config: configPromise });
|
||||||
|
const currentAgbs = await payload.find({
|
||||||
|
collection: 'agbs-collection',
|
||||||
|
where: {
|
||||||
|
isCurrent: { equals: true },
|
||||||
|
},
|
||||||
|
limit: 1,
|
||||||
|
});
|
||||||
|
|
||||||
|
if (currentAgbs.totalDocs > 0) {
|
||||||
|
const agb = currentAgbs.docs[0];
|
||||||
|
const media = agb.file as any;
|
||||||
|
if (media && media.url) {
|
||||||
|
// If it's a remote URL or absolute path, we might need to fetch it
|
||||||
|
// For now assume it's a local file in public/media
|
||||||
|
const filePath = path.join(process.cwd(), 'public', media.url);
|
||||||
|
if (fs.existsSync(filePath)) {
|
||||||
|
const fileBuffer = fs.readFileSync(filePath);
|
||||||
|
return new NextResponse(fileBuffer, {
|
||||||
|
status: 200,
|
||||||
|
headers: {
|
||||||
|
'Content-Type': 'application/pdf',
|
||||||
|
'Content-Disposition': `attachment; filename="${media.filename}"`,
|
||||||
|
},
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Fallback for hardcoded legacy AGB
|
||||||
const filePath = path.join(process.cwd(), 'public', 'AVB-KLZ-4-2026.pdf');
|
const filePath = path.join(process.cwd(), 'public', 'AVB-KLZ-4-2026.pdf');
|
||||||
if (fs.existsSync(filePath)) {
|
if (fs.existsSync(filePath)) {
|
||||||
const fileBuffer = fs.readFileSync(filePath);
|
const fileBuffer = fs.readFileSync(filePath);
|
||||||
|
|||||||
76
components/AgbHistoryBlock.tsx
Normal file
76
components/AgbHistoryBlock.tsx
Normal file
@@ -0,0 +1,76 @@
|
|||||||
|
import React from 'react';
|
||||||
|
import { getPayload } from 'payload';
|
||||||
|
import configPromise from '@payload-config';
|
||||||
|
import { Heading } from '@/components/ui';
|
||||||
|
|
||||||
|
export const AgbHistoryBlock: React.FC<{ title: string }> = async ({ title }) => {
|
||||||
|
const payload = await getPayload({ config: configPromise });
|
||||||
|
|
||||||
|
const agbs = await payload.find({
|
||||||
|
collection: 'agbs-collection',
|
||||||
|
sort: '-versionDate',
|
||||||
|
limit: 100,
|
||||||
|
});
|
||||||
|
|
||||||
|
if (agbs.totalDocs === 0) {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
return (
|
||||||
|
<div className="my-16 p-8 md:p-12 bg-neutral-light rounded-3xl shadow-sm border border-neutral-medium">
|
||||||
|
<Heading level={3} className="mb-8 text-saturated">
|
||||||
|
{title || 'Vorherige Versionen'}
|
||||||
|
</Heading>
|
||||||
|
<div className="grid grid-cols-1 md:grid-cols-2 gap-6">
|
||||||
|
{agbs.docs.map((agb: any) => {
|
||||||
|
const date = new Date(agb.versionDate).toLocaleDateString('de-DE', {
|
||||||
|
year: 'numeric',
|
||||||
|
month: 'long',
|
||||||
|
day: 'numeric',
|
||||||
|
});
|
||||||
|
|
||||||
|
const fileUrl = typeof agb.file === 'object' ? agb.file.url : '';
|
||||||
|
const filename = typeof agb.file === 'object' ? agb.file.filename : 'agb.pdf';
|
||||||
|
|
||||||
|
return (
|
||||||
|
<div
|
||||||
|
key={agb.id}
|
||||||
|
className="flex items-center justify-between p-6 bg-white rounded-2xl shadow-sm border border-neutral-medium hover:border-primary transition-all group"
|
||||||
|
>
|
||||||
|
<div>
|
||||||
|
<h4 className="font-bold text-saturated group-hover:text-primary transition-colors">
|
||||||
|
{agb.title}
|
||||||
|
</h4>
|
||||||
|
<p className="text-sm text-text-secondary mt-1">Gültig ab {date}</p>
|
||||||
|
</div>
|
||||||
|
{fileUrl && (
|
||||||
|
<a
|
||||||
|
href={fileUrl}
|
||||||
|
download={filename}
|
||||||
|
className="p-3 bg-neutral-light text-primary rounded-full hover:bg-primary hover:text-white transition-all shadow-sm"
|
||||||
|
title="PDF herunterladen"
|
||||||
|
>
|
||||||
|
<svg
|
||||||
|
xmlns="http://www.w3.org/2000/svg"
|
||||||
|
width="24"
|
||||||
|
height="24"
|
||||||
|
viewBox="0 0 24 24"
|
||||||
|
fill="none"
|
||||||
|
stroke="currentColor"
|
||||||
|
strokeWidth="2"
|
||||||
|
strokeLinecap="round"
|
||||||
|
strokeLinejoin="round"
|
||||||
|
>
|
||||||
|
<path d="M21 15v4a2 2 0 0 1-2 2H5a2 2 0 0 1-2-2v-4" />
|
||||||
|
<polyline points="7 10 12 15 17 10" />
|
||||||
|
<line x1="12" x2="12" y1="15" y2="3" />
|
||||||
|
</svg>
|
||||||
|
</a>
|
||||||
|
)}
|
||||||
|
</div>
|
||||||
|
);
|
||||||
|
})}
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
);
|
||||||
|
};
|
||||||
@@ -38,6 +38,7 @@ import GallerySection from '@/components/home/GallerySection';
|
|||||||
import VideoSection from '@/components/home/VideoSection';
|
import VideoSection from '@/components/home/VideoSection';
|
||||||
import CTA from '@/components/home/CTA';
|
import CTA from '@/components/home/CTA';
|
||||||
import { PDFDownloadBlock } from '@/components/PDFDownloadBlock';
|
import { PDFDownloadBlock } from '@/components/PDFDownloadBlock';
|
||||||
|
import { AgbHistoryBlock } from '@/components/AgbHistoryBlock';
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Splits a text string on \n and intersperses <br /> elements.
|
* Splits a text string on \n and intersperses <br /> elements.
|
||||||
@@ -436,6 +437,8 @@ const jsxConverters: JSXConverters = {
|
|||||||
'block-pdfDownload': ({ node }: any) => (
|
'block-pdfDownload': ({ node }: any) => (
|
||||||
<PDFDownloadBlock label={node.fields.label} style={node.fields.style} />
|
<PDFDownloadBlock label={node.fields.label} style={node.fields.style} />
|
||||||
),
|
),
|
||||||
|
agbHistory: ({ node }: any) => <AgbHistoryBlock title={node.fields.title} />,
|
||||||
|
'block-agbHistory': ({ node }: any) => <AgbHistoryBlock title={node.fields.title} />,
|
||||||
// ─── New Page Blocks ───────────────────────────────────────────
|
// ─── New Page Blocks ───────────────────────────────────────────
|
||||||
heroSection: ({ node }: any) => {
|
heroSection: ({ node }: any) => {
|
||||||
const f = node.fields;
|
const f = node.fields;
|
||||||
@@ -793,8 +796,8 @@ const jsxConverters: JSXConverters = {
|
|||||||
</Section>
|
</Section>
|
||||||
);
|
);
|
||||||
},
|
},
|
||||||
imageGallery: ({ node }: any) => <Gallery />,
|
imageGallery: ({ node: _node }: any) => <Gallery />,
|
||||||
'block-imageGallery': ({ node }: any) => <Gallery />,
|
'block-imageGallery': ({ node: _node }: any) => <Gallery />,
|
||||||
categoryGrid: ({ node }: any) => {
|
categoryGrid: ({ node }: any) => {
|
||||||
const cats = node.fields.categories || [];
|
const cats = node.fields.categories || [];
|
||||||
return (
|
return (
|
||||||
|
|||||||
@@ -116,6 +116,7 @@
|
|||||||
"pdf:datasheets": "tsx ./scripts/generate-pdf-datasheets.ts",
|
"pdf:datasheets": "tsx ./scripts/generate-pdf-datasheets.ts",
|
||||||
"pdf:datasheets:legacy": "tsx ./scripts/generate-pdf-datasheets-pdf-lib.ts",
|
"pdf:datasheets:legacy": "tsx ./scripts/generate-pdf-datasheets-pdf-lib.ts",
|
||||||
"cms:migrate": "payload migrate",
|
"cms:migrate": "payload migrate",
|
||||||
|
"cms:migrate:create": "payload migrate:create",
|
||||||
"cms:seed": "tsx ./scripts/seed-payload.ts",
|
"cms:seed": "tsx ./scripts/seed-payload.ts",
|
||||||
"assets:push:testing": "bash ./scripts/assets-sync.sh local testing",
|
"assets:push:testing": "bash ./scripts/assets-sync.sh local testing",
|
||||||
"assets:push:staging": "bash ./scripts/assets-sync.sh local staging",
|
"assets:push:staging": "bash ./scripts/assets-sync.sh local staging",
|
||||||
@@ -139,7 +140,7 @@
|
|||||||
"prepare": "husky",
|
"prepare": "husky",
|
||||||
"preinstall": "npx only-allow pnpm"
|
"preinstall": "npx only-allow pnpm"
|
||||||
},
|
},
|
||||||
"version": "2.3.22-rc.1",
|
"version": "2.3.22-rc.4",
|
||||||
"pnpm": {
|
"pnpm": {
|
||||||
"onlyBuiltDependencies": [
|
"onlyBuiltDependencies": [
|
||||||
"@parcel/watcher",
|
"@parcel/watcher",
|
||||||
|
|||||||
@@ -73,6 +73,7 @@ export interface Config {
|
|||||||
'form-submissions': FormSubmission;
|
'form-submissions': FormSubmission;
|
||||||
products: Product;
|
products: Product;
|
||||||
pages: Page;
|
pages: Page;
|
||||||
|
'agbs-collection': AgbsCollection;
|
||||||
'payload-kv': PayloadKv;
|
'payload-kv': PayloadKv;
|
||||||
'payload-locked-documents': PayloadLockedDocument;
|
'payload-locked-documents': PayloadLockedDocument;
|
||||||
'payload-preferences': PayloadPreference;
|
'payload-preferences': PayloadPreference;
|
||||||
@@ -86,8 +87,11 @@ export interface Config {
|
|||||||
'form-submissions': FormSubmissionsSelect<false> | FormSubmissionsSelect<true>;
|
'form-submissions': FormSubmissionsSelect<false> | FormSubmissionsSelect<true>;
|
||||||
products: ProductsSelect<false> | ProductsSelect<true>;
|
products: ProductsSelect<false> | ProductsSelect<true>;
|
||||||
pages: PagesSelect<false> | PagesSelect<true>;
|
pages: PagesSelect<false> | PagesSelect<true>;
|
||||||
|
'agbs-collection': AgbsCollectionSelect<false> | AgbsCollectionSelect<true>;
|
||||||
'payload-kv': PayloadKvSelect<false> | PayloadKvSelect<true>;
|
'payload-kv': PayloadKvSelect<false> | PayloadKvSelect<true>;
|
||||||
'payload-locked-documents': PayloadLockedDocumentsSelect<false> | PayloadLockedDocumentsSelect<true>;
|
'payload-locked-documents':
|
||||||
|
| PayloadLockedDocumentsSelect<false>
|
||||||
|
| PayloadLockedDocumentsSelect<true>;
|
||||||
'payload-preferences': PayloadPreferencesSelect<false> | PayloadPreferencesSelect<true>;
|
'payload-preferences': PayloadPreferencesSelect<false> | PayloadPreferencesSelect<true>;
|
||||||
'payload-migrations': PayloadMigrationsSelect<false> | PayloadMigrationsSelect<true>;
|
'payload-migrations': PayloadMigrationsSelect<false> | PayloadMigrationsSelect<true>;
|
||||||
};
|
};
|
||||||
@@ -358,6 +362,19 @@ export interface Page {
|
|||||||
createdAt: string;
|
createdAt: string;
|
||||||
_status?: ('draft' | 'published') | null;
|
_status?: ('draft' | 'published') | null;
|
||||||
}
|
}
|
||||||
|
/**
|
||||||
|
* This interface was referenced by `Config`'s JSON-Schema
|
||||||
|
* via the `definition` "agbs-collection".
|
||||||
|
*/
|
||||||
|
export interface AgbsCollection {
|
||||||
|
id: number;
|
||||||
|
title: string;
|
||||||
|
versionDate: string;
|
||||||
|
file: number | Media;
|
||||||
|
isCurrent?: boolean | null;
|
||||||
|
updatedAt: string;
|
||||||
|
createdAt: string;
|
||||||
|
}
|
||||||
/**
|
/**
|
||||||
* This interface was referenced by `Config`'s JSON-Schema
|
* This interface was referenced by `Config`'s JSON-Schema
|
||||||
* via the `definition` "payload-kv".
|
* via the `definition` "payload-kv".
|
||||||
@@ -405,6 +422,10 @@ export interface PayloadLockedDocument {
|
|||||||
| ({
|
| ({
|
||||||
relationTo: 'pages';
|
relationTo: 'pages';
|
||||||
value: number | Page;
|
value: number | Page;
|
||||||
|
} | null)
|
||||||
|
| ({
|
||||||
|
relationTo: 'agbs-collection';
|
||||||
|
value: number | AgbsCollection;
|
||||||
} | null);
|
} | null);
|
||||||
globalSlug?: string | null;
|
globalSlug?: string | null;
|
||||||
user: {
|
user: {
|
||||||
@@ -592,6 +613,18 @@ export interface PagesSelect<T extends boolean = true> {
|
|||||||
createdAt?: T;
|
createdAt?: T;
|
||||||
_status?: T;
|
_status?: T;
|
||||||
}
|
}
|
||||||
|
/**
|
||||||
|
* This interface was referenced by `Config`'s JSON-Schema
|
||||||
|
* via the `definition` "agbs-collection_select".
|
||||||
|
*/
|
||||||
|
export interface AgbsCollectionSelect<T extends boolean = true> {
|
||||||
|
title?: T;
|
||||||
|
versionDate?: T;
|
||||||
|
file?: T;
|
||||||
|
isCurrent?: T;
|
||||||
|
updatedAt?: T;
|
||||||
|
createdAt?: T;
|
||||||
|
}
|
||||||
/**
|
/**
|
||||||
* This interface was referenced by `Config`'s JSON-Schema
|
* This interface was referenced by `Config`'s JSON-Schema
|
||||||
* via the `definition` "payload-kv_select".
|
* via the `definition` "payload-kv_select".
|
||||||
@@ -980,7 +1013,6 @@ export interface Auth {
|
|||||||
[k: string]: unknown;
|
[k: string]: unknown;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
declare module 'payload' {
|
declare module 'payload' {
|
||||||
export interface GeneratedTypes extends Config {}
|
export interface GeneratedTypes extends Config {}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -21,6 +21,7 @@ import { Posts } from './src/payload/collections/Posts';
|
|||||||
import { FormSubmissions } from './src/payload/collections/FormSubmissions';
|
import { FormSubmissions } from './src/payload/collections/FormSubmissions';
|
||||||
import { Products } from './src/payload/collections/Products';
|
import { Products } from './src/payload/collections/Products';
|
||||||
import { Pages } from './src/payload/collections/Pages';
|
import { Pages } from './src/payload/collections/Pages';
|
||||||
|
import { Agbs } from './src/payload/collections/Agbs';
|
||||||
import { seedDatabase } from './src/payload/seed';
|
import { seedDatabase } from './src/payload/seed';
|
||||||
|
|
||||||
const filename = fileURLToPath(import.meta.url);
|
const filename = fileURLToPath(import.meta.url);
|
||||||
@@ -56,7 +57,7 @@ export default buildConfig({
|
|||||||
defaultLocale: 'de',
|
defaultLocale: 'de',
|
||||||
fallback: true,
|
fallback: true,
|
||||||
},
|
},
|
||||||
collections: [Users, Media, Posts, FormSubmissions, Products, Pages],
|
collections: [Users, Media, Posts, FormSubmissions, Products, Pages, Agbs],
|
||||||
editor: lexicalEditor({
|
editor: lexicalEditor({
|
||||||
features: ({ defaultFeatures }) => [
|
features: ({ defaultFeatures }) => [
|
||||||
...defaultFeatures,
|
...defaultFeatures,
|
||||||
|
|||||||
@@ -115,8 +115,10 @@ async function main() {
|
|||||||
// Intercept Next.js chunks and data to bypass Varnish 404-caching
|
// Intercept Next.js chunks and data to bypass Varnish 404-caching
|
||||||
// Includes standard /_next/ and subpath /gatekeeper/_next/
|
// Includes standard /_next/ and subpath /gatekeeper/_next/
|
||||||
if (
|
if (
|
||||||
(url.includes('/_next/static/') || url.includes('/_next/data/') || url.includes('/gatekeeper/_next/')) &&
|
(url.includes('/_next/static/') ||
|
||||||
!url.includes('?cb=') &&
|
url.includes('/_next/data/') ||
|
||||||
|
url.includes('/gatekeeper/_next/')) &&
|
||||||
|
!url.includes('?cb=') &&
|
||||||
!url.includes('&cb=')
|
!url.includes('&cb=')
|
||||||
) {
|
) {
|
||||||
const buster = `cb=${Date.now()}`;
|
const buster = `cb=${Date.now()}`;
|
||||||
@@ -154,10 +156,10 @@ async function main() {
|
|||||||
const navigateWithRetry = async (url: string, label: string) => {
|
const navigateWithRetry = async (url: string, label: string) => {
|
||||||
chunkErrorsDetected = false;
|
chunkErrorsDetected = false;
|
||||||
console.log(`\n🧪 Testing ${label} on: ${url}`);
|
console.log(`\n🧪 Testing ${label} on: ${url}`);
|
||||||
|
|
||||||
// First attempt: Wait for network to be relatively idle
|
// First attempt: Wait for network to be relatively idle
|
||||||
await page.goto(url, { waitUntil: 'networkidle2', timeout: 30000 });
|
await page.goto(url, { waitUntil: 'networkidle2', timeout: 30000 });
|
||||||
|
|
||||||
// REDIRECTION CHECK: Logging redirected landing page
|
// REDIRECTION CHECK: Logging redirected landing page
|
||||||
const finalUrl = page.url();
|
const finalUrl = page.url();
|
||||||
if (finalUrl !== url && !finalUrl.includes(url)) {
|
if (finalUrl !== url && !finalUrl.includes(url)) {
|
||||||
@@ -167,7 +169,9 @@ async function main() {
|
|||||||
if (chunkErrorsDetected) {
|
if (chunkErrorsDetected) {
|
||||||
const buster = `cb=${Date.now()}`;
|
const buster = `cb=${Date.now()}`;
|
||||||
const cbUrl = finalUrl.includes('?') ? `${finalUrl}&${buster}` : `${finalUrl}?${buster}`;
|
const cbUrl = finalUrl.includes('?') ? `${finalUrl}&${buster}` : `${finalUrl}?${buster}`;
|
||||||
console.warn(` ⚠️ Assets failed to load (Varnish staleness suspected). Retrying with cache-buster: ${cbUrl}`);
|
console.warn(
|
||||||
|
` ⚠️ Assets failed to load (Varnish staleness suspected). Retrying with cache-buster: ${cbUrl}`,
|
||||||
|
);
|
||||||
chunkErrorsDetected = false;
|
chunkErrorsDetected = false;
|
||||||
await page.goto(cbUrl, { waitUntil: 'networkidle2', timeout: 30000 });
|
await page.goto(cbUrl, { waitUntil: 'networkidle2', timeout: 30000 });
|
||||||
}
|
}
|
||||||
@@ -266,10 +270,10 @@ async function main() {
|
|||||||
console.log(` 🔔 Alert text: ${alertText}`);
|
console.log(` 🔔 Alert text: ${alertText}`);
|
||||||
|
|
||||||
// Detection robust for both English and German versions
|
// Detection robust for both English and German versions
|
||||||
const isError =
|
const isError =
|
||||||
alertText?.toLowerCase().includes('failed') ||
|
alertText?.toLowerCase().includes('failed') ||
|
||||||
alertText?.toLowerCase().includes('wrong') ||
|
alertText?.toLowerCase().includes('wrong') ||
|
||||||
alertText?.toLowerCase().includes('fehlgeschlagen') ||
|
alertText?.toLowerCase().includes('fehlgeschlagen') ||
|
||||||
alertText?.toLowerCase().includes('schief gelaufen') ||
|
alertText?.toLowerCase().includes('schief gelaufen') ||
|
||||||
alertText?.toLowerCase().includes('fehler');
|
alertText?.toLowerCase().includes('fehler');
|
||||||
|
|
||||||
@@ -329,10 +333,10 @@ async function main() {
|
|||||||
const alertText = await page.$eval('[role="alert"]', (el) => el.textContent);
|
const alertText = await page.$eval('[role="alert"]', (el) => el.textContent);
|
||||||
console.log(` 🔔 Alert text: ${alertText}`);
|
console.log(` 🔔 Alert text: ${alertText}`);
|
||||||
|
|
||||||
const isError =
|
const isError =
|
||||||
alertText?.toLowerCase().includes('failed') ||
|
alertText?.toLowerCase().includes('failed') ||
|
||||||
alertText?.toLowerCase().includes('wrong') ||
|
alertText?.toLowerCase().includes('wrong') ||
|
||||||
alertText?.toLowerCase().includes('fehlgeschlagen') ||
|
alertText?.toLowerCase().includes('fehlgeschlagen') ||
|
||||||
alertText?.toLowerCase().includes('schief gelaufen') ||
|
alertText?.toLowerCase().includes('schief gelaufen') ||
|
||||||
alertText?.toLowerCase().includes('fehler');
|
alertText?.toLowerCase().includes('fehler');
|
||||||
|
|
||||||
@@ -349,9 +353,15 @@ async function main() {
|
|||||||
// 5. Cleanup: Delete test submissions from Payload CMS
|
// 5. Cleanup: Delete test submissions from Payload CMS
|
||||||
console.log(`\n🧹 Starting cleanup of test submissions...`);
|
console.log(`\n🧹 Starting cleanup of test submissions...`);
|
||||||
const payloadSecret = process.env.PAYLOAD_SECRET;
|
const payloadSecret = process.env.PAYLOAD_SECRET;
|
||||||
|
|
||||||
if (!payloadSecret) {
|
if (!payloadSecret) {
|
||||||
console.warn(` ⚠️ PAYLOAD_SECRET not found in environment. Cleanup will likely fail with 403.`);
|
console.warn(
|
||||||
|
` ⚠️ PAYLOAD_SECRET not found in environment. Cleanup will likely fail with 403 if the server expects a secret.`,
|
||||||
|
);
|
||||||
|
} else {
|
||||||
|
console.log(
|
||||||
|
` 🔐 PAYLOAD_SECRET found (${payloadSecret.substring(0, 3)}...). Sending x-e2e-secret header.`,
|
||||||
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
try {
|
try {
|
||||||
@@ -361,7 +371,7 @@ async function main() {
|
|||||||
|
|
||||||
// Fetch test submissions with bypass header
|
// Fetch test submissions with bypass header
|
||||||
const searchResponse = await axios.get(searchUrl, {
|
const searchResponse = await axios.get(searchUrl, {
|
||||||
headers: {
|
headers: {
|
||||||
Cookie: `klz_gatekeeper_session=${gatekeeperPassword}`,
|
Cookie: `klz_gatekeeper_session=${gatekeeperPassword}`,
|
||||||
'x-e2e-secret': payloadSecret || '',
|
'x-e2e-secret': payloadSecret || '',
|
||||||
},
|
},
|
||||||
@@ -373,29 +383,31 @@ async function main() {
|
|||||||
for (const doc of testSubmissions) {
|
for (const doc of testSubmissions) {
|
||||||
try {
|
try {
|
||||||
await axios.delete(`${apiUrl}/${doc.id}`, {
|
await axios.delete(`${apiUrl}/${doc.id}`, {
|
||||||
headers: {
|
headers: {
|
||||||
Cookie: `klz_gatekeeper_session=${gatekeeperPassword}`,
|
Cookie: `klz_gatekeeper_session=${gatekeeperPassword}`,
|
||||||
'x-e2e-secret': payloadSecret || '',
|
'x-e2e-secret': payloadSecret || '',
|
||||||
},
|
},
|
||||||
});
|
});
|
||||||
console.log(` ✅ Deleted submission: ${doc.id} (${doc.name})`);
|
console.log(` ✅ Deleted submission: ${doc.id} (${doc.name})`);
|
||||||
} catch (delErr: any) {
|
} catch (delErr: any) {
|
||||||
console.warn(
|
console.warn(` ⚠️ Cleanup attempt on ${doc.id} failed: ${delErr.message}`);
|
||||||
` ⚠️ Cleanup attempt on ${doc.id} failed: ${delErr.message}`,
|
|
||||||
);
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if (testSubmissions.length > 0) {
|
if (testSubmissions.length > 0) {
|
||||||
console.log(`✅ Cleanup completed successfully.`);
|
console.log(`✅ Cleanup completed successfully.`);
|
||||||
}
|
}
|
||||||
} catch (err: any) {
|
} catch (err: any) {
|
||||||
if (err.response?.status === 403) {
|
if (err.response?.status === 403) {
|
||||||
console.error(
|
console.error(` ❌ Cleanup failed with 403 Forbidden.`);
|
||||||
` ❌ Cleanup failed with 403 Forbidden. Ensure FormSubmissions access control and PAYLOAD_SECRET are aligned.`,
|
console.error(` Detail: The server rejected the x-e2e-secret header.`);
|
||||||
);
|
console.error(` Server Response: ${JSON.stringify(err.response.data)}`);
|
||||||
} else {
|
} else {
|
||||||
console.error(` ❌ Cleanup fetch failed: ${err.message}`);
|
console.error(` ❌ Cleanup fetch failed: ${err.message}`);
|
||||||
|
if (err.response) {
|
||||||
|
console.error(` Status: ${err.response.status}`);
|
||||||
|
console.error(` Body: ${JSON.stringify(err.response.data)}`);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -1,5 +0,0 @@
|
|||||||
{
|
|
||||||
"id": "20260225_003500_add_pages_collection",
|
|
||||||
"name": "20260225_003500_add_pages_collection",
|
|
||||||
"batch": 3
|
|
||||||
}
|
|
||||||
56
src/migrations/20260427_123000_add_agbs_collection.ts
Normal file
56
src/migrations/20260427_123000_add_agbs_collection.ts
Normal file
@@ -0,0 +1,56 @@
|
|||||||
|
import { MigrateUpArgs, MigrateDownArgs, sql } from '@payloadcms/db-postgres';
|
||||||
|
|
||||||
|
export async function up({ db }: MigrateUpArgs): Promise<void> {
|
||||||
|
await db.execute(sql`
|
||||||
|
CREATE TABLE IF NOT EXISTS "agbs_collection" (
|
||||||
|
"id" serial PRIMARY KEY NOT NULL,
|
||||||
|
"version_date" timestamp(3) with time zone NOT NULL,
|
||||||
|
"file_id" integer NOT NULL,
|
||||||
|
"is_current" boolean DEFAULT false,
|
||||||
|
"updated_at" timestamp(3) with time zone DEFAULT now() NOT NULL,
|
||||||
|
"created_at" timestamp(3) with time zone DEFAULT now() NOT NULL
|
||||||
|
);
|
||||||
|
|
||||||
|
CREATE TABLE IF NOT EXISTS "agbs_collection_locales" (
|
||||||
|
"id" serial PRIMARY KEY NOT NULL,
|
||||||
|
"title" varchar,
|
||||||
|
"_locale" "enum__locales" NOT NULL,
|
||||||
|
"_parent_id" integer NOT NULL
|
||||||
|
);
|
||||||
|
|
||||||
|
DO $$ BEGIN
|
||||||
|
ALTER TABLE "agbs_collection_locales" ADD CONSTRAINT "agbs_collection_locales_locale_parent_id_unique" UNIQUE("_locale", "_parent_id");
|
||||||
|
EXCEPTION WHEN duplicate_object THEN null; END $$;
|
||||||
|
|
||||||
|
DO $$ BEGIN
|
||||||
|
ALTER TABLE "agbs_collection_locales" ADD CONSTRAINT "agbs_collection_locales_parent_id_fk"
|
||||||
|
FOREIGN KEY ("_parent_id") REFERENCES "agbs_collection"("id") ON DELETE cascade;
|
||||||
|
EXCEPTION WHEN duplicate_object THEN null; END $$;
|
||||||
|
|
||||||
|
DO $$ BEGIN
|
||||||
|
ALTER TABLE "agbs_collection" ADD CONSTRAINT "agbs_collection_file_id_media_id_fk"
|
||||||
|
FOREIGN KEY ("file_id") REFERENCES "public"."media"("id") ON DELETE set null;
|
||||||
|
EXCEPTION WHEN duplicate_object THEN null; END $$;
|
||||||
|
|
||||||
|
CREATE INDEX IF NOT EXISTS "agbs_collection_updated_at_idx" ON "agbs_collection" USING btree ("updated_at");
|
||||||
|
CREATE INDEX IF NOT EXISTS "agbs_collection_created_at_idx" ON "agbs_collection" USING btree ("created_at");
|
||||||
|
|
||||||
|
-- Add to payload_locked_documents_rels
|
||||||
|
ALTER TABLE "payload_locked_documents_rels" ADD COLUMN IF NOT EXISTS "agbs_collection_id" integer;
|
||||||
|
DO $$ BEGIN
|
||||||
|
ALTER TABLE "payload_locked_documents_rels" ADD CONSTRAINT "payload_locked_documents_rels_agbs_collection_fk"
|
||||||
|
FOREIGN KEY ("agbs_collection_id") REFERENCES "public"."agbs_collection"("id") ON DELETE cascade;
|
||||||
|
EXCEPTION WHEN duplicate_object THEN null; END $$;
|
||||||
|
CREATE INDEX IF NOT EXISTS "payload_locked_documents_rels_agbs_collection_id_idx"
|
||||||
|
ON "payload_locked_documents_rels" USING btree ("agbs_collection_id");
|
||||||
|
`);
|
||||||
|
}
|
||||||
|
|
||||||
|
export async function down({ db }: MigrateDownArgs): Promise<void> {
|
||||||
|
await db.execute(sql`
|
||||||
|
ALTER TABLE "payload_locked_documents_rels" DROP CONSTRAINT IF EXISTS "payload_locked_documents_rels_agbs_collection_fk";
|
||||||
|
ALTER TABLE "payload_locked_documents_rels" DROP COLUMN IF EXISTS "agbs_collection_id";
|
||||||
|
DROP TABLE IF EXISTS "agbs_collection_locales" CASCADE;
|
||||||
|
DROP TABLE IF EXISTS "agbs_collection" CASCADE;
|
||||||
|
`);
|
||||||
|
}
|
||||||
@@ -5,6 +5,8 @@ import * as migration_20260225_175000_native_localization from './20260225_17500
|
|||||||
import * as migration_20260305_215000_products_featured_image from './20260305_215000_products_featured_image';
|
import * as migration_20260305_215000_products_featured_image from './20260305_215000_products_featured_image';
|
||||||
import * as migration_20260312_120000_pages_redirect_fields from './20260312_120000_pages_redirect_fields';
|
import * as migration_20260312_120000_pages_redirect_fields from './20260312_120000_pages_redirect_fields';
|
||||||
|
|
||||||
|
import * as migration_20260427_123000_add_agbs_collection from './20260427_123000_add_agbs_collection';
|
||||||
|
|
||||||
export const migrations = [
|
export const migrations = [
|
||||||
{
|
{
|
||||||
up: migration_20260223_195005_products_collection.up,
|
up: migration_20260223_195005_products_collection.up,
|
||||||
@@ -36,4 +38,9 @@ export const migrations = [
|
|||||||
down: migration_20260312_120000_pages_redirect_fields.down,
|
down: migration_20260312_120000_pages_redirect_fields.down,
|
||||||
name: '20260312_120000_pages_redirect_fields',
|
name: '20260312_120000_pages_redirect_fields',
|
||||||
},
|
},
|
||||||
|
{
|
||||||
|
up: migration_20260427_123000_add_agbs_collection.up,
|
||||||
|
down: migration_20260427_123000_add_agbs_collection.down,
|
||||||
|
name: '20260427_123000_add_agbs_collection',
|
||||||
|
},
|
||||||
];
|
];
|
||||||
|
|||||||
18
src/payload/blocks/AgbHistory.ts
Normal file
18
src/payload/blocks/AgbHistory.ts
Normal file
@@ -0,0 +1,18 @@
|
|||||||
|
import { Block } from 'payload';
|
||||||
|
|
||||||
|
export const AgbHistory: Block = {
|
||||||
|
slug: 'agbHistory',
|
||||||
|
labels: {
|
||||||
|
singular: 'AGB Historie',
|
||||||
|
plural: 'AGB Historien',
|
||||||
|
},
|
||||||
|
fields: [
|
||||||
|
{
|
||||||
|
name: 'title',
|
||||||
|
type: 'text',
|
||||||
|
label: 'Titel',
|
||||||
|
localized: true,
|
||||||
|
defaultValue: 'Vorherige Versionen',
|
||||||
|
},
|
||||||
|
],
|
||||||
|
};
|
||||||
@@ -17,6 +17,7 @@ import { TeamProfile } from './TeamProfile';
|
|||||||
import { TechnicalGrid } from './TechnicalGrid';
|
import { TechnicalGrid } from './TechnicalGrid';
|
||||||
import { VisualLinkPreview } from './VisualLinkPreview';
|
import { VisualLinkPreview } from './VisualLinkPreview';
|
||||||
import { PDFDownload } from './PDFDownload';
|
import { PDFDownload } from './PDFDownload';
|
||||||
|
import { AgbHistory } from './AgbHistory';
|
||||||
import { homeBlocksArray } from './HomeBlocks';
|
import { homeBlocksArray } from './HomeBlocks';
|
||||||
|
|
||||||
export const payloadBlocks = [
|
export const payloadBlocks = [
|
||||||
@@ -40,4 +41,5 @@ export const payloadBlocks = [
|
|||||||
TechnicalGrid,
|
TechnicalGrid,
|
||||||
VisualLinkPreview,
|
VisualLinkPreview,
|
||||||
PDFDownload,
|
PDFDownload,
|
||||||
|
AgbHistory,
|
||||||
];
|
];
|
||||||
|
|||||||
49
src/payload/collections/Agbs.ts
Normal file
49
src/payload/collections/Agbs.ts
Normal file
@@ -0,0 +1,49 @@
|
|||||||
|
import { CollectionConfig } from 'payload';
|
||||||
|
|
||||||
|
export const Agbs: CollectionConfig = {
|
||||||
|
slug: 'agbs-collection',
|
||||||
|
admin: {
|
||||||
|
useAsTitle: 'title',
|
||||||
|
defaultColumns: ['title', 'versionDate', 'updatedAt'],
|
||||||
|
group: 'Rechtliches',
|
||||||
|
},
|
||||||
|
access: {
|
||||||
|
read: () => true,
|
||||||
|
},
|
||||||
|
fields: [
|
||||||
|
{
|
||||||
|
name: 'title',
|
||||||
|
type: 'text',
|
||||||
|
required: true,
|
||||||
|
localized: true,
|
||||||
|
label: 'Titel (z.B. AGB April 2026)',
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: 'versionDate',
|
||||||
|
type: 'date',
|
||||||
|
required: true,
|
||||||
|
label: 'Gültig ab',
|
||||||
|
admin: {
|
||||||
|
date: {
|
||||||
|
pickerAppearance: 'dayOnly',
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: 'file',
|
||||||
|
type: 'upload',
|
||||||
|
relationTo: 'media',
|
||||||
|
required: true,
|
||||||
|
label: 'PDF Datei',
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: 'isCurrent',
|
||||||
|
type: 'checkbox',
|
||||||
|
label: 'Aktuelle Version',
|
||||||
|
defaultValue: false,
|
||||||
|
admin: {
|
||||||
|
position: 'sidebar',
|
||||||
|
},
|
||||||
|
},
|
||||||
|
],
|
||||||
|
};
|
||||||
Reference in New Issue
Block a user