n8n-nodes-neuronwriter
Version:
n8n community node for NeuronWriter API integration - SEO content analysis and optimization
678 lines (677 loc) • 29.6 kB
JavaScript
"use strict";
Object.defineProperty(exports, "__esModule", { value: true });
exports.NeuronWriter = void 0;
const n8n_workflow_1 = require("n8n-workflow");
class NeuronWriter {
constructor() {
this.description = {
displayName: 'NeuronWriter',
name: 'neuronWriter',
icon: 'file:neuronwriter.svg',
group: ['transform'],
version: 1,
subtitle: '={{$parameter["operation"] + ": " + $parameter["resource"]}}',
description: 'Interact with NeuronWriter API for SEO content analysis and optimization',
defaults: {
name: 'NeuronWriter',
},
inputs: ["main" /* NodeConnectionType.Main */],
outputs: ["main" /* NodeConnectionType.Main */],
credentials: [
{
name: 'neuronWriterApi',
required: true,
},
],
requestDefaults: {
headers: {
'X-API-KEY': '={{$credentials.apiKey}}',
'Accept': 'application/json',
'Content-Type': 'application/json',
},
},
properties: [
// Resource selector
{
displayName: 'Resource',
name: 'resource',
type: 'options',
noDataExpression: true,
options: [
{
name: 'Project',
value: 'project',
description: 'Operations on NeuronWriter projects',
},
{
name: 'Query',
value: 'query',
description: 'SEO analysis queries and results',
},
{
name: 'Content',
value: 'content',
description: 'Content management operations',
},
],
default: 'query',
},
// PROJECT OPERATIONS
{
displayName: 'Operation',
name: 'operation',
type: 'options',
noDataExpression: true,
displayOptions: {
show: {
resource: ['project'],
},
},
options: [
{
name: 'List',
value: 'list',
description: 'Get all projects in your account',
action: 'List all projects',
},
],
default: 'list',
},
// QUERY OPERATIONS
{
displayName: 'Operation',
name: 'operation',
type: 'options',
noDataExpression: true,
displayOptions: {
show: {
resource: ['query'],
},
},
options: [
{
name: 'Create',
value: 'create',
description: 'Create a new SEO analysis query for a keyword',
action: 'Create a new query',
},
{
name: 'Get',
value: 'get',
description: 'Get analysis results and recommendations for a query',
action: 'Get query results',
},
{
name: 'List',
value: 'list',
description: 'List all queries in a project with filters',
action: 'List queries',
},
],
default: 'create',
},
// CONTENT OPERATIONS
{
displayName: 'Operation',
name: 'operation',
type: 'options',
noDataExpression: true,
displayOptions: {
show: {
resource: ['content'],
},
},
options: [
{
name: 'Get',
value: 'get',
description: 'Get saved content for a query',
action: 'Get saved content',
},
{
name: 'Import',
value: 'import',
description: 'Import HTML content and save it to a query',
action: 'Import content',
},
{
name: 'Evaluate',
value: 'evaluate',
description: 'Evaluate content and get SEO score without saving',
action: 'Evaluate content',
},
],
default: 'get',
},
// QUERY CREATE FIELDS
{
displayName: 'Project ID',
name: 'projectId',
type: 'string',
required: true,
displayOptions: {
show: {
resource: ['query'],
operation: ['create', 'list'],
},
},
default: '',
description: 'The ID of your NeuronWriter project. You can get this from the project URL or by using the "List Projects" operation.',
placeholder: 'e.g., c2fe46bce8019bff',
},
{
displayName: 'Keyword',
name: 'keyword',
type: 'string',
required: true,
displayOptions: {
show: {
resource: ['query'],
operation: ['create'],
},
},
default: '',
description: 'Main keyword for SEO analysis and content optimization',
placeholder: 'e.g., best running shoes 2024',
},
{
displayName: 'Search Engine',
name: 'engine',
type: 'options',
displayOptions: {
show: {
resource: ['query'],
operation: ['create'],
},
},
options: [
{
name: 'Google.com (Global)',
value: 'google.com',
},
{
name: 'Google.co.uk (United Kingdom)',
value: 'google.co.uk',
},
{
name: 'Google.fr (France)',
value: 'google.fr',
},
{
name: 'Google.de (Germany)',
value: 'google.de',
},
{
name: 'Google.es (Spain)',
value: 'google.es',
},
{
name: 'Google.it (Italy)',
value: 'google.it',
},
{
name: 'Google.ca (Canada)',
value: 'google.ca',
},
{
name: 'Google.com.au (Australia)',
value: 'google.com.au',
},
],
default: 'google.com',
description: 'Search engine to use for SERP analysis',
},
{
displayName: 'Language',
name: 'language',
type: 'options',
displayOptions: {
show: {
resource: ['query'],
operation: ['create'],
},
},
options: [
{
name: 'English',
value: 'English',
},
{
name: 'French',
value: 'French',
},
{
name: 'Spanish',
value: 'Spanish',
},
{
name: 'German',
value: 'German',
},
{
name: 'Italian',
value: 'Italian',
},
{
name: 'Portuguese',
value: 'Portuguese',
},
{
name: 'Dutch',
value: 'Dutch',
},
{
name: 'Polish',
value: 'Polish',
},
],
default: 'English',
description: 'Content language for analysis',
},
{
displayName: 'Additional Keywords',
name: 'additionalKeywords',
type: 'string',
displayOptions: {
show: {
resource: ['query'],
operation: ['create'],
},
},
default: '',
description: 'Optional: Comma-separated list of supporting keywords to include in the analysis',
placeholder: 'e.g., running shoes, athletic footwear, sport shoes',
},
// QUERY GET FIELDS
{
displayName: 'Query ID',
name: 'queryId',
type: 'string',
required: true,
displayOptions: {
show: {
resource: ['query', 'content'],
operation: ['get'],
},
},
default: '',
description: 'The ID of the query to retrieve. This is returned when creating a query.',
placeholder: 'e.g., 79ca6b6b45fb9d67',
},
// QUERY LIST FIELDS
{
displayName: 'Additional Options',
name: 'additionalOptions',
type: 'collection',
placeholder: 'Add Option',
displayOptions: {
show: {
resource: ['query'],
operation: ['list'],
},
},
default: {},
options: [
{
displayName: 'Status',
name: 'status',
type: 'options',
options: [
{
name: 'All',
value: '',
},
{
name: 'Waiting',
value: 'waiting',
},
{
name: 'In Progress',
value: 'in progress',
},
{
name: 'Ready',
value: 'ready',
},
],
default: 'ready',
description: 'Filter queries by their analysis status',
},
{
displayName: 'Source',
name: 'source',
type: 'options',
options: [
{
name: 'All',
value: '',
},
{
name: 'NeuronWriter Interface',
value: 'neuron',
},
{
name: 'API',
value: 'neuron-api',
},
],
default: '',
description: 'Filter by how the query was created',
},
{
displayName: 'Tags',
name: 'tags',
type: 'string',
default: '',
description: 'Comma-separated list of tags to filter by',
placeholder: 'e.g., Done, Published',
},
],
},
// CONTENT FIELDS
{
displayName: 'Query ID',
name: 'queryId',
type: 'string',
required: true,
displayOptions: {
show: {
resource: ['content'],
operation: ['import', 'evaluate'],
},
},
default: '',
description: 'The ID of the query to associate content with',
placeholder: 'e.g., 79ca6b6b45fb9d67',
},
{
displayName: 'HTML Content',
name: 'html',
type: 'string',
typeOptions: {
alwaysOpenEditWindow: true,
editor: 'htmlEditor',
rows: 10,
},
displayOptions: {
show: {
resource: ['content'],
operation: ['import', 'evaluate'],
},
},
default: '',
description: 'HTML content to import or evaluate for SEO optimization',
placeholder: '<h1>Your Article Title</h1><p>Your content here...</p>',
},
{
displayName: 'Title',
name: 'title',
type: 'string',
displayOptions: {
show: {
resource: ['content'],
operation: ['import', 'evaluate'],
},
},
default: '',
description: 'Article title (will override any <title> in HTML)',
placeholder: 'e.g., The Ultimate Guide to Running Shoes',
},
{
displayName: 'Meta Description',
name: 'description',
type: 'string',
typeOptions: {
rows: 3,
},
displayOptions: {
show: {
resource: ['content'],
operation: ['import', 'evaluate'],
},
},
default: '',
description: 'Meta description for SEO (will override any meta description in HTML)',
placeholder: 'e.g., Discover the best running shoes of 2024 with our comprehensive guide...',
},
// CONTENT IMPORT OPTIONS
{
displayName: 'Import Options',
name: 'importOptions',
type: 'collection',
placeholder: 'Add Option',
displayOptions: {
show: {
resource: ['content'],
operation: ['import', 'evaluate'],
},
},
default: {},
options: [
{
displayName: 'Import from URL',
name: 'url',
type: 'string',
default: '',
description: 'URL to import content from (alternative to providing HTML directly)',
placeholder: 'https://example.com/article',
},
{
displayName: 'Container ID',
name: 'id',
type: 'string',
default: '',
description: 'ID of the HTML container that contains the main content (when importing from URL)',
placeholder: 'e.g., main-content',
},
{
displayName: 'Container Class',
name: 'class',
type: 'string',
default: '',
description: 'CSS class of the HTML container that contains the main content (when importing from URL)',
placeholder: 'e.g., article-content',
},
],
},
// CONTENT GET OPTIONS
{
displayName: 'Revision Type',
name: 'revisionType',
type: 'options',
displayOptions: {
show: {
resource: ['content'],
operation: ['get'],
},
},
options: [
{
name: 'Manual Saves Only',
value: 'manual',
},
{
name: 'All (Including Autosaves)',
value: 'all',
},
],
default: 'all',
description: 'Type of content revisions to retrieve',
},
],
};
}
async execute() {
var _a;
const items = this.getInputData();
const returnData = [];
for (let i = 0; i < items.length; i++) {
try {
const resource = this.getNodeParameter('resource', i);
const operation = this.getNodeParameter('operation', i);
let responseData;
if (resource === 'project') {
if (operation === 'list') {
responseData = await this.helpers.httpRequestWithAuthentication.call(this, 'neuronWriterApi', {
method: 'POST',
url: 'https://app.neuronwriter.com/neuron-api/0.5/writer/list-projects',
body: {},
json: true,
});
}
}
else if (resource === 'query') {
if (operation === 'create') {
const projectId = this.getNodeParameter('projectId', i);
const keyword = this.getNodeParameter('keyword', i);
const engine = this.getNodeParameter('engine', i);
const language = this.getNodeParameter('language', i);
const additionalKeywords = this.getNodeParameter('additionalKeywords', i);
const body = {
project: projectId,
keyword,
engine,
language,
};
if (additionalKeywords) {
body.additional_keywords = additionalKeywords.split(',').map(k => k.trim()).filter(k => k);
}
responseData = await this.helpers.httpRequestWithAuthentication.call(this, 'neuronWriterApi', {
method: 'POST',
url: 'https://app.neuronwriter.com/neuron-api/0.5/writer/new-query',
body,
json: true,
});
}
else if (operation === 'get') {
const queryId = this.getNodeParameter('queryId', i);
responseData = await this.helpers.httpRequestWithAuthentication.call(this, 'neuronWriterApi', {
method: 'POST',
url: 'https://app.neuronwriter.com/neuron-api/0.5/writer/get-query',
body: { query: queryId },
json: true,
});
}
else if (operation === 'list') {
const projectId = this.getNodeParameter('projectId', i);
const additionalOptions = this.getNodeParameter('additionalOptions', i);
const body = {
project: projectId,
};
if (additionalOptions.status) {
body.status = additionalOptions.status;
}
if (additionalOptions.source) {
body.source = additionalOptions.source;
}
if (additionalOptions.tags) {
const tags = additionalOptions.tags.split(',').map(t => t.trim()).filter(t => t);
if (tags.length > 0) {
body.tags = tags;
}
}
responseData = await this.helpers.httpRequestWithAuthentication.call(this, 'neuronWriterApi', {
method: 'POST',
url: 'https://app.neuronwriter.com/neuron-api/0.5/writer/list-queries',
body,
json: true,
});
}
}
else if (resource === 'content') {
if (operation === 'get') {
const queryId = this.getNodeParameter('queryId', i);
const revisionType = this.getNodeParameter('revisionType', i);
responseData = await this.helpers.httpRequestWithAuthentication.call(this, 'neuronWriterApi', {
method: 'POST',
url: 'https://app.neuronwriter.com/neuron-api/0.5/writer/get-content',
body: {
query: queryId,
revision_type: revisionType
},
json: true,
});
}
else if (operation === 'import' || operation === 'evaluate') {
const queryId = this.getNodeParameter('queryId', i);
const html = this.getNodeParameter('html', i);
const title = this.getNodeParameter('title', i);
const description = this.getNodeParameter('description', i);
const importOptions = this.getNodeParameter('importOptions', i);
const endpoint = operation === 'import' ? '/import-content' : '/evaluate-content';
const body = {
query: queryId,
};
// If URL is provided, use it instead of HTML
if (importOptions.url) {
body.url = importOptions.url;
if (importOptions.id)
body.id = importOptions.id;
if (importOptions.class)
body.class = importOptions.class;
}
else if (html) {
body.html = html;
}
else {
throw new n8n_workflow_1.NodeOperationError(this.getNode(), 'Either HTML content or URL must be provided', { itemIndex: i });
}
if (title)
body.title = title;
if (description)
body.description = description;
responseData = await this.helpers.httpRequestWithAuthentication.call(this, 'neuronWriterApi', {
method: 'POST',
url: endpoint,
body,
json: true,
});
}
}
// Enhanced response data with metadata
const enhancedResponse = {
...responseData,
_metadata: {
resource,
operation,
timestamp: new Date().toISOString(),
nodeVersion: 1,
}
};
returnData.push({
json: enhancedResponse,
pairedItem: { item: i },
});
}
catch (error) {
// Enhanced error handling
if (this.continueOnFail()) {
const errorData = {
error: (error === null || error === void 0 ? void 0 : error.message) || 'Unknown error',
resource: this.getNodeParameter('resource', i),
operation: this.getNodeParameter('operation', i),
timestamp: new Date().toISOString(),
};
returnData.push({
json: errorData,
pairedItem: { item: i },
});
continue;
}
// Provide more specific error messages
if ((error === null || error === void 0 ? void 0 : error.httpCode) === '401') {
throw new n8n_workflow_1.NodeOperationError(this.getNode(), 'Invalid API key. Please check your NeuronWriter API credentials.', { itemIndex: i });
}
else if ((error === null || error === void 0 ? void 0 : error.httpCode) === '404') {
throw new n8n_workflow_1.NodeOperationError(this.getNode(), 'Resource not found. Please check your Project ID or Query ID.', { itemIndex: i });
}
else if ((_a = error === null || error === void 0 ? void 0 : error.message) === null || _a === void 0 ? void 0 : _a.includes('project')) {
throw new n8n_workflow_1.NodeOperationError(this.getNode(), `Project error: ${error.message}. Make sure the Project ID exists and you have access to it.`, { itemIndex: i });
}
throw new n8n_workflow_1.NodeOperationError(this.getNode(), error, { itemIndex: i });
}
}
return [returnData];
}
}
exports.NeuronWriter = NeuronWriter;