@warleon/n8n-nodes-payload-cms 1.2.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/LICENSE +21 -0
- package/README.md +264 -0
- package/dist/credentials/PayloadCmsApi.credentials.d.ts +7 -0
- package/dist/credentials/PayloadCmsApi.credentials.js +53 -0
- package/dist/index.d.ts +2 -0
- package/dist/index.js +18 -0
- package/dist/nodes/PayloadCms/PayloadCms.node.d.ts +33 -0
- package/dist/nodes/PayloadCms/PayloadCms.node.js +796 -0
- package/package.json +61 -0
package/LICENSE
ADDED
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
MIT License
|
|
2
|
+
|
|
3
|
+
Copyright (c) 2025 n8n-nodes-payload-dynamic
|
|
4
|
+
|
|
5
|
+
Permission is hereby granted, free of charge, to any person obtaining a copy
|
|
6
|
+
of this software and associated documentation files (the "Software"), to deal
|
|
7
|
+
in the Software without restriction, including without limitation the rights
|
|
8
|
+
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
|
9
|
+
copies of the Software, and to permit persons to whom the Software is
|
|
10
|
+
furnished to do so, subject to the following conditions:
|
|
11
|
+
|
|
12
|
+
The above copyright notice and this permission notice shall be included in all
|
|
13
|
+
copies or substantial portions of the Software.
|
|
14
|
+
|
|
15
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
|
16
|
+
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
|
17
|
+
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
|
18
|
+
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
|
19
|
+
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
|
20
|
+
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
|
21
|
+
SOFTWARE.
|
package/README.md
ADDED
|
@@ -0,0 +1,264 @@
|
|
|
1
|
+
# n8n Payload CMS Node
|
|
2
|
+
|
|
3
|
+
A dynamic n8n node for Payload CMS that automatically discovers collections and operations, allowing you to interact with any Payload CMS instance via the REST API.
|
|
4
|
+
|
|
5
|
+
_Forked from [n8n-payload-dynamic](https://github.com/leadership-institute/n8n-payload-dynamic)_
|
|
6
|
+
|
|
7
|
+
## Features
|
|
8
|
+
|
|
9
|
+
- **Dynamic Collection Discovery**: Automatically discovers available collections from your Payload CMS instance
|
|
10
|
+
- **Dynamic Global Discovery**: Automatically discovers available globals from your Payload CMS instance
|
|
11
|
+
- **Full CRUD Operations**: Support for all standard operations (Create, Read, Update, Delete, Count)
|
|
12
|
+
- **Advanced Query Support**: Includes support for filtering, sorting, pagination, depth control, and localization
|
|
13
|
+
- **Flexible Authentication**: Uses API key authentication with configurable API prefix
|
|
14
|
+
- **Error Handling**: Robust error handling with detailed error messages
|
|
15
|
+
|
|
16
|
+
## Installation
|
|
17
|
+
|
|
18
|
+
### Automatic
|
|
19
|
+
|
|
20
|
+
### Manual
|
|
21
|
+
|
|
22
|
+
1. Clone or download this repository
|
|
23
|
+
2. Install dependencies:
|
|
24
|
+
```bash
|
|
25
|
+
npm install
|
|
26
|
+
```
|
|
27
|
+
3. Build the node:
|
|
28
|
+
```bash
|
|
29
|
+
npm run build
|
|
30
|
+
```
|
|
31
|
+
4. Install the node in your n8n instance by copying the built files to your n8n custom nodes directory
|
|
32
|
+
|
|
33
|
+
## Configuration
|
|
34
|
+
|
|
35
|
+
### Credentials Setup
|
|
36
|
+
|
|
37
|
+
1. In n8n, create new credentials of type "Payload CMS API"
|
|
38
|
+
2. Configure the following fields:
|
|
39
|
+
- **Base URL**: The base URL of your Payload CMS instance (e.g., `https://your-payload-instance.com`)
|
|
40
|
+
- **Email**: Your PayloadCMS user email
|
|
41
|
+
- **Password**: Your PayloadCMS user password
|
|
42
|
+
- **User Collection**: The collection slug for users (default: `users`)
|
|
43
|
+
- **API Prefix**: The API route prefix (default: `/api`)
|
|
44
|
+
|
|
45
|
+
### Getting Credentials
|
|
46
|
+
|
|
47
|
+
Use any valid user account from your PayloadCMS instance. The node will automatically:
|
|
48
|
+
|
|
49
|
+
- Login when first used
|
|
50
|
+
- Cache the authentication token for 1 hour
|
|
51
|
+
- Refresh tokens as needed
|
|
52
|
+
- Handle session management transparently
|
|
53
|
+
|
|
54
|
+
No API keys are required - the node uses PayloadCMS's standard login authentication flow.
|
|
55
|
+
|
|
56
|
+
## Usage
|
|
57
|
+
|
|
58
|
+
### Collection Operations
|
|
59
|
+
|
|
60
|
+
The node supports the following operations on collections:
|
|
61
|
+
|
|
62
|
+
- **Find**: Retrieve multiple documents from a collection
|
|
63
|
+
- **Find by ID**: Retrieve a specific document by its ID
|
|
64
|
+
- **Create**: Create a new document in a collection
|
|
65
|
+
- **Update**: Update multiple documents in a collection (requires where clause)
|
|
66
|
+
- **Update by ID**: Update a specific document by its ID
|
|
67
|
+
- **Delete**: Delete multiple documents from a collection (requires where clause)
|
|
68
|
+
- **Delete by ID**: Delete a specific document by its ID
|
|
69
|
+
- **Count**: Count documents in a collection
|
|
70
|
+
|
|
71
|
+
### Global Operations
|
|
72
|
+
|
|
73
|
+
The node supports the following operations on globals:
|
|
74
|
+
|
|
75
|
+
- **Get**: Retrieve global data
|
|
76
|
+
- **Update**: Update global data
|
|
77
|
+
|
|
78
|
+
### Auth Operations
|
|
79
|
+
|
|
80
|
+
The node supports the following authentication operations:
|
|
81
|
+
|
|
82
|
+
- **Login**: Authenticate a user with email and password
|
|
83
|
+
- **Logout**: Log out the current user
|
|
84
|
+
- **Me**: Get current authenticated user information
|
|
85
|
+
- **Refresh Token**: Refresh the authentication token
|
|
86
|
+
- **Forgot Password**: Send a forgot password email
|
|
87
|
+
- **Reset Password**: Reset user password with a token
|
|
88
|
+
- **Verify**: Verify user account with a verification token
|
|
89
|
+
- **Unlock**: Unlock a user account
|
|
90
|
+
|
|
91
|
+
### Query Parameters
|
|
92
|
+
|
|
93
|
+
The node supports all standard Payload CMS query parameters:
|
|
94
|
+
|
|
95
|
+
- **Depth**: Control how deep to populate relationships (default: 1)
|
|
96
|
+
- **Limit**: Maximum number of documents to return (default: 10)
|
|
97
|
+
- **Page**: Page number for pagination (default: 1)
|
|
98
|
+
- **Sort**: Sort field (use `-` prefix for descending, e.g., `-createdAt`)
|
|
99
|
+
- **Where**: JSON object for filtering documents
|
|
100
|
+
- **Select**: Comma-separated list of fields to include in the response
|
|
101
|
+
- **Locale**: Locale for localized content
|
|
102
|
+
|
|
103
|
+
### Example Usage
|
|
104
|
+
|
|
105
|
+
#### Finding Posts with Filtering
|
|
106
|
+
|
|
107
|
+
```json
|
|
108
|
+
{
|
|
109
|
+
"resource": "collection",
|
|
110
|
+
"collection": "posts",
|
|
111
|
+
"operation": "find",
|
|
112
|
+
"additionalOptions": {
|
|
113
|
+
"where": {
|
|
114
|
+
"status": {
|
|
115
|
+
"equals": "published"
|
|
116
|
+
}
|
|
117
|
+
},
|
|
118
|
+
"limit": 20,
|
|
119
|
+
"sort": "-createdAt"
|
|
120
|
+
}
|
|
121
|
+
}
|
|
122
|
+
```
|
|
123
|
+
|
|
124
|
+
#### Creating a New Document
|
|
125
|
+
|
|
126
|
+
```json
|
|
127
|
+
{
|
|
128
|
+
"resource": "collection",
|
|
129
|
+
"collection": "posts",
|
|
130
|
+
"operation": "create",
|
|
131
|
+
"data": {
|
|
132
|
+
"title": "My New Post",
|
|
133
|
+
"content": "This is the content of my new post",
|
|
134
|
+
"status": "draft"
|
|
135
|
+
}
|
|
136
|
+
}
|
|
137
|
+
```
|
|
138
|
+
|
|
139
|
+
#### Updating Global Settings
|
|
140
|
+
|
|
141
|
+
```json
|
|
142
|
+
{
|
|
143
|
+
"resource": "global",
|
|
144
|
+
"global": "settings",
|
|
145
|
+
"operation": "update",
|
|
146
|
+
"data": {
|
|
147
|
+
"siteName": "My Updated Site Name",
|
|
148
|
+
"siteDescription": "Updated description"
|
|
149
|
+
}
|
|
150
|
+
}
|
|
151
|
+
```
|
|
152
|
+
|
|
153
|
+
## Dynamic Discovery
|
|
154
|
+
|
|
155
|
+
The node automatically discovers available collections and globals from your Payload CMS instance using the following methods:
|
|
156
|
+
|
|
157
|
+
1. **Primary Method**: Attempts to fetch collections/globals from admin endpoints (`/api/collections`, `/api/globals`)
|
|
158
|
+
2. **Fallback Method**: Tests common collection/global names to discover what's available
|
|
159
|
+
|
|
160
|
+
### Common Collections Tested
|
|
161
|
+
|
|
162
|
+
- users
|
|
163
|
+
- posts
|
|
164
|
+
- pages
|
|
165
|
+
- media
|
|
166
|
+
- categories
|
|
167
|
+
- tags
|
|
168
|
+
|
|
169
|
+
### Common Globals Tested
|
|
170
|
+
|
|
171
|
+
- settings
|
|
172
|
+
- config
|
|
173
|
+
- navigation
|
|
174
|
+
- footer
|
|
175
|
+
- header
|
|
176
|
+
|
|
177
|
+
## Error Handling
|
|
178
|
+
|
|
179
|
+
The node includes comprehensive error handling:
|
|
180
|
+
|
|
181
|
+
- **Connection Errors**: Clear messages when unable to connect to Payload CMS
|
|
182
|
+
- **Authentication Errors**: Specific messages for API key issues
|
|
183
|
+
- **Discovery Errors**: Helpful messages when collections/globals cannot be discovered
|
|
184
|
+
- **Operation Errors**: Detailed error information for failed operations
|
|
185
|
+
|
|
186
|
+
## Development
|
|
187
|
+
|
|
188
|
+
### Building
|
|
189
|
+
|
|
190
|
+
```bash
|
|
191
|
+
npm run build
|
|
192
|
+
```
|
|
193
|
+
|
|
194
|
+
### Development Mode
|
|
195
|
+
|
|
196
|
+
```bash
|
|
197
|
+
npm run dev
|
|
198
|
+
```
|
|
199
|
+
|
|
200
|
+
### Linting
|
|
201
|
+
|
|
202
|
+
```bash
|
|
203
|
+
npm run lint
|
|
204
|
+
```
|
|
205
|
+
|
|
206
|
+
### Formatting
|
|
207
|
+
|
|
208
|
+
```bash
|
|
209
|
+
npm run format
|
|
210
|
+
```
|
|
211
|
+
|
|
212
|
+
## Payload CMS REST API Reference
|
|
213
|
+
|
|
214
|
+
This node is built according to the official Payload CMS REST API documentation:
|
|
215
|
+
https://payloadcms.com/docs/rest-api/overview
|
|
216
|
+
|
|
217
|
+
## Supported Payload CMS Versions
|
|
218
|
+
|
|
219
|
+
This node is designed to work with Payload CMS v2.x and v3.x. It uses the standard REST API endpoints that are consistent across versions.
|
|
220
|
+
|
|
221
|
+
## Contributing
|
|
222
|
+
|
|
223
|
+
1. Fork the repository
|
|
224
|
+
2. Create a feature branch
|
|
225
|
+
3. Make your changes
|
|
226
|
+
4. Add tests if applicable
|
|
227
|
+
5. Submit a pull request
|
|
228
|
+
|
|
229
|
+
## License
|
|
230
|
+
|
|
231
|
+
This project is licensed under the MIT License.
|
|
232
|
+
|
|
233
|
+
## Support
|
|
234
|
+
|
|
235
|
+
For issues and questions:
|
|
236
|
+
|
|
237
|
+
1. Check the Payload CMS documentation: https://payloadcms.com/docs
|
|
238
|
+
2. Review the n8n node development documentation
|
|
239
|
+
3. Open an issue in this repository
|
|
240
|
+
|
|
241
|
+
## Changelog
|
|
242
|
+
|
|
243
|
+
### v1.2.0
|
|
244
|
+
|
|
245
|
+
- **Simplified Authentication**: Removed API key authentication, now uses username/password only
|
|
246
|
+
- **Cleaner Interface**: Simplified credentials configuration with fewer options
|
|
247
|
+
- **Focused on PayloadCMS Standards**: Aligned with PayloadCMS's primary authentication method
|
|
248
|
+
- **Reduced Complexity**: Streamlined codebase by removing unused authentication paths
|
|
249
|
+
|
|
250
|
+
### v1.1.0
|
|
251
|
+
|
|
252
|
+
- **Automatic Authentication**: Added support for username/password authentication with automatic login
|
|
253
|
+
- **Token Caching**: Implemented 1-hour token caching for improved performance
|
|
254
|
+
- **Session Management**: Automatic token refresh and session handling
|
|
255
|
+
- **Backward Compatibility**: Maintained support for API key authentication
|
|
256
|
+
- **Enhanced Credentials**: New authentication method selection in credentials configuration
|
|
257
|
+
|
|
258
|
+
### v1.0.0
|
|
259
|
+
|
|
260
|
+
- Initial release
|
|
261
|
+
- Dynamic collection and global discovery
|
|
262
|
+
- Full CRUD operations support
|
|
263
|
+
- Advanced query parameter support
|
|
264
|
+
- Robust error handling
|
|
@@ -0,0 +1,53 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.PayloadCmsApi = void 0;
|
|
4
|
+
class PayloadCmsApi {
|
|
5
|
+
name = "payloadCmsApi";
|
|
6
|
+
displayName = "Payload CMS API";
|
|
7
|
+
documentationUrl = "https://payloadcms.com/docs/rest-api/overview";
|
|
8
|
+
properties = [
|
|
9
|
+
{
|
|
10
|
+
displayName: "Base URL",
|
|
11
|
+
name: "baseUrl",
|
|
12
|
+
type: "string",
|
|
13
|
+
default: "https://your-payload-instance.com",
|
|
14
|
+
placeholder: "https://your-payload-instance.com",
|
|
15
|
+
description: "The base URL of your Payload CMS instance",
|
|
16
|
+
required: true,
|
|
17
|
+
},
|
|
18
|
+
{
|
|
19
|
+
displayName: "Email",
|
|
20
|
+
name: "email",
|
|
21
|
+
type: "string",
|
|
22
|
+
default: "",
|
|
23
|
+
description: "Your PayloadCMS user email",
|
|
24
|
+
required: true,
|
|
25
|
+
},
|
|
26
|
+
{
|
|
27
|
+
displayName: "Password",
|
|
28
|
+
name: "password",
|
|
29
|
+
type: "string",
|
|
30
|
+
typeOptions: {
|
|
31
|
+
password: true,
|
|
32
|
+
},
|
|
33
|
+
default: "",
|
|
34
|
+
description: "Your PayloadCMS user password",
|
|
35
|
+
required: true,
|
|
36
|
+
},
|
|
37
|
+
{
|
|
38
|
+
displayName: "User Collection",
|
|
39
|
+
name: "userCollection",
|
|
40
|
+
type: "string",
|
|
41
|
+
default: "users",
|
|
42
|
+
description: "The collection slug for users (default: users)",
|
|
43
|
+
},
|
|
44
|
+
{
|
|
45
|
+
displayName: "API Prefix",
|
|
46
|
+
name: "apiPrefix",
|
|
47
|
+
type: "string",
|
|
48
|
+
default: "/api",
|
|
49
|
+
description: "The API route prefix (default: /api)",
|
|
50
|
+
},
|
|
51
|
+
];
|
|
52
|
+
}
|
|
53
|
+
exports.PayloadCmsApi = PayloadCmsApi;
|
package/dist/index.d.ts
ADDED
package/dist/index.js
ADDED
|
@@ -0,0 +1,18 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) {
|
|
3
|
+
if (k2 === undefined) k2 = k;
|
|
4
|
+
var desc = Object.getOwnPropertyDescriptor(m, k);
|
|
5
|
+
if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) {
|
|
6
|
+
desc = { enumerable: true, get: function() { return m[k]; } };
|
|
7
|
+
}
|
|
8
|
+
Object.defineProperty(o, k2, desc);
|
|
9
|
+
}) : (function(o, m, k, k2) {
|
|
10
|
+
if (k2 === undefined) k2 = k;
|
|
11
|
+
o[k2] = m[k];
|
|
12
|
+
}));
|
|
13
|
+
var __exportStar = (this && this.__exportStar) || function(m, exports) {
|
|
14
|
+
for (var p in m) if (p !== "default" && !Object.prototype.hasOwnProperty.call(exports, p)) __createBinding(exports, m, p);
|
|
15
|
+
};
|
|
16
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
17
|
+
__exportStar(require("./nodes/PayloadCms/PayloadCms.node"), exports);
|
|
18
|
+
__exportStar(require("./credentials/PayloadCmsApi.credentials"), exports);
|
|
@@ -0,0 +1,33 @@
|
|
|
1
|
+
import { IExecuteFunctions, ILoadOptionsFunctions, INodeExecutionData, INodePropertyOptions, INodeType, INodeTypeDescription } from "n8n-workflow";
|
|
2
|
+
import { AxiosRequestConfig } from "axios";
|
|
3
|
+
interface PayloadCollection {
|
|
4
|
+
slug: string;
|
|
5
|
+
labels?: {
|
|
6
|
+
singular: string;
|
|
7
|
+
plural: string;
|
|
8
|
+
};
|
|
9
|
+
fields?: any[];
|
|
10
|
+
auth?: boolean;
|
|
11
|
+
}
|
|
12
|
+
interface PayloadGlobal {
|
|
13
|
+
slug: string;
|
|
14
|
+
label?: string;
|
|
15
|
+
fields?: any[];
|
|
16
|
+
}
|
|
17
|
+
export declare class PayloadCms implements INodeType {
|
|
18
|
+
private static authTokenCache;
|
|
19
|
+
description: INodeTypeDescription;
|
|
20
|
+
methods: {
|
|
21
|
+
loadOptions: {
|
|
22
|
+
getCollections(this: ILoadOptionsFunctions): Promise<INodePropertyOptions[]>;
|
|
23
|
+
getGlobals(this: ILoadOptionsFunctions): Promise<INodePropertyOptions[]>;
|
|
24
|
+
getAuthCollections(this: ILoadOptionsFunctions): Promise<INodePropertyOptions[]>;
|
|
25
|
+
};
|
|
26
|
+
};
|
|
27
|
+
discoverCollections(this: ILoadOptionsFunctions): Promise<PayloadCollection[]>;
|
|
28
|
+
discoverGlobals(this: ILoadOptionsFunctions): Promise<PayloadGlobal[]>;
|
|
29
|
+
authenticate(this: IExecuteFunctions | ILoadOptionsFunctions, credentials: any): Promise<string>;
|
|
30
|
+
makeAuthenticatedRequest(this: IExecuteFunctions | ILoadOptionsFunctions, config: AxiosRequestConfig): Promise<any>;
|
|
31
|
+
execute(this: IExecuteFunctions): Promise<INodeExecutionData[][]>;
|
|
32
|
+
}
|
|
33
|
+
export {};
|
|
@@ -0,0 +1,796 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
var __importDefault = (this && this.__importDefault) || function (mod) {
|
|
3
|
+
return (mod && mod.__esModule) ? mod : { "default": mod };
|
|
4
|
+
};
|
|
5
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
6
|
+
exports.PayloadCms = void 0;
|
|
7
|
+
const n8n_workflow_1 = require("n8n-workflow");
|
|
8
|
+
const axios_1 = __importDefault(require("axios"));
|
|
9
|
+
class PayloadCms {
|
|
10
|
+
// Cache for authentication tokens
|
|
11
|
+
static authTokenCache = new Map();
|
|
12
|
+
description = {
|
|
13
|
+
displayName: "Payload CMS",
|
|
14
|
+
name: "payloadCms",
|
|
15
|
+
icon: "file:payloadcms.svg",
|
|
16
|
+
group: ["transform"],
|
|
17
|
+
version: 1,
|
|
18
|
+
subtitle: '={{$parameter["operation"] + ": " + $parameter["resource"]}}',
|
|
19
|
+
description: "Interact with Payload CMS collections and globals",
|
|
20
|
+
defaults: {
|
|
21
|
+
name: "Payload CMS",
|
|
22
|
+
},
|
|
23
|
+
inputs: ["main" /* NodeConnectionType.Main */],
|
|
24
|
+
outputs: ["main" /* NodeConnectionType.Main */],
|
|
25
|
+
credentials: [
|
|
26
|
+
{
|
|
27
|
+
name: "payloadCmsApi",
|
|
28
|
+
required: true,
|
|
29
|
+
},
|
|
30
|
+
],
|
|
31
|
+
properties: [
|
|
32
|
+
{
|
|
33
|
+
displayName: "Resource",
|
|
34
|
+
name: "resource",
|
|
35
|
+
type: "options",
|
|
36
|
+
noDataExpression: true,
|
|
37
|
+
required: true,
|
|
38
|
+
default: "collection",
|
|
39
|
+
options: [
|
|
40
|
+
{
|
|
41
|
+
name: "Collection",
|
|
42
|
+
value: "collection",
|
|
43
|
+
},
|
|
44
|
+
{
|
|
45
|
+
name: "Global",
|
|
46
|
+
value: "global",
|
|
47
|
+
},
|
|
48
|
+
{
|
|
49
|
+
name: "Auth",
|
|
50
|
+
value: "auth",
|
|
51
|
+
},
|
|
52
|
+
],
|
|
53
|
+
},
|
|
54
|
+
// Collection operations
|
|
55
|
+
{
|
|
56
|
+
displayName: "Collection",
|
|
57
|
+
name: "collection",
|
|
58
|
+
type: "options",
|
|
59
|
+
typeOptions: {
|
|
60
|
+
loadOptionsMethod: "getCollections",
|
|
61
|
+
},
|
|
62
|
+
required: true,
|
|
63
|
+
default: "",
|
|
64
|
+
displayOptions: {
|
|
65
|
+
show: {
|
|
66
|
+
resource: ["collection"],
|
|
67
|
+
},
|
|
68
|
+
},
|
|
69
|
+
description: "Choose the collection to operate on",
|
|
70
|
+
},
|
|
71
|
+
{
|
|
72
|
+
displayName: "Operation",
|
|
73
|
+
name: "operation",
|
|
74
|
+
type: "options",
|
|
75
|
+
noDataExpression: true,
|
|
76
|
+
required: true,
|
|
77
|
+
default: "find",
|
|
78
|
+
displayOptions: {
|
|
79
|
+
show: {
|
|
80
|
+
resource: ["collection"],
|
|
81
|
+
},
|
|
82
|
+
},
|
|
83
|
+
options: [
|
|
84
|
+
{
|
|
85
|
+
name: "Find",
|
|
86
|
+
value: "find",
|
|
87
|
+
description: "Find documents in collection",
|
|
88
|
+
},
|
|
89
|
+
{
|
|
90
|
+
name: "Find by ID",
|
|
91
|
+
value: "findById",
|
|
92
|
+
description: "Find document by ID",
|
|
93
|
+
},
|
|
94
|
+
{
|
|
95
|
+
name: "Create",
|
|
96
|
+
value: "create",
|
|
97
|
+
description: "Create new document",
|
|
98
|
+
},
|
|
99
|
+
{
|
|
100
|
+
name: "Update",
|
|
101
|
+
value: "update",
|
|
102
|
+
description: "Update documents",
|
|
103
|
+
},
|
|
104
|
+
{
|
|
105
|
+
name: "Update by ID",
|
|
106
|
+
value: "updateById",
|
|
107
|
+
description: "Update document by ID",
|
|
108
|
+
},
|
|
109
|
+
{
|
|
110
|
+
name: "Delete",
|
|
111
|
+
value: "delete",
|
|
112
|
+
description: "Delete documents",
|
|
113
|
+
},
|
|
114
|
+
{
|
|
115
|
+
name: "Delete by ID",
|
|
116
|
+
value: "deleteById",
|
|
117
|
+
description: "Delete document by ID",
|
|
118
|
+
},
|
|
119
|
+
{
|
|
120
|
+
name: "Count",
|
|
121
|
+
value: "count",
|
|
122
|
+
description: "Count documents",
|
|
123
|
+
},
|
|
124
|
+
],
|
|
125
|
+
},
|
|
126
|
+
// Global operations
|
|
127
|
+
{
|
|
128
|
+
displayName: "Global",
|
|
129
|
+
name: "global",
|
|
130
|
+
type: "options",
|
|
131
|
+
typeOptions: {
|
|
132
|
+
loadOptionsMethod: "getGlobals",
|
|
133
|
+
},
|
|
134
|
+
required: true,
|
|
135
|
+
default: "",
|
|
136
|
+
displayOptions: {
|
|
137
|
+
show: {
|
|
138
|
+
resource: ["global"],
|
|
139
|
+
},
|
|
140
|
+
},
|
|
141
|
+
description: "Choose the global to operate on",
|
|
142
|
+
},
|
|
143
|
+
{
|
|
144
|
+
displayName: "Operation",
|
|
145
|
+
name: "operation",
|
|
146
|
+
type: "options",
|
|
147
|
+
noDataExpression: true,
|
|
148
|
+
required: true,
|
|
149
|
+
default: "get",
|
|
150
|
+
displayOptions: {
|
|
151
|
+
show: {
|
|
152
|
+
resource: ["global"],
|
|
153
|
+
},
|
|
154
|
+
},
|
|
155
|
+
options: [
|
|
156
|
+
{
|
|
157
|
+
name: "Get",
|
|
158
|
+
value: "get",
|
|
159
|
+
description: "Get global data",
|
|
160
|
+
},
|
|
161
|
+
{
|
|
162
|
+
name: "Update",
|
|
163
|
+
value: "update",
|
|
164
|
+
description: "Update global data",
|
|
165
|
+
},
|
|
166
|
+
],
|
|
167
|
+
},
|
|
168
|
+
// ID field for operations that need it
|
|
169
|
+
{
|
|
170
|
+
displayName: "Document ID",
|
|
171
|
+
name: "documentId",
|
|
172
|
+
type: "string",
|
|
173
|
+
required: true,
|
|
174
|
+
default: "",
|
|
175
|
+
displayOptions: {
|
|
176
|
+
show: {
|
|
177
|
+
resource: ["collection"],
|
|
178
|
+
operation: ["findById", "updateById", "deleteById"],
|
|
179
|
+
},
|
|
180
|
+
},
|
|
181
|
+
description: "The ID of the document to operate on",
|
|
182
|
+
},
|
|
183
|
+
// Data field for create/update operations
|
|
184
|
+
{
|
|
185
|
+
displayName: "Data",
|
|
186
|
+
name: "data",
|
|
187
|
+
type: "json",
|
|
188
|
+
required: true,
|
|
189
|
+
default: "{}",
|
|
190
|
+
displayOptions: {
|
|
191
|
+
show: {
|
|
192
|
+
resource: ["collection"],
|
|
193
|
+
operation: ["create", "update", "updateById"],
|
|
194
|
+
},
|
|
195
|
+
},
|
|
196
|
+
description: "The data to send (JSON format)",
|
|
197
|
+
},
|
|
198
|
+
{
|
|
199
|
+
displayName: "Data",
|
|
200
|
+
name: "data",
|
|
201
|
+
type: "json",
|
|
202
|
+
required: true,
|
|
203
|
+
default: "{}",
|
|
204
|
+
displayOptions: {
|
|
205
|
+
show: {
|
|
206
|
+
resource: ["global"],
|
|
207
|
+
operation: ["update"],
|
|
208
|
+
},
|
|
209
|
+
},
|
|
210
|
+
description: "The data to send (JSON format)",
|
|
211
|
+
},
|
|
212
|
+
// Auth operations
|
|
213
|
+
{
|
|
214
|
+
displayName: "Auth Collection",
|
|
215
|
+
name: "authCollection",
|
|
216
|
+
type: "options",
|
|
217
|
+
typeOptions: {
|
|
218
|
+
loadOptionsMethod: "getAuthCollections",
|
|
219
|
+
},
|
|
220
|
+
required: true,
|
|
221
|
+
default: "users",
|
|
222
|
+
displayOptions: {
|
|
223
|
+
show: {
|
|
224
|
+
resource: ["auth"],
|
|
225
|
+
},
|
|
226
|
+
},
|
|
227
|
+
description: "Choose the auth-enabled collection",
|
|
228
|
+
},
|
|
229
|
+
{
|
|
230
|
+
displayName: "Operation",
|
|
231
|
+
name: "operation",
|
|
232
|
+
type: "options",
|
|
233
|
+
noDataExpression: true,
|
|
234
|
+
required: true,
|
|
235
|
+
default: "login",
|
|
236
|
+
displayOptions: {
|
|
237
|
+
show: {
|
|
238
|
+
resource: ["auth"],
|
|
239
|
+
},
|
|
240
|
+
},
|
|
241
|
+
options: [
|
|
242
|
+
{
|
|
243
|
+
name: "Login",
|
|
244
|
+
value: "login",
|
|
245
|
+
description: "Login user",
|
|
246
|
+
},
|
|
247
|
+
{
|
|
248
|
+
name: "Logout",
|
|
249
|
+
value: "logout",
|
|
250
|
+
description: "Logout user",
|
|
251
|
+
},
|
|
252
|
+
{
|
|
253
|
+
name: "Me",
|
|
254
|
+
value: "me",
|
|
255
|
+
description: "Get current user",
|
|
256
|
+
},
|
|
257
|
+
{
|
|
258
|
+
name: "Refresh Token",
|
|
259
|
+
value: "refresh",
|
|
260
|
+
description: "Refresh authentication token",
|
|
261
|
+
},
|
|
262
|
+
{
|
|
263
|
+
name: "Forgot Password",
|
|
264
|
+
value: "forgotPassword",
|
|
265
|
+
description: "Send forgot password email",
|
|
266
|
+
},
|
|
267
|
+
{
|
|
268
|
+
name: "Reset Password",
|
|
269
|
+
value: "resetPassword",
|
|
270
|
+
description: "Reset user password",
|
|
271
|
+
},
|
|
272
|
+
{
|
|
273
|
+
name: "Verify",
|
|
274
|
+
value: "verify",
|
|
275
|
+
description: "Verify user account",
|
|
276
|
+
},
|
|
277
|
+
{
|
|
278
|
+
name: "Unlock",
|
|
279
|
+
value: "unlock",
|
|
280
|
+
description: "Unlock user account",
|
|
281
|
+
},
|
|
282
|
+
],
|
|
283
|
+
},
|
|
284
|
+
// Auth data fields
|
|
285
|
+
{
|
|
286
|
+
displayName: "Email",
|
|
287
|
+
name: "email",
|
|
288
|
+
type: "string",
|
|
289
|
+
required: true,
|
|
290
|
+
default: "",
|
|
291
|
+
displayOptions: {
|
|
292
|
+
show: {
|
|
293
|
+
resource: ["auth"],
|
|
294
|
+
operation: ["login", "forgotPassword"],
|
|
295
|
+
},
|
|
296
|
+
},
|
|
297
|
+
description: "User email address",
|
|
298
|
+
},
|
|
299
|
+
{
|
|
300
|
+
displayName: "Password",
|
|
301
|
+
name: "password",
|
|
302
|
+
type: "string",
|
|
303
|
+
typeOptions: {
|
|
304
|
+
password: true,
|
|
305
|
+
},
|
|
306
|
+
required: true,
|
|
307
|
+
default: "",
|
|
308
|
+
displayOptions: {
|
|
309
|
+
show: {
|
|
310
|
+
resource: ["auth"],
|
|
311
|
+
operation: ["login"],
|
|
312
|
+
},
|
|
313
|
+
},
|
|
314
|
+
description: "User password",
|
|
315
|
+
},
|
|
316
|
+
{
|
|
317
|
+
displayName: "Token",
|
|
318
|
+
name: "token",
|
|
319
|
+
type: "string",
|
|
320
|
+
required: true,
|
|
321
|
+
default: "",
|
|
322
|
+
displayOptions: {
|
|
323
|
+
show: {
|
|
324
|
+
resource: ["auth"],
|
|
325
|
+
operation: ["verify", "resetPassword"],
|
|
326
|
+
},
|
|
327
|
+
},
|
|
328
|
+
description: "Verification or reset token",
|
|
329
|
+
},
|
|
330
|
+
{
|
|
331
|
+
displayName: "New Password",
|
|
332
|
+
name: "newPassword",
|
|
333
|
+
type: "string",
|
|
334
|
+
typeOptions: {
|
|
335
|
+
password: true,
|
|
336
|
+
},
|
|
337
|
+
required: true,
|
|
338
|
+
default: "",
|
|
339
|
+
displayOptions: {
|
|
340
|
+
show: {
|
|
341
|
+
resource: ["auth"],
|
|
342
|
+
operation: ["resetPassword"],
|
|
343
|
+
},
|
|
344
|
+
},
|
|
345
|
+
description: "New password for reset",
|
|
346
|
+
},
|
|
347
|
+
// Query parameters
|
|
348
|
+
{
|
|
349
|
+
displayName: "Additional Options",
|
|
350
|
+
name: "additionalOptions",
|
|
351
|
+
type: "collection",
|
|
352
|
+
placeholder: "Add Option",
|
|
353
|
+
default: {},
|
|
354
|
+
options: [
|
|
355
|
+
{
|
|
356
|
+
displayName: "Depth",
|
|
357
|
+
name: "depth",
|
|
358
|
+
type: "number",
|
|
359
|
+
default: 1,
|
|
360
|
+
description: "How deep to populate relationships",
|
|
361
|
+
},
|
|
362
|
+
{
|
|
363
|
+
displayName: "Limit",
|
|
364
|
+
name: "limit",
|
|
365
|
+
type: "number",
|
|
366
|
+
default: 10,
|
|
367
|
+
description: "Maximum number of documents to return",
|
|
368
|
+
},
|
|
369
|
+
{
|
|
370
|
+
displayName: "Page",
|
|
371
|
+
name: "page",
|
|
372
|
+
type: "number",
|
|
373
|
+
default: 1,
|
|
374
|
+
description: "Page number for pagination",
|
|
375
|
+
},
|
|
376
|
+
{
|
|
377
|
+
displayName: "Sort",
|
|
378
|
+
name: "sort",
|
|
379
|
+
type: "string",
|
|
380
|
+
default: "",
|
|
381
|
+
description: "Sort field (use - for descending, e.g., -createdAt)",
|
|
382
|
+
},
|
|
383
|
+
{
|
|
384
|
+
displayName: "Where",
|
|
385
|
+
name: "where",
|
|
386
|
+
type: "json",
|
|
387
|
+
default: "{}",
|
|
388
|
+
description: "Where clause for filtering (JSON format)",
|
|
389
|
+
},
|
|
390
|
+
{
|
|
391
|
+
displayName: "Select",
|
|
392
|
+
name: "select",
|
|
393
|
+
type: "string",
|
|
394
|
+
default: "",
|
|
395
|
+
description: "Fields to select (comma-separated)",
|
|
396
|
+
},
|
|
397
|
+
{
|
|
398
|
+
displayName: "Locale",
|
|
399
|
+
name: "locale",
|
|
400
|
+
type: "string",
|
|
401
|
+
default: "",
|
|
402
|
+
description: "Locale for localized content",
|
|
403
|
+
},
|
|
404
|
+
],
|
|
405
|
+
},
|
|
406
|
+
],
|
|
407
|
+
};
|
|
408
|
+
methods = {
|
|
409
|
+
loadOptions: {
|
|
410
|
+
async getCollections() {
|
|
411
|
+
try {
|
|
412
|
+
const collections = await PayloadCms.prototype.discoverCollections.call(this);
|
|
413
|
+
return collections.map((collection) => ({
|
|
414
|
+
name: collection.labels?.plural || collection.slug,
|
|
415
|
+
value: collection.slug,
|
|
416
|
+
}));
|
|
417
|
+
}
|
|
418
|
+
catch (error) {
|
|
419
|
+
throw new n8n_workflow_1.NodeOperationError(this.getNode(), `Failed to load collections: ${error instanceof Error ? error.message : "Unknown error"}`);
|
|
420
|
+
}
|
|
421
|
+
},
|
|
422
|
+
async getGlobals() {
|
|
423
|
+
try {
|
|
424
|
+
const globals = await PayloadCms.prototype.discoverGlobals.call(this);
|
|
425
|
+
return globals.map((global) => ({
|
|
426
|
+
name: global.label || global.slug,
|
|
427
|
+
value: global.slug,
|
|
428
|
+
}));
|
|
429
|
+
}
|
|
430
|
+
catch (error) {
|
|
431
|
+
throw new n8n_workflow_1.NodeOperationError(this.getNode(), `Failed to load globals: ${error instanceof Error ? error.message : "Unknown error"}`);
|
|
432
|
+
}
|
|
433
|
+
},
|
|
434
|
+
async getAuthCollections() {
|
|
435
|
+
try {
|
|
436
|
+
const collections = await PayloadCms.prototype.discoverCollections.call(this);
|
|
437
|
+
// Filter for auth-enabled collections or return common auth collections
|
|
438
|
+
const authCollections = collections.filter((collection) => collection.auth || collection.slug === "users");
|
|
439
|
+
if (authCollections.length > 0) {
|
|
440
|
+
return authCollections.map((collection) => ({
|
|
441
|
+
name: collection.labels?.plural || collection.slug,
|
|
442
|
+
value: collection.slug,
|
|
443
|
+
}));
|
|
444
|
+
}
|
|
445
|
+
// Fallback to common auth collection names
|
|
446
|
+
return [
|
|
447
|
+
{ name: "Users", value: "users" },
|
|
448
|
+
{ name: "Admins", value: "admins" },
|
|
449
|
+
{ name: "Members", value: "members" },
|
|
450
|
+
];
|
|
451
|
+
}
|
|
452
|
+
catch (error) {
|
|
453
|
+
// Return default auth collections if discovery fails
|
|
454
|
+
return [{ name: "Users", value: "users" }];
|
|
455
|
+
}
|
|
456
|
+
},
|
|
457
|
+
},
|
|
458
|
+
};
|
|
459
|
+
async discoverCollections() {
|
|
460
|
+
const credentials = await this.getCredentials("payloadCmsApi");
|
|
461
|
+
const baseUrl = credentials.baseUrl;
|
|
462
|
+
const apiPrefix = credentials.apiPrefix || "/api";
|
|
463
|
+
try {
|
|
464
|
+
// First, try to get collections from a potential admin endpoint
|
|
465
|
+
// This is a common pattern in many CMS systems
|
|
466
|
+
const response = await PayloadCms.prototype.makeAuthenticatedRequest.call(this, {
|
|
467
|
+
method: "GET",
|
|
468
|
+
url: `${baseUrl}${apiPrefix}/collections`,
|
|
469
|
+
});
|
|
470
|
+
if (response.data && Array.isArray(response.data)) {
|
|
471
|
+
return response.data;
|
|
472
|
+
}
|
|
473
|
+
}
|
|
474
|
+
catch (error) {
|
|
475
|
+
// If that fails, we'll try some common collection names
|
|
476
|
+
// This is a fallback approach for discovery
|
|
477
|
+
}
|
|
478
|
+
// Fallback: Try some common collection names
|
|
479
|
+
const commonCollections = [
|
|
480
|
+
"users",
|
|
481
|
+
"posts",
|
|
482
|
+
"pages",
|
|
483
|
+
"media",
|
|
484
|
+
"categories",
|
|
485
|
+
"tags",
|
|
486
|
+
];
|
|
487
|
+
const discoveredCollections = [];
|
|
488
|
+
for (const slug of commonCollections) {
|
|
489
|
+
try {
|
|
490
|
+
await PayloadCms.prototype.makeAuthenticatedRequest.call(this, {
|
|
491
|
+
method: "GET",
|
|
492
|
+
url: `${baseUrl}${apiPrefix}/${slug}`,
|
|
493
|
+
params: { limit: 1 },
|
|
494
|
+
});
|
|
495
|
+
discoveredCollections.push({
|
|
496
|
+
slug,
|
|
497
|
+
labels: {
|
|
498
|
+
singular: slug.slice(0, -1),
|
|
499
|
+
plural: slug,
|
|
500
|
+
},
|
|
501
|
+
});
|
|
502
|
+
}
|
|
503
|
+
catch (error) {
|
|
504
|
+
// Collection doesn't exist, continue
|
|
505
|
+
}
|
|
506
|
+
}
|
|
507
|
+
if (discoveredCollections.length === 0) {
|
|
508
|
+
throw new Error("Could not discover any collections. Please ensure your Payload CMS instance is accessible and you have valid credentials.");
|
|
509
|
+
}
|
|
510
|
+
return discoveredCollections;
|
|
511
|
+
}
|
|
512
|
+
async discoverGlobals() {
|
|
513
|
+
const credentials = await this.getCredentials("payloadCmsApi");
|
|
514
|
+
const baseUrl = credentials.baseUrl;
|
|
515
|
+
const apiPrefix = credentials.apiPrefix || "/api";
|
|
516
|
+
try {
|
|
517
|
+
// Try to get globals from a potential admin endpoint
|
|
518
|
+
const response = await PayloadCms.prototype.makeAuthenticatedRequest.call(this, {
|
|
519
|
+
method: "GET",
|
|
520
|
+
url: `${baseUrl}${apiPrefix}/globals`,
|
|
521
|
+
});
|
|
522
|
+
if (response.data && Array.isArray(response.data)) {
|
|
523
|
+
return response.data;
|
|
524
|
+
}
|
|
525
|
+
}
|
|
526
|
+
catch (error) {
|
|
527
|
+
// If that fails, we'll try some common global names
|
|
528
|
+
}
|
|
529
|
+
// Fallback: Try some common global names
|
|
530
|
+
const commonGlobals = [
|
|
531
|
+
"settings",
|
|
532
|
+
"config",
|
|
533
|
+
"navigation",
|
|
534
|
+
"footer",
|
|
535
|
+
"header",
|
|
536
|
+
];
|
|
537
|
+
const discoveredGlobals = [];
|
|
538
|
+
for (const slug of commonGlobals) {
|
|
539
|
+
try {
|
|
540
|
+
await PayloadCms.prototype.makeAuthenticatedRequest.call(this, {
|
|
541
|
+
method: "GET",
|
|
542
|
+
url: `${baseUrl}${apiPrefix}/globals/${slug}`,
|
|
543
|
+
});
|
|
544
|
+
discoveredGlobals.push({
|
|
545
|
+
slug,
|
|
546
|
+
label: slug.charAt(0).toUpperCase() + slug.slice(1),
|
|
547
|
+
});
|
|
548
|
+
}
|
|
549
|
+
catch (error) {
|
|
550
|
+
// Global doesn't exist, continue
|
|
551
|
+
}
|
|
552
|
+
}
|
|
553
|
+
return discoveredGlobals;
|
|
554
|
+
}
|
|
555
|
+
// Helper method to authenticate and get token
|
|
556
|
+
async authenticate(credentials) {
|
|
557
|
+
const baseUrl = credentials.baseUrl;
|
|
558
|
+
const apiPrefix = credentials.apiPrefix || "/api";
|
|
559
|
+
const email = credentials.email;
|
|
560
|
+
const password = credentials.password;
|
|
561
|
+
const userCollection = credentials.userCollection || "users";
|
|
562
|
+
// Create cache key
|
|
563
|
+
const cacheKey = `${baseUrl}:${email}:${userCollection}`;
|
|
564
|
+
// Check if we have a valid cached token
|
|
565
|
+
const cached = PayloadCms.authTokenCache.get(cacheKey);
|
|
566
|
+
if (cached && cached.expires > Date.now()) {
|
|
567
|
+
return cached.token;
|
|
568
|
+
}
|
|
569
|
+
try {
|
|
570
|
+
// Login to get token
|
|
571
|
+
const loginResponse = await axios_1.default.post(`${baseUrl}${apiPrefix}/${userCollection}/login`, {
|
|
572
|
+
email,
|
|
573
|
+
password,
|
|
574
|
+
}, {
|
|
575
|
+
headers: {
|
|
576
|
+
"Content-Type": "application/json",
|
|
577
|
+
},
|
|
578
|
+
});
|
|
579
|
+
if (loginResponse.data && loginResponse.data.token) {
|
|
580
|
+
const token = loginResponse.data.token;
|
|
581
|
+
// Cache token for 1 hour (adjust as needed)
|
|
582
|
+
const expires = Date.now() + 60 * 60 * 1000;
|
|
583
|
+
PayloadCms.authTokenCache.set(cacheKey, { token, expires });
|
|
584
|
+
return token;
|
|
585
|
+
}
|
|
586
|
+
throw new Error("No token received from login response");
|
|
587
|
+
}
|
|
588
|
+
catch (error) {
|
|
589
|
+
throw new n8n_workflow_1.NodeOperationError(this.getNode(), `Authentication failed: ${error instanceof Error ? error.message : "Unknown error"}`);
|
|
590
|
+
}
|
|
591
|
+
}
|
|
592
|
+
// Helper method to make authenticated requests
|
|
593
|
+
async makeAuthenticatedRequest(config) {
|
|
594
|
+
const credentials = await this.getCredentials("payloadCmsApi");
|
|
595
|
+
const token = await PayloadCms.prototype.authenticate.call(this, credentials);
|
|
596
|
+
// Add authorization header
|
|
597
|
+
config.headers = {
|
|
598
|
+
...config.headers,
|
|
599
|
+
Authorization: `Bearer ${token}`,
|
|
600
|
+
"Content-Type": "application/json",
|
|
601
|
+
};
|
|
602
|
+
return (0, axios_1.default)(config);
|
|
603
|
+
}
|
|
604
|
+
async execute() {
|
|
605
|
+
const items = this.getInputData();
|
|
606
|
+
const returnData = [];
|
|
607
|
+
for (let i = 0; i < items.length; i++) {
|
|
608
|
+
try {
|
|
609
|
+
const resource = this.getNodeParameter("resource", i);
|
|
610
|
+
const operation = this.getNodeParameter("operation", i);
|
|
611
|
+
const additionalOptions = this.getNodeParameter("additionalOptions", i, {});
|
|
612
|
+
const credentials = await this.getCredentials("payloadCmsApi");
|
|
613
|
+
const baseUrl = credentials.baseUrl;
|
|
614
|
+
const apiPrefix = credentials.apiPrefix || "/api";
|
|
615
|
+
let url = "";
|
|
616
|
+
let method = "GET";
|
|
617
|
+
let data = undefined;
|
|
618
|
+
const params = {};
|
|
619
|
+
// Add query parameters
|
|
620
|
+
if (additionalOptions.depth !== undefined)
|
|
621
|
+
params.depth = additionalOptions.depth;
|
|
622
|
+
if (additionalOptions.limit !== undefined)
|
|
623
|
+
params.limit = additionalOptions.limit;
|
|
624
|
+
if (additionalOptions.page !== undefined)
|
|
625
|
+
params.page = additionalOptions.page;
|
|
626
|
+
if (additionalOptions.sort)
|
|
627
|
+
params.sort = additionalOptions.sort;
|
|
628
|
+
if (additionalOptions.where) {
|
|
629
|
+
// Handle where clause - it should be a JSON object
|
|
630
|
+
const whereClause = typeof additionalOptions.where === "string"
|
|
631
|
+
? JSON.parse(additionalOptions.where)
|
|
632
|
+
: additionalOptions.where;
|
|
633
|
+
params.where = JSON.stringify(whereClause);
|
|
634
|
+
}
|
|
635
|
+
if (additionalOptions.select)
|
|
636
|
+
params.select = additionalOptions.select;
|
|
637
|
+
if (additionalOptions.locale)
|
|
638
|
+
params.locale = additionalOptions.locale;
|
|
639
|
+
if (resource === "collection") {
|
|
640
|
+
const collection = this.getNodeParameter("collection", i);
|
|
641
|
+
switch (operation) {
|
|
642
|
+
case "find":
|
|
643
|
+
url = `${baseUrl}${apiPrefix}/${collection}`;
|
|
644
|
+
method = "GET";
|
|
645
|
+
break;
|
|
646
|
+
case "findById":
|
|
647
|
+
const docId = this.getNodeParameter("documentId", i);
|
|
648
|
+
url = `${baseUrl}${apiPrefix}/${collection}/${docId}`;
|
|
649
|
+
method = "GET";
|
|
650
|
+
break;
|
|
651
|
+
case "create":
|
|
652
|
+
url = `${baseUrl}${apiPrefix}/${collection}`;
|
|
653
|
+
method = "POST";
|
|
654
|
+
data = this.getNodeParameter("data", i);
|
|
655
|
+
break;
|
|
656
|
+
case "update":
|
|
657
|
+
url = `${baseUrl}${apiPrefix}/${collection}`;
|
|
658
|
+
method = "PATCH";
|
|
659
|
+
data = this.getNodeParameter("data", i);
|
|
660
|
+
break;
|
|
661
|
+
case "updateById":
|
|
662
|
+
const updateId = this.getNodeParameter("documentId", i);
|
|
663
|
+
url = `${baseUrl}${apiPrefix}/${collection}/${updateId}`;
|
|
664
|
+
method = "PATCH";
|
|
665
|
+
data = this.getNodeParameter("data", i);
|
|
666
|
+
break;
|
|
667
|
+
case "delete":
|
|
668
|
+
url = `${baseUrl}${apiPrefix}/${collection}`;
|
|
669
|
+
method = "DELETE";
|
|
670
|
+
// For bulk delete, we need to pass where clause in the body
|
|
671
|
+
if (additionalOptions.where) {
|
|
672
|
+
data = {
|
|
673
|
+
where: typeof additionalOptions.where === "string"
|
|
674
|
+
? JSON.parse(additionalOptions.where)
|
|
675
|
+
: additionalOptions.where,
|
|
676
|
+
};
|
|
677
|
+
}
|
|
678
|
+
break;
|
|
679
|
+
case "deleteById":
|
|
680
|
+
const deleteId = this.getNodeParameter("documentId", i);
|
|
681
|
+
url = `${baseUrl}${apiPrefix}/${collection}/${deleteId}`;
|
|
682
|
+
method = "DELETE";
|
|
683
|
+
break;
|
|
684
|
+
case "count":
|
|
685
|
+
url = `${baseUrl}${apiPrefix}/${collection}/count`;
|
|
686
|
+
method = "GET";
|
|
687
|
+
break;
|
|
688
|
+
}
|
|
689
|
+
}
|
|
690
|
+
else if (resource === "global") {
|
|
691
|
+
const global = this.getNodeParameter("global", i);
|
|
692
|
+
switch (operation) {
|
|
693
|
+
case "get":
|
|
694
|
+
url = `${baseUrl}${apiPrefix}/globals/${global}`;
|
|
695
|
+
method = "GET";
|
|
696
|
+
break;
|
|
697
|
+
case "update":
|
|
698
|
+
url = `${baseUrl}${apiPrefix}/globals/${global}`;
|
|
699
|
+
method = "POST";
|
|
700
|
+
data = this.getNodeParameter("data", i);
|
|
701
|
+
break;
|
|
702
|
+
}
|
|
703
|
+
}
|
|
704
|
+
else if (resource === "auth") {
|
|
705
|
+
const authCollection = this.getNodeParameter("authCollection", i);
|
|
706
|
+
switch (operation) {
|
|
707
|
+
case "login":
|
|
708
|
+
url = `${baseUrl}${apiPrefix}/${authCollection}/login`;
|
|
709
|
+
method = "POST";
|
|
710
|
+
data = {
|
|
711
|
+
email: this.getNodeParameter("email", i),
|
|
712
|
+
password: this.getNodeParameter("password", i),
|
|
713
|
+
};
|
|
714
|
+
break;
|
|
715
|
+
case "logout":
|
|
716
|
+
url = `${baseUrl}${apiPrefix}/${authCollection}/logout`;
|
|
717
|
+
method = "POST";
|
|
718
|
+
break;
|
|
719
|
+
case "me":
|
|
720
|
+
url = `${baseUrl}${apiPrefix}/${authCollection}/me`;
|
|
721
|
+
method = "GET";
|
|
722
|
+
break;
|
|
723
|
+
case "refresh":
|
|
724
|
+
url = `${baseUrl}${apiPrefix}/${authCollection}/refresh-token`;
|
|
725
|
+
method = "POST";
|
|
726
|
+
break;
|
|
727
|
+
case "forgotPassword":
|
|
728
|
+
url = `${baseUrl}${apiPrefix}/${authCollection}/forgot-password`;
|
|
729
|
+
method = "POST";
|
|
730
|
+
data = {
|
|
731
|
+
email: this.getNodeParameter("email", i),
|
|
732
|
+
};
|
|
733
|
+
break;
|
|
734
|
+
case "resetPassword":
|
|
735
|
+
url = `${baseUrl}${apiPrefix}/${authCollection}/reset-password`;
|
|
736
|
+
method = "POST";
|
|
737
|
+
data = {
|
|
738
|
+
token: this.getNodeParameter("token", i),
|
|
739
|
+
password: this.getNodeParameter("newPassword", i),
|
|
740
|
+
};
|
|
741
|
+
break;
|
|
742
|
+
case "verify":
|
|
743
|
+
const token = this.getNodeParameter("token", i);
|
|
744
|
+
url = `${baseUrl}${apiPrefix}/${authCollection}/verify/${token}`;
|
|
745
|
+
method = "POST";
|
|
746
|
+
break;
|
|
747
|
+
case "unlock":
|
|
748
|
+
url = `${baseUrl}${apiPrefix}/${authCollection}/unlock`;
|
|
749
|
+
method = "POST";
|
|
750
|
+
data = {
|
|
751
|
+
email: this.getNodeParameter("email", i),
|
|
752
|
+
};
|
|
753
|
+
break;
|
|
754
|
+
}
|
|
755
|
+
}
|
|
756
|
+
const requestConfig = {
|
|
757
|
+
method: method,
|
|
758
|
+
url,
|
|
759
|
+
params,
|
|
760
|
+
};
|
|
761
|
+
if (data) {
|
|
762
|
+
requestConfig.data =
|
|
763
|
+
typeof data === "string" ? JSON.parse(data) : data;
|
|
764
|
+
}
|
|
765
|
+
const response = await PayloadCms.prototype.makeAuthenticatedRequest.call(this, requestConfig);
|
|
766
|
+
returnData.push({
|
|
767
|
+
json: response.data,
|
|
768
|
+
pairedItem: {
|
|
769
|
+
item: i,
|
|
770
|
+
},
|
|
771
|
+
});
|
|
772
|
+
}
|
|
773
|
+
catch (error) {
|
|
774
|
+
if (this.continueOnFail()) {
|
|
775
|
+
returnData.push({
|
|
776
|
+
json: {
|
|
777
|
+
error: error instanceof Error
|
|
778
|
+
? error.message
|
|
779
|
+
: "Unknown error occurred",
|
|
780
|
+
},
|
|
781
|
+
pairedItem: {
|
|
782
|
+
item: i,
|
|
783
|
+
},
|
|
784
|
+
});
|
|
785
|
+
}
|
|
786
|
+
else {
|
|
787
|
+
throw error instanceof Error
|
|
788
|
+
? error
|
|
789
|
+
: new Error("Unknown error occurred");
|
|
790
|
+
}
|
|
791
|
+
}
|
|
792
|
+
}
|
|
793
|
+
return [returnData];
|
|
794
|
+
}
|
|
795
|
+
}
|
|
796
|
+
exports.PayloadCms = PayloadCms;
|
package/package.json
ADDED
|
@@ -0,0 +1,61 @@
|
|
|
1
|
+
{
|
|
2
|
+
"name": "@warleon/n8n-nodes-payload-cms",
|
|
3
|
+
"version": "1.2.0",
|
|
4
|
+
"description": "Dynamic n8n node for Payload CMS that auto-discovers collections and operations forked and extended from https://github.com/leadership-institute/n8n-payload-dynamic",
|
|
5
|
+
"main": "dist/index.js",
|
|
6
|
+
"author": "warleon",
|
|
7
|
+
"license": "MIT",
|
|
8
|
+
"homepage": "https://github.com/warleon/n8n-payload-dynamic#readme",
|
|
9
|
+
"repository": {
|
|
10
|
+
"type": "git",
|
|
11
|
+
"url": "git+https://github.com/warleon/n8n-payload-dynamic.git"
|
|
12
|
+
},
|
|
13
|
+
"bugs": {
|
|
14
|
+
"url": "https://github.com/warleon/n8n-payload-dynamic/issues"
|
|
15
|
+
},
|
|
16
|
+
"files": [
|
|
17
|
+
"dist/**/*",
|
|
18
|
+
"README.md",
|
|
19
|
+
"LICENSE"
|
|
20
|
+
],
|
|
21
|
+
"publishConfig": {
|
|
22
|
+
"access": "public"
|
|
23
|
+
},
|
|
24
|
+
"scripts": {
|
|
25
|
+
"build": "tsc",
|
|
26
|
+
"dev": "tsc --watch",
|
|
27
|
+
"format": "prettier --write .",
|
|
28
|
+
"lint": "eslint .",
|
|
29
|
+
"test": "jest",
|
|
30
|
+
"prepublishOnly": "npm run build"
|
|
31
|
+
},
|
|
32
|
+
"keywords": [
|
|
33
|
+
"n8n",
|
|
34
|
+
"n8n-community-node",
|
|
35
|
+
"n8n-node",
|
|
36
|
+
"payload",
|
|
37
|
+
"payloadcms",
|
|
38
|
+
"cms",
|
|
39
|
+
"headless-cms",
|
|
40
|
+
"rest-api",
|
|
41
|
+
"automation",
|
|
42
|
+
"workflow"
|
|
43
|
+
],
|
|
44
|
+
"n8n": {
|
|
45
|
+
"nodes": [
|
|
46
|
+
"dist/nodes/PayloadCms/PayloadCms.node.js"
|
|
47
|
+
],
|
|
48
|
+
"credentials": [
|
|
49
|
+
"dist/credentials/PayloadCmsApi.credentials.js"
|
|
50
|
+
]
|
|
51
|
+
},
|
|
52
|
+
"devDependencies": {
|
|
53
|
+
"@types/node": "^20.0.0",
|
|
54
|
+
"n8n-core": "^1.0.0",
|
|
55
|
+
"n8n-workflow": "^1.0.0",
|
|
56
|
+
"typescript": "^5.0.0"
|
|
57
|
+
},
|
|
58
|
+
"dependencies": {
|
|
59
|
+
"axios": "^1.10.0"
|
|
60
|
+
}
|
|
61
|
+
}
|