UNPKG

@ai-growth/n8n-nodes-wordpress

Version:

n8n node for WordPress integration with AI GROWTH - SEO WP plugin

670 lines 30 kB
"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