@nitrogenbuilder/connector-payload 0.1.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +101 -0
- package/dist/collection-registry.d.ts +7 -0
- package/dist/collection-registry.js +12 -0
- package/dist/collections/NitrogenTemplates.d.ts +2 -0
- package/dist/collections/NitrogenTemplates.js +78 -0
- package/dist/components/NitrogenEditButton.d.ts +4 -0
- package/dist/components/NitrogenEditButton.js +43 -0
- package/dist/editor/NitrogenEditorLayout.d.ts +15 -0
- package/dist/editor/NitrogenEditorLayout.js +15 -0
- package/dist/editor/NitrogenEditorPage.d.ts +20 -0
- package/dist/editor/NitrogenEditorPage.js +87 -0
- package/dist/editor/index.d.ts +2 -0
- package/dist/editor/index.js +2 -0
- package/dist/endpoints/all.d.ts +2 -0
- package/dist/endpoints/all.js +48 -0
- package/dist/endpoints/collection-endpoints.d.ts +8 -0
- package/dist/endpoints/collection-endpoints.js +168 -0
- package/dist/endpoints/helpers.d.ts +64 -0
- package/dist/endpoints/helpers.js +104 -0
- package/dist/endpoints/media.d.ts +2 -0
- package/dist/endpoints/media.js +93 -0
- package/dist/endpoints/menu.d.ts +2 -0
- package/dist/endpoints/menu.js +19 -0
- package/dist/endpoints/nitrogen-settings.d.ts +2 -0
- package/dist/endpoints/nitrogen-settings.js +22 -0
- package/dist/endpoints/templates.d.ts +2 -0
- package/dist/endpoints/templates.js +114 -0
- package/dist/frontend/NitrogenPageClient.d.ts +16 -0
- package/dist/frontend/NitrogenPageClient.js +55 -0
- package/dist/frontend/NitrogenWrapper.d.ts +45 -0
- package/dist/frontend/NitrogenWrapper.js +38 -0
- package/dist/frontend/index.d.ts +7 -0
- package/dist/frontend/index.js +8 -0
- package/dist/globals/NitrogenSettings.d.ts +2 -0
- package/dist/globals/NitrogenSettings.js +41 -0
- package/dist/index.d.ts +21 -0
- package/dist/index.js +140 -0
- package/dist/types.d.ts +125 -0
- package/dist/types.js +7 -0
- package/package.json +49 -0
package/README.md
ADDED
|
@@ -0,0 +1,101 @@
|
|
|
1
|
+
# Nitrogen Connector Payload
|
|
2
|
+
|
|
3
|
+
A Payload CMS plugin for visual page building with Nitrogen.
|
|
4
|
+
|
|
5
|
+
## Setup
|
|
6
|
+
|
|
7
|
+
This project uses the `nitrogen-connector-payload` plugin for visual page building with Nitrogen. Here's how to install it in a fresh Payload CMS project:
|
|
8
|
+
|
|
9
|
+
### 1. Install Dependencies
|
|
10
|
+
|
|
11
|
+
```bash
|
|
12
|
+
pnpm add nitrogen-connector-payload //This is not done yet. It's dev only for now.
|
|
13
|
+
pnpm add @nitrogenbuilder/client-react @nitrogenbuilder/client-core @nitrogenbuilder/types
|
|
14
|
+
```
|
|
15
|
+
|
|
16
|
+
### 2. Add Plugin to Payload Config
|
|
17
|
+
|
|
18
|
+
In your `src/plugins/index.ts` (or wherever you configure plugins):
|
|
19
|
+
|
|
20
|
+
```ts
|
|
21
|
+
import { nitrogenConnectorPlugin } from "nitrogen-connector-payload"; //This is not done yet. For Dev: "link:../nitrogen-connector-payload"
|
|
22
|
+
|
|
23
|
+
export const plugins: Plugin[] = [
|
|
24
|
+
nitrogenConnectorPlugin({
|
|
25
|
+
collections: ["pages", "posts"], // Collections to enable Nitrogen editing on
|
|
26
|
+
}),
|
|
27
|
+
// ... other plugins
|
|
28
|
+
];
|
|
29
|
+
```
|
|
30
|
+
|
|
31
|
+
### 3. Wrap Your Page Routes
|
|
32
|
+
|
|
33
|
+
In your page route files (e.g., `src/app/(frontend)/[slug]/page.tsx`), wrap your content with `NitrogenWrapper`:
|
|
34
|
+
|
|
35
|
+
```tsx
|
|
36
|
+
import { NitrogenWrapper } from 'nitrogen-connector-payload/frontend'
|
|
37
|
+
|
|
38
|
+
export default async function Page({ params, searchParams }) {
|
|
39
|
+
const page = await queryPage(...)
|
|
40
|
+
const search = await searchParams
|
|
41
|
+
|
|
42
|
+
return (
|
|
43
|
+
<NitrogenWrapper page={page} searchParams={search} collection="pages">
|
|
44
|
+
{/* Your regular page content */}
|
|
45
|
+
<RenderHero {...page.hero} />
|
|
46
|
+
<RenderBlocks blocks={page.layout} />
|
|
47
|
+
</NitrogenWrapper>
|
|
48
|
+
)
|
|
49
|
+
}
|
|
50
|
+
```
|
|
51
|
+
|
|
52
|
+
That's it! The `NitrogenWrapper` component automatically:
|
|
53
|
+
|
|
54
|
+
- Detects when the Nitrogen editor is requesting the page (via `?nitrogen-builder` query param)
|
|
55
|
+
- Checks if the page has Nitrogen data
|
|
56
|
+
- Renders the Nitrogen visual builder when needed, otherwise renders your regular content
|
|
57
|
+
|
|
58
|
+
### Adding Custom Components
|
|
59
|
+
|
|
60
|
+
To register custom Nitrogen components, create a client component:
|
|
61
|
+
|
|
62
|
+
```tsx
|
|
63
|
+
// src/components/NitrogenComponents.tsx
|
|
64
|
+
"use client";
|
|
65
|
+
|
|
66
|
+
import { nitrogen } from "nitrogen-connector-payload/frontend";
|
|
67
|
+
import type {
|
|
68
|
+
ComponentSettings,
|
|
69
|
+
ComponentSettingsToProps,
|
|
70
|
+
} from "nitrogen-connector-payload/frontend";
|
|
71
|
+
import MyComponent from "./MyComponent";
|
|
72
|
+
|
|
73
|
+
const myComponentSettings = {
|
|
74
|
+
categories: {
|
|
75
|
+
content: {
|
|
76
|
+
label: "Content",
|
|
77
|
+
groups: {
|
|
78
|
+
content: {
|
|
79
|
+
label: "Content",
|
|
80
|
+
props: {
|
|
81
|
+
title: { type: "string", default: "Hello" },
|
|
82
|
+
},
|
|
83
|
+
},
|
|
84
|
+
},
|
|
85
|
+
},
|
|
86
|
+
},
|
|
87
|
+
} as const satisfies ComponentSettings;
|
|
88
|
+
|
|
89
|
+
// Register on module load
|
|
90
|
+
nitrogen.registerModule("my-component", MyComponent, myComponentSettings);
|
|
91
|
+
|
|
92
|
+
export {};
|
|
93
|
+
```
|
|
94
|
+
|
|
95
|
+
Then import it in your page route:
|
|
96
|
+
|
|
97
|
+
```tsx
|
|
98
|
+
import "@/components/NitrogenComponents";
|
|
99
|
+
```
|
|
100
|
+
|
|
101
|
+
---
|
|
@@ -0,0 +1,7 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Runtime registry of collections configured for Nitrogen editing.
|
|
3
|
+
*
|
|
4
|
+
* The plugin registers collections at init time via `registerCollection()`.
|
|
5
|
+
*/
|
|
6
|
+
export declare function registerCollection(alias: string, collectionSlug: string): void;
|
|
7
|
+
export declare function resolveCollection(postType: string): string;
|
|
@@ -0,0 +1,12 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Runtime registry of collections configured for Nitrogen editing.
|
|
3
|
+
*
|
|
4
|
+
* The plugin registers collections at init time via `registerCollection()`.
|
|
5
|
+
*/
|
|
6
|
+
const registeredCollections = new Map();
|
|
7
|
+
export function registerCollection(alias, collectionSlug) {
|
|
8
|
+
registeredCollections.set(alias, collectionSlug);
|
|
9
|
+
}
|
|
10
|
+
export function resolveCollection(postType) {
|
|
11
|
+
return registeredCollections.get(postType) || postType;
|
|
12
|
+
}
|
|
@@ -0,0 +1,78 @@
|
|
|
1
|
+
export const NitrogenTemplates = {
|
|
2
|
+
slug: 'nitrogen-templates',
|
|
3
|
+
admin: {
|
|
4
|
+
useAsTitle: 'title',
|
|
5
|
+
defaultColumns: ['title', 'associatedCollection', 'updatedAt'],
|
|
6
|
+
components: {
|
|
7
|
+
edit: {
|
|
8
|
+
beforeDocumentControls: [
|
|
9
|
+
{
|
|
10
|
+
path: 'nitrogen-connector-payload/components/NitrogenEditButton#NitrogenEditButton',
|
|
11
|
+
clientProps: {
|
|
12
|
+
collection: 'nitrogen-templates',
|
|
13
|
+
},
|
|
14
|
+
},
|
|
15
|
+
],
|
|
16
|
+
},
|
|
17
|
+
},
|
|
18
|
+
},
|
|
19
|
+
access: {
|
|
20
|
+
read: () => true,
|
|
21
|
+
create: ({ req }) => !!req.user,
|
|
22
|
+
update: ({ req }) => !!req.user,
|
|
23
|
+
delete: ({ req }) => !!req.user,
|
|
24
|
+
},
|
|
25
|
+
fields: [
|
|
26
|
+
{
|
|
27
|
+
name: 'title',
|
|
28
|
+
type: 'text',
|
|
29
|
+
required: true,
|
|
30
|
+
},
|
|
31
|
+
{
|
|
32
|
+
name: 'slug',
|
|
33
|
+
type: 'text',
|
|
34
|
+
required: true,
|
|
35
|
+
unique: true,
|
|
36
|
+
},
|
|
37
|
+
{
|
|
38
|
+
name: 'status',
|
|
39
|
+
type: 'select',
|
|
40
|
+
options: [
|
|
41
|
+
{ label: 'Draft', value: 'draft' },
|
|
42
|
+
{ label: 'Published', value: 'published' },
|
|
43
|
+
],
|
|
44
|
+
defaultValue: 'draft',
|
|
45
|
+
admin: {
|
|
46
|
+
position: 'sidebar',
|
|
47
|
+
},
|
|
48
|
+
},
|
|
49
|
+
{
|
|
50
|
+
name: 'templateType',
|
|
51
|
+
type: 'text',
|
|
52
|
+
admin: {
|
|
53
|
+
description: 'Template type identifier for filtering',
|
|
54
|
+
},
|
|
55
|
+
},
|
|
56
|
+
{
|
|
57
|
+
name: 'associatedCollection',
|
|
58
|
+
type: 'text',
|
|
59
|
+
admin: {
|
|
60
|
+
description: 'Which collection this template applies to (e.g., "posts", "header", "footer")',
|
|
61
|
+
},
|
|
62
|
+
},
|
|
63
|
+
{
|
|
64
|
+
name: 'nitrogenData',
|
|
65
|
+
type: 'json',
|
|
66
|
+
admin: {
|
|
67
|
+
description: 'Nitrogen page builder JSON module tree',
|
|
68
|
+
},
|
|
69
|
+
},
|
|
70
|
+
{
|
|
71
|
+
name: 'templateSettings',
|
|
72
|
+
type: 'json',
|
|
73
|
+
admin: {
|
|
74
|
+
description: 'Template-level Nitrogen settings',
|
|
75
|
+
},
|
|
76
|
+
},
|
|
77
|
+
],
|
|
78
|
+
};
|
|
@@ -0,0 +1,43 @@
|
|
|
1
|
+
"use client";
|
|
2
|
+
import { jsx as _jsx } from "react/jsx-runtime";
|
|
3
|
+
import { useState, useEffect } from "react";
|
|
4
|
+
export const NitrogenEditButton = ({ collection }) => {
|
|
5
|
+
const [id, setId] = useState(undefined);
|
|
6
|
+
const [token, setToken] = useState(undefined);
|
|
7
|
+
useEffect(() => {
|
|
8
|
+
const segments = window.location.pathname.split("/").filter(Boolean);
|
|
9
|
+
// segments: ["admin", "collections", "{collection-slug}", "{id}"]
|
|
10
|
+
if (segments.length >= 4) {
|
|
11
|
+
setId(segments[3]);
|
|
12
|
+
}
|
|
13
|
+
// Fetch the connector token from NitrogenSettings global
|
|
14
|
+
fetch("/api/globals/nitrogen-settings")
|
|
15
|
+
.then((res) => res.json())
|
|
16
|
+
.then((data) => {
|
|
17
|
+
if (data.connectorToken) {
|
|
18
|
+
setToken(data.connectorToken);
|
|
19
|
+
}
|
|
20
|
+
})
|
|
21
|
+
.catch((err) => {
|
|
22
|
+
console.error("Failed to fetch nitrogen settings:", err);
|
|
23
|
+
});
|
|
24
|
+
}, []);
|
|
25
|
+
if (!id || !token)
|
|
26
|
+
return null;
|
|
27
|
+
// Determine which query param to use based on collection type
|
|
28
|
+
const param = collection === "nitrogen-templates" ? "templateId" : "pageId";
|
|
29
|
+
// Always pass the collection slug so the editor knows exactly which collection to query.
|
|
30
|
+
const href = `/nitrogen-editor?token=${encodeURIComponent(token)}&collection=${encodeURIComponent(collection)}&${param}=${id}`;
|
|
31
|
+
return (_jsx("a", { href: href, target: "_blank", rel: "noopener noreferrer", style: {
|
|
32
|
+
display: "inline-flex",
|
|
33
|
+
alignItems: "center",
|
|
34
|
+
gap: "8px",
|
|
35
|
+
padding: "8px 16px",
|
|
36
|
+
background: "#6366f1",
|
|
37
|
+
color: "#fff",
|
|
38
|
+
borderRadius: "4px",
|
|
39
|
+
textDecoration: "none",
|
|
40
|
+
fontSize: "14px",
|
|
41
|
+
fontWeight: 500,
|
|
42
|
+
}, children: "Edit with Nitrogen" }));
|
|
43
|
+
};
|
|
@@ -0,0 +1,15 @@
|
|
|
1
|
+
import React from 'react';
|
|
2
|
+
/**
|
|
3
|
+
* Root layout for the Nitrogen editor route group.
|
|
4
|
+
* Provides <html>, <head>, and <body> so the editor page
|
|
5
|
+
* doesn't conflict with Next.js's layout requirements.
|
|
6
|
+
*
|
|
7
|
+
* Mount this as the route-group root layout, e.g.
|
|
8
|
+
* `app/(nitrogen-editor)/layout.tsx`:
|
|
9
|
+
*
|
|
10
|
+
* import { NitrogenEditorLayout } from 'nitrogen-connector-payload/editor'
|
|
11
|
+
* export default NitrogenEditorLayout
|
|
12
|
+
*/
|
|
13
|
+
export default function NitrogenEditorLayout({ children, }: {
|
|
14
|
+
children: React.ReactNode;
|
|
15
|
+
}): import("react/jsx-runtime").JSX.Element;
|
|
@@ -0,0 +1,15 @@
|
|
|
1
|
+
import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
|
|
2
|
+
/**
|
|
3
|
+
* Root layout for the Nitrogen editor route group.
|
|
4
|
+
* Provides <html>, <head>, and <body> so the editor page
|
|
5
|
+
* doesn't conflict with Next.js's layout requirements.
|
|
6
|
+
*
|
|
7
|
+
* Mount this as the route-group root layout, e.g.
|
|
8
|
+
* `app/(nitrogen-editor)/layout.tsx`:
|
|
9
|
+
*
|
|
10
|
+
* import { NitrogenEditorLayout } from 'nitrogen-connector-payload/editor'
|
|
11
|
+
* export default NitrogenEditorLayout
|
|
12
|
+
*/
|
|
13
|
+
export default function NitrogenEditorLayout({ children, }) {
|
|
14
|
+
return (_jsxs("html", { lang: "en", suppressHydrationWarning: true, children: [_jsxs("head", { children: [_jsx("meta", { charSet: "UTF-8" }), _jsx("meta", { name: "viewport", content: "width=device-width, initial-scale=1.0" }), _jsx("title", { children: "Nitrogen Editor" }), _jsx("link", { rel: "preconnect", href: "https://fonts.googleapis.com" }), _jsx("link", { rel: "preconnect", href: "https://fonts.gstatic.com", crossOrigin: "" }), _jsx("link", { href: "https://fonts.googleapis.com/css2?family=Roboto:wght@300;400;500;700&display=swap", rel: "stylesheet" })] }), _jsx("body", { suppressHydrationWarning: true, children: children })] }));
|
|
15
|
+
}
|
|
@@ -0,0 +1,20 @@
|
|
|
1
|
+
interface NitrogenEditorSearchParams {
|
|
2
|
+
pageId?: string;
|
|
3
|
+
templateId?: string;
|
|
4
|
+
collection?: string;
|
|
5
|
+
development?: string;
|
|
6
|
+
}
|
|
7
|
+
/**
|
|
8
|
+
* Standalone editor page that loads the Nitrogen builder.
|
|
9
|
+
*
|
|
10
|
+
* Mount this in your Next.js app at e.g. `app/(app)/nitrogen-editor/page.tsx`:
|
|
11
|
+
*
|
|
12
|
+
* import { NitrogenEditorPage } from 'nitrogen-connector-payload/editor'
|
|
13
|
+
* export default NitrogenEditorPage
|
|
14
|
+
*
|
|
15
|
+
* Accessible at `/nitrogen-editor?pageId=xxx` or `/nitrogen-editor?templateId=xxx`.
|
|
16
|
+
*/
|
|
17
|
+
export default function NitrogenEditorPage({ searchParams, }: {
|
|
18
|
+
searchParams: Promise<NitrogenEditorSearchParams>;
|
|
19
|
+
}): Promise<import("react/jsx-runtime").JSX.Element>;
|
|
20
|
+
export {};
|
|
@@ -0,0 +1,87 @@
|
|
|
1
|
+
import { jsx as _jsx, Fragment as _Fragment, jsxs as _jsxs } from "react/jsx-runtime";
|
|
2
|
+
import { getPayload } from 'payload';
|
|
3
|
+
import { headers } from 'next/headers';
|
|
4
|
+
import { redirect } from 'next/navigation';
|
|
5
|
+
/**
|
|
6
|
+
* Standalone editor page that loads the Nitrogen builder.
|
|
7
|
+
*
|
|
8
|
+
* Mount this in your Next.js app at e.g. `app/(app)/nitrogen-editor/page.tsx`:
|
|
9
|
+
*
|
|
10
|
+
* import { NitrogenEditorPage } from 'nitrogen-connector-payload/editor'
|
|
11
|
+
* export default NitrogenEditorPage
|
|
12
|
+
*
|
|
13
|
+
* Accessible at `/nitrogen-editor?pageId=xxx` or `/nitrogen-editor?templateId=xxx`.
|
|
14
|
+
*/
|
|
15
|
+
export default async function NitrogenEditorPage({ searchParams, }) {
|
|
16
|
+
// Dynamic import so consumers provide their own @payload-config
|
|
17
|
+
const payloadConfig = (await import('@payload-config')).default;
|
|
18
|
+
const payload = await getPayload({ config: payloadConfig });
|
|
19
|
+
const params = await searchParams;
|
|
20
|
+
// Authenticate — redirect to login if not authenticated
|
|
21
|
+
const headersList = await headers();
|
|
22
|
+
const { user } = await payload.auth({ headers: headersList });
|
|
23
|
+
if (!user) {
|
|
24
|
+
return redirect('/admin/login');
|
|
25
|
+
}
|
|
26
|
+
// Load global settings
|
|
27
|
+
const settings = (await payload.findGlobal({
|
|
28
|
+
slug: 'nitrogen-settings',
|
|
29
|
+
}));
|
|
30
|
+
const isDevelopment = params.development === 'true';
|
|
31
|
+
const siteUrl = isDevelopment ? settings.developmentUrl : settings.frontendUrl;
|
|
32
|
+
const nitrogenConfig = settings.nitrogenConfig || {};
|
|
33
|
+
// Determine the postType for the editor's API calls
|
|
34
|
+
let postType;
|
|
35
|
+
if (params.collection) {
|
|
36
|
+
postType = params.collection;
|
|
37
|
+
}
|
|
38
|
+
else if (params.templateId) {
|
|
39
|
+
postType = 'nitrogen-templates';
|
|
40
|
+
}
|
|
41
|
+
else {
|
|
42
|
+
return redirect('/admin');
|
|
43
|
+
}
|
|
44
|
+
// Build the config object the builder reads from window.nitrogenConfig
|
|
45
|
+
const builderConfig = {
|
|
46
|
+
licenseKey: settings.licenseKey || '',
|
|
47
|
+
provider: {
|
|
48
|
+
type: 'payload',
|
|
49
|
+
apiUrl: '/api/nitrogen/v1',
|
|
50
|
+
collection: postType,
|
|
51
|
+
},
|
|
52
|
+
siteUrl: siteUrl || '',
|
|
53
|
+
urlMaps: nitrogenConfig.urlMaps || [],
|
|
54
|
+
wysiwygColors: nitrogenConfig.wysiwygColors || {},
|
|
55
|
+
cssInjection: nitrogenConfig.cssInjection || '',
|
|
56
|
+
};
|
|
57
|
+
const editId = params.templateId || params.pageId || '';
|
|
58
|
+
const isEditorDev = process.env.NITROGEN_EDITOR_DEV === 'true';
|
|
59
|
+
const viteOrigin = 'http://localhost:5173';
|
|
60
|
+
const cdnBase = 'https://cdn.jsdelivr.net/npm/@nitrogenbuilder/editor@0.4';
|
|
61
|
+
return (_jsxs(_Fragment, { children: [isEditorDev ? (_jsxs(_Fragment, { children: [_jsx("link", { rel: "stylesheet", crossOrigin: "", href: `${viteOrigin}/fa620pro/css/all.css` }), _jsx("link", { rel: "stylesheet", crossOrigin: "", href: `${viteOrigin}/fa620pro/css/sharp-solid.css` }), _jsx("link", { rel: "stylesheet", crossOrigin: "", href: `${viteOrigin}/src/index.scss` })] })) : (_jsxs(_Fragment, { children: [_jsx("link", { rel: "stylesheet", crossOrigin: "", href: `${cdnBase}/fa620pro/css/all.css` }), _jsx("link", { rel: "stylesheet", crossOrigin: "", href: `${cdnBase}/fa620pro/css/sharp-solid.css` }), _jsx("link", { rel: "stylesheet", crossOrigin: "", href: `${cdnBase}/index.css` })] })), _jsx("script", { dangerouslySetInnerHTML: {
|
|
62
|
+
__html: [
|
|
63
|
+
`window.nitrogenConfig = ${JSON.stringify(builderConfig)};`,
|
|
64
|
+
`window.nitrogenEditId = "${editId}";`,
|
|
65
|
+
// Inject authorId into the URL so the editor can read it from window.location.search
|
|
66
|
+
`(function(){var u=new URL(window.location.href);if(!u.searchParams.has("authorId")){u.searchParams.set("authorId",${JSON.stringify(String(user.id))});window.history.replaceState(null,"",u.toString())}})();`,
|
|
67
|
+
].join(''),
|
|
68
|
+
} }), _jsxs("div", { id: "root", children: [_jsx("div", { style: {
|
|
69
|
+
position: 'fixed',
|
|
70
|
+
inset: 0,
|
|
71
|
+
zIndex: 9999999,
|
|
72
|
+
display: 'flex',
|
|
73
|
+
alignItems: 'center',
|
|
74
|
+
justifyContent: 'center',
|
|
75
|
+
backgroundColor: '#0F1214',
|
|
76
|
+
}, children: _jsx("img", { src: isEditorDev
|
|
77
|
+
? `${viteOrigin}/src/components/logo-white.svg`
|
|
78
|
+
: `${cdnBase}/logo-white.svg`, alt: "Loading\u2026", style: {
|
|
79
|
+
width: '24rem',
|
|
80
|
+
maxWidth: '100%',
|
|
81
|
+
animation: 'nitrogen-loader-pulse 3s ease-in-out infinite',
|
|
82
|
+
} }) }), _jsx("style", { dangerouslySetInnerHTML: {
|
|
83
|
+
__html: `@keyframes nitrogen-loader-pulse{0%{transform:scale(1);opacity:0}50%{opacity:1}100%{transform:scale(1.05);opacity:0}}`,
|
|
84
|
+
} })] }), isEditorDev ? (_jsxs(_Fragment, { children: [_jsx("script", { type: "module", dangerouslySetInnerHTML: {
|
|
85
|
+
__html: `import RefreshRuntime from "${viteOrigin}/@react-refresh";RefreshRuntime.injectIntoGlobalHook(window);window.$RefreshReg$ = () => {};window.$RefreshSig$ = () => (type) => type;window.__vite_plugin_react_preamble_installed__ = true;`,
|
|
86
|
+
} }), _jsx("script", { type: "module", src: `${viteOrigin}/@vite/client` }), _jsx("script", { type: "module", crossOrigin: "", src: `${viteOrigin}/src/main.tsx` })] })) : (_jsx("script", { type: "module", crossOrigin: "", src: `${cdnBase}/index.js` }))] }));
|
|
87
|
+
}
|
|
@@ -0,0 +1,48 @@
|
|
|
1
|
+
import { getNitrogenSettings } from './helpers';
|
|
2
|
+
import { resolveCollection } from '../collection-registry';
|
|
3
|
+
export const allEndpoints = [
|
|
4
|
+
// GET /api/nitrogen/v1/all — List all items with filtering
|
|
5
|
+
{
|
|
6
|
+
path: '/nitrogen/v1/all',
|
|
7
|
+
method: 'get',
|
|
8
|
+
handler: async (req) => {
|
|
9
|
+
const { payload } = req;
|
|
10
|
+
const settings = await getNitrogenSettings(payload);
|
|
11
|
+
const url = new URL(req.url || '', 'http://localhost');
|
|
12
|
+
const postType = url.searchParams.get('post_type') || '';
|
|
13
|
+
if (!postType) {
|
|
14
|
+
return Response.json({ error: 'post_type query parameter is required' }, { status: 400 });
|
|
15
|
+
}
|
|
16
|
+
const statusParam = url.searchParams.get('post_status') || 'published';
|
|
17
|
+
const paged = parseInt(url.searchParams.get('paged') || '1', 10);
|
|
18
|
+
const perPage = parseInt(url.searchParams.get('per_page') || '20', 10);
|
|
19
|
+
const statuses = statusParam.split(',').map((s) => s.trim());
|
|
20
|
+
const collection = resolveCollection(postType);
|
|
21
|
+
const where = {};
|
|
22
|
+
if (!statuses.includes('any')) {
|
|
23
|
+
where.status = { in: statuses };
|
|
24
|
+
}
|
|
25
|
+
try {
|
|
26
|
+
const result = await payload.find({
|
|
27
|
+
collection: collection,
|
|
28
|
+
where,
|
|
29
|
+
page: paged,
|
|
30
|
+
limit: perPage,
|
|
31
|
+
depth: 0,
|
|
32
|
+
});
|
|
33
|
+
const items = result.docs.map((doc) => ({
|
|
34
|
+
id: doc.id,
|
|
35
|
+
title: String(doc.title || ''),
|
|
36
|
+
slug: String(doc.slug || ''),
|
|
37
|
+
permalink: `${settings.frontendUrl || ''}/${String(doc.slug || '')}`,
|
|
38
|
+
relative_permalink: `/${String(doc.slug || '')}`,
|
|
39
|
+
type: collection,
|
|
40
|
+
}));
|
|
41
|
+
return Response.json(items);
|
|
42
|
+
}
|
|
43
|
+
catch {
|
|
44
|
+
return Response.json({ error: `Collection "${collection}" not found` }, { status: 400 });
|
|
45
|
+
}
|
|
46
|
+
},
|
|
47
|
+
},
|
|
48
|
+
];
|
|
@@ -0,0 +1,8 @@
|
|
|
1
|
+
import type { Endpoint } from 'payload';
|
|
2
|
+
/**
|
|
3
|
+
* Creates a complete set of CRUD endpoints for a given Payload collection.
|
|
4
|
+
*
|
|
5
|
+
* @param collectionSlug - The Payload collection slug to query (e.g., 'pages', 'posts')
|
|
6
|
+
* @param endpointPrefix - The URL path prefix (e.g., 'pages', 'posts')
|
|
7
|
+
*/
|
|
8
|
+
export declare function createCollectionEndpoints(collectionSlug: string, endpointPrefix: string): Endpoint[];
|
|
@@ -0,0 +1,168 @@
|
|
|
1
|
+
import { getNitrogenSettings, buildDynamicData, buildPageResponse, buildListItemResponse, requireAuth, } from './helpers';
|
|
2
|
+
/**
|
|
3
|
+
* Creates a complete set of CRUD endpoints for a given Payload collection.
|
|
4
|
+
*
|
|
5
|
+
* @param collectionSlug - The Payload collection slug to query (e.g., 'pages', 'posts')
|
|
6
|
+
* @param endpointPrefix - The URL path prefix (e.g., 'pages', 'posts')
|
|
7
|
+
*/
|
|
8
|
+
export function createCollectionEndpoints(collectionSlug, endpointPrefix) {
|
|
9
|
+
const collection = collectionSlug;
|
|
10
|
+
return [
|
|
11
|
+
// GET /api/nitrogen/v1/{prefix} — List items
|
|
12
|
+
{
|
|
13
|
+
path: `/nitrogen/v1/${endpointPrefix}`,
|
|
14
|
+
method: 'get',
|
|
15
|
+
handler: async (req) => {
|
|
16
|
+
const { payload } = req;
|
|
17
|
+
const settings = await getNitrogenSettings(payload);
|
|
18
|
+
const url = new URL(req.url || '', 'http://localhost');
|
|
19
|
+
const statusParam = url.searchParams.get('post_status') || 'published';
|
|
20
|
+
const statuses = statusParam.split(',').map((s) => s.trim());
|
|
21
|
+
const where = {};
|
|
22
|
+
if (!statuses.includes('any')) {
|
|
23
|
+
where.status = { in: statuses };
|
|
24
|
+
}
|
|
25
|
+
const result = await payload.find({
|
|
26
|
+
collection,
|
|
27
|
+
where,
|
|
28
|
+
limit: 0,
|
|
29
|
+
depth: 1,
|
|
30
|
+
});
|
|
31
|
+
const items = result.docs.map((doc) => buildListItemResponse(doc, settings));
|
|
32
|
+
return Response.json(items);
|
|
33
|
+
},
|
|
34
|
+
},
|
|
35
|
+
// POST /api/nitrogen/v1/{prefix} — Create a new item
|
|
36
|
+
{
|
|
37
|
+
path: `/nitrogen/v1/${endpointPrefix}`,
|
|
38
|
+
method: 'post',
|
|
39
|
+
handler: async (req) => {
|
|
40
|
+
const authError = requireAuth(req);
|
|
41
|
+
if (authError)
|
|
42
|
+
return authError;
|
|
43
|
+
const { payload } = req;
|
|
44
|
+
const body = (await req.json?.());
|
|
45
|
+
const data = body?.data || body;
|
|
46
|
+
const doc = await payload.create({
|
|
47
|
+
collection,
|
|
48
|
+
data: {
|
|
49
|
+
title: data?.title || 'Untitled',
|
|
50
|
+
slug: data?.slug ||
|
|
51
|
+
(data?.title || 'untitled')
|
|
52
|
+
.toLowerCase()
|
|
53
|
+
.replace(/[^a-z0-9]+/g, '-')
|
|
54
|
+
.replace(/(^-|-$)/g, ''),
|
|
55
|
+
author: data?.author || (req.user && 'id' in req.user ? req.user.id : undefined),
|
|
56
|
+
status: 'draft',
|
|
57
|
+
},
|
|
58
|
+
});
|
|
59
|
+
return Response.json({ data: { id: doc.id } });
|
|
60
|
+
},
|
|
61
|
+
},
|
|
62
|
+
// GET /api/nitrogen/v1/{prefix}/:id — Get a single item
|
|
63
|
+
{
|
|
64
|
+
path: `/nitrogen/v1/${endpointPrefix}/:id`,
|
|
65
|
+
method: 'get',
|
|
66
|
+
handler: async (req) => {
|
|
67
|
+
const { payload, routeParams } = req;
|
|
68
|
+
const id = routeParams?.id;
|
|
69
|
+
try {
|
|
70
|
+
const doc = (await payload.findByID({
|
|
71
|
+
collection,
|
|
72
|
+
id,
|
|
73
|
+
depth: 1,
|
|
74
|
+
}));
|
|
75
|
+
const settings = await getNitrogenSettings(payload);
|
|
76
|
+
const dynamicData = buildDynamicData(doc, settings);
|
|
77
|
+
return Response.json(buildPageResponse(doc, settings, dynamicData));
|
|
78
|
+
}
|
|
79
|
+
catch {
|
|
80
|
+
return Response.json({ error: 'Not found' }, { status: 404 });
|
|
81
|
+
}
|
|
82
|
+
},
|
|
83
|
+
},
|
|
84
|
+
// PATCH /api/nitrogen/v1/{prefix}/:id — Update an item
|
|
85
|
+
{
|
|
86
|
+
path: `/nitrogen/v1/${endpointPrefix}/:id`,
|
|
87
|
+
method: 'patch',
|
|
88
|
+
handler: async (req) => {
|
|
89
|
+
const authError = requireAuth(req);
|
|
90
|
+
if (authError)
|
|
91
|
+
return authError;
|
|
92
|
+
const { payload, routeParams } = req;
|
|
93
|
+
const id = routeParams?.id;
|
|
94
|
+
const body = (await req.json?.());
|
|
95
|
+
// Only use body.data as wrapper if it's an object (not a string which is page content)
|
|
96
|
+
const data = (body?.data && typeof body.data === 'object') ? body.data : body;
|
|
97
|
+
if (!data?.title) {
|
|
98
|
+
return Response.json({ error: 'Title is required' }, { status: 400 });
|
|
99
|
+
}
|
|
100
|
+
const updateData = { title: data.title };
|
|
101
|
+
if (data.author) {
|
|
102
|
+
updateData.author = data.author;
|
|
103
|
+
}
|
|
104
|
+
if ('data' in data && data.data !== undefined) {
|
|
105
|
+
updateData.nitrogenData =
|
|
106
|
+
typeof data.data === 'string' ? JSON.parse(data.data) : data.data;
|
|
107
|
+
}
|
|
108
|
+
if (data.settings !== undefined) {
|
|
109
|
+
updateData.pageSettings = data.settings;
|
|
110
|
+
}
|
|
111
|
+
await payload.update({
|
|
112
|
+
collection,
|
|
113
|
+
id,
|
|
114
|
+
data: updateData,
|
|
115
|
+
});
|
|
116
|
+
return Response.json({ success: true });
|
|
117
|
+
},
|
|
118
|
+
},
|
|
119
|
+
// POST /api/nitrogen/v1/{prefix}/slug — Get item by slug (POST variant)
|
|
120
|
+
{
|
|
121
|
+
path: `/nitrogen/v1/${endpointPrefix}/slug`,
|
|
122
|
+
method: 'post',
|
|
123
|
+
handler: async (req) => {
|
|
124
|
+
const { payload } = req;
|
|
125
|
+
const body = (await req.json?.());
|
|
126
|
+
const slug = body?.slug || body?.data?.slug;
|
|
127
|
+
if (!slug) {
|
|
128
|
+
return Response.json({ error: 'Slug is required' }, { status: 400 });
|
|
129
|
+
}
|
|
130
|
+
const result = await payload.find({
|
|
131
|
+
collection,
|
|
132
|
+
where: { slug: { equals: slug } },
|
|
133
|
+
limit: 1,
|
|
134
|
+
depth: 1,
|
|
135
|
+
});
|
|
136
|
+
if (!result.docs.length) {
|
|
137
|
+
return Response.json({ error: 'Not found' }, { status: 404 });
|
|
138
|
+
}
|
|
139
|
+
const doc = result.docs[0];
|
|
140
|
+
const settings = await getNitrogenSettings(payload);
|
|
141
|
+
const dynamicData = buildDynamicData(doc, settings);
|
|
142
|
+
return Response.json(buildPageResponse(doc, settings, dynamicData));
|
|
143
|
+
},
|
|
144
|
+
},
|
|
145
|
+
// GET /api/nitrogen/v1/{prefix}/slug/:slug — Get item by slug
|
|
146
|
+
{
|
|
147
|
+
path: `/nitrogen/v1/${endpointPrefix}/slug/:slug`,
|
|
148
|
+
method: 'get',
|
|
149
|
+
handler: async (req) => {
|
|
150
|
+
const { payload, routeParams } = req;
|
|
151
|
+
const slug = routeParams?.slug;
|
|
152
|
+
const result = await payload.find({
|
|
153
|
+
collection,
|
|
154
|
+
where: { slug: { equals: slug } },
|
|
155
|
+
limit: 1,
|
|
156
|
+
depth: 1,
|
|
157
|
+
});
|
|
158
|
+
if (!result.docs.length) {
|
|
159
|
+
return Response.json({ error: 'Not found' }, { status: 404 });
|
|
160
|
+
}
|
|
161
|
+
const doc = result.docs[0];
|
|
162
|
+
const settings = await getNitrogenSettings(payload);
|
|
163
|
+
const dynamicData = buildDynamicData(doc, settings);
|
|
164
|
+
return Response.json(buildPageResponse(doc, settings, dynamicData));
|
|
165
|
+
},
|
|
166
|
+
},
|
|
167
|
+
];
|
|
168
|
+
}
|