n8n-nodes-blossom 2.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.md +19 -0
- package/README.md +473 -0
- package/dist/credentials/BlossomApi.credentials.d.ts +10 -0
- package/dist/credentials/BlossomApi.credentials.js +137 -0
- package/dist/credentials/BlossomApi.credentials.js.map +1 -0
- package/dist/icons/blossom.dark.svg +7 -0
- package/dist/icons/blossom.svg +7 -0
- package/dist/icons/github.dark.svg +3 -0
- package/dist/icons/github.svg +3 -0
- package/dist/nodes/Blossom/Blossom.node.d.ts +5 -0
- package/dist/nodes/Blossom/Blossom.node.js +566 -0
- package/dist/nodes/Blossom/Blossom.node.js.map +1 -0
- package/dist/nodes/Blossom/Blossom.node.json +19 -0
- package/dist/nodes/Blossom/resources/groups/index.d.ts +2 -0
- package/dist/nodes/Blossom/resources/groups/index.js +439 -0
- package/dist/nodes/Blossom/resources/groups/index.js.map +1 -0
- package/dist/nodes/Blossom/resources/memberships/index.d.ts +2 -0
- package/dist/nodes/Blossom/resources/memberships/index.js +228 -0
- package/dist/nodes/Blossom/resources/memberships/index.js.map +1 -0
- package/dist/nodes/Blossom/resources/performances/index.d.ts +2 -0
- package/dist/nodes/Blossom/resources/performances/index.js +55 -0
- package/dist/nodes/Blossom/resources/performances/index.js.map +1 -0
- package/dist/nodes/Blossom/resources/suppliers/index.d.ts +2 -0
- package/dist/nodes/Blossom/resources/suppliers/index.js +156 -0
- package/dist/nodes/Blossom/resources/suppliers/index.js.map +1 -0
- package/dist/nodes/Blossom/resources/users/index.d.ts +2 -0
- package/dist/nodes/Blossom/resources/users/index.js +355 -0
- package/dist/nodes/Blossom/resources/users/index.js.map +1 -0
- package/dist/nodes/Blossom/resources/utilities/index.d.ts +2 -0
- package/dist/nodes/Blossom/resources/utilities/index.js +254 -0
- package/dist/nodes/Blossom/resources/utilities/index.js.map +1 -0
- package/dist/nodes/Blossom/shared/transport.d.ts +3 -0
- package/dist/nodes/Blossom/shared/transport.js +107 -0
- package/dist/nodes/Blossom/shared/transport.js.map +1 -0
- package/dist/package.json +54 -0
- package/dist/tsconfig.tsbuildinfo +1 -0
- package/package.json +54 -0
package/LICENSE.md
ADDED
|
@@ -0,0 +1,19 @@
|
|
|
1
|
+
Copyright 2022 n8n
|
|
2
|
+
|
|
3
|
+
Permission is hereby granted, free of charge, to any person obtaining a copy of
|
|
4
|
+
this software and associated documentation files (the "Software"), to deal in
|
|
5
|
+
the Software without restriction, including without limitation the rights to
|
|
6
|
+
use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies
|
|
7
|
+
of the Software, and to permit persons to whom the Software is furnished to do
|
|
8
|
+
so, subject to the following conditions:
|
|
9
|
+
|
|
10
|
+
The above copyright notice and this permission notice shall be included in all
|
|
11
|
+
copies or substantial portions of the Software.
|
|
12
|
+
|
|
13
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
|
14
|
+
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
|
15
|
+
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
|
16
|
+
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
|
17
|
+
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
|
18
|
+
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
|
19
|
+
SOFTWARE.
|
package/README.md
ADDED
|
@@ -0,0 +1,473 @@
|
|
|
1
|
+
# n8n-nodes-blossom
|
|
2
|
+
|
|
3
|
+
A comprehensive n8n community node for integrating with Blossom Learning Management System (LMS) API. This node provides full access to the Blossom Sync API V2, supporting all operations for users, groups, memberships, utilities, suppliers, and performance data.
|
|
4
|
+
|
|
5
|
+
## Features
|
|
6
|
+
|
|
7
|
+
- **Complete API Coverage**: Supports all Blossom Sync API V2 operations
|
|
8
|
+
- **Multiple Authentication Methods**: Basic Auth, API Key, JWT, and OAuth 2.0
|
|
9
|
+
- **CSV Import/Export**: Bulk operations for users, groups, and performance data
|
|
10
|
+
- **File Upload Support**: Avatar images, diploma files, and CSV imports
|
|
11
|
+
- **Generic API Access**: Make custom requests to any Blossom endpoint
|
|
12
|
+
- **Comprehensive Error Handling**: Detailed error messages and validation
|
|
13
|
+
|
|
14
|
+
## Installation
|
|
15
|
+
|
|
16
|
+
```bash
|
|
17
|
+
npm install n8n-nodes-blossom
|
|
18
|
+
```
|
|
19
|
+
|
|
20
|
+
### Important Note for Existing Users
|
|
21
|
+
|
|
22
|
+
If you already have an older version installed and are experiencing issues with updates, you may need to:
|
|
23
|
+
|
|
24
|
+
1. **Clear npm cache** (if getting 404 errors):
|
|
25
|
+
```bash
|
|
26
|
+
npm cache clean --force
|
|
27
|
+
```
|
|
28
|
+
|
|
29
|
+
2. **Uninstall the old version**:
|
|
30
|
+
```bash
|
|
31
|
+
npm uninstall n8n-nodes-blossom
|
|
32
|
+
```
|
|
33
|
+
|
|
34
|
+
3. **Install the latest version**:
|
|
35
|
+
```bash
|
|
36
|
+
npm install n8n-nodes-blossom@latest
|
|
37
|
+
```
|
|
38
|
+
|
|
39
|
+
4. **If still having issues, try installing specific version**:
|
|
40
|
+
```bash
|
|
41
|
+
npm install n8n-nodes-blossom@2.0.0
|
|
42
|
+
```
|
|
43
|
+
|
|
44
|
+
**Note**: Older versions (1.1.1 and below) were removed from NPM for security reasons. If you're getting 404 errors, it means n8n is trying to download a version that no longer exists.
|
|
45
|
+
|
|
46
|
+
## Authentication
|
|
47
|
+
|
|
48
|
+
The node supports multiple authentication methods:
|
|
49
|
+
|
|
50
|
+
### Basic Authentication
|
|
51
|
+
- **Username**: Your Blossom username
|
|
52
|
+
- **Password**: Your Blossom password
|
|
53
|
+
- **Base URL**: Your Blossom instance URL (e.g., `https://blossom-kc.com/`)
|
|
54
|
+
|
|
55
|
+
### API Key Authentication
|
|
56
|
+
- **API Key**: Your Blossom API key
|
|
57
|
+
- **Base URL**: Your Blossom instance URL
|
|
58
|
+
|
|
59
|
+
### JWT Authentication
|
|
60
|
+
- **JWT Token**: JWT token generated with payload: `{"iss":"<user_name>","exp":<unix_timestamp>}`
|
|
61
|
+
- **Base URL**: Your Blossom instance URL
|
|
62
|
+
|
|
63
|
+
### OAuth 2.0 Authentication
|
|
64
|
+
- **OAuth 2.0 Token**: Access token obtained from `{{baseUrl}}/WebServices/sync_2?auth_token`
|
|
65
|
+
- **Base URL**: Your Blossom instance URL
|
|
66
|
+
|
|
67
|
+
## Available Operations
|
|
68
|
+
|
|
69
|
+
### User Management
|
|
70
|
+
- **Update User**: Create or update user with full profile details
|
|
71
|
+
- **Import Users CSV**: Bulk import users from CSV/Excel files
|
|
72
|
+
- **Delete User**: Delete a single user (soft delete)
|
|
73
|
+
- **Delete Users CSV**: Bulk delete users from CSV/Excel files
|
|
74
|
+
- **Set Avatar**: Upload or remove user avatar (JPG/PNG)
|
|
75
|
+
|
|
76
|
+
### Group Management
|
|
77
|
+
- **Update Group**: Create or update groups, courses, roles, organizational units, templates, qualifications, and workplans
|
|
78
|
+
- **Import Groups CSV**: Bulk import groups from CSV/Excel files
|
|
79
|
+
- **Delete Group**: Delete a group object
|
|
80
|
+
- **Attach Sub Group**: Attach sub group to parent group
|
|
81
|
+
- **Detach Sub Group**: Detach sub group from parent
|
|
82
|
+
- **Attach Instance**: Attach group/course to template
|
|
83
|
+
- **Detach Instance**: Detach group/course from template
|
|
84
|
+
|
|
85
|
+
### Membership Management
|
|
86
|
+
- **Attach User to Group**: Add user to group, course, OU, qualification, or workplan
|
|
87
|
+
- **Detach User from Group**: Remove user from group
|
|
88
|
+
- **Detach User from OU**: Remove user from organizational unit
|
|
89
|
+
- **Import Groups Members CSV**: Bulk attach users to groups via CSV
|
|
90
|
+
- **Attach Manager**: Add manager to group with permissions
|
|
91
|
+
- **Detach Manager**: Remove manager from group
|
|
92
|
+
|
|
93
|
+
### Utility Functions
|
|
94
|
+
- **Test**: Test API connection and get random number
|
|
95
|
+
- **Run Auto Enrollment Rules**: Execute auto enrollment for all workspaces and users
|
|
96
|
+
- **Run Scheduled Imports**: Execute scheduled imports from SFTP or local folder
|
|
97
|
+
- **Remove Empty Org Units**: Delete empty organizational units
|
|
98
|
+
- **User Authorities**: Set HR manager, professional manager, coach, and supervisor
|
|
99
|
+
- **Power Manager**: Set/unset user as power manager
|
|
100
|
+
- **Upload Diploma**: Upload or remove diploma file for user in group
|
|
101
|
+
|
|
102
|
+
### Supplier Management
|
|
103
|
+
- **Update Supplier**: Create or update supplier by external ID
|
|
104
|
+
- **Delete Supplier**: Delete supplier by external ID
|
|
105
|
+
|
|
106
|
+
### Performance Management
|
|
107
|
+
- **Import Assignment Performances CSV**: Import assignment performance data
|
|
108
|
+
- **Import Group Performances CSV**: Import group performance data for qualifications and courses
|
|
109
|
+
|
|
110
|
+
### Generic API
|
|
111
|
+
- **Make Request**: Make custom API requests to any Blossom endpoint
|
|
112
|
+
|
|
113
|
+
## Usage Examples
|
|
114
|
+
|
|
115
|
+
### 1. Create a New User
|
|
116
|
+
|
|
117
|
+
```json
|
|
118
|
+
{
|
|
119
|
+
"resource": "User",
|
|
120
|
+
"operation": "updateUser",
|
|
121
|
+
"domain": "1",
|
|
122
|
+
"details": {
|
|
123
|
+
"external_id": "user123",
|
|
124
|
+
"username": "john.doe",
|
|
125
|
+
"firstname": "John",
|
|
126
|
+
"lastname": "Doe",
|
|
127
|
+
"email": "john.doe@company.com",
|
|
128
|
+
"password": "securePassword123",
|
|
129
|
+
"birthday": "1990-01-15",
|
|
130
|
+
"job_title": "Software Developer",
|
|
131
|
+
"department": "IT"
|
|
132
|
+
}
|
|
133
|
+
}
|
|
134
|
+
```
|
|
135
|
+
|
|
136
|
+
### 2. Import Users from CSV
|
|
137
|
+
|
|
138
|
+
```json
|
|
139
|
+
{
|
|
140
|
+
"resource": "User",
|
|
141
|
+
"operation": "importUsersCSV",
|
|
142
|
+
"domain": "1",
|
|
143
|
+
"options": {
|
|
144
|
+
"keep_old_values": "1",
|
|
145
|
+
"temp_password": "0",
|
|
146
|
+
"new_user_notification": "1"
|
|
147
|
+
},
|
|
148
|
+
"sheet_file": {
|
|
149
|
+
"value": "csv_content_here",
|
|
150
|
+
"options": {
|
|
151
|
+
"filename": "users.csv",
|
|
152
|
+
"contentType": "text/csv"
|
|
153
|
+
}
|
|
154
|
+
}
|
|
155
|
+
}
|
|
156
|
+
```
|
|
157
|
+
|
|
158
|
+
### 3. Create a Group/Course
|
|
159
|
+
|
|
160
|
+
```json
|
|
161
|
+
{
|
|
162
|
+
"resource": "Group",
|
|
163
|
+
"operation": "updateGroup",
|
|
164
|
+
"domain": "1",
|
|
165
|
+
"details": {
|
|
166
|
+
"external_id": "course456",
|
|
167
|
+
"name": "Advanced JavaScript",
|
|
168
|
+
"type": "course",
|
|
169
|
+
"description": "Learn advanced JavaScript concepts",
|
|
170
|
+
"open_date": "2024-02-01",
|
|
171
|
+
"close_date": "2024-03-01",
|
|
172
|
+
"passing_grade": "80"
|
|
173
|
+
}
|
|
174
|
+
}
|
|
175
|
+
```
|
|
176
|
+
|
|
177
|
+
### 4. Attach User to Group
|
|
178
|
+
|
|
179
|
+
```json
|
|
180
|
+
{
|
|
181
|
+
"resource": "Membership",
|
|
182
|
+
"operation": "attachUserToGroup",
|
|
183
|
+
"domain": "1",
|
|
184
|
+
"user_identifier": {
|
|
185
|
+
"external_id": "user123"
|
|
186
|
+
},
|
|
187
|
+
"group_identifier": {
|
|
188
|
+
"external_id": "course456"
|
|
189
|
+
}
|
|
190
|
+
}
|
|
191
|
+
```
|
|
192
|
+
|
|
193
|
+
### 5. Upload User Avatar
|
|
194
|
+
|
|
195
|
+
```json
|
|
196
|
+
{
|
|
197
|
+
"resource": "User",
|
|
198
|
+
"operation": "setAvatar",
|
|
199
|
+
"domain": "1",
|
|
200
|
+
"user_identifier": {
|
|
201
|
+
"external_id": "user123"
|
|
202
|
+
},
|
|
203
|
+
"remove_avatar": "0",
|
|
204
|
+
"avatarfile": {
|
|
205
|
+
"value": "binary_image_data",
|
|
206
|
+
"options": {
|
|
207
|
+
"filename": "avatar.jpg",
|
|
208
|
+
"contentType": "image/jpeg"
|
|
209
|
+
}
|
|
210
|
+
}
|
|
211
|
+
}
|
|
212
|
+
```
|
|
213
|
+
|
|
214
|
+
### 6. Test API Connection
|
|
215
|
+
|
|
216
|
+
```json
|
|
217
|
+
{
|
|
218
|
+
"resource": "Utility",
|
|
219
|
+
"operation": "test"
|
|
220
|
+
}
|
|
221
|
+
```
|
|
222
|
+
|
|
223
|
+
## CSV Import Templates
|
|
224
|
+
|
|
225
|
+
### Users CSV Template
|
|
226
|
+
Required columns: `external_id`, `username`, `firstname`, `lastname`, `email`
|
|
227
|
+
Optional columns: `password`, `birthday`, `job_title`, `department`, `company`, `address`, `city`, `zip`, `bphone`, `hphone`, `mphone`, `gender`, `employment_date`, `about`, `user_nt`, `disabled`, `חטיבה`, `קבלן שטח`, `קבלן משרדי`
|
|
228
|
+
|
|
229
|
+
### Groups CSV Template
|
|
230
|
+
Required columns: `external_id`, `name`, `type`
|
|
231
|
+
Optional columns: `description`, `open_date`, `close_date`, `passing_grade`, `gathering_area`, `location`, `audience`, `estimated_budget`, `publish_grades_criteria`, `publish_grades_on_add`, `hide_score`, `hide_from_members`, `hide_from_user_profile`, `parent_external_id`, `template_external_id`, `classification`
|
|
232
|
+
|
|
233
|
+
### Group Members CSV Template
|
|
234
|
+
Required columns: `user_external_id` (or `user_id`), `workspace_external_id` (or `group_id`)
|
|
235
|
+
Optional columns: `manager_external_id`, `manager_type`
|
|
236
|
+
|
|
237
|
+
## Error Handling
|
|
238
|
+
|
|
239
|
+
The node provides comprehensive error handling with detailed error messages:
|
|
240
|
+
|
|
241
|
+
- **API Errors**: Returns specific error messages from the Blossom API
|
|
242
|
+
- **Validation Errors**: Validates required parameters before making requests
|
|
243
|
+
- **File Upload Errors**: Handles file format and size validation
|
|
244
|
+
- **Authentication Errors**: Clear messages for authentication failures
|
|
245
|
+
|
|
246
|
+
## Rate Limits
|
|
247
|
+
|
|
248
|
+
- **CSV Operations**: Maximum 4 calls per 24 hours
|
|
249
|
+
- **General API**: 30 requests per second
|
|
250
|
+
- **Scheduled Operations**: Run outside working hours for best performance
|
|
251
|
+
|
|
252
|
+
## What's Included
|
|
253
|
+
|
|
254
|
+
This starter repository includes two example nodes to learn from:
|
|
255
|
+
|
|
256
|
+
- **[Example Node](nodes/Example/)** - A simple starter node that shows the basic structure with a custom `execute` method
|
|
257
|
+
- **[GitHub Issues Node](nodes/GithubIssues/)** - A complete, production-ready example built using the **declarative style**:
|
|
258
|
+
- **Low-code approach** - Define operations declaratively without writing request logic
|
|
259
|
+
- Multiple resources (Issues, Comments)
|
|
260
|
+
- Multiple operations (Get, Get All, Create)
|
|
261
|
+
- Two authentication methods (OAuth2 and Personal Access Token)
|
|
262
|
+
- List search functionality for dynamic dropdowns
|
|
263
|
+
- Proper error handling and typing
|
|
264
|
+
- Ideal for HTTP API-based integrations
|
|
265
|
+
|
|
266
|
+
> [!TIP]
|
|
267
|
+
> The declarative/low-code style (used in GitHub Issues) is the recommended approach for building nodes that interact with HTTP APIs. It significantly reduces boilerplate code and handles requests automatically.
|
|
268
|
+
|
|
269
|
+
Browse these examples to understand both approaches, then modify them or create your own.
|
|
270
|
+
|
|
271
|
+
## Finding Inspiration
|
|
272
|
+
|
|
273
|
+
Looking for more examples? Check out these resources:
|
|
274
|
+
|
|
275
|
+
- **[npm Community Nodes](https://www.npmjs.com/search?q=keywords:n8n-community-node-package)** - Browse thousands of community-built nodes on npm using the `n8n-community-node-package` tag
|
|
276
|
+
- **[n8n Built-in Nodes](https://github.com/n8n-io/n8n/tree/master/packages/nodes-base/nodes)** - Study the source code of n8n's official nodes for production-ready patterns and best practices
|
|
277
|
+
- **[n8n Credentials](https://github.com/n8n-io/n8n/tree/master/packages/nodes-base/credentials)** - See how authentication is implemented for various services
|
|
278
|
+
|
|
279
|
+
These are excellent resources to understand how to structure your nodes, handle different API patterns, and implement advanced features.
|
|
280
|
+
|
|
281
|
+
## Prerequisites
|
|
282
|
+
|
|
283
|
+
Before you begin, install the following on your development machine:
|
|
284
|
+
|
|
285
|
+
### Required
|
|
286
|
+
|
|
287
|
+
- **[Node.js](https://nodejs.org/)** (v22 or higher) and npm
|
|
288
|
+
- Linux/Mac/WSL: Install via [nvm](https://github.com/nvm-sh/nvm)
|
|
289
|
+
- Windows: Follow [Microsoft's NodeJS guide](https://learn.microsoft.com/en-us/windows/dev-environment/javascript/nodejs-on-windows)
|
|
290
|
+
- **[git](https://git-scm.com/downloads)**
|
|
291
|
+
|
|
292
|
+
### Recommended
|
|
293
|
+
|
|
294
|
+
- Follow n8n's [development environment setup guide](https://docs.n8n.io/integrations/creating-nodes/build/node-development-environment/)
|
|
295
|
+
|
|
296
|
+
> [!NOTE]
|
|
297
|
+
> The `@n8n/node-cli` is included as a dev dependency and will be installed automatically when you run `npm install`. The CLI includes n8n for local development, so you don't need to install n8n globally.
|
|
298
|
+
|
|
299
|
+
## Getting Started with this Starter
|
|
300
|
+
|
|
301
|
+
Follow these steps to create your own n8n community node package:
|
|
302
|
+
|
|
303
|
+
### 1. Create Your Repository
|
|
304
|
+
|
|
305
|
+
[Generate a new repository](https://github.com/n8n-io/n8n-nodes-starter/generate) from this template, then clone it:
|
|
306
|
+
|
|
307
|
+
```bash
|
|
308
|
+
git clone https://github.com/<your-organization>/<your-repo-name>.git
|
|
309
|
+
cd <your-repo-name>
|
|
310
|
+
```
|
|
311
|
+
|
|
312
|
+
### 2. Install Dependencies
|
|
313
|
+
|
|
314
|
+
```bash
|
|
315
|
+
npm install
|
|
316
|
+
```
|
|
317
|
+
|
|
318
|
+
This installs all required dependencies including the `@n8n/node-cli`.
|
|
319
|
+
|
|
320
|
+
### 3. Explore the Examples
|
|
321
|
+
|
|
322
|
+
Browse the example nodes in [nodes/](nodes/) and [credentials/](credentials/) to understand the structure:
|
|
323
|
+
|
|
324
|
+
- Start with [nodes/Example/](nodes/Example/) for a basic node
|
|
325
|
+
- Study [nodes/GithubIssues/](nodes/GithubIssues/) for a real-world implementation
|
|
326
|
+
|
|
327
|
+
### 4. Build Your Node
|
|
328
|
+
|
|
329
|
+
Edit the example nodes to fit your use case, or create new node files by copying the structure from [nodes/Example/](nodes/Example/).
|
|
330
|
+
|
|
331
|
+
> [!TIP]
|
|
332
|
+
> If you want to scaffold a completely new node package, use `npm create @n8n/node` to start fresh with the CLI's interactive generator.
|
|
333
|
+
|
|
334
|
+
### 5. Configure Your Package
|
|
335
|
+
|
|
336
|
+
Update `package.json` with your details:
|
|
337
|
+
|
|
338
|
+
- `name` - Your package name (must start with `n8n-nodes-`)
|
|
339
|
+
- `author` - Your name and email
|
|
340
|
+
- `repository` - Your repository URL
|
|
341
|
+
- `description` - What your node does
|
|
342
|
+
|
|
343
|
+
Make sure your node is registered in the `n8n.nodes` array.
|
|
344
|
+
|
|
345
|
+
### 6. Develop and Test Locally
|
|
346
|
+
|
|
347
|
+
Start n8n with your node loaded:
|
|
348
|
+
|
|
349
|
+
```bash
|
|
350
|
+
npm run dev
|
|
351
|
+
```
|
|
352
|
+
|
|
353
|
+
This command runs `n8n-node dev` which:
|
|
354
|
+
|
|
355
|
+
- Builds your node with watch mode
|
|
356
|
+
- Starts n8n with your node available
|
|
357
|
+
- Automatically rebuilds when you make changes
|
|
358
|
+
- Opens n8n in your browser (usually http://localhost:5678)
|
|
359
|
+
|
|
360
|
+
You can now test your node in n8n workflows!
|
|
361
|
+
|
|
362
|
+
> [!NOTE]
|
|
363
|
+
> Learn more about CLI commands in the [@n8n/node-cli documentation](https://www.npmjs.com/package/@n8n/node-cli).
|
|
364
|
+
|
|
365
|
+
### 7. Lint Your Code
|
|
366
|
+
|
|
367
|
+
Check for errors:
|
|
368
|
+
|
|
369
|
+
```bash
|
|
370
|
+
npm run lint
|
|
371
|
+
```
|
|
372
|
+
|
|
373
|
+
Auto-fix issues when possible:
|
|
374
|
+
|
|
375
|
+
```bash
|
|
376
|
+
npm run lint:fix
|
|
377
|
+
```
|
|
378
|
+
|
|
379
|
+
### 8. Build for Production
|
|
380
|
+
|
|
381
|
+
When ready to publish:
|
|
382
|
+
|
|
383
|
+
```bash
|
|
384
|
+
npm run build
|
|
385
|
+
```
|
|
386
|
+
|
|
387
|
+
This compiles your TypeScript code to the `dist/` folder.
|
|
388
|
+
|
|
389
|
+
### 9. Prepare for Publishing
|
|
390
|
+
|
|
391
|
+
Before publishing:
|
|
392
|
+
|
|
393
|
+
1. **Update documentation**: Replace this README with your node's documentation. Use [README_TEMPLATE.md](README_TEMPLATE.md) as a starting point.
|
|
394
|
+
2. **Update the LICENSE**: Add your details to the [LICENSE](LICENSE.md) file.
|
|
395
|
+
3. **Test thoroughly**: Ensure your node works in different scenarios.
|
|
396
|
+
|
|
397
|
+
### 10. Publish to npm
|
|
398
|
+
|
|
399
|
+
Publish your package to make it available to the n8n community:
|
|
400
|
+
|
|
401
|
+
```bash
|
|
402
|
+
npm publish
|
|
403
|
+
```
|
|
404
|
+
|
|
405
|
+
Learn more about [publishing to npm](https://docs.npmjs.com/packages-and-modules/contributing-packages-to-the-registry).
|
|
406
|
+
|
|
407
|
+
### 11. Submit for Verification (Optional)
|
|
408
|
+
|
|
409
|
+
Get your node verified for n8n Cloud:
|
|
410
|
+
|
|
411
|
+
1. Ensure your node meets the [requirements](https://docs.n8n.io/integrations/creating-nodes/deploy/submit-community-nodes/):
|
|
412
|
+
- Uses MIT license ✅ (included in this starter)
|
|
413
|
+
- No external package dependencies
|
|
414
|
+
- Follows n8n's design guidelines
|
|
415
|
+
- Passes quality and security review
|
|
416
|
+
|
|
417
|
+
2. Submit through the [n8n Creator Portal](https://creators.n8n.io/nodes)
|
|
418
|
+
|
|
419
|
+
**Benefits of verification:**
|
|
420
|
+
|
|
421
|
+
- Available directly in n8n Cloud
|
|
422
|
+
- Discoverable in the n8n nodes panel
|
|
423
|
+
- Verified badge for quality assurance
|
|
424
|
+
- Increased visibility in the n8n community
|
|
425
|
+
|
|
426
|
+
## Available Scripts
|
|
427
|
+
|
|
428
|
+
This starter includes several npm scripts to streamline development:
|
|
429
|
+
|
|
430
|
+
| Script | Description |
|
|
431
|
+
| --------------------- | ---------------------------------------------------------------- |
|
|
432
|
+
| `npm run dev` | Start n8n with your node and watch for changes (runs `n8n-node dev`) |
|
|
433
|
+
| `npm run build` | Compile TypeScript to JavaScript for production (runs `n8n-node build`) |
|
|
434
|
+
| `npm run build:watch` | Build in watch mode (auto-rebuild on changes) |
|
|
435
|
+
| `npm run lint` | Check your code for errors and style issues (runs `n8n-node lint`) |
|
|
436
|
+
| `npm run lint:fix` | Automatically fix linting issues when possible (runs `n8n-node lint --fix`) |
|
|
437
|
+
| `npm run release` | Create a new release (runs `n8n-node release`) |
|
|
438
|
+
|
|
439
|
+
> [!TIP]
|
|
440
|
+
> These scripts use the [@n8n/node-cli](https://www.npmjs.com/package/@n8n/node-cli) under the hood. You can also run CLI commands directly, e.g., `npx n8n-node dev`.
|
|
441
|
+
|
|
442
|
+
## Troubleshooting
|
|
443
|
+
|
|
444
|
+
### My node doesn't appear in n8n
|
|
445
|
+
|
|
446
|
+
1. Make sure you ran `npm install` to install dependencies
|
|
447
|
+
2. Check that your node is listed in `package.json` under `n8n.nodes`
|
|
448
|
+
3. Restart the dev server with `npm run dev`
|
|
449
|
+
4. Check the console for any error messages
|
|
450
|
+
|
|
451
|
+
### Linting errors
|
|
452
|
+
|
|
453
|
+
Run `npm run lint:fix` to automatically fix most common issues. For remaining errors, check the [n8n node development guidelines](https://docs.n8n.io/integrations/creating-nodes/).
|
|
454
|
+
|
|
455
|
+
### TypeScript errors
|
|
456
|
+
|
|
457
|
+
Make sure you're using Node.js v22 or higher and have run `npm install` to get all type definitions.
|
|
458
|
+
|
|
459
|
+
## Resources
|
|
460
|
+
|
|
461
|
+
- **[n8n Node Documentation](https://docs.n8n.io/integrations/creating-nodes/)** - Complete guide to building nodes
|
|
462
|
+
- **[n8n Community Forum](https://community.n8n.io/)** - Get help and share your nodes
|
|
463
|
+
- **[@n8n/node-cli Documentation](https://www.npmjs.com/package/@n8n/node-cli)** - CLI tool reference
|
|
464
|
+
- **[n8n Creator Portal](https://creators.n8n.io/nodes)** - Submit your node for verification
|
|
465
|
+
- **[Submit Community Nodes Guide](https://docs.n8n.io/integrations/creating-nodes/deploy/submit-community-nodes/)** - Verification requirements and process
|
|
466
|
+
|
|
467
|
+
## Contributing
|
|
468
|
+
|
|
469
|
+
Have suggestions for improving this starter? [Open an issue](https://github.com/n8n-io/n8n-nodes-starter/issues) or submit a pull request!
|
|
470
|
+
|
|
471
|
+
## License
|
|
472
|
+
|
|
473
|
+
[MIT](https://github.com/n8n-io/n8n-nodes-starter/blob/master/LICENSE.md)
|
|
@@ -0,0 +1,10 @@
|
|
|
1
|
+
import type { IAuthenticateGeneric, ICredentialTestRequest, ICredentialType, INodeProperties, Icon } from 'n8n-workflow';
|
|
2
|
+
export declare class BlossomApi implements ICredentialType {
|
|
3
|
+
name: string;
|
|
4
|
+
displayName: string;
|
|
5
|
+
icon: Icon;
|
|
6
|
+
documentationUrl: string;
|
|
7
|
+
properties: INodeProperties[];
|
|
8
|
+
authenticate: IAuthenticateGeneric;
|
|
9
|
+
test: ICredentialTestRequest;
|
|
10
|
+
}
|
|
@@ -0,0 +1,137 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.BlossomApi = void 0;
|
|
4
|
+
class BlossomApi {
|
|
5
|
+
constructor() {
|
|
6
|
+
this.name = 'blossomApi';
|
|
7
|
+
this.displayName = 'Blossom API';
|
|
8
|
+
this.icon = { light: 'file:../icons/blossom.svg', dark: 'file:../icons/blossom.dark.svg' };
|
|
9
|
+
this.documentationUrl = 'https://blossom-kc.com/';
|
|
10
|
+
this.properties = [
|
|
11
|
+
{
|
|
12
|
+
displayName: 'Base URL',
|
|
13
|
+
name: 'baseUrl',
|
|
14
|
+
type: 'string',
|
|
15
|
+
default: '',
|
|
16
|
+
placeholder: 'https://blossom-kc.com/',
|
|
17
|
+
required: true,
|
|
18
|
+
description: 'The base URL of your Blossom instance (e.g., https://blossom-kc.com/)',
|
|
19
|
+
},
|
|
20
|
+
{
|
|
21
|
+
displayName: 'Authentication Type',
|
|
22
|
+
name: 'authType',
|
|
23
|
+
type: 'options',
|
|
24
|
+
options: [
|
|
25
|
+
{
|
|
26
|
+
name: 'Basic Auth',
|
|
27
|
+
value: 'basic',
|
|
28
|
+
description: 'Use username and password for Basic Authentication',
|
|
29
|
+
},
|
|
30
|
+
{
|
|
31
|
+
name: 'API Key',
|
|
32
|
+
value: 'apiKey',
|
|
33
|
+
description: 'Use API key for authentication',
|
|
34
|
+
},
|
|
35
|
+
{
|
|
36
|
+
name: 'JWT',
|
|
37
|
+
value: 'jwt',
|
|
38
|
+
description: 'Use JWT token for authentication',
|
|
39
|
+
},
|
|
40
|
+
{
|
|
41
|
+
name: 'OAuth 2.0',
|
|
42
|
+
value: 'oauth2',
|
|
43
|
+
description: 'Use OAuth 2.0 for authentication',
|
|
44
|
+
},
|
|
45
|
+
],
|
|
46
|
+
default: 'basic',
|
|
47
|
+
description: 'The authentication method to use',
|
|
48
|
+
},
|
|
49
|
+
{
|
|
50
|
+
displayName: 'Username',
|
|
51
|
+
name: 'username',
|
|
52
|
+
type: 'string',
|
|
53
|
+
default: '',
|
|
54
|
+
required: true,
|
|
55
|
+
displayOptions: {
|
|
56
|
+
show: {
|
|
57
|
+
authType: ['basic'],
|
|
58
|
+
},
|
|
59
|
+
},
|
|
60
|
+
description: 'Username for Basic Authentication',
|
|
61
|
+
},
|
|
62
|
+
{
|
|
63
|
+
displayName: 'Password',
|
|
64
|
+
name: 'password',
|
|
65
|
+
type: 'string',
|
|
66
|
+
typeOptions: { password: true },
|
|
67
|
+
default: '',
|
|
68
|
+
required: true,
|
|
69
|
+
displayOptions: {
|
|
70
|
+
show: {
|
|
71
|
+
authType: ['basic'],
|
|
72
|
+
},
|
|
73
|
+
},
|
|
74
|
+
description: 'Password for Basic Authentication',
|
|
75
|
+
},
|
|
76
|
+
{
|
|
77
|
+
displayName: 'API Key',
|
|
78
|
+
name: 'apiKey',
|
|
79
|
+
type: 'string',
|
|
80
|
+
typeOptions: { password: true },
|
|
81
|
+
default: '',
|
|
82
|
+
required: true,
|
|
83
|
+
displayOptions: {
|
|
84
|
+
show: {
|
|
85
|
+
authType: ['apiKey'],
|
|
86
|
+
},
|
|
87
|
+
},
|
|
88
|
+
description: 'Your API key for authentication',
|
|
89
|
+
},
|
|
90
|
+
{
|
|
91
|
+
displayName: 'JWT Token',
|
|
92
|
+
name: 'jwtToken',
|
|
93
|
+
type: 'string',
|
|
94
|
+
typeOptions: { password: true },
|
|
95
|
+
default: '',
|
|
96
|
+
required: true,
|
|
97
|
+
displayOptions: {
|
|
98
|
+
show: {
|
|
99
|
+
authType: ['jwt'],
|
|
100
|
+
},
|
|
101
|
+
},
|
|
102
|
+
description: 'JWT token for authentication. Generate using: {"iss":"<user_name>","exp":<unix_timestamp>}',
|
|
103
|
+
},
|
|
104
|
+
{
|
|
105
|
+
displayName: 'OAuth 2.0 Token',
|
|
106
|
+
name: 'oauth2Token',
|
|
107
|
+
type: 'string',
|
|
108
|
+
typeOptions: { password: true },
|
|
109
|
+
default: '',
|
|
110
|
+
required: true,
|
|
111
|
+
displayOptions: {
|
|
112
|
+
show: {
|
|
113
|
+
authType: ['oauth2'],
|
|
114
|
+
},
|
|
115
|
+
},
|
|
116
|
+
description: 'OAuth 2.0 access token. Get from: {{baseUrl}}/WebServices/sync_2?auth_token',
|
|
117
|
+
},
|
|
118
|
+
];
|
|
119
|
+
this.authenticate = {
|
|
120
|
+
type: 'generic',
|
|
121
|
+
properties: {
|
|
122
|
+
headers: {
|
|
123
|
+
'Content-Type': 'application/json',
|
|
124
|
+
},
|
|
125
|
+
},
|
|
126
|
+
};
|
|
127
|
+
this.test = {
|
|
128
|
+
request: {
|
|
129
|
+
baseURL: '={{$credentials.baseUrl}}',
|
|
130
|
+
url: '/WebServices/sync_2/Test',
|
|
131
|
+
method: 'GET',
|
|
132
|
+
},
|
|
133
|
+
};
|
|
134
|
+
}
|
|
135
|
+
}
|
|
136
|
+
exports.BlossomApi = BlossomApi;
|
|
137
|
+
//# sourceMappingURL=BlossomApi.credentials.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"BlossomApi.credentials.js","sourceRoot":"","sources":["../../credentials/BlossomApi.credentials.ts"],"names":[],"mappings":";;;AAQA,MAAa,UAAU;IAAvB;QACC,SAAI,GAAG,YAAY,CAAC;QAEpB,gBAAW,GAAG,aAAa,CAAC;QAE5B,SAAI,GAAS,EAAE,KAAK,EAAE,2BAA2B,EAAE,IAAI,EAAE,gCAAgC,EAAE,CAAC;QAE5F,qBAAgB,GAAG,yBAAyB,CAAC;QAE7C,eAAU,GAAsB;YAC/B;gBACC,WAAW,EAAE,UAAU;gBACvB,IAAI,EAAE,SAAS;gBACf,IAAI,EAAE,QAAQ;gBACd,OAAO,EAAE,EAAE;gBACX,WAAW,EAAE,yBAAyB;gBACtC,QAAQ,EAAE,IAAI;gBACd,WAAW,EAAE,uEAAuE;aACpF;YACD;gBACC,WAAW,EAAE,qBAAqB;gBAClC,IAAI,EAAE,UAAU;gBAChB,IAAI,EAAE,SAAS;gBACf,OAAO,EAAE;oBACR;wBACC,IAAI,EAAE,YAAY;wBAClB,KAAK,EAAE,OAAO;wBACd,WAAW,EAAE,oDAAoD;qBACjE;oBACD;wBACC,IAAI,EAAE,SAAS;wBACf,KAAK,EAAE,QAAQ;wBACf,WAAW,EAAE,gCAAgC;qBAC7C;oBACD;wBACC,IAAI,EAAE,KAAK;wBACX,KAAK,EAAE,KAAK;wBACZ,WAAW,EAAE,kCAAkC;qBAC/C;oBACD;wBACC,IAAI,EAAE,WAAW;wBACjB,KAAK,EAAE,QAAQ;wBACf,WAAW,EAAE,kCAAkC;qBAC/C;iBACD;gBACD,OAAO,EAAE,OAAO;gBAChB,WAAW,EAAE,kCAAkC;aAC/C;YACD;gBACC,WAAW,EAAE,UAAU;gBACvB,IAAI,EAAE,UAAU;gBAChB,IAAI,EAAE,QAAQ;gBACd,OAAO,EAAE,EAAE;gBACX,QAAQ,EAAE,IAAI;gBACd,cAAc,EAAE;oBACf,IAAI,EAAE;wBACL,QAAQ,EAAE,CAAC,OAAO,CAAC;qBACnB;iBACD;gBACD,WAAW,EAAE,mCAAmC;aAChD;YACD;gBACC,WAAW,EAAE,UAAU;gBACvB,IAAI,EAAE,UAAU;gBAChB,IAAI,EAAE,QAAQ;gBACd,WAAW,EAAE,EAAE,QAAQ,EAAE,IAAI,EAAE;gBAC/B,OAAO,EAAE,EAAE;gBACX,QAAQ,EAAE,IAAI;gBACd,cAAc,EAAE;oBACf,IAAI,EAAE;wBACL,QAAQ,EAAE,CAAC,OAAO,CAAC;qBACnB;iBACD;gBACD,WAAW,EAAE,mCAAmC;aAChD;YACD;gBACC,WAAW,EAAE,SAAS;gBACtB,IAAI,EAAE,QAAQ;gBACd,IAAI,EAAE,QAAQ;gBACd,WAAW,EAAE,EAAE,QAAQ,EAAE,IAAI,EAAE;gBAC/B,OAAO,EAAE,EAAE;gBACX,QAAQ,EAAE,IAAI;gBACd,cAAc,EAAE;oBACf,IAAI,EAAE;wBACL,QAAQ,EAAE,CAAC,QAAQ,CAAC;qBACpB;iBACD;gBACD,WAAW,EAAE,iCAAiC;aAC9C;YACD;gBACC,WAAW,EAAE,WAAW;gBACxB,IAAI,EAAE,UAAU;gBAChB,IAAI,EAAE,QAAQ;gBACd,WAAW,EAAE,EAAE,QAAQ,EAAE,IAAI,EAAE;gBAC/B,OAAO,EAAE,EAAE;gBACX,QAAQ,EAAE,IAAI;gBACd,cAAc,EAAE;oBACf,IAAI,EAAE;wBACL,QAAQ,EAAE,CAAC,KAAK,CAAC;qBACjB;iBACD;gBACD,WAAW,EAAE,4FAA4F;aACzG;YACD;gBACC,WAAW,EAAE,iBAAiB;gBAC9B,IAAI,EAAE,aAAa;gBACnB,IAAI,EAAE,QAAQ;gBACd,WAAW,EAAE,EAAE,QAAQ,EAAE,IAAI,EAAE;gBAC/B,OAAO,EAAE,EAAE;gBACX,QAAQ,EAAE,IAAI;gBACd,cAAc,EAAE;oBACf,IAAI,EAAE;wBACL,QAAQ,EAAE,CAAC,QAAQ,CAAC;qBACpB;iBACD;gBACD,WAAW,EAAE,6EAA6E;aAC1F;SACD,CAAC;QAEF,iBAAY,GAAyB;YACpC,IAAI,EAAE,SAAS;YACf,UAAU,EAAE;gBACX,OAAO,EAAE;oBACR,cAAc,EAAE,kBAAkB;iBAClC;aACD;SACD,CAAC;QAEF,SAAI,GAA2B;YAC9B,OAAO,EAAE;gBACR,OAAO,EAAE,2BAA2B;gBACpC,GAAG,EAAE,0BAA0B;gBAC/B,MAAM,EAAE,KAAK;aACb;SACD,CAAC;IACH,CAAC;CAAA;AAvID,gCAuIC"}
|
|
@@ -0,0 +1,7 @@
|
|
|
1
|
+
<svg width="60" height="60" viewBox="0 0 60 60" fill="none" xmlns="http://www.w3.org/2000/svg">
|
|
2
|
+
<rect width="60" height="60" rx="12" fill="#1F2937"/>
|
|
3
|
+
<path d="M30 15C35.5228 15 40 19.4772 40 25C40 30.5228 35.5228 35 30 35C24.4772 35 20 30.5228 20 25C20 19.4772 24.4772 15 30 15Z" fill="#E5E7EB"/>
|
|
4
|
+
<path d="M30 25C35.5228 25 40 29.4772 40 35C40 40.5228 35.5228 45 30 45C24.4772 45 20 40.5228 20 35C20 29.4772 24.4772 25 30 25Z" fill="#E5E7EB" opacity="0.8"/>
|
|
5
|
+
<circle cx="30" cy="30" r="8" fill="#1F2937"/>
|
|
6
|
+
<text x="30" y="35" text-anchor="middle" fill="#E5E7EB" font-family="Arial, sans-serif" font-size="12" font-weight="bold">B</text>
|
|
7
|
+
</svg>
|
|
@@ -0,0 +1,7 @@
|
|
|
1
|
+
<svg width="60" height="60" viewBox="0 0 60 60" fill="none" xmlns="http://www.w3.org/2000/svg">
|
|
2
|
+
<rect width="60" height="60" rx="12" fill="#4F46E5"/>
|
|
3
|
+
<path d="M30 15C35.5228 15 40 19.4772 40 25C40 30.5228 35.5228 35 30 35C24.4772 35 20 30.5228 20 25C20 19.4772 24.4772 15 30 15Z" fill="white"/>
|
|
4
|
+
<path d="M30 25C35.5228 25 40 29.4772 40 35C40 40.5228 35.5228 45 30 45C24.4772 45 20 40.5228 20 35C20 29.4772 24.4772 25 30 25Z" fill="white" opacity="0.8"/>
|
|
5
|
+
<circle cx="30" cy="30" r="8" fill="#4F46E5"/>
|
|
6
|
+
<text x="30" y="35" text-anchor="middle" fill="white" font-family="Arial, sans-serif" font-size="12" font-weight="bold">B</text>
|
|
7
|
+
</svg>
|
|
@@ -0,0 +1,3 @@
|
|
|
1
|
+
<svg width="40" height="40" viewBox="0 0 40 40" fill="none" xmlns="http://www.w3.org/2000/svg">
|
|
2
|
+
<path fill-rule="evenodd" clip-rule="evenodd" d="M20.0165 0C8.94791 0 0 9.01388 0 20.1653C0 29.0792 5.73324 36.6246 13.6868 39.2952C14.6812 39.496 15.0454 38.8613 15.0454 38.3274C15.0454 37.8599 15.0126 36.2575 15.0126 34.5879C9.4445 35.79 8.28498 32.1841 8.28498 32.1841C7.39015 29.847 6.06429 29.2463 6.06429 29.2463C4.24185 28.011 6.19704 28.011 6.19704 28.011C8.21861 28.1446 9.27938 30.081 9.27938 30.081C11.0686 33.1522 13.9518 32.2844 15.1118 31.7502C15.2773 30.4481 15.8079 29.5467 16.3713 29.046C11.9303 28.5785 7.25781 26.8425 7.25781 19.0967C7.25781 16.8932 8.05267 15.0905 9.31216 13.6884C9.11344 13.1877 8.41732 11.1174 9.51128 8.34644C9.51128 8.34644 11.2014 7.81217 15.0122 10.4164C16.6438 9.97495 18.3263 9.7504 20.0165 9.74851C21.7067 9.74851 23.4295 9.98246 25.0205 10.4164C28.8317 7.81217 30.5218 8.34644 30.5218 8.34644C31.6158 11.1174 30.9192 13.1877 30.7205 13.6884C32.0132 15.0905 32.7753 16.8932 32.7753 19.0967C32.7753 26.8425 28.1028 28.5449 23.6287 29.046C24.358 29.6802 24.9873 30.882 24.9873 32.7851C24.9873 35.4893 24.9545 37.6596 24.9545 38.327C24.9545 38.8613 25.3192 39.496 26.3132 39.2956C34.2667 36.6242 39.9999 29.0792 39.9999 20.1653C40.0327 9.01388 31.052 0 20.0165 0Z" fill="white"/>
|
|
3
|
+
</svg>
|
|
@@ -0,0 +1,3 @@
|
|
|
1
|
+
<svg width="40" height="40" viewBox="0 0 40 40" fill="none" xmlns="http://www.w3.org/2000/svg">
|
|
2
|
+
<path fill-rule="evenodd" clip-rule="evenodd" d="M20.0165 0C8.94791 0 0 9.01388 0 20.1653C0 29.0792 5.73324 36.6246 13.6868 39.2952C14.6812 39.496 15.0454 38.8613 15.0454 38.3274C15.0454 37.8599 15.0126 36.2575 15.0126 34.5879C9.4445 35.79 8.28498 32.1841 8.28498 32.1841C7.39015 29.847 6.06429 29.2463 6.06429 29.2463C4.24185 28.011 6.19704 28.011 6.19704 28.011C8.21861 28.1446 9.27938 30.081 9.27938 30.081C11.0686 33.1522 13.9518 32.2844 15.1118 31.7502C15.2773 30.4481 15.8079 29.5467 16.3713 29.046C11.9303 28.5785 7.25781 26.8425 7.25781 19.0967C7.25781 16.8932 8.05267 15.0905 9.31216 13.6884C9.11344 13.1877 8.41732 11.1174 9.51128 8.34644C9.51128 8.34644 11.2014 7.81217 15.0122 10.4164C16.6438 9.97495 18.3263 9.7504 20.0165 9.74851C21.7067 9.74851 23.4295 9.98246 25.0205 10.4164C28.8317 7.81217 30.5218 8.34644 30.5218 8.34644C31.6158 11.1174 30.9192 13.1877 30.7205 13.6884C32.0132 15.0905 32.7753 16.8932 32.7753 19.0967C32.7753 26.8425 28.1028 28.5449 23.6287 29.046C24.358 29.6802 24.9873 30.882 24.9873 32.7851C24.9873 35.4893 24.9545 37.6596 24.9545 38.327C24.9545 38.8613 25.3192 39.496 26.3132 39.2956C34.2667 36.6242 39.9999 29.0792 39.9999 20.1653C40.0327 9.01388 31.052 0 20.0165 0Z" fill="#24292F"/>
|
|
3
|
+
</svg>
|