@ai-growth/n8n-nodes-wordpress
Version:
n8n node for WordPress integration with AI GROWTH - SEO WP plugin
670 lines • 30 kB
JavaScript
"use strict";
Object.defineProperty(exports, "__esModule", { value: true });
exports.NodeService = void 0;
const WordPressClient_1 = require("../utils/WordPressClient");
const ContentService_1 = require("../services/ContentService");
const PostCreateService_1 = require("../services/PostCreateService");
const TaxonomyService_1 = require("../services/TaxonomyService");
const MediaUploadService_1 = require("../services/MediaUploadService");
const FeaturedImageService_1 = require("../services/FeaturedImageService");
/**
* Serviço para executar operações do nó WordPress
*/
class NodeService {
/**
* Construtor do serviço
* @param execFunctions Funções de execução do n8n
* @param credentials Credenciais do WordPress
*/
constructor(execFunctions, credentials) {
this.execFunctions = execFunctions;
// Obter credenciais
const domain = credentials.url;
const username = credentials.username;
const password = credentials.password;
// Criar cliente WordPress (usando v2 como padrão, igual ao modelo padrão do n8n)
this.client = new WordPressClient_1.WordPressClient({
url: domain,
username,
password,
}, {
apiVersion: 'v2',
debug: false,
});
// Inicializar serviços
this.contentService = new ContentService_1.ContentService(this.client);
this.postCreateService = new PostCreateService_1.PostCreateService(this.client);
this.taxonomyService = new TaxonomyService_1.TaxonomyService(this.client);
this.mediaService = new MediaUploadService_1.MediaUploadService(this.client);
this.featuredImageService = new FeaturedImageService_1.FeaturedImageService(this.client);
}
/**
* Obtém o cliente WordPress para acesso direto
* @returns Cliente WordPress
*/
getClient() {
return this.client;
}
/**
* Executa uma operação de post/página
* @param params Parâmetros da operação
* @param resource Tipo de recurso (post ou page)
* @param operation Operação a ser realizada
* @returns Dados resultantes da operação
*/
async executeOperation(params, resource, operation) {
switch (operation) {
case 'create':
return this.createResource(params, resource);
case 'update':
return this.updateResource(params, resource);
case 'get':
return this.getResource(params, resource);
case 'getAll':
return this.getAllResources(params, resource);
case 'delete':
return this.deleteResource(params, resource);
default:
throw new Error(`Unsupported operation: ${operation}`);
}
}
/**
* Converte parâmetros do nó para o formato da API WordPress
* @param params Parâmetros do nó
* @returns Parâmetros formatados para a API
*/
convertToWordPressPost(params) {
console.log('[NodeService] Converting node parameters to WordPress post:', {
title: params.title,
hasContent: !!params.content,
status: params.status,
meta_title: params.meta_title,
meta_description: params.meta_description,
meta_keywords: params.meta_keywords,
og_title: params.og_title,
og_description: params.og_description,
twitter_title: params.twitter_title,
twitter_description: params.twitter_description,
hasMetaTitle: params.meta_title !== undefined,
hasMetaDescription: params.meta_description !== undefined,
hasMetaKeywords: params.meta_keywords !== undefined
});
const wpPost = {
title: params.title,
content: params.content,
status: params.status,
};
if (params.excerpt) {
wpPost.excerpt = params.excerpt;
}
if (params.slug) {
wpPost.slug = params.slug;
}
if (params.featured_image_url) {
wpPost.featured_image_url = params.featured_image_url;
}
// Manter categorias e tags como estão para processamento posterior
if (params.categories) {
wpPost.categories = params.categories;
}
if (params.tags) {
wpPost.tags = params.tags;
}
// Adicionar metadados SEO - incluir mesmo se forem strings vazias
if (params.meta_title !== undefined) {
wpPost.meta_title = params.meta_title;
console.log('[NodeService] Preserving meta_title:', params.meta_title);
}
if (params.meta_description !== undefined) {
wpPost.meta_description = params.meta_description;
console.log('[NodeService] Preserving meta_description:', params.meta_description);
}
if (params.meta_keywords !== undefined) {
wpPost.meta_keywords = params.meta_keywords;
console.log('[NodeService] Preserving meta_keywords:', params.meta_keywords);
}
// Adicionar campos sociais
if (params.og_title !== undefined) {
wpPost.og_title = params.og_title;
console.log('[NodeService] Preserving og_title:', params.og_title);
}
if (params.og_description !== undefined) {
wpPost.og_description = params.og_description;
console.log('[NodeService] Preserving og_description:', params.og_description);
}
if (params.twitter_title !== undefined) {
wpPost.twitter_title = params.twitter_title;
console.log('[NodeService] Preserving twitter_title:', params.twitter_title);
}
if (params.twitter_description !== undefined) {
wpPost.twitter_description = params.twitter_description;
console.log('[NodeService] Preserving twitter_description:', params.twitter_description);
}
// Adicionar FAQ e CTA
if (params.faq) {
wpPost.faq = params.faq;
}
if (params.cta) {
wpPost.cta = params.cta;
}
console.log('[NodeService] Converted WordPress post:', {
id: wpPost.id,
title: wpPost.title,
hasContent: !!wpPost.content,
status: wpPost.status,
meta_title: wpPost.meta_title,
meta_description: wpPost.meta_description,
meta_keywords: wpPost.meta_keywords,
og_title: wpPost.og_title,
og_description: wpPost.og_description,
twitter_title: wpPost.twitter_title,
twitter_description: wpPost.twitter_description,
hasMetaTitle: wpPost.meta_title !== undefined,
hasMetaDescription: wpPost.meta_description !== undefined,
hasMetaKeywords: wpPost.meta_keywords !== undefined
});
return wpPost;
}
/**
* Cria um novo post/página
* @param params Parâmetros de criação
* @param resource Tipo de recurso (post ou page)
* @returns Recurso criado
*/
async createResource(params, resource) {
var _a, _b;
// Ativar automaticamente updateSeoMetadata se houver campos de metadados SEO
const hasSeoMetadata = params.meta_title !== undefined ||
params.meta_description !== undefined ||
params.meta_keywords !== undefined ||
params.og_title !== undefined ||
params.og_description !== undefined ||
params.twitter_title !== undefined ||
params.twitter_description !== undefined;
const options = {
updateSeoMetadata: params.updateSeoMetadata || hasSeoMetadata,
updateFaqs: params.updateFaqs || !!params.faq,
updateCta: params.updateCta || !!params.cta,
};
console.log('[NodeService] Create options determined:', {
updateSeoMetadata: options.updateSeoMetadata,
updateFaqs: options.updateFaqs,
updateCta: options.updateCta,
hasSeoMetadata,
hasFaq: !!params.faq,
hasCta: !!params.cta
});
const wpPost = this.convertToWordPressPost(params);
// Processar imagem featured se uma URL foi fornecida
if (params.featured_image_url) {
try {
const mediaResult = await this.featuredImageService.setFeaturedImageFromUrl(0, // ID será definido após a criação do post
resource, params.featured_image_url);
// Temporariamente armazenar a URL para processamento posterior
wpPost.featured_image_url = params.featured_image_url;
}
catch (error) {
console.error('[NodeService] Error processing featured image URL:', error);
// Continuar sem a imagem se houver erro
}
}
let result;
if (resource === 'post') {
result = await this.postCreateService.createPost(wpPost, options);
}
else {
result = await this.postCreateService.createPage(wpPost, options);
}
// Sempre recarregar o post/página se houver metadados SEO para garantir que sejam incluídos no resultado
if (options.updateSeoMetadata && (result === null || result === void 0 ? void 0 : result.id)) {
console.log('[NodeService] Reloading post with SEO metadata after creation');
if (resource === 'post') {
result = await this.contentService.getPost(result.id, {
includeSeoMetadata: true,
includeFaqs: options.updateFaqs,
includeCta: options.updateCta
});
}
else {
result = await this.contentService.getPage(result.id, {
includeSeoMetadata: true,
includeFaqs: options.updateFaqs,
includeCta: options.updateCta
});
}
console.log('[NodeService] Post reloaded with SEO metadata:', {
id: result === null || result === void 0 ? void 0 : result.id,
meta_title: result === null || result === void 0 ? void 0 : result.meta_title,
meta_description: result === null || result === void 0 ? void 0 : result.meta_description,
meta_keywords: result === null || result === void 0 ? void 0 : result.meta_keywords,
og_title: result === null || result === void 0 ? void 0 : result.og_title,
og_description: result === null || result === void 0 ? void 0 : result.og_description,
twitter_title: result === null || result === void 0 ? void 0 : result.twitter_title,
twitter_description: result === null || result === void 0 ? void 0 : result.twitter_description
});
}
// Processar imagem featured após criação do post/página
if (params.featured_image_url && (result === null || result === void 0 ? void 0 : result.id)) {
try {
await this.featuredImageService.setFeaturedImageFromUrl(result.id, resource, params.featured_image_url);
// Recarregar o post para obter a imagem featured atualizada
if (resource === 'post') {
result = await this.contentService.getPost(result.id, {
includeSeoMetadata: options.updateSeoMetadata,
includeFaqs: options.updateFaqs,
includeCta: options.updateCta
});
}
else {
result = await this.contentService.getPage(result.id, {
includeSeoMetadata: options.updateSeoMetadata,
includeFaqs: options.updateFaqs,
includeCta: options.updateCta
});
}
}
catch (error) {
console.error('[NodeService] Error setting featured image after creation:', error);
}
}
// Processar taxonomias (categorias e tags)
if (((_a = params.categories) === null || _a === void 0 ? void 0 : _a.length) || ((_b = params.tags) === null || _b === void 0 ? void 0 : _b.length)) {
// Aguardar associação de taxonomias
const categories = params.categories || [];
const tags = params.tags || [];
if (resource === 'post') {
await this.taxonomyService.associateTaxonomiesWithPost(result.id, categories, tags);
}
else {
await this.taxonomyService.associateTaxonomiesWithPage(result.id, categories, tags);
}
// Recarregar o recurso para retornar com as taxonomias atualizadas
if (resource === 'post') {
result = await this.contentService.getPost(result.id, {
includeSeoMetadata: options.updateSeoMetadata,
includeFaqs: options.updateFaqs,
includeCta: options.updateCta
});
}
else {
result = await this.contentService.getPage(result.id, {
includeSeoMetadata: options.updateSeoMetadata,
includeFaqs: options.updateFaqs,
includeCta: options.updateCta
});
}
}
return result;
}
/**
* Atualiza um post/página existente
* @param params Parâmetros de atualização
* @param resource Tipo de recurso (post ou page)
* @returns Recurso atualizado
*/
async updateResource(params, resource) {
var _a, _b;
const { id } = params;
// Ativar automaticamente updateSeoMetadata se houver campos de metadados SEO
const hasSeoMetadata = params.meta_title !== undefined ||
params.meta_description !== undefined ||
params.meta_keywords !== undefined ||
params.og_title !== undefined ||
params.og_description !== undefined ||
params.twitter_title !== undefined ||
params.twitter_description !== undefined;
const options = {
updateSeoMetadata: params.updateSeoMetadata || hasSeoMetadata,
updateFaqs: params.updateFaqs || !!params.faq,
updateCta: params.updateCta || !!params.cta,
};
console.log('[NodeService] Update options determined:', {
updateSeoMetadata: options.updateSeoMetadata,
updateFaqs: options.updateFaqs,
updateCta: options.updateCta,
hasSeoMetadata,
hasFaq: !!params.faq,
hasCta: !!params.cta
});
const wpPost = this.convertToWordPressPost(params);
// Processar imagem featured se uma URL foi fornecida
if (params.featured_image_url) {
try {
await this.featuredImageService.setFeaturedImageFromUrl(id, resource, params.featured_image_url);
}
catch (error) {
console.error('[NodeService] Error processing featured image URL during update:', error);
}
}
// Adicionar metadados SEO - incluir mesmo se forem strings vazias
if (params.meta_title !== undefined) {
wpPost.meta_title = params.meta_title;
console.log('[NodeService] Update - preserving meta_title:', params.meta_title);
}
if (params.meta_description !== undefined) {
wpPost.meta_description = params.meta_description;
console.log('[NodeService] Update - preserving meta_description:', params.meta_description);
}
if (params.meta_keywords !== undefined) {
wpPost.meta_keywords = params.meta_keywords;
console.log('[NodeService] Update - preserving meta_keywords:', params.meta_keywords);
}
// Adicionar campos sociais
if (params.og_title !== undefined) {
wpPost.og_title = params.og_title;
console.log('[NodeService] Update - preserving og_title:', params.og_title);
}
if (params.og_description !== undefined) {
wpPost.og_description = params.og_description;
console.log('[NodeService] Update - preserving og_description:', params.og_description);
}
if (params.twitter_title !== undefined) {
wpPost.twitter_title = params.twitter_title;
console.log('[NodeService] Update - preserving twitter_title:', params.twitter_title);
}
if (params.twitter_description !== undefined) {
wpPost.twitter_description = params.twitter_description;
console.log('[NodeService] Update - preserving twitter_description:', params.twitter_description);
}
// Adicionar FAQ e CTA
if (params.faq) {
wpPost.faq = params.faq;
}
if (params.cta) {
wpPost.cta = params.cta;
}
let result;
if (resource === 'post') {
result = await this.postCreateService.updatePost(id, wpPost, options);
}
else {
result = await this.postCreateService.updatePage(id, wpPost, options);
}
// Sempre recarregar o post/página se houver metadados SEO para garantir que sejam incluídos no resultado
if (options.updateSeoMetadata && (result === null || result === void 0 ? void 0 : result.id)) {
console.log('[NodeService] Reloading post with SEO metadata after update');
if (resource === 'post') {
result = await this.contentService.getPost(result.id, {
includeSeoMetadata: true,
includeFaqs: options.updateFaqs,
includeCta: options.updateCta
});
}
else {
result = await this.contentService.getPage(result.id, {
includeSeoMetadata: true,
includeFaqs: options.updateFaqs,
includeCta: options.updateCta
});
}
console.log('[NodeService] Post reloaded with SEO metadata after update:', {
id: result === null || result === void 0 ? void 0 : result.id,
meta_title: result === null || result === void 0 ? void 0 : result.meta_title,
meta_description: result === null || result === void 0 ? void 0 : result.meta_description,
meta_keywords: result === null || result === void 0 ? void 0 : result.meta_keywords,
og_title: result === null || result === void 0 ? void 0 : result.og_title,
og_description: result === null || result === void 0 ? void 0 : result.og_description,
twitter_title: result === null || result === void 0 ? void 0 : result.twitter_title,
twitter_description: result === null || result === void 0 ? void 0 : result.twitter_description
});
}
// Processar taxonomias (categorias e tags)
if (((_a = params.categories) === null || _a === void 0 ? void 0 : _a.length) || ((_b = params.tags) === null || _b === void 0 ? void 0 : _b.length)) {
// Aguardar associação de taxonomias
const categories = params.categories || [];
const tags = params.tags || [];
if (resource === 'post') {
await this.taxonomyService.associateTaxonomiesWithPost(id, categories, tags);
}
else {
await this.taxonomyService.associateTaxonomiesWithPage(id, categories, tags);
}
// Recarregar o recurso para retornar com as taxonomias atualizadas
if (resource === 'post') {
result = await this.contentService.getPost(id, {
includeSeoMetadata: options.updateSeoMetadata,
includeFaqs: options.updateFaqs,
includeCta: options.updateCta
});
}
else {
result = await this.contentService.getPage(id, {
includeSeoMetadata: options.updateSeoMetadata,
includeFaqs: options.updateFaqs,
includeCta: options.updateCta
});
}
}
return result;
}
/**
* Obtém um post/página específico
* @param params Parâmetros da consulta
* @param resource Tipo de recurso (post ou page)
* @returns Recurso obtido
*/
async getResource(params, resource) {
const { id, includeSeoMetadata, includeFaqs, includeCta } = params;
const options = {
includeSeoMetadata: includeSeoMetadata || false,
includeFaqs: includeFaqs || false,
includeCta: includeCta || false,
};
if (resource === 'post') {
return this.contentService.getPost(id, options);
}
else {
return this.contentService.getPage(id, options);
}
}
/**
* Obtém múltiplos posts/páginas
* @param params Parâmetros da consulta
* @param resource Tipo de recurso (post ou page)
* @returns Lista de recursos
*/
async getAllResources(params, resource) {
const options = {
page: params.page || 1,
perPage: params.limit || 10,
status: params.status || 'publish',
search: params.search,
categories: params.categories,
tags: params.tags,
includeSeoMetadata: params.includeSeoMetadata || false,
includeFaqs: params.includeFaqs || false,
includeCta: params.includeCta || false,
};
if (resource === 'post') {
return this.contentService.getPosts(options);
}
else {
return this.contentService.getPages(options);
}
}
/**
* Exclui um post/página
* @param params Parâmetros de exclusão
* @param resource Tipo de recurso (post ou page)
* @returns true se excluído com sucesso
*/
async deleteResource(params, resource) {
const { id, force } = params;
if (resource === 'post') {
return this.postCreateService.deletePost(id, force);
}
else {
return this.postCreateService.deletePage(id, force);
}
}
/**
* Converte um único resultado em formato de nó n8n
* @param result Resultado da operação
* @returns Dados formatados para o n8n
*/
formatSingleOutput(result) {
var _a, _b, _c, _d;
if (!result) {
return [{
json: {
success: false,
message: 'No data returned from WordPress API',
timestamp: new Date().toISOString(),
}
}];
}
// Extrair informações essenciais solicitadas
const outputData = {
success: true,
timestamp: new Date().toISOString(),
// Informações principais solicitadas
id: result.id,
title: ((_a = result.title) === null || _a === void 0 ? void 0 : _a.rendered) || result.title || '',
content: ((_b = result.content) === null || _b === void 0 ? void 0 : _b.rendered) || result.content || '',
url: result.link || result.permalink || null,
// Categories e tags processadas
categories: this.extractTaxonomyNames(result.categories_data || []),
tags: this.extractTaxonomyNames(result.tags_data || []),
// Informações adicionais úteis
status: result.status,
statusLabel: this.getStatusLabel(result.status),
excerpt: ((_c = result.excerpt) === null || _c === void 0 ? void 0 : _c.rendered) || result.excerpt || '',
slug: result.slug || '',
// Metadados SEO se disponíveis
meta_title: result.meta_title || '',
meta_description: result.meta_description || '',
meta_keywords: result.meta_keywords || '',
og_title: result.og_title || '',
og_description: result.og_description || '',
twitter_title: result.twitter_title || '',
twitter_description: result.twitter_description || '',
// Imagem featured se disponível
featured_image_url: result.featured_media_url || null,
featured_image_id: result.featured_media || null,
// Campos úteis para workflows
permalink: result.link || result.permalink || null,
id_str: result.id ? result.id.toString() : '',
content_text: ((_d = result.content) === null || _d === void 0 ? void 0 : _d.rendered) ? this.stripHtml(result.content.rendered) : '',
// Dados completos para compatibilidade
...result,
};
return [{ json: outputData }];
}
/**
* Extrai nomes das taxonomias (categorias/tags) dos dados
* @param taxonomyData Dados das taxonomias
* @returns Array com os nomes
*/
extractTaxonomyNames(taxonomyData) {
if (!Array.isArray(taxonomyData)) {
return [];
}
return taxonomyData.map(item => item.name || '').filter(name => name.length > 0);
}
/**
* Converte múltiplos resultados em formato de nó n8n
* @param results Lista de resultados
* @returns Dados formatados para o n8n
*/
formatMultipleOutput(results) {
if (!results || !Array.isArray(results) || results.length === 0) {
return [{
json: {
success: false,
items: [],
count: 0,
message: 'No items found matching the criteria',
timestamp: new Date().toISOString(),
}
}];
}
// Adicionar metadados úteis para cada item
const enhancedResults = results.map(item => {
var _a, _b, _c, _d;
return {
success: true,
// Informações principais solicitadas
id: item.id,
title: ((_a = item.title) === null || _a === void 0 ? void 0 : _a.rendered) || item.title || '',
content: ((_b = item.content) === null || _b === void 0 ? void 0 : _b.rendered) || item.content || '',
url: item.link || item.permalink || null,
// Categories e tags processadas
categories: this.extractTaxonomyNames(item.categories_data || []),
tags: this.extractTaxonomyNames(item.tags_data || []),
// Informações adicionais úteis
status: item.status,
statusLabel: this.getStatusLabel(item.status),
excerpt: ((_c = item.excerpt) === null || _c === void 0 ? void 0 : _c.rendered) || item.excerpt || '',
slug: item.slug || '',
// Metadados SEO se disponíveis
meta_title: item.meta_title || '',
meta_description: item.meta_description || '',
meta_keywords: item.meta_keywords || '',
og_title: item.og_title || '',
og_description: item.og_description || '',
twitter_title: item.twitter_title || '',
twitter_description: item.twitter_description || '',
// Imagem featured se disponível
featured_image_url: item.featured_media_url || null,
featured_image_id: item.featured_media || null,
// Campos úteis para workflows
permalink: item.link || item.permalink || null,
id_str: item.id ? item.id.toString() : '',
content_text: ((_d = item.content) === null || _d === void 0 ? void 0 : _d.rendered) ? this.stripHtml(item.content.rendered) : '',
// Dados completos para compatibilidade
...item,
};
});
// Para cada item, criar um objeto de saída para n8n
return enhancedResults.map(item => ({ json: item }));
}
/**
* Converte resultado booleano em formato de nó n8n
* @param success Resultado da operação
* @param id ID do recurso (opcional)
* @returns Dados formatados para o n8n
*/
formatBooleanOutput(success, id) {
return [{
json: {
success,
id,
message: success
? id
? `Operation completed successfully for item with ID ${id}`
: 'Operation completed successfully'
: 'Operation failed',
timestamp: new Date().toISOString(),
}
}];
}
/**
* Remove tags HTML de uma string
* @param html String HTML
* @returns Texto sem HTML
*/
stripHtml(html) {
// Remover tags HTML
const text = html.replace(/<\/?[^>]+(>|$)/g, ' ');
// Normalizar espaços
return text.replace(/\s+/g, ' ').trim();
}
/**
* Obtém o label do status para exibição
* @param status Status do WordPress
* @returns Label formatado
*/
getStatusLabel(status) {
const statusMap = {
'publish': 'Published',
'draft': 'Draft',
'pending': 'Pending Review',
'private': 'Private',
'future': 'Scheduled',
'trash': 'Trash',
};
return statusMap[status] || status;
}
}
exports.NodeService = NodeService;
//# sourceMappingURL=NodeService.js.map