@dropinblog/n8n-nodes-dropinblog 1.0.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 +57 -0
- package/dist/credentials/DropInBlogOAuth2Api.credentials.d.ts +10 -0
- package/dist/credentials/DropInBlogOAuth2Api.credentials.js +65 -0
- package/dist/nodes/DropInBlog/DropInBlog.node.d.ts +12 -0
- package/dist/nodes/DropInBlog/DropInBlog.node.js +376 -0
- package/dist/nodes/DropInBlog/DropInBlogTrigger.node.d.ts +18 -0
- package/dist/nodes/DropInBlog/DropInBlogTrigger.node.js +131 -0
- package/dist/nodes/DropInBlog/GenericFunctions.d.ts +2 -0
- package/dist/nodes/DropInBlog/GenericFunctions.js +19 -0
- package/dist/nodes/DropInBlog/constants.d.ts +1 -0
- package/dist/nodes/DropInBlog/constants.js +4 -0
- package/dist/nodes/DropInBlog/logo.svg +1 -0
- package/index.js +2 -0
- package/package.json +64 -0
package/LICENSE
ADDED
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
MIT License
|
|
2
|
+
|
|
3
|
+
Copyright (c) 2026 DropInBlog
|
|
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,57 @@
|
|
|
1
|
+
# n8n-nodes-dropinblog
|
|
2
|
+
|
|
3
|
+
This is an n8n community node. It lets you use [DropInBlog](https://dropinblog.com) in your n8n workflows.
|
|
4
|
+
|
|
5
|
+
DropInBlog is an SEO-friendly blog solution that embeds into any website.
|
|
6
|
+
|
|
7
|
+
[n8n](https://n8n.io/) is a [fair-code licensed](https://docs.n8n.io/reference/license/) workflow automation platform.
|
|
8
|
+
|
|
9
|
+
[Installation](#installation)
|
|
10
|
+
[Operations](#operations)
|
|
11
|
+
[Credentials](#credentials)
|
|
12
|
+
[Compatibility](#compatibility)
|
|
13
|
+
[Resources](#resources)
|
|
14
|
+
|
|
15
|
+
## Installation
|
|
16
|
+
|
|
17
|
+
Follow the [installation guide](https://docs.n8n.io/integrations/community-nodes/installation/) in the n8n community nodes documentation.
|
|
18
|
+
|
|
19
|
+
## Operations
|
|
20
|
+
|
|
21
|
+
### DropInBlog Node
|
|
22
|
+
|
|
23
|
+
- **Post**
|
|
24
|
+
- **Create** - Create a new blog post with title, content, and optional fields:
|
|
25
|
+
- Status
|
|
26
|
+
- Slug
|
|
27
|
+
- Featured image URL
|
|
28
|
+
- SEO title and description
|
|
29
|
+
- Keyword
|
|
30
|
+
- Author name
|
|
31
|
+
- Category names
|
|
32
|
+
- **Get** - Retrieve a post by its numeric ID or slug
|
|
33
|
+
- **Search** - Search blog posts with optional filters:
|
|
34
|
+
- Status (Draft/Published)
|
|
35
|
+
- Limit
|
|
36
|
+
|
|
37
|
+
### DropInBlog Trigger Node
|
|
38
|
+
|
|
39
|
+
- **Post Published** - Triggers when a post is published in your DropInBlog
|
|
40
|
+
|
|
41
|
+
## Credentials
|
|
42
|
+
|
|
43
|
+
This node uses OAuth2 authentication with DropInBlog. You'll need to:
|
|
44
|
+
|
|
45
|
+
1. Contact [DropInBlog support](https://dropinblog.com/contact/) to request OAuth credentials for your account
|
|
46
|
+
2. Configure the OAuth2 credentials in n8n with the Client ID and Client Secret provided
|
|
47
|
+
3. Complete the OAuth authorization flow
|
|
48
|
+
|
|
49
|
+
## Compatibility
|
|
50
|
+
|
|
51
|
+
Tested with n8n version 1.48.0 and above. Requires Node.js 18.10 or later.
|
|
52
|
+
|
|
53
|
+
## Resources
|
|
54
|
+
|
|
55
|
+
* [n8n community nodes documentation](https://docs.n8n.io/integrations/#community-nodes)
|
|
56
|
+
* [DropInBlog API documentation](https://dropinblog.readme.io/reference/api-reference)
|
|
57
|
+
* [DropInBlog website](https://dropinblog.com)
|
|
@@ -0,0 +1,10 @@
|
|
|
1
|
+
import type { IAuthenticateGeneric, ICredentialTestRequest, ICredentialType, INodeProperties } from 'n8n-workflow';
|
|
2
|
+
export declare class DropInBlogOAuth2Api implements ICredentialType {
|
|
3
|
+
name: string;
|
|
4
|
+
extends: string[];
|
|
5
|
+
displayName: string;
|
|
6
|
+
documentationUrl: string;
|
|
7
|
+
properties: INodeProperties[];
|
|
8
|
+
authenticate: IAuthenticateGeneric;
|
|
9
|
+
test: ICredentialTestRequest;
|
|
10
|
+
}
|
|
@@ -0,0 +1,65 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.DropInBlogOAuth2Api = void 0;
|
|
4
|
+
const constants_1 = require("../nodes/DropInBlog/constants");
|
|
5
|
+
class DropInBlogOAuth2Api {
|
|
6
|
+
constructor() {
|
|
7
|
+
this.name = 'dropInBlogOAuth2Api';
|
|
8
|
+
this.extends = ['oAuth2Api'];
|
|
9
|
+
this.displayName = 'DropInBlog OAuth2 API';
|
|
10
|
+
this.documentationUrl = 'https://dropinblog.com/docs/api';
|
|
11
|
+
this.properties = [
|
|
12
|
+
{
|
|
13
|
+
displayName: 'Grant Type',
|
|
14
|
+
name: 'grantType',
|
|
15
|
+
type: 'hidden',
|
|
16
|
+
default: 'authorizationCode',
|
|
17
|
+
},
|
|
18
|
+
{
|
|
19
|
+
displayName: 'Authorization URL',
|
|
20
|
+
name: 'authUrl',
|
|
21
|
+
type: 'hidden',
|
|
22
|
+
default: 'https://app.dropinblog.com/oauth/authorize',
|
|
23
|
+
},
|
|
24
|
+
{
|
|
25
|
+
displayName: 'Access Token URL',
|
|
26
|
+
name: 'accessTokenUrl',
|
|
27
|
+
type: 'hidden',
|
|
28
|
+
default: `${constants_1.API_BASE_URL}/oauth/token`,
|
|
29
|
+
},
|
|
30
|
+
{
|
|
31
|
+
displayName: 'Scope',
|
|
32
|
+
name: 'scope',
|
|
33
|
+
type: 'hidden',
|
|
34
|
+
default: '',
|
|
35
|
+
},
|
|
36
|
+
{
|
|
37
|
+
displayName: 'Auth URI Query Parameters',
|
|
38
|
+
name: 'authQueryParameters',
|
|
39
|
+
type: 'hidden',
|
|
40
|
+
default: '',
|
|
41
|
+
},
|
|
42
|
+
{
|
|
43
|
+
displayName: 'Authentication',
|
|
44
|
+
name: 'authentication',
|
|
45
|
+
type: 'hidden',
|
|
46
|
+
default: 'body',
|
|
47
|
+
},
|
|
48
|
+
];
|
|
49
|
+
this.authenticate = {
|
|
50
|
+
type: 'generic',
|
|
51
|
+
properties: {
|
|
52
|
+
headers: {
|
|
53
|
+
Authorization: '=Bearer {{$credentials.oauthTokenData.access_token}}',
|
|
54
|
+
},
|
|
55
|
+
},
|
|
56
|
+
};
|
|
57
|
+
this.test = {
|
|
58
|
+
request: {
|
|
59
|
+
baseURL: constants_1.API_BASE_URL,
|
|
60
|
+
url: '/v2/automations/blogs',
|
|
61
|
+
},
|
|
62
|
+
};
|
|
63
|
+
}
|
|
64
|
+
}
|
|
65
|
+
exports.DropInBlogOAuth2Api = DropInBlogOAuth2Api;
|
|
@@ -0,0 +1,12 @@
|
|
|
1
|
+
import type { IExecuteFunctions, ILoadOptionsFunctions, INodeExecutionData, INodePropertyOptions, INodeType, INodeTypeDescription } from 'n8n-workflow';
|
|
2
|
+
import { getBlogs } from './GenericFunctions';
|
|
3
|
+
export declare class DropInBlog implements INodeType {
|
|
4
|
+
description: INodeTypeDescription;
|
|
5
|
+
methods: {
|
|
6
|
+
loadOptions: {
|
|
7
|
+
getBlogs: typeof getBlogs;
|
|
8
|
+
getStatuses(this: ILoadOptionsFunctions): Promise<INodePropertyOptions[]>;
|
|
9
|
+
};
|
|
10
|
+
};
|
|
11
|
+
execute(this: IExecuteFunctions): Promise<INodeExecutionData[][]>;
|
|
12
|
+
}
|
|
@@ -0,0 +1,376 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.DropInBlog = void 0;
|
|
4
|
+
const n8n_workflow_1 = require("n8n-workflow");
|
|
5
|
+
const constants_1 = require("./constants");
|
|
6
|
+
const GenericFunctions_1 = require("./GenericFunctions");
|
|
7
|
+
class DropInBlog {
|
|
8
|
+
constructor() {
|
|
9
|
+
this.description = {
|
|
10
|
+
displayName: 'DropInBlog',
|
|
11
|
+
name: 'dropInBlog',
|
|
12
|
+
icon: 'file:logo.svg',
|
|
13
|
+
group: ['transform'],
|
|
14
|
+
version: 1,
|
|
15
|
+
subtitle: '={{$parameter["operation"] + ": " + $parameter["resource"]}}',
|
|
16
|
+
description: 'Interact with DropInBlog',
|
|
17
|
+
defaults: {
|
|
18
|
+
name: 'DropInBlog',
|
|
19
|
+
},
|
|
20
|
+
inputs: [n8n_workflow_1.NodeConnectionTypes.Main],
|
|
21
|
+
outputs: [n8n_workflow_1.NodeConnectionTypes.Main],
|
|
22
|
+
credentials: [
|
|
23
|
+
{
|
|
24
|
+
name: 'dropInBlogOAuth2Api',
|
|
25
|
+
required: true,
|
|
26
|
+
},
|
|
27
|
+
],
|
|
28
|
+
properties: [
|
|
29
|
+
{
|
|
30
|
+
displayName: 'Resource',
|
|
31
|
+
name: 'resource',
|
|
32
|
+
type: 'options',
|
|
33
|
+
noDataExpression: true,
|
|
34
|
+
options: [
|
|
35
|
+
{
|
|
36
|
+
name: 'Post',
|
|
37
|
+
value: 'post',
|
|
38
|
+
},
|
|
39
|
+
],
|
|
40
|
+
default: 'post',
|
|
41
|
+
description: 'The resource to operate on',
|
|
42
|
+
},
|
|
43
|
+
{
|
|
44
|
+
displayName: 'Operation',
|
|
45
|
+
name: 'operation',
|
|
46
|
+
type: 'options',
|
|
47
|
+
noDataExpression: true,
|
|
48
|
+
displayOptions: {
|
|
49
|
+
show: {
|
|
50
|
+
resource: ['post'],
|
|
51
|
+
},
|
|
52
|
+
},
|
|
53
|
+
options: [
|
|
54
|
+
{
|
|
55
|
+
name: 'Create',
|
|
56
|
+
value: 'create',
|
|
57
|
+
description: 'Create a new blog post',
|
|
58
|
+
action: 'Create a post',
|
|
59
|
+
},
|
|
60
|
+
{
|
|
61
|
+
name: 'Get',
|
|
62
|
+
value: 'get',
|
|
63
|
+
description: 'Get a post by ID or slug',
|
|
64
|
+
action: 'Get a post',
|
|
65
|
+
},
|
|
66
|
+
{
|
|
67
|
+
name: 'Search',
|
|
68
|
+
value: 'search',
|
|
69
|
+
description: 'Search blog posts',
|
|
70
|
+
action: 'Search posts',
|
|
71
|
+
},
|
|
72
|
+
],
|
|
73
|
+
default: 'create',
|
|
74
|
+
},
|
|
75
|
+
// Blog ID (shared by create, get, and search)
|
|
76
|
+
{
|
|
77
|
+
displayName: 'Blog',
|
|
78
|
+
name: 'blogId',
|
|
79
|
+
type: 'options',
|
|
80
|
+
typeOptions: {
|
|
81
|
+
loadOptionsMethod: 'getBlogs',
|
|
82
|
+
},
|
|
83
|
+
required: true,
|
|
84
|
+
default: '',
|
|
85
|
+
displayOptions: {
|
|
86
|
+
show: {
|
|
87
|
+
resource: ['post'],
|
|
88
|
+
operation: ['create', 'get', 'search'],
|
|
89
|
+
},
|
|
90
|
+
},
|
|
91
|
+
description: 'The blog to work with. Choose from the list.',
|
|
92
|
+
},
|
|
93
|
+
// Post ID or Slug (get only)
|
|
94
|
+
{
|
|
95
|
+
displayName: 'Post ID or Slug',
|
|
96
|
+
name: 'postIdentifier',
|
|
97
|
+
type: 'string',
|
|
98
|
+
required: true,
|
|
99
|
+
default: '',
|
|
100
|
+
displayOptions: {
|
|
101
|
+
show: {
|
|
102
|
+
resource: ['post'],
|
|
103
|
+
operation: ['get'],
|
|
104
|
+
},
|
|
105
|
+
},
|
|
106
|
+
description: 'The numeric ID or slug of the post to retrieve',
|
|
107
|
+
},
|
|
108
|
+
// Title (create only)
|
|
109
|
+
{
|
|
110
|
+
displayName: 'Title',
|
|
111
|
+
name: 'title',
|
|
112
|
+
type: 'string',
|
|
113
|
+
required: true,
|
|
114
|
+
default: '',
|
|
115
|
+
displayOptions: {
|
|
116
|
+
show: {
|
|
117
|
+
resource: ['post'],
|
|
118
|
+
operation: ['create'],
|
|
119
|
+
},
|
|
120
|
+
},
|
|
121
|
+
description: 'The title of the post',
|
|
122
|
+
},
|
|
123
|
+
// Content (create only)
|
|
124
|
+
{
|
|
125
|
+
displayName: 'Content',
|
|
126
|
+
name: 'content',
|
|
127
|
+
type: 'string',
|
|
128
|
+
typeOptions: {
|
|
129
|
+
rows: 10,
|
|
130
|
+
},
|
|
131
|
+
required: true,
|
|
132
|
+
default: '',
|
|
133
|
+
displayOptions: {
|
|
134
|
+
show: {
|
|
135
|
+
resource: ['post'],
|
|
136
|
+
operation: ['create'],
|
|
137
|
+
},
|
|
138
|
+
},
|
|
139
|
+
description: 'The HTML content of the post',
|
|
140
|
+
},
|
|
141
|
+
// Search Query (search only)
|
|
142
|
+
{
|
|
143
|
+
displayName: 'Search Query',
|
|
144
|
+
name: 'search',
|
|
145
|
+
type: 'string',
|
|
146
|
+
required: true,
|
|
147
|
+
default: '',
|
|
148
|
+
displayOptions: {
|
|
149
|
+
show: {
|
|
150
|
+
resource: ['post'],
|
|
151
|
+
operation: ['search'],
|
|
152
|
+
},
|
|
153
|
+
},
|
|
154
|
+
description: 'The search query to find posts',
|
|
155
|
+
},
|
|
156
|
+
// Additional Fields (create only)
|
|
157
|
+
{
|
|
158
|
+
displayName: 'Additional Fields',
|
|
159
|
+
name: 'additionalFields',
|
|
160
|
+
type: 'collection',
|
|
161
|
+
placeholder: 'Add Field',
|
|
162
|
+
default: {},
|
|
163
|
+
displayOptions: {
|
|
164
|
+
show: {
|
|
165
|
+
resource: ['post'],
|
|
166
|
+
operation: ['create'],
|
|
167
|
+
},
|
|
168
|
+
},
|
|
169
|
+
options: [
|
|
170
|
+
{
|
|
171
|
+
displayName: 'Author Name',
|
|
172
|
+
name: 'author_name',
|
|
173
|
+
type: 'string',
|
|
174
|
+
default: '',
|
|
175
|
+
description: 'The name of the author (will create or find existing author by name)',
|
|
176
|
+
},
|
|
177
|
+
{
|
|
178
|
+
displayName: 'Category Names',
|
|
179
|
+
name: 'category_names',
|
|
180
|
+
type: 'string',
|
|
181
|
+
default: '',
|
|
182
|
+
description: 'Comma separated list of category names to attach to your post (will create categories if they do not exist)',
|
|
183
|
+
},
|
|
184
|
+
{
|
|
185
|
+
displayName: 'Featured Image URL',
|
|
186
|
+
name: 'featured_image',
|
|
187
|
+
type: 'string',
|
|
188
|
+
default: '',
|
|
189
|
+
description: 'URL of the featured image',
|
|
190
|
+
},
|
|
191
|
+
{
|
|
192
|
+
displayName: 'Keyword',
|
|
193
|
+
name: 'keyword',
|
|
194
|
+
type: 'string',
|
|
195
|
+
default: '',
|
|
196
|
+
description: 'Primary SEO keyword for the post',
|
|
197
|
+
},
|
|
198
|
+
{
|
|
199
|
+
displayName: 'SEO Title',
|
|
200
|
+
name: 'seo_title',
|
|
201
|
+
type: 'string',
|
|
202
|
+
default: '',
|
|
203
|
+
description: 'Meta title for SEO (defaults to post title)',
|
|
204
|
+
},
|
|
205
|
+
{
|
|
206
|
+
displayName: 'SEO Description',
|
|
207
|
+
name: 'seo_description',
|
|
208
|
+
type: 'string',
|
|
209
|
+
typeOptions: {
|
|
210
|
+
rows: 3,
|
|
211
|
+
},
|
|
212
|
+
default: '',
|
|
213
|
+
description: 'Meta description for SEO',
|
|
214
|
+
},
|
|
215
|
+
{
|
|
216
|
+
displayName: 'Slug',
|
|
217
|
+
name: 'slug',
|
|
218
|
+
type: 'string',
|
|
219
|
+
default: '',
|
|
220
|
+
description: 'URL slug for the post (auto generated from title if not provided)',
|
|
221
|
+
},
|
|
222
|
+
{
|
|
223
|
+
displayName: 'Status',
|
|
224
|
+
name: 'status_id',
|
|
225
|
+
type: 'options',
|
|
226
|
+
typeOptions: {
|
|
227
|
+
loadOptionsMethod: 'getStatuses',
|
|
228
|
+
},
|
|
229
|
+
default: '',
|
|
230
|
+
description: 'The status of the post. Choose from the list.',
|
|
231
|
+
},
|
|
232
|
+
],
|
|
233
|
+
},
|
|
234
|
+
// Search Filters (search only)
|
|
235
|
+
{
|
|
236
|
+
displayName: 'Filters',
|
|
237
|
+
name: 'searchFilters',
|
|
238
|
+
type: 'collection',
|
|
239
|
+
placeholder: 'Add Filter',
|
|
240
|
+
default: {},
|
|
241
|
+
displayOptions: {
|
|
242
|
+
show: {
|
|
243
|
+
resource: ['post'],
|
|
244
|
+
operation: ['search'],
|
|
245
|
+
},
|
|
246
|
+
},
|
|
247
|
+
options: [
|
|
248
|
+
{
|
|
249
|
+
displayName: 'Status',
|
|
250
|
+
name: 'status',
|
|
251
|
+
type: 'options',
|
|
252
|
+
options: [
|
|
253
|
+
{
|
|
254
|
+
name: 'Draft',
|
|
255
|
+
value: 'draft',
|
|
256
|
+
},
|
|
257
|
+
{
|
|
258
|
+
name: 'Published',
|
|
259
|
+
value: 'published',
|
|
260
|
+
},
|
|
261
|
+
],
|
|
262
|
+
default: '',
|
|
263
|
+
description: 'Filter by post status',
|
|
264
|
+
},
|
|
265
|
+
{
|
|
266
|
+
displayName: 'Limit',
|
|
267
|
+
name: 'limit',
|
|
268
|
+
type: 'number',
|
|
269
|
+
typeOptions: {
|
|
270
|
+
minValue: 1,
|
|
271
|
+
maxValue: 50,
|
|
272
|
+
},
|
|
273
|
+
default: 20,
|
|
274
|
+
description: 'Max number of results to return (max 50)',
|
|
275
|
+
},
|
|
276
|
+
],
|
|
277
|
+
},
|
|
278
|
+
],
|
|
279
|
+
};
|
|
280
|
+
this.methods = {
|
|
281
|
+
loadOptions: {
|
|
282
|
+
getBlogs: GenericFunctions_1.getBlogs,
|
|
283
|
+
async getStatuses() {
|
|
284
|
+
const returnData = [];
|
|
285
|
+
const statuses = await this.helpers.requestWithAuthentication.call(this, 'dropInBlogOAuth2Api', {
|
|
286
|
+
method: 'GET',
|
|
287
|
+
url: `${constants_1.API_BASE_URL}/v2/automations/statuses`,
|
|
288
|
+
json: true,
|
|
289
|
+
});
|
|
290
|
+
for (const status of statuses) {
|
|
291
|
+
returnData.push({
|
|
292
|
+
name: status.name,
|
|
293
|
+
value: status.id,
|
|
294
|
+
});
|
|
295
|
+
}
|
|
296
|
+
return returnData;
|
|
297
|
+
},
|
|
298
|
+
},
|
|
299
|
+
};
|
|
300
|
+
}
|
|
301
|
+
async execute() {
|
|
302
|
+
const items = this.getInputData();
|
|
303
|
+
const returnData = [];
|
|
304
|
+
for (let i = 0; i < items.length; i++) {
|
|
305
|
+
try {
|
|
306
|
+
const resource = this.getNodeParameter('resource', i);
|
|
307
|
+
const operation = this.getNodeParameter('operation', i);
|
|
308
|
+
if (resource === 'post') {
|
|
309
|
+
if (operation === 'create') {
|
|
310
|
+
const blogId = this.getNodeParameter('blogId', i);
|
|
311
|
+
const title = this.getNodeParameter('title', i);
|
|
312
|
+
const content = this.getNodeParameter('content', i);
|
|
313
|
+
const additionalFields = this.getNodeParameter('additionalFields', i);
|
|
314
|
+
const body = {
|
|
315
|
+
title,
|
|
316
|
+
content,
|
|
317
|
+
...Object.fromEntries(Object.entries(additionalFields).filter(([, v]) => v !== undefined && v !== '')),
|
|
318
|
+
};
|
|
319
|
+
// the write endpoint for posts
|
|
320
|
+
const response = await this.helpers.requestWithAuthentication.call(this, 'dropInBlogOAuth2Api', {
|
|
321
|
+
method: 'POST',
|
|
322
|
+
url: `${constants_1.API_BASE_URL}/v2/blog/${blogId}/posts`,
|
|
323
|
+
body,
|
|
324
|
+
json: true,
|
|
325
|
+
});
|
|
326
|
+
const executionData = this.helpers.constructExecutionMetaData(this.helpers.returnJsonArray(response), { itemData: { item: i } });
|
|
327
|
+
returnData.push(...executionData);
|
|
328
|
+
}
|
|
329
|
+
else if (operation === 'get') {
|
|
330
|
+
const blogId = this.getNodeParameter('blogId', i);
|
|
331
|
+
const postIdentifier = this.getNodeParameter('postIdentifier', i);
|
|
332
|
+
// the read endpoint for individual posts
|
|
333
|
+
const response = await this.helpers.requestWithAuthentication.call(this, 'dropInBlogOAuth2Api', {
|
|
334
|
+
method: 'GET',
|
|
335
|
+
url: `${constants_1.API_BASE_URL}/v2/automations/${blogId}/posts/${encodeURIComponent(postIdentifier)}`,
|
|
336
|
+
json: true,
|
|
337
|
+
});
|
|
338
|
+
const executionData = this.helpers.constructExecutionMetaData(this.helpers.returnJsonArray(response), { itemData: { item: i } });
|
|
339
|
+
returnData.push(...executionData);
|
|
340
|
+
}
|
|
341
|
+
else if (operation === 'search') {
|
|
342
|
+
const blogId = this.getNodeParameter('blogId', i);
|
|
343
|
+
const search = this.getNodeParameter('search', i);
|
|
344
|
+
const searchFilters = this.getNodeParameter('searchFilters', i);
|
|
345
|
+
const qs = { search };
|
|
346
|
+
if (searchFilters.status) {
|
|
347
|
+
qs.status = searchFilters.status;
|
|
348
|
+
}
|
|
349
|
+
if (searchFilters.limit) {
|
|
350
|
+
qs.limit = searchFilters.limit;
|
|
351
|
+
}
|
|
352
|
+
// the read/search endpoint for posts
|
|
353
|
+
const response = await this.helpers.requestWithAuthentication.call(this, 'dropInBlogOAuth2Api', {
|
|
354
|
+
method: 'GET',
|
|
355
|
+
url: `${constants_1.API_BASE_URL}/v2/automations/${blogId}/posts/search`,
|
|
356
|
+
qs,
|
|
357
|
+
json: true,
|
|
358
|
+
});
|
|
359
|
+
const executionData = this.helpers.constructExecutionMetaData(this.helpers.returnJsonArray(response), { itemData: { item: i } });
|
|
360
|
+
returnData.push(...executionData);
|
|
361
|
+
}
|
|
362
|
+
}
|
|
363
|
+
}
|
|
364
|
+
catch (error) {
|
|
365
|
+
if (this.continueOnFail()) {
|
|
366
|
+
const executionData = this.helpers.constructExecutionMetaData(this.helpers.returnJsonArray({ error: error.message }), { itemData: { item: i } });
|
|
367
|
+
returnData.push(...executionData);
|
|
368
|
+
continue;
|
|
369
|
+
}
|
|
370
|
+
throw new n8n_workflow_1.NodeApiError(this.getNode(), error);
|
|
371
|
+
}
|
|
372
|
+
}
|
|
373
|
+
return [returnData];
|
|
374
|
+
}
|
|
375
|
+
}
|
|
376
|
+
exports.DropInBlog = DropInBlog;
|
|
@@ -0,0 +1,18 @@
|
|
|
1
|
+
import type { IHookFunctions, INodeType, INodeTypeDescription, IWebhookFunctions, IWebhookResponseData } from 'n8n-workflow';
|
|
2
|
+
import { getBlogs } from './GenericFunctions';
|
|
3
|
+
export declare class DropInBlogTrigger implements INodeType {
|
|
4
|
+
description: INodeTypeDescription;
|
|
5
|
+
methods: {
|
|
6
|
+
loadOptions: {
|
|
7
|
+
getBlogs: typeof getBlogs;
|
|
8
|
+
};
|
|
9
|
+
};
|
|
10
|
+
webhookMethods: {
|
|
11
|
+
default: {
|
|
12
|
+
checkExists(this: IHookFunctions): Promise<boolean>;
|
|
13
|
+
create(this: IHookFunctions): Promise<boolean>;
|
|
14
|
+
delete(this: IHookFunctions): Promise<boolean>;
|
|
15
|
+
};
|
|
16
|
+
};
|
|
17
|
+
webhook(this: IWebhookFunctions): Promise<IWebhookResponseData>;
|
|
18
|
+
}
|
|
@@ -0,0 +1,131 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.DropInBlogTrigger = void 0;
|
|
4
|
+
const n8n_workflow_1 = require("n8n-workflow");
|
|
5
|
+
const constants_1 = require("./constants");
|
|
6
|
+
const GenericFunctions_1 = require("./GenericFunctions");
|
|
7
|
+
class DropInBlogTrigger {
|
|
8
|
+
constructor() {
|
|
9
|
+
this.description = {
|
|
10
|
+
displayName: 'DropInBlog Trigger',
|
|
11
|
+
name: 'dropInBlogTrigger',
|
|
12
|
+
icon: 'file:logo.svg',
|
|
13
|
+
group: ['trigger'],
|
|
14
|
+
version: 1,
|
|
15
|
+
subtitle: '={{$parameter["event"]}}',
|
|
16
|
+
description: 'Starts the workflow when a DropInBlog event occurs',
|
|
17
|
+
defaults: {
|
|
18
|
+
name: 'DropInBlog Trigger',
|
|
19
|
+
},
|
|
20
|
+
inputs: [],
|
|
21
|
+
outputs: [n8n_workflow_1.NodeConnectionTypes.Main],
|
|
22
|
+
credentials: [
|
|
23
|
+
{
|
|
24
|
+
name: 'dropInBlogOAuth2Api',
|
|
25
|
+
required: true,
|
|
26
|
+
},
|
|
27
|
+
],
|
|
28
|
+
webhooks: [
|
|
29
|
+
{
|
|
30
|
+
name: 'default',
|
|
31
|
+
httpMethod: 'POST',
|
|
32
|
+
responseMode: 'onReceived',
|
|
33
|
+
path: 'webhook',
|
|
34
|
+
},
|
|
35
|
+
],
|
|
36
|
+
properties: [
|
|
37
|
+
{
|
|
38
|
+
displayName: 'Blog',
|
|
39
|
+
name: 'blogId',
|
|
40
|
+
type: 'options',
|
|
41
|
+
typeOptions: {
|
|
42
|
+
loadOptionsMethod: 'getBlogs',
|
|
43
|
+
},
|
|
44
|
+
required: true,
|
|
45
|
+
default: '',
|
|
46
|
+
description: 'The blog to listen for events from. Choose from the list.',
|
|
47
|
+
},
|
|
48
|
+
{
|
|
49
|
+
displayName: 'Event',
|
|
50
|
+
name: 'event',
|
|
51
|
+
type: 'options',
|
|
52
|
+
required: true,
|
|
53
|
+
default: 'post.published',
|
|
54
|
+
options: [
|
|
55
|
+
{
|
|
56
|
+
name: 'Post Published',
|
|
57
|
+
value: 'post.published',
|
|
58
|
+
description: 'Triggers when a post is published',
|
|
59
|
+
},
|
|
60
|
+
],
|
|
61
|
+
description: 'The event to listen for',
|
|
62
|
+
},
|
|
63
|
+
],
|
|
64
|
+
};
|
|
65
|
+
this.methods = {
|
|
66
|
+
loadOptions: {
|
|
67
|
+
getBlogs: GenericFunctions_1.getBlogs,
|
|
68
|
+
},
|
|
69
|
+
};
|
|
70
|
+
this.webhookMethods = {
|
|
71
|
+
default: {
|
|
72
|
+
async checkExists() {
|
|
73
|
+
const webhookData = this.getWorkflowStaticData('node');
|
|
74
|
+
if (webhookData.webhookId === undefined) {
|
|
75
|
+
return false;
|
|
76
|
+
}
|
|
77
|
+
return true;
|
|
78
|
+
},
|
|
79
|
+
async create() {
|
|
80
|
+
const webhookUrl = this.getNodeWebhookUrl('default');
|
|
81
|
+
const blogId = this.getNodeParameter('blogId');
|
|
82
|
+
const event = this.getNodeParameter('event');
|
|
83
|
+
const body = {
|
|
84
|
+
hookUrl: webhookUrl,
|
|
85
|
+
// The API expects events as a JSON-stringified array (intentional double-serialization)
|
|
86
|
+
events: JSON.stringify([event]),
|
|
87
|
+
source: 'n8n',
|
|
88
|
+
};
|
|
89
|
+
const response = await this.helpers.requestWithAuthentication.call(this, 'dropInBlogOAuth2Api', {
|
|
90
|
+
method: 'POST',
|
|
91
|
+
url: `${constants_1.API_BASE_URL}/v2/blog/${blogId}/webhooks`,
|
|
92
|
+
body,
|
|
93
|
+
json: true,
|
|
94
|
+
});
|
|
95
|
+
if (response.hookId === undefined) {
|
|
96
|
+
return false;
|
|
97
|
+
}
|
|
98
|
+
const webhookData = this.getWorkflowStaticData('node');
|
|
99
|
+
webhookData.webhookId = response.hookId;
|
|
100
|
+
webhookData.blogId = blogId;
|
|
101
|
+
return true;
|
|
102
|
+
},
|
|
103
|
+
async delete() {
|
|
104
|
+
const webhookData = this.getWorkflowStaticData('node');
|
|
105
|
+
if (webhookData.webhookId !== undefined) {
|
|
106
|
+
try {
|
|
107
|
+
await this.helpers.requestWithAuthentication.call(this, 'dropInBlogOAuth2Api', {
|
|
108
|
+
method: 'DELETE',
|
|
109
|
+
url: `${constants_1.API_BASE_URL}/v2/blog/${webhookData.blogId}/webhooks/${webhookData.webhookId}`,
|
|
110
|
+
json: true,
|
|
111
|
+
});
|
|
112
|
+
delete webhookData.webhookId;
|
|
113
|
+
delete webhookData.blogId;
|
|
114
|
+
}
|
|
115
|
+
catch (error) {
|
|
116
|
+
return false;
|
|
117
|
+
}
|
|
118
|
+
}
|
|
119
|
+
return true;
|
|
120
|
+
},
|
|
121
|
+
},
|
|
122
|
+
};
|
|
123
|
+
}
|
|
124
|
+
async webhook() {
|
|
125
|
+
const bodyData = this.getBodyData();
|
|
126
|
+
return {
|
|
127
|
+
workflowData: [this.helpers.returnJsonArray(bodyData)],
|
|
128
|
+
};
|
|
129
|
+
}
|
|
130
|
+
}
|
|
131
|
+
exports.DropInBlogTrigger = DropInBlogTrigger;
|
|
@@ -0,0 +1,19 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.getBlogs = getBlogs;
|
|
4
|
+
const constants_1 = require("./constants");
|
|
5
|
+
async function getBlogs() {
|
|
6
|
+
const returnData = [];
|
|
7
|
+
const blogs = await this.helpers.requestWithAuthentication.call(this, 'dropInBlogOAuth2Api', {
|
|
8
|
+
method: 'GET',
|
|
9
|
+
url: `${constants_1.API_BASE_URL}/v2/automations/blogs`,
|
|
10
|
+
json: true,
|
|
11
|
+
});
|
|
12
|
+
for (const blog of blogs) {
|
|
13
|
+
returnData.push({
|
|
14
|
+
name: blog.name,
|
|
15
|
+
value: blog.id,
|
|
16
|
+
});
|
|
17
|
+
}
|
|
18
|
+
return returnData;
|
|
19
|
+
}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export declare const API_BASE_URL = "https://api.dropinblog.com";
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 1024 1024"><path fill="#ed254e" d="M0 0h1024v1024H0z"/><path fill="#fff" d="M245 717s232-165 493-205c-261-40-493-205-493-205h391l205 205-205 205Z"/></svg>
|
package/index.js
ADDED
package/package.json
ADDED
|
@@ -0,0 +1,64 @@
|
|
|
1
|
+
{
|
|
2
|
+
"name": "@dropinblog/n8n-nodes-dropinblog",
|
|
3
|
+
"version": "1.0.0",
|
|
4
|
+
"description": "n8n community node for DropInBlog - Create posts and receive webhook notifications when posts are published",
|
|
5
|
+
"keywords": [
|
|
6
|
+
"n8n-community-node-package",
|
|
7
|
+
"n8n",
|
|
8
|
+
"dropinblog",
|
|
9
|
+
"blog",
|
|
10
|
+
"cms"
|
|
11
|
+
],
|
|
12
|
+
"license": "MIT",
|
|
13
|
+
"publishConfig": {
|
|
14
|
+
"access": "public"
|
|
15
|
+
},
|
|
16
|
+
"homepage": "https://dropinblog.com",
|
|
17
|
+
"author": {
|
|
18
|
+
"name": "DropInBlog"
|
|
19
|
+
},
|
|
20
|
+
"repository": {
|
|
21
|
+
"type": "git",
|
|
22
|
+
"url": "https://github.com/DropInBlog/n8n-nodes.git"
|
|
23
|
+
},
|
|
24
|
+
"engines": {
|
|
25
|
+
"node": ">=18.10",
|
|
26
|
+
"pnpm": ">=9.1"
|
|
27
|
+
},
|
|
28
|
+
"packageManager": "pnpm@9.1.4",
|
|
29
|
+
"main": "index.js",
|
|
30
|
+
"scripts": {
|
|
31
|
+
"preinstall": "npx only-allow pnpm",
|
|
32
|
+
"build": "tsc && gulp build:icons",
|
|
33
|
+
"dev": "tsc --watch",
|
|
34
|
+
"format": "prettier nodes/**/*.ts credentials/**/*.ts --write",
|
|
35
|
+
"lint": "eslint nodes/**/*.ts credentials/**/*.ts package.json",
|
|
36
|
+
"lintfix": "eslint nodes/**/*.ts credentials/**/*.ts package.json --fix",
|
|
37
|
+
"prepublishOnly": "pnpm build && pnpm lint -c .eslintrc.prepublish.js nodes/**/*.ts credentials/**/*.ts"
|
|
38
|
+
},
|
|
39
|
+
"files": [
|
|
40
|
+
"dist"
|
|
41
|
+
],
|
|
42
|
+
"n8n": {
|
|
43
|
+
"n8nNodesApiVersion": 1,
|
|
44
|
+
"credentials": [
|
|
45
|
+
"dist/credentials/DropInBlogOAuth2Api.credentials.js"
|
|
46
|
+
],
|
|
47
|
+
"nodes": [
|
|
48
|
+
"dist/nodes/DropInBlog/DropInBlog.node.js",
|
|
49
|
+
"dist/nodes/DropInBlog/DropInBlogTrigger.node.js"
|
|
50
|
+
]
|
|
51
|
+
},
|
|
52
|
+
"devDependencies": {
|
|
53
|
+
"@typescript-eslint/parser": "^7.15.0",
|
|
54
|
+
"eslint": "^8.56.0",
|
|
55
|
+
"eslint-plugin-n8n-nodes-base": "^1.16.1",
|
|
56
|
+
"gulp": "^4.0.2",
|
|
57
|
+
"n8n-workflow": "^1.48.0",
|
|
58
|
+
"prettier": "^3.3.2",
|
|
59
|
+
"typescript": "^5.5.3"
|
|
60
|
+
},
|
|
61
|
+
"peerDependencies": {
|
|
62
|
+
"n8n-workflow": "*"
|
|
63
|
+
}
|
|
64
|
+
}
|