Erste brauchbare Version
This commit is contained in:
@@ -0,0 +1,10 @@
|
|||||||
|
DATABASE_URL=postgresql://...
|
||||||
|
PAYLOAD_SECRET=ein-langer-zufälliger-secret-string
|
||||||
|
|
||||||
|
MAIL_HOST=smtp.example.org
|
||||||
|
MAIL_PORT=465
|
||||||
|
MAIL_DISPLAYNAME=My Company Name
|
||||||
|
MAIL_USER=noreply@example.org
|
||||||
|
MAIL_PASSWORD=password
|
||||||
|
|
||||||
|
NEXT_PUBLIC_PAYLOAD_URL=http://example.org
|
||||||
@@ -39,3 +39,7 @@ yarn-error.log*
|
|||||||
# typescript
|
# typescript
|
||||||
*.tsbuildinfo
|
*.tsbuildinfo
|
||||||
next-env.d.ts
|
next-env.d.ts
|
||||||
|
|
||||||
|
# Payload CMS
|
||||||
|
/media/
|
||||||
|
*.db
|
||||||
@@ -1,6 +1,9 @@
|
|||||||
import { type ReactNode } from 'react';
|
import { type ReactNode } from 'react';
|
||||||
import { type Metadata } from 'next';
|
import { type Metadata } from 'next';
|
||||||
import '@/app/globals.css';
|
import { getPreview } from '@/lib/preview';
|
||||||
|
import RefreshRouteOnSave from '@/components/RefreshRouteOnSave';
|
||||||
|
import EndPreview from '@/components/EndPreviewButton';
|
||||||
|
import '@/app/(app)/globals.css';
|
||||||
|
|
||||||
type Props = Readonly<{
|
type Props = Readonly<{
|
||||||
children: ReactNode;
|
children: ReactNode;
|
||||||
@@ -18,11 +21,15 @@ export const metadata: Metadata = {
|
|||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
export default function RootLayout({ children }: Props) {
|
export default async function RootLayout({ children }: Props) {
|
||||||
|
const { isPreview } = await getPreview();
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<html lang="de" >
|
<html lang="de" >
|
||||||
<body>
|
<body>
|
||||||
|
<RefreshRouteOnSave />
|
||||||
{children}
|
{children}
|
||||||
|
{isPreview && <EndPreview />}
|
||||||
</body>
|
</body>
|
||||||
</html>
|
</html>
|
||||||
);
|
);
|
||||||
@@ -0,0 +1,29 @@
|
|||||||
|
import Link from 'next/link';
|
||||||
|
import { getPreview } from '@/lib/preview';
|
||||||
|
import config from '@payload-config';
|
||||||
|
import { getPayload } from 'payload';
|
||||||
|
|
||||||
|
export default async function Page() {
|
||||||
|
const { isPreview, where } = await getPreview();
|
||||||
|
const payload = await getPayload({ config });
|
||||||
|
|
||||||
|
const posts = await payload.find({
|
||||||
|
collection: 'posts',
|
||||||
|
where,
|
||||||
|
draft: isPreview
|
||||||
|
});
|
||||||
|
|
||||||
|
return (
|
||||||
|
<main className="flex flex-col">
|
||||||
|
{posts.docs.map((post) => (
|
||||||
|
<Link
|
||||||
|
key={post.id}
|
||||||
|
className="border border-1 border-primary p-4 m-4"
|
||||||
|
href={`/post/${post.slug}`}
|
||||||
|
>
|
||||||
|
{post.title}
|
||||||
|
</Link>
|
||||||
|
))}
|
||||||
|
</main>
|
||||||
|
);
|
||||||
|
}
|
||||||
@@ -0,0 +1,15 @@
|
|||||||
|
import Link from 'next/link';
|
||||||
|
|
||||||
|
export default function Page() {
|
||||||
|
return (
|
||||||
|
<div className="min-h-screen flex flex-col justify-center items-center">
|
||||||
|
<p>Post nicht gefunden</p>
|
||||||
|
<Link
|
||||||
|
className="border border-1 border-primary"
|
||||||
|
href="/"
|
||||||
|
>
|
||||||
|
Zurück
|
||||||
|
</Link>
|
||||||
|
</div>
|
||||||
|
);
|
||||||
|
}
|
||||||
@@ -0,0 +1,48 @@
|
|||||||
|
import { notFound } from 'next/navigation';
|
||||||
|
import Link from 'next/link';
|
||||||
|
import { getPreview } from '@/lib/preview';
|
||||||
|
import config from '@payload-config';
|
||||||
|
import { getPayload } from 'payload';
|
||||||
|
import { RichText } from '@payloadcms/richtext-lexical/react';
|
||||||
|
|
||||||
|
type Props = Readonly<{
|
||||||
|
params: Promise<{
|
||||||
|
slug: string;
|
||||||
|
}>;
|
||||||
|
}>;
|
||||||
|
|
||||||
|
export default async function Page({ params }: Props) {
|
||||||
|
const { isPreview, where } = await getPreview();
|
||||||
|
const { slug } = await params;
|
||||||
|
const payload = await getPayload({ config });
|
||||||
|
|
||||||
|
const result = await payload.find({
|
||||||
|
collection: 'posts',
|
||||||
|
where: {
|
||||||
|
...where,
|
||||||
|
slug: {
|
||||||
|
equals: slug
|
||||||
|
}
|
||||||
|
},
|
||||||
|
draft: isPreview,
|
||||||
|
limit: 1
|
||||||
|
});
|
||||||
|
|
||||||
|
const post = result.docs[0];
|
||||||
|
|
||||||
|
if (!post)
|
||||||
|
return notFound();
|
||||||
|
|
||||||
|
return (
|
||||||
|
<div className="m-4">
|
||||||
|
<Link
|
||||||
|
className="border border-1 border-primary"
|
||||||
|
href="/"
|
||||||
|
>
|
||||||
|
Zurück
|
||||||
|
</Link>
|
||||||
|
<h1 className="font-bold">{post.title}</h1>
|
||||||
|
<RichText data={post.content} />
|
||||||
|
</div>
|
||||||
|
);
|
||||||
|
}
|
||||||
@@ -0,0 +1,29 @@
|
|||||||
|
import config from '@payload-config';
|
||||||
|
import { NotFoundPage, generatePageMetadata } from '@payloadcms/next/views';
|
||||||
|
import { importMap } from '../importMap';
|
||||||
|
|
||||||
|
type Props = Readonly<{
|
||||||
|
params: Promise<{
|
||||||
|
segments: string[];
|
||||||
|
}>;
|
||||||
|
searchParams: Promise<{
|
||||||
|
[key: string]: string | string[];
|
||||||
|
}>;
|
||||||
|
}>;
|
||||||
|
|
||||||
|
export function generateMetadata({ params, searchParams }: Props) {
|
||||||
|
return generatePageMetadata({
|
||||||
|
config,
|
||||||
|
params,
|
||||||
|
searchParams
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
export default function NotFound({ params, searchParams }: Props) {
|
||||||
|
return NotFoundPage({
|
||||||
|
config,
|
||||||
|
params,
|
||||||
|
searchParams,
|
||||||
|
importMap
|
||||||
|
});
|
||||||
|
}
|
||||||
@@ -0,0 +1,29 @@
|
|||||||
|
import config from '@payload-config';
|
||||||
|
import { RootPage, generatePageMetadata } from '@payloadcms/next/views';
|
||||||
|
import { importMap } from '../importMap';
|
||||||
|
|
||||||
|
type Props = Readonly<{
|
||||||
|
params: Promise<{
|
||||||
|
segments: string[];
|
||||||
|
}>;
|
||||||
|
searchParams: Promise<{
|
||||||
|
[key: string]: string | string[];
|
||||||
|
}>;
|
||||||
|
}>;
|
||||||
|
|
||||||
|
export function generateMetadata({ params, searchParams}: Props) {
|
||||||
|
return generatePageMetadata({
|
||||||
|
config,
|
||||||
|
params,
|
||||||
|
searchParams
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
export default function Page({ params, searchParams }: Props) {
|
||||||
|
return RootPage({
|
||||||
|
config,
|
||||||
|
params,
|
||||||
|
searchParams,
|
||||||
|
importMap
|
||||||
|
});
|
||||||
|
}
|
||||||
@@ -0,0 +1,58 @@
|
|||||||
|
import { RscEntryLexicalCell as RscEntryLexicalCell_44fe37237e0ebf4470c9990d8cb7b07e } from '@payloadcms/richtext-lexical/rsc'
|
||||||
|
import { RscEntryLexicalField as RscEntryLexicalField_44fe37237e0ebf4470c9990d8cb7b07e } from '@payloadcms/richtext-lexical/rsc'
|
||||||
|
import { LexicalDiffComponent as LexicalDiffComponent_44fe37237e0ebf4470c9990d8cb7b07e } from '@payloadcms/richtext-lexical/rsc'
|
||||||
|
import { UploadFeatureClient as UploadFeatureClient_e70f5e05f09f93e00b997edb1ef0c864 } from '@payloadcms/richtext-lexical/client'
|
||||||
|
import { BoldFeatureClient as BoldFeatureClient_e70f5e05f09f93e00b997edb1ef0c864 } from '@payloadcms/richtext-lexical/client'
|
||||||
|
import { ItalicFeatureClient as ItalicFeatureClient_e70f5e05f09f93e00b997edb1ef0c864 } from '@payloadcms/richtext-lexical/client'
|
||||||
|
import { UnderlineFeatureClient as UnderlineFeatureClient_e70f5e05f09f93e00b997edb1ef0c864 } from '@payloadcms/richtext-lexical/client'
|
||||||
|
import { StrikethroughFeatureClient as StrikethroughFeatureClient_e70f5e05f09f93e00b997edb1ef0c864 } from '@payloadcms/richtext-lexical/client'
|
||||||
|
import { SubscriptFeatureClient as SubscriptFeatureClient_e70f5e05f09f93e00b997edb1ef0c864 } from '@payloadcms/richtext-lexical/client'
|
||||||
|
import { SuperscriptFeatureClient as SuperscriptFeatureClient_e70f5e05f09f93e00b997edb1ef0c864 } from '@payloadcms/richtext-lexical/client'
|
||||||
|
import { InlineCodeFeatureClient as InlineCodeFeatureClient_e70f5e05f09f93e00b997edb1ef0c864 } from '@payloadcms/richtext-lexical/client'
|
||||||
|
import { ParagraphFeatureClient as ParagraphFeatureClient_e70f5e05f09f93e00b997edb1ef0c864 } from '@payloadcms/richtext-lexical/client'
|
||||||
|
import { HeadingFeatureClient as HeadingFeatureClient_e70f5e05f09f93e00b997edb1ef0c864 } from '@payloadcms/richtext-lexical/client'
|
||||||
|
import { AlignFeatureClient as AlignFeatureClient_e70f5e05f09f93e00b997edb1ef0c864 } from '@payloadcms/richtext-lexical/client'
|
||||||
|
import { IndentFeatureClient as IndentFeatureClient_e70f5e05f09f93e00b997edb1ef0c864 } from '@payloadcms/richtext-lexical/client'
|
||||||
|
import { UnorderedListFeatureClient as UnorderedListFeatureClient_e70f5e05f09f93e00b997edb1ef0c864 } from '@payloadcms/richtext-lexical/client'
|
||||||
|
import { OrderedListFeatureClient as OrderedListFeatureClient_e70f5e05f09f93e00b997edb1ef0c864 } from '@payloadcms/richtext-lexical/client'
|
||||||
|
import { ChecklistFeatureClient as ChecklistFeatureClient_e70f5e05f09f93e00b997edb1ef0c864 } from '@payloadcms/richtext-lexical/client'
|
||||||
|
import { LinkFeatureClient as LinkFeatureClient_e70f5e05f09f93e00b997edb1ef0c864 } from '@payloadcms/richtext-lexical/client'
|
||||||
|
import { RelationshipFeatureClient as RelationshipFeatureClient_e70f5e05f09f93e00b997edb1ef0c864 } from '@payloadcms/richtext-lexical/client'
|
||||||
|
import { BlockquoteFeatureClient as BlockquoteFeatureClient_e70f5e05f09f93e00b997edb1ef0c864 } from '@payloadcms/richtext-lexical/client'
|
||||||
|
import { HorizontalRuleFeatureClient as HorizontalRuleFeatureClient_e70f5e05f09f93e00b997edb1ef0c864 } from '@payloadcms/richtext-lexical/client'
|
||||||
|
import { InlineToolbarFeatureClient as InlineToolbarFeatureClient_e70f5e05f09f93e00b997edb1ef0c864 } from '@payloadcms/richtext-lexical/client'
|
||||||
|
import { Icon as Icon_f934946492fabd3bfa95068204328949 } from '../../../components/PayloadInjects'
|
||||||
|
import { Logo as Logo_f934946492fabd3bfa95068204328949 } from '../../../components/PayloadInjects'
|
||||||
|
import { Action as Action_f934946492fabd3bfa95068204328949 } from '../../../components/PayloadInjects'
|
||||||
|
import { CollectionCards as CollectionCards_f9c02e79a4aed9a3924487c0cd4cafb1 } from '@payloadcms/next/rsc'
|
||||||
|
|
||||||
|
/** @type import('payload').ImportMap */
|
||||||
|
export const importMap = {
|
||||||
|
"@payloadcms/richtext-lexical/rsc#RscEntryLexicalCell": RscEntryLexicalCell_44fe37237e0ebf4470c9990d8cb7b07e,
|
||||||
|
"@payloadcms/richtext-lexical/rsc#RscEntryLexicalField": RscEntryLexicalField_44fe37237e0ebf4470c9990d8cb7b07e,
|
||||||
|
"@payloadcms/richtext-lexical/rsc#LexicalDiffComponent": LexicalDiffComponent_44fe37237e0ebf4470c9990d8cb7b07e,
|
||||||
|
"@payloadcms/richtext-lexical/client#UploadFeatureClient": UploadFeatureClient_e70f5e05f09f93e00b997edb1ef0c864,
|
||||||
|
"@payloadcms/richtext-lexical/client#BoldFeatureClient": BoldFeatureClient_e70f5e05f09f93e00b997edb1ef0c864,
|
||||||
|
"@payloadcms/richtext-lexical/client#ItalicFeatureClient": ItalicFeatureClient_e70f5e05f09f93e00b997edb1ef0c864,
|
||||||
|
"@payloadcms/richtext-lexical/client#UnderlineFeatureClient": UnderlineFeatureClient_e70f5e05f09f93e00b997edb1ef0c864,
|
||||||
|
"@payloadcms/richtext-lexical/client#StrikethroughFeatureClient": StrikethroughFeatureClient_e70f5e05f09f93e00b997edb1ef0c864,
|
||||||
|
"@payloadcms/richtext-lexical/client#SubscriptFeatureClient": SubscriptFeatureClient_e70f5e05f09f93e00b997edb1ef0c864,
|
||||||
|
"@payloadcms/richtext-lexical/client#SuperscriptFeatureClient": SuperscriptFeatureClient_e70f5e05f09f93e00b997edb1ef0c864,
|
||||||
|
"@payloadcms/richtext-lexical/client#InlineCodeFeatureClient": InlineCodeFeatureClient_e70f5e05f09f93e00b997edb1ef0c864,
|
||||||
|
"@payloadcms/richtext-lexical/client#ParagraphFeatureClient": ParagraphFeatureClient_e70f5e05f09f93e00b997edb1ef0c864,
|
||||||
|
"@payloadcms/richtext-lexical/client#HeadingFeatureClient": HeadingFeatureClient_e70f5e05f09f93e00b997edb1ef0c864,
|
||||||
|
"@payloadcms/richtext-lexical/client#AlignFeatureClient": AlignFeatureClient_e70f5e05f09f93e00b997edb1ef0c864,
|
||||||
|
"@payloadcms/richtext-lexical/client#IndentFeatureClient": IndentFeatureClient_e70f5e05f09f93e00b997edb1ef0c864,
|
||||||
|
"@payloadcms/richtext-lexical/client#UnorderedListFeatureClient": UnorderedListFeatureClient_e70f5e05f09f93e00b997edb1ef0c864,
|
||||||
|
"@payloadcms/richtext-lexical/client#OrderedListFeatureClient": OrderedListFeatureClient_e70f5e05f09f93e00b997edb1ef0c864,
|
||||||
|
"@payloadcms/richtext-lexical/client#ChecklistFeatureClient": ChecklistFeatureClient_e70f5e05f09f93e00b997edb1ef0c864,
|
||||||
|
"@payloadcms/richtext-lexical/client#LinkFeatureClient": LinkFeatureClient_e70f5e05f09f93e00b997edb1ef0c864,
|
||||||
|
"@payloadcms/richtext-lexical/client#RelationshipFeatureClient": RelationshipFeatureClient_e70f5e05f09f93e00b997edb1ef0c864,
|
||||||
|
"@payloadcms/richtext-lexical/client#BlockquoteFeatureClient": BlockquoteFeatureClient_e70f5e05f09f93e00b997edb1ef0c864,
|
||||||
|
"@payloadcms/richtext-lexical/client#HorizontalRuleFeatureClient": HorizontalRuleFeatureClient_e70f5e05f09f93e00b997edb1ef0c864,
|
||||||
|
"@payloadcms/richtext-lexical/client#InlineToolbarFeatureClient": InlineToolbarFeatureClient_e70f5e05f09f93e00b997edb1ef0c864,
|
||||||
|
"/components/PayloadInjects#Icon": Icon_f934946492fabd3bfa95068204328949,
|
||||||
|
"/components/PayloadInjects#Logo": Logo_f934946492fabd3bfa95068204328949,
|
||||||
|
"/components/PayloadInjects#Action": Action_f934946492fabd3bfa95068204328949,
|
||||||
|
"@payloadcms/next/rsc#CollectionCards": CollectionCards_f9c02e79a4aed9a3924487c0cd4cafb1
|
||||||
|
}
|
||||||
@@ -0,0 +1,55 @@
|
|||||||
|
import { RscEntryLexicalCell } from'@payloadcms/richtext-lexical/rsc';
|
||||||
|
import { RscEntryLexicalField } from'@payloadcms/richtext-lexical/rsc';
|
||||||
|
import { LexicalDiffComponent } from'@payloadcms/richtext-lexical/rsc';
|
||||||
|
import { InlineToolbarFeatureClient } from'@payloadcms/richtext-lexical/client';
|
||||||
|
import { HorizontalRuleFeatureClient } from'@payloadcms/richtext-lexical/client';
|
||||||
|
import { UploadFeatureClient } from'@payloadcms/richtext-lexical/client';
|
||||||
|
import { BlockquoteFeatureClient } from'@payloadcms/richtext-lexical/client';
|
||||||
|
import { RelationshipFeatureClient } from'@payloadcms/richtext-lexical/client';
|
||||||
|
import { LinkFeatureClient } from'@payloadcms/richtext-lexical/client';
|
||||||
|
import { ChecklistFeatureClient } from'@payloadcms/richtext-lexical/client';
|
||||||
|
import { OrderedListFeatureClient } from'@payloadcms/richtext-lexical/client';
|
||||||
|
import { UnorderedListFeatureClient } from'@payloadcms/richtext-lexical/client';
|
||||||
|
import { IndentFeatureClient } from'@payloadcms/richtext-lexical/client';
|
||||||
|
import { AlignFeatureClient } from'@payloadcms/richtext-lexical/client';
|
||||||
|
import { HeadingFeatureClient } from'@payloadcms/richtext-lexical/client';
|
||||||
|
import { ParagraphFeatureClient } from'@payloadcms/richtext-lexical/client';
|
||||||
|
import { InlineCodeFeatureClient } from'@payloadcms/richtext-lexical/client';
|
||||||
|
import { SuperscriptFeatureClient } from'@payloadcms/richtext-lexical/client';
|
||||||
|
import { SubscriptFeatureClient } from'@payloadcms/richtext-lexical/client';
|
||||||
|
import { StrikethroughFeatureClient } from'@payloadcms/richtext-lexical/client';
|
||||||
|
import { UnderlineFeatureClient } from'@payloadcms/richtext-lexical/client';
|
||||||
|
import { BoldFeatureClient } from'@payloadcms/richtext-lexical/client';
|
||||||
|
import { ItalicFeatureClient } from'@payloadcms/richtext-lexical/client';
|
||||||
|
import { CollectionCards } from'@payloadcms/next/rsc';
|
||||||
|
import { Logo, Icon, Action } from '@/components/PayloadInjects';
|
||||||
|
|
||||||
|
export const importMap = {
|
||||||
|
'@payloadcms/richtext-lexical/rsc#RscEntryLexicalCell': RscEntryLexicalCell,
|
||||||
|
'@payloadcms/richtext-lexical/rsc#RscEntryLexicalField': RscEntryLexicalField,
|
||||||
|
'@payloadcms/richtext-lexical/rsc#LexicalDiffComponent': LexicalDiffComponent,
|
||||||
|
'@payloadcms/richtext-lexical/client#InlineToolbarFeatureClient': InlineToolbarFeatureClient,
|
||||||
|
'@payloadcms/richtext-lexical/client#HorizontalRuleFeatureClient': HorizontalRuleFeatureClient,
|
||||||
|
'@payloadcms/richtext-lexical/client#UploadFeatureClient': UploadFeatureClient,
|
||||||
|
'@payloadcms/richtext-lexical/client#BlockquoteFeatureClient': BlockquoteFeatureClient,
|
||||||
|
'@payloadcms/richtext-lexical/client#RelationshipFeatureClient': RelationshipFeatureClient,
|
||||||
|
'@payloadcms/richtext-lexical/client#LinkFeatureClient': LinkFeatureClient,
|
||||||
|
'@payloadcms/richtext-lexical/client#ChecklistFeatureClient': ChecklistFeatureClient,
|
||||||
|
'@payloadcms/richtext-lexical/client#OrderedListFeatureClient': OrderedListFeatureClient,
|
||||||
|
'@payloadcms/richtext-lexical/client#UnorderedListFeatureClient': UnorderedListFeatureClient,
|
||||||
|
'@payloadcms/richtext-lexical/client#IndentFeatureClient': IndentFeatureClient,
|
||||||
|
'@payloadcms/richtext-lexical/client#AlignFeatureClient': AlignFeatureClient,
|
||||||
|
'@payloadcms/richtext-lexical/client#HeadingFeatureClient': HeadingFeatureClient,
|
||||||
|
'@payloadcms/richtext-lexical/client#ParagraphFeatureClient': ParagraphFeatureClient,
|
||||||
|
'@payloadcms/richtext-lexical/client#InlineCodeFeatureClient': InlineCodeFeatureClient,
|
||||||
|
'@payloadcms/richtext-lexical/client#SuperscriptFeatureClient': SuperscriptFeatureClient,
|
||||||
|
'@payloadcms/richtext-lexical/client#SubscriptFeatureClient': SubscriptFeatureClient,
|
||||||
|
'@payloadcms/richtext-lexical/client#StrikethroughFeatureClient': StrikethroughFeatureClient,
|
||||||
|
'@payloadcms/richtext-lexical/client#UnderlineFeatureClient': UnderlineFeatureClient,
|
||||||
|
'@payloadcms/richtext-lexical/client#BoldFeatureClient': BoldFeatureClient,
|
||||||
|
'@payloadcms/richtext-lexical/client#ItalicFeatureClient': ItalicFeatureClient,
|
||||||
|
'@payloadcms/next/rsc#CollectionCards': CollectionCards,
|
||||||
|
'/components/PayloadInjects#Logo': Logo,
|
||||||
|
'/components/PayloadInjects#Icon': Icon,
|
||||||
|
'/components/PayloadInjects#Action': Action
|
||||||
|
};
|
||||||
@@ -0,0 +1,10 @@
|
|||||||
|
import config from '@payload-config';
|
||||||
|
import { REST_DELETE, REST_GET, REST_OPTIONS, REST_PATCH, REST_POST, REST_PUT } from '@payloadcms/next/routes';
|
||||||
|
import '@payloadcms/next/css';
|
||||||
|
|
||||||
|
export const GET = REST_GET(config);
|
||||||
|
export const POST = REST_POST(config);
|
||||||
|
export const DELETE = REST_DELETE(config);
|
||||||
|
export const PATCH = REST_PATCH(config);
|
||||||
|
export const PUT = REST_PUT(config);
|
||||||
|
export const OPTIONS = REST_OPTIONS(config);
|
||||||
@@ -0,0 +1,35 @@
|
|||||||
|
import { draftMode } from 'next/headers';
|
||||||
|
import { redirect } from 'next/navigation';
|
||||||
|
import { getPayload } from 'payload';
|
||||||
|
import config from '@payload-config';
|
||||||
|
|
||||||
|
export async function GET(request: Request) {
|
||||||
|
const { searchParams } = new URL(request.url);
|
||||||
|
|
||||||
|
const slug = searchParams.get('slug');
|
||||||
|
|
||||||
|
if (!slug)
|
||||||
|
return new Response('Missing slug', {
|
||||||
|
status: 400
|
||||||
|
});
|
||||||
|
|
||||||
|
const payload = await getPayload({
|
||||||
|
config
|
||||||
|
});
|
||||||
|
|
||||||
|
const { user } = await payload.auth({
|
||||||
|
headers: request.headers
|
||||||
|
});
|
||||||
|
|
||||||
|
if (!user)
|
||||||
|
return new Response('Unauthorized', {
|
||||||
|
status: 401
|
||||||
|
});
|
||||||
|
|
||||||
|
const draft = await draftMode();
|
||||||
|
|
||||||
|
draft.enable();
|
||||||
|
|
||||||
|
// redirect(`/${slug}`);
|
||||||
|
redirect('/');
|
||||||
|
}
|
||||||
@@ -0,0 +1,5 @@
|
|||||||
|
import config from '@payload-config';
|
||||||
|
import { GRAPHQL_PLAYGROUND_GET } from '@payloadcms/next/routes';
|
||||||
|
import '@payloadcms/next/css';
|
||||||
|
|
||||||
|
export const GET = GRAPHQL_PLAYGROUND_GET(config);
|
||||||
@@ -0,0 +1,5 @@
|
|||||||
|
import config from '@payload-config';
|
||||||
|
import { GRAPHQL_POST, REST_OPTIONS } from '@payloadcms/next/routes';
|
||||||
|
|
||||||
|
export const POST = GRAPHQL_POST(config);
|
||||||
|
export const OPTIONS = REST_OPTIONS(config);
|
||||||
@@ -0,0 +1,32 @@
|
|||||||
|
import { type ReactNode } from 'react';
|
||||||
|
import config from '@payload-config';
|
||||||
|
import { type ServerFunctionClientArgs } from 'payload';
|
||||||
|
import { handleServerFunctions, RootLayout } from '@payloadcms/next/layouts';
|
||||||
|
import { importMap } from './admin/importMap';
|
||||||
|
import '@payloadcms/next/css';
|
||||||
|
|
||||||
|
type Props = Readonly<{
|
||||||
|
children: ReactNode;
|
||||||
|
}>;
|
||||||
|
|
||||||
|
async function serverFunction(args: ServerFunctionClientArgs) {
|
||||||
|
'use server';
|
||||||
|
|
||||||
|
return handleServerFunctions({
|
||||||
|
...args,
|
||||||
|
config,
|
||||||
|
importMap
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
export default function Layout({ children }: Props) {
|
||||||
|
return (
|
||||||
|
<RootLayout
|
||||||
|
config={config}
|
||||||
|
importMap={importMap}
|
||||||
|
serverFunction={serverFunction}
|
||||||
|
>
|
||||||
|
{children}
|
||||||
|
</RootLayout>
|
||||||
|
);
|
||||||
|
}
|
||||||
@@ -1,5 +0,0 @@
|
|||||||
export default function Page() {
|
|
||||||
return (
|
|
||||||
<p>Works!</p>
|
|
||||||
);
|
|
||||||
}
|
|
||||||
@@ -0,0 +1,36 @@
|
|||||||
|
import { type CollectionConfig } from 'payload';
|
||||||
|
|
||||||
|
export const Media: CollectionConfig = {
|
||||||
|
slug: 'media',
|
||||||
|
|
||||||
|
upload: {
|
||||||
|
staticDir: 'media'
|
||||||
|
},
|
||||||
|
|
||||||
|
labels: {
|
||||||
|
singular: 'Medium',
|
||||||
|
plural: 'Medien'
|
||||||
|
},
|
||||||
|
|
||||||
|
admin: {
|
||||||
|
useAsTitle: 'title',
|
||||||
|
group: 'Inhalte',
|
||||||
|
description: 'Artikel verwalten',
|
||||||
|
defaultColumns: [ 'id', 'title', 'alt' ],
|
||||||
|
hideAPIURL: true
|
||||||
|
},
|
||||||
|
|
||||||
|
fields: [
|
||||||
|
{
|
||||||
|
name: 'title',
|
||||||
|
label: 'Titel',
|
||||||
|
type: 'text',
|
||||||
|
required: true
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: 'alt',
|
||||||
|
label: 'Alt-Tag',
|
||||||
|
type: 'text'
|
||||||
|
}
|
||||||
|
]
|
||||||
|
};
|
||||||
@@ -0,0 +1,83 @@
|
|||||||
|
import { type CollectionConfig } from 'payload';
|
||||||
|
import { lexicalEditor, UploadFeature } from '@payloadcms/richtext-lexical';
|
||||||
|
|
||||||
|
export const Posts: CollectionConfig = {
|
||||||
|
slug: 'posts',
|
||||||
|
|
||||||
|
labels: {
|
||||||
|
singular: 'Artikel',
|
||||||
|
plural: 'Artikel'
|
||||||
|
},
|
||||||
|
|
||||||
|
admin: {
|
||||||
|
useAsTitle: 'title',
|
||||||
|
group: 'Inhalte',
|
||||||
|
description: 'Artikel verwalten',
|
||||||
|
defaultColumns: [ 'id', 'slug', 'title', 'author', '_status' ],
|
||||||
|
hideAPIURL: true,
|
||||||
|
preview: doc =>
|
||||||
|
`${process.env.NEXT_PUBLIC_PAYLOAD_URL}/post/${doc.slug}/?preview`
|
||||||
|
},
|
||||||
|
|
||||||
|
versions: {
|
||||||
|
drafts: true
|
||||||
|
},
|
||||||
|
|
||||||
|
access: {
|
||||||
|
read: ({ data, req }) => (data?._status && data?._status !== 'draft') || [ 'admin', 'superadmin' ].includes(req.user?.role),
|
||||||
|
create: ({ req }) => [ 'admin', 'superadmin' ].includes(req.user?.role),
|
||||||
|
update: ({ req }) => [ 'admin', 'superadmin' ].includes(req.user?.role),
|
||||||
|
delete: ({ req }) => [ 'admin', 'superadmin' ].includes(req.user?.role)
|
||||||
|
},
|
||||||
|
|
||||||
|
orderable: true,
|
||||||
|
|
||||||
|
fields: [
|
||||||
|
{
|
||||||
|
name: 'title',
|
||||||
|
label: 'Titel',
|
||||||
|
type: 'text',
|
||||||
|
required: true
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: 'slug',
|
||||||
|
label: 'Slug',
|
||||||
|
type: 'text',
|
||||||
|
required: true,
|
||||||
|
unique: true,
|
||||||
|
admin: {
|
||||||
|
placeholder: 'Zum Beispiel: mein-toller-post'
|
||||||
|
|
||||||
|
}
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: 'content',
|
||||||
|
label: 'Inhalt',
|
||||||
|
type: 'richText',
|
||||||
|
editor: lexicalEditor({
|
||||||
|
features: ({ defaultFeatures }) => [
|
||||||
|
...defaultFeatures,
|
||||||
|
UploadFeature({
|
||||||
|
collections: {
|
||||||
|
media: {
|
||||||
|
fields: []
|
||||||
|
}
|
||||||
|
}
|
||||||
|
})
|
||||||
|
]
|
||||||
|
})
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: 'author',
|
||||||
|
label: 'Autor',
|
||||||
|
type: 'relationship',
|
||||||
|
relationTo: 'users',
|
||||||
|
filterOptions: () => {
|
||||||
|
return {
|
||||||
|
// eslint-disable-next-line camelcase
|
||||||
|
role: { not_equals: 'superadmin' }
|
||||||
|
};
|
||||||
|
}
|
||||||
|
}
|
||||||
|
]
|
||||||
|
};
|
||||||
@@ -0,0 +1,89 @@
|
|||||||
|
import { type CollectionConfig, type AccessArgs } from 'payload';
|
||||||
|
|
||||||
|
function isSelfOrAdmin({ req }: AccessArgs) {
|
||||||
|
if (req.user?.role === 'superadmin')
|
||||||
|
return true;
|
||||||
|
|
||||||
|
if (req.user?.role === 'admin')
|
||||||
|
return {
|
||||||
|
id: {
|
||||||
|
equals: req.user?.id
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
export const Users: CollectionConfig = {
|
||||||
|
slug: 'users',
|
||||||
|
|
||||||
|
labels: {
|
||||||
|
singular: 'Benutzer',
|
||||||
|
plural: 'Benutzer'
|
||||||
|
},
|
||||||
|
|
||||||
|
admin: {
|
||||||
|
useAsTitle: 'fullName',
|
||||||
|
group: 'System',
|
||||||
|
description: 'Benutzer verwalten',
|
||||||
|
defaultColumns: [ 'id', 'fullName', 'email', 'role' ],
|
||||||
|
hideAPIURL: true
|
||||||
|
},
|
||||||
|
|
||||||
|
access: {
|
||||||
|
read: isSelfOrAdmin,
|
||||||
|
create: ({ req }) => req.user?.role === 'superadmin',
|
||||||
|
update: isSelfOrAdmin,
|
||||||
|
delete: ({ req }) => req.user?.role === 'superadmin'
|
||||||
|
},
|
||||||
|
|
||||||
|
auth: true,
|
||||||
|
|
||||||
|
fields: [
|
||||||
|
{
|
||||||
|
name: 'firstName',
|
||||||
|
label: 'Vorname',
|
||||||
|
type: 'text',
|
||||||
|
required: true
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: 'lastName',
|
||||||
|
label: 'Nachname',
|
||||||
|
type: 'text',
|
||||||
|
required: true
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: 'role',
|
||||||
|
label: 'Rolle',
|
||||||
|
type: 'select',
|
||||||
|
required: true,
|
||||||
|
access: {
|
||||||
|
update: ({ req }) => req.user?.role === 'superadmin'
|
||||||
|
},
|
||||||
|
options: [
|
||||||
|
{
|
||||||
|
label: 'Super-Admin (GSH)',
|
||||||
|
value: 'superadmin'
|
||||||
|
},
|
||||||
|
{
|
||||||
|
label: 'Administrator',
|
||||||
|
value: 'admin'
|
||||||
|
}
|
||||||
|
]
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: 'fullName',
|
||||||
|
label: 'Name',
|
||||||
|
type: 'text',
|
||||||
|
admin: {
|
||||||
|
hidden: true,
|
||||||
|
readOnly: true
|
||||||
|
},
|
||||||
|
hooks: {
|
||||||
|
beforeChange: [
|
||||||
|
({ data }) => `${data?.firstName} ${data?.lastName}`
|
||||||
|
]
|
||||||
|
}
|
||||||
|
}
|
||||||
|
]
|
||||||
|
};
|
||||||
@@ -0,0 +1,29 @@
|
|||||||
|
'use client';
|
||||||
|
|
||||||
|
import { useRouter } from 'next/navigation';
|
||||||
|
|
||||||
|
export default function EndPreviewButton() {
|
||||||
|
const router = useRouter();
|
||||||
|
|
||||||
|
const endPreview = () => {
|
||||||
|
// Cookie entfernen
|
||||||
|
document.cookie = 'payload-preview=; Max-Age=0; Path=/';
|
||||||
|
|
||||||
|
// ?preview entfernen
|
||||||
|
const url = new URL(window.location.href);
|
||||||
|
url.searchParams.delete('preview');
|
||||||
|
|
||||||
|
// Neuladen
|
||||||
|
router.replace(url.pathname + url.search);
|
||||||
|
router.refresh();
|
||||||
|
};
|
||||||
|
|
||||||
|
return (
|
||||||
|
<button
|
||||||
|
className="fixed bottom-0 left-0 bg-red-500 rounded-md p-2 m-2 text-sm text-white cursor-pointer"
|
||||||
|
onClick={endPreview}
|
||||||
|
>
|
||||||
|
Preview beenden
|
||||||
|
</button>
|
||||||
|
);
|
||||||
|
}
|
||||||
@@ -0,0 +1,38 @@
|
|||||||
|
'use client';
|
||||||
|
|
||||||
|
import Image from 'next/image';
|
||||||
|
import { Button } from '@payloadcms/ui';
|
||||||
|
|
||||||
|
export function Logo() {
|
||||||
|
return (
|
||||||
|
<Image
|
||||||
|
src="/logo.png"
|
||||||
|
width={7845}
|
||||||
|
height={956}
|
||||||
|
alt=""
|
||||||
|
/>
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
export function Icon() {
|
||||||
|
return (
|
||||||
|
<Image
|
||||||
|
src="/icon.png"
|
||||||
|
width={836}
|
||||||
|
height={956}
|
||||||
|
alt=""
|
||||||
|
/>
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
export function Action() {
|
||||||
|
function goToSite() {
|
||||||
|
window.location.href = window.location.origin;
|
||||||
|
}
|
||||||
|
|
||||||
|
return (
|
||||||
|
<Button onClick={goToSite}>
|
||||||
|
Zur Website wechseln
|
||||||
|
</Button>
|
||||||
|
);
|
||||||
|
}
|
||||||
@@ -0,0 +1,15 @@
|
|||||||
|
'use client';
|
||||||
|
|
||||||
|
import { useRouter } from 'next/navigation';
|
||||||
|
import { RefreshRouteOnSave as PayloadLivePreview } from '@payloadcms/live-preview-react';
|
||||||
|
|
||||||
|
export default function RefreshRouteOnSave() {
|
||||||
|
const router = useRouter();
|
||||||
|
|
||||||
|
return (
|
||||||
|
<PayloadLivePreview
|
||||||
|
refresh={() => router.refresh()}
|
||||||
|
serverURL={process.env.NEXT_PUBLIC_PAYLOAD_URL ?? ''}
|
||||||
|
/>
|
||||||
|
);
|
||||||
|
}
|
||||||
@@ -0,0 +1,35 @@
|
|||||||
|
import { cookies, headers } from 'next/headers';
|
||||||
|
import { getPayload, type Where } from 'payload';
|
||||||
|
import config from '@payload-config';
|
||||||
|
|
||||||
|
|
||||||
|
export async function getPreview() {
|
||||||
|
const cookieStore = await cookies();
|
||||||
|
|
||||||
|
let isPreview = cookieStore.get('payload-preview')?.value !== undefined;
|
||||||
|
|
||||||
|
if (isPreview) {
|
||||||
|
const payload = await getPayload({ config });
|
||||||
|
|
||||||
|
const headersList = await headers();
|
||||||
|
|
||||||
|
const { user } = await payload.auth({
|
||||||
|
headers: headersList
|
||||||
|
});
|
||||||
|
|
||||||
|
if (!user)
|
||||||
|
isPreview = false;
|
||||||
|
}
|
||||||
|
|
||||||
|
const where: Where = {};
|
||||||
|
|
||||||
|
if (!isPreview)
|
||||||
|
where._status = {
|
||||||
|
equals: 'published'
|
||||||
|
};
|
||||||
|
|
||||||
|
return {
|
||||||
|
isPreview,
|
||||||
|
where
|
||||||
|
};
|
||||||
|
};
|
||||||
+2
-1
@@ -1,8 +1,9 @@
|
|||||||
import { type NextConfig } from 'next';
|
import { type NextConfig } from 'next';
|
||||||
|
import { withPayload } from '@payloadcms/next/withPayload';
|
||||||
|
|
||||||
const nextConfig: NextConfig = {
|
const nextConfig: NextConfig = {
|
||||||
output: 'standalone',
|
output: 'standalone',
|
||||||
poweredByHeader: false
|
poweredByHeader: false
|
||||||
};
|
};
|
||||||
|
|
||||||
export default nextConfig;
|
export default withPayload(nextConfig);
|
||||||
Generated
+6292
-209
File diff suppressed because it is too large
Load Diff
+8
-1
@@ -9,9 +9,16 @@
|
|||||||
"lint": "eslint"
|
"lint": "eslint"
|
||||||
},
|
},
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
|
"@payloadcms/db-postgres": "^3.88.0",
|
||||||
|
"@payloadcms/db-sqlite": "^3.88.0",
|
||||||
|
"@payloadcms/email-nodemailer": "^3.88.0",
|
||||||
|
"@payloadcms/live-preview-react": "^3.88.0",
|
||||||
|
"@payloadcms/next": "^3.88.0",
|
||||||
|
"@payloadcms/richtext-lexical": "^3.88.0",
|
||||||
"next": "16.2.10",
|
"next": "16.2.10",
|
||||||
"react": "19.2.4",
|
"react": "19.2.4",
|
||||||
"react-dom": "19.2.4"
|
"react-dom": "19.2.4",
|
||||||
|
"sharp": "^0.35.3"
|
||||||
},
|
},
|
||||||
"devDependencies": {
|
"devDependencies": {
|
||||||
"@tailwindcss/postcss": "^4",
|
"@tailwindcss/postcss": "^4",
|
||||||
|
|||||||
@@ -0,0 +1,83 @@
|
|||||||
|
import sharp from 'sharp';
|
||||||
|
import { lexicalEditor } from '@payloadcms/richtext-lexical';
|
||||||
|
import { postgresAdapter } from '@payloadcms/db-postgres';
|
||||||
|
import { sqliteAdapter } from '@payloadcms/db-sqlite';
|
||||||
|
import { nodemailerAdapter } from '@payloadcms/email-nodemailer';
|
||||||
|
import { buildConfig } from 'payload';
|
||||||
|
import { de } from '@payloadcms/translations/languages/de';
|
||||||
|
import { Users } from '@/collections/Users';
|
||||||
|
import { Media } from '@/collections/Media';
|
||||||
|
import { Posts } from '@/collections/Posts';
|
||||||
|
|
||||||
|
export default buildConfig({
|
||||||
|
admin: {
|
||||||
|
user: 'users',
|
||||||
|
avatar: 'default',
|
||||||
|
meta: {
|
||||||
|
icons: [
|
||||||
|
{
|
||||||
|
rel: 'icon',
|
||||||
|
url: '/favicon.ico'
|
||||||
|
}
|
||||||
|
]
|
||||||
|
},
|
||||||
|
components: {
|
||||||
|
graphics: {
|
||||||
|
Logo: '/components/PayloadInjects#Logo',
|
||||||
|
Icon: '/components/PayloadInjects#Icon'
|
||||||
|
},
|
||||||
|
actions: [ '/components/PayloadInjects#Action' ]
|
||||||
|
},
|
||||||
|
|
||||||
|
livePreview: {
|
||||||
|
url: ({ collectionConfig, data }) => {
|
||||||
|
if (collectionConfig?.slug === 'posts')
|
||||||
|
return `${process.env.NEXT_PUBLIC_PAYLOAD_URL}/post/${data.slug}/?preview`;
|
||||||
|
|
||||||
|
return process.env.NEXT_PUBLIC_PAYLOAD_URL;
|
||||||
|
},
|
||||||
|
collections: [ 'posts' ]
|
||||||
|
}
|
||||||
|
},
|
||||||
|
|
||||||
|
i18n: {
|
||||||
|
supportedLanguages: { de }
|
||||||
|
},
|
||||||
|
|
||||||
|
editor: lexicalEditor(),
|
||||||
|
|
||||||
|
collections: [
|
||||||
|
Users,
|
||||||
|
Media,
|
||||||
|
Posts
|
||||||
|
],
|
||||||
|
|
||||||
|
secret: process.env.PAYLOAD_SECRET || '',
|
||||||
|
|
||||||
|
// db: postgresAdapter({
|
||||||
|
// pool: {
|
||||||
|
// connectionString: process.env.DATABASE_URL
|
||||||
|
// }
|
||||||
|
// }),
|
||||||
|
db: sqliteAdapter({
|
||||||
|
client: {
|
||||||
|
url: process.env.DATABASE_URL || '',
|
||||||
|
authToken: process.env.DATABASE_AUTH_TOKEN
|
||||||
|
}
|
||||||
|
}),
|
||||||
|
|
||||||
|
email: nodemailerAdapter({
|
||||||
|
defaultFromAddress: process.env.MAIL_USER ?? '',
|
||||||
|
defaultFromName: process.env.MAIL_DISPLAYNAME ?? '',
|
||||||
|
transportOptions: {
|
||||||
|
host: process.env.MAIL_HOST,
|
||||||
|
port: process.env.MAIL_PORT,
|
||||||
|
auth: {
|
||||||
|
user: process.env.MAIL_USER,
|
||||||
|
pass: process.env.MAIL_PASSWORD
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}),
|
||||||
|
|
||||||
|
sharp
|
||||||
|
});
|
||||||
@@ -0,0 +1,18 @@
|
|||||||
|
import { NextResponse, type NextRequest } from 'next/server';
|
||||||
|
|
||||||
|
export async function proxy(request: NextRequest) {
|
||||||
|
const isPreview = request.nextUrl.searchParams.get('preview') !== null;
|
||||||
|
|
||||||
|
if (!isPreview)
|
||||||
|
return NextResponse.next();
|
||||||
|
|
||||||
|
// ?preview entfernen
|
||||||
|
const url = request.nextUrl.clone();
|
||||||
|
url.searchParams.delete('preview');
|
||||||
|
const response = NextResponse.redirect(url);
|
||||||
|
|
||||||
|
// Cookie hinzufügen
|
||||||
|
response.cookies.set('payload-preview', '1');
|
||||||
|
|
||||||
|
return response;
|
||||||
|
}
|
||||||
Binary file not shown.
|
After Width: | Height: | Size: 15 KiB |
Binary file not shown.
|
After Width: | Height: | Size: 51 KiB |
Binary file not shown.
|
After Width: | Height: | Size: 275 KiB |
+2
-1
@@ -19,7 +19,8 @@
|
|||||||
}
|
}
|
||||||
],
|
],
|
||||||
"paths": {
|
"paths": {
|
||||||
"@/*": ["./*"]
|
"@/*": ["./*"],
|
||||||
|
"@payload-config": ["./payload.config.ts"]
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"include": [
|
"include": [
|
||||||
|
|||||||
Reference in New Issue
Block a user