UNPKG

@ai-growth/n8n-nodes-wordpress

Version:

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

541 lines 26 kB
"use strict"; Object.defineProperty(exports, "__esModule", { value: true }); exports.PostCreateService = void 0; const ErrorUtils_1 = require("../utils/ErrorUtils"); const SeoService_1 = require("./SeoService"); const FaqService_1 = require("./FaqService"); const CtaService_1 = require("./CtaService"); const MetadataService_1 = require("./MetadataService"); const FeaturedImageService_1 = require("./FeaturedImageService"); const TaxonomyService_1 = require("./TaxonomyService"); /** * Serviço para criação e atualização de posts e páginas no WordPress */ class PostCreateService { /** * Construtor do serviço * @param client Cliente WordPress */ constructor(client) { this.client = client; this.seoService = new SeoService_1.SeoService(client); this.faqService = new FaqService_1.FaqService(client, this.seoService); this.ctaService = new CtaService_1.CtaService(client, this.seoService); this.metadataService = new MetadataService_1.MetadataService(client); this.featuredImageService = new FeaturedImageService_1.FeaturedImageService(client); this.taxonomyService = new TaxonomyService_1.TaxonomyService(client); } /** * Cria um novo post no WordPress * @param post Dados do post a ser criado * @param options Opções da operação * @returns Post criado */ async createPost(post, options = {}) { try { // Separar metadados do post principal const { meta_title, meta_description, meta_keywords, og_title, og_description, twitter_title, twitter_description, faq, cta, ...postData } = post; // Converter para o formato esperado pela API WordPress const postPayload = this.formatPostForApi(postData, 'post'); // Criar post no WordPress const response = await this.client.post('posts', postPayload); if (!response || !response.id) { throw new ErrorUtils_1.WordPressError('Failed to create WordPress post: Invalid response', ErrorUtils_1.WordPressErrorType.SERVER); } // ID do post criado const postId = response.id; // Processar taxonomias (categorias e tags) se necessário if (post.categories || post.tags) { await this.taxonomyService.associateTaxonomiesWithPost(postId, post.categories || [], post.tags || []); } // Verificar se deve processar imagem destacada automaticamente const shouldProcessFeaturedImage = options.updateFeaturedImage || !!post.featured_image_url || !!post.featured_image_id || post.featured_media !== undefined; // Configurar opções para processamento de metadados const metadataOptions = { ...options, updateFeaturedImage: shouldProcessFeaturedImage }; // Processar metadados SEO se necessário await this.processMetadata(postId, post, metadataOptions); // Obter post completo com metadados return this.getCompletePost(postId, 'post', options); } catch (error) { if (error instanceof ErrorUtils_1.WordPressError) { throw error; } throw new ErrorUtils_1.WordPressError(`Failed to create WordPress post: ${error.message}`, ErrorUtils_1.WordPressErrorType.SERVER, undefined, error); } } /** * Cria uma nova página no WordPress * @param page Dados da página a ser criada * @param options Opções da operação * @returns Página criada */ async createPage(page, options = {}) { try { // Separar metadados da página principal const { meta_title, meta_description, meta_keywords, og_title, og_description, twitter_title, twitter_description, faq, cta, ...pageData } = page; // Converter para o formato esperado pela API WordPress const pagePayload = this.formatPostForApi(pageData, 'page'); // Criar página no WordPress const response = await this.client.post('pages', pagePayload); if (!response || !response.id) { throw new ErrorUtils_1.WordPressError('Failed to create WordPress page: Invalid response', ErrorUtils_1.WordPressErrorType.SERVER); } // ID da página criada const pageId = response.id; // Verificar se deve processar imagem destacada automaticamente const shouldProcessFeaturedImage = options.updateFeaturedImage || !!page.featured_image_url || !!page.featured_image_id || page.featured_media !== undefined; // Configurar opções para processamento de metadados const metadataOptions = { ...options, updateFeaturedImage: shouldProcessFeaturedImage }; // Processar metadados SEO se necessário await this.processMetadata(pageId, page, metadataOptions); // Obter página completa com metadados return this.getCompletePost(pageId, 'page', options); } catch (error) { if (error instanceof ErrorUtils_1.WordPressError) { throw error; } throw new ErrorUtils_1.WordPressError(`Failed to create WordPress page: ${error.message}`, ErrorUtils_1.WordPressErrorType.SERVER, undefined, error); } } /** * Atualiza um post existente no WordPress * @param postId ID do post a ser atualizado * @param post Dados do post * @param options Opções da operação * @returns Post atualizado */ async updatePost(postId, post, options = {}) { try { // Verificar se o post existe await this.validateResourceExists(postId, 'post'); // Separar metadados do post principal const { meta_title, meta_description, meta_keywords, og_title, og_description, twitter_title, twitter_description, faq, cta, ...postData } = post; // Construir payload de atualização apenas com campos especificados const postPayload = {}; // Adicionar campos apenas se definidos if (postData.title !== undefined) { postPayload.title = postData.title; } if (postData.content !== undefined) { postPayload.content = postData.content; } if (postData.status !== undefined) { postPayload.status = postData.status; } if (postData.excerpt !== undefined) { postPayload.excerpt = postData.excerpt; } if (postData.slug !== undefined) { postPayload.slug = postData.slug; } if (postData.featured_media !== undefined) { postPayload.featured_media = postData.featured_media; } if (postData.categories !== undefined) { postPayload.categories = postData.categories; } if (postData.tags !== undefined) { postPayload.tags = postData.tags; } // Se não há campos para atualizar, verificar se há metadados para atualizar if (Object.keys(postPayload).length === 0 && !meta_title && !meta_description && !meta_keywords && !og_title && !og_description && !twitter_title && !twitter_description && !faq && !cta) { throw new ErrorUtils_1.WordPressError('No fields specified for update', ErrorUtils_1.WordPressErrorType.VALIDATION); } // Atualizar post no WordPress (apenas se houver campos para atualizar) let response = null; if (Object.keys(postPayload).length > 0) { response = await this.client.put(`posts/${postId}`, postPayload); if (!response || !response.id) { throw new ErrorUtils_1.WordPressError(`Failed to update WordPress post with ID ${postId}: Invalid response`, ErrorUtils_1.WordPressErrorType.SERVER); } } // Processar metadados SEO se necessário await this.processMetadata(postId, { ...post, type: 'post' }, options); // Obter post completo com metadados return this.getCompletePost(postId, 'post', options); } catch (error) { if (error instanceof ErrorUtils_1.WordPressError) { throw error; } throw new ErrorUtils_1.WordPressError(`Failed to update WordPress post with ID ${postId}: ${error.message}`, ErrorUtils_1.WordPressErrorType.SERVER, undefined, error); } } /** * Atualiza uma página existente no WordPress * @param pageId ID da página a ser atualizada * @param page Dados da página * @param options Opções da operação * @returns Página atualizada */ async updatePage(pageId, page, options = {}) { try { // Verificar se a página existe await this.validateResourceExists(pageId, 'page'); // Separar metadados da página principal const { meta_title, meta_description, meta_keywords, og_title, og_description, twitter_title, twitter_description, faq, cta, ...pageData } = page; // Construir payload de atualização apenas com campos especificados const pagePayload = {}; // Adicionar campos apenas se definidos if (pageData.title !== undefined) { pagePayload.title = pageData.title; } if (pageData.content !== undefined) { pagePayload.content = pageData.content; } if (pageData.status !== undefined) { pagePayload.status = pageData.status; } if (pageData.excerpt !== undefined) { pagePayload.excerpt = pageData.excerpt; } if (pageData.slug !== undefined) { pagePayload.slug = pageData.slug; } if (pageData.featured_media !== undefined) { pagePayload.featured_media = pageData.featured_media; } // Se não há campos para atualizar, verificar se há metadados para atualizar if (Object.keys(pagePayload).length === 0 && !meta_title && !meta_description && !meta_keywords && !og_title && !og_description && !twitter_title && !twitter_description && !faq && !cta) { throw new ErrorUtils_1.WordPressError('No fields specified for update', ErrorUtils_1.WordPressErrorType.VALIDATION); } // Atualizar página no WordPress (apenas se houver campos para atualizar) let response = null; if (Object.keys(pagePayload).length > 0) { response = await this.client.put(`pages/${pageId}`, pagePayload); if (!response || !response.id) { throw new ErrorUtils_1.WordPressError(`Failed to update WordPress page with ID ${pageId}: Invalid response`, ErrorUtils_1.WordPressErrorType.SERVER); } } // Processar metadados SEO se necessário await this.processMetadata(pageId, { ...page, type: 'page' }, options); // Obter página completa com metadados return this.getCompletePost(pageId, 'page', options); } catch (error) { if (error instanceof ErrorUtils_1.WordPressError) { throw error; } throw new ErrorUtils_1.WordPressError(`Failed to update WordPress page with ID ${pageId}: ${error.message}`, ErrorUtils_1.WordPressErrorType.SERVER, undefined, error); } } /** * Exclui um post do WordPress * @param postId ID do post a ser excluído * @param force Forçar exclusão permanente (true) ou mover para lixeira (false) * @returns true se excluído com sucesso */ async deletePost(postId, force = false) { try { // Parâmetros da requisição const params = {}; // Adicionar parâmetro force se solicitado if (force) { params.force = 'true'; } // Excluir post await this.client.get(`posts/${postId}`, params); await this.client.delete(`posts/${postId}`); return true; } catch (error) { if (error instanceof ErrorUtils_1.WordPressError) { throw error; } throw new ErrorUtils_1.WordPressError(`Failed to delete WordPress post with ID ${postId}: ${error.message}`, ErrorUtils_1.WordPressErrorType.SERVER, undefined, error); } } /** * Exclui uma página do WordPress * @param pageId ID da página a ser excluída * @param force Forçar exclusão permanente (true) ou mover para lixeira (false) * @returns true se excluída com sucesso */ async deletePage(pageId, force = false) { try { // Parâmetros da requisição const params = {}; // Adicionar parâmetro force se solicitado if (force) { params.force = 'true'; } // Excluir página await this.client.get(`pages/${pageId}`, params); await this.client.delete(`pages/${pageId}`); return true; } catch (error) { if (error instanceof ErrorUtils_1.WordPressError) { throw error; } throw new ErrorUtils_1.WordPressError(`Failed to delete WordPress page with ID ${pageId}: ${error.message}`, ErrorUtils_1.WordPressErrorType.SERVER, undefined, error); } } /** * Processa os metadados SEO, FAQ e CTA * @param postId ID do post/página * @param post Dados do post/página * @param options Opções da operação * @returns void */ async processMetadata(postId, post, options) { try { const { updateSeoMetadata = false, updateFaqs = false, updateCta = false, updateFeaturedImage = false } = options; // Atualizar metadados SEO se solicitado if (updateSeoMetadata) { console.log('[PostCreateService] Processing SEO metadata:', { postId, updateSeoMetadata, meta_title: post.meta_title, meta_description: post.meta_description, meta_keywords: post.meta_keywords, og_title: post.og_title, og_description: post.og_description, twitter_title: post.twitter_title, twitter_description: post.twitter_description, hasMetaTitle: post.meta_title !== undefined, hasMetaDescription: post.meta_description !== undefined, hasMetaKeywords: post.meta_keywords !== undefined }); // Verificar se há pelo menos um campo de metadados definido (incluindo strings vazias) if (post.meta_title !== undefined || post.meta_description !== undefined || post.meta_keywords !== undefined || post.og_title !== undefined || post.og_description !== undefined || post.twitter_title !== undefined || post.twitter_description !== undefined) { const seoMetadata = {}; if (post.meta_title !== undefined) { seoMetadata.meta_title = post.meta_title; console.log('[PostCreateService] Including meta_title:', post.meta_title); } if (post.meta_description !== undefined) { seoMetadata.meta_description = post.meta_description; console.log('[PostCreateService] Including meta_description:', post.meta_description); } if (post.meta_keywords !== undefined) { seoMetadata.meta_keywords = post.meta_keywords; console.log('[PostCreateService] Including meta_keywords:', post.meta_keywords); } if (post.og_title !== undefined) { seoMetadata.og_title = post.og_title; console.log('[PostCreateService] Including og_title:', post.og_title); } if (post.og_description !== undefined) { seoMetadata.og_description = post.og_description; console.log('[PostCreateService] Including og_description:', post.og_description); } if (post.twitter_title !== undefined) { seoMetadata.twitter_title = post.twitter_title; console.log('[PostCreateService] Including twitter_title:', post.twitter_title); } if (post.twitter_description !== undefined) { seoMetadata.twitter_description = post.twitter_description; console.log('[PostCreateService] Including twitter_description:', post.twitter_description); } console.log('[PostCreateService] Calling MetadataService.updateMetadata with:', seoMetadata); await this.metadataService.updateMetadata(postId, seoMetadata); } else { console.log('[PostCreateService] No SEO metadata fields provided to update'); } } else { console.log('[PostCreateService] SEO metadata update not requested'); } // Verificar disponibilidade do plugin apenas para FAQs e CTA const pluginInfo = await this.seoService.checkPluginAvailability(); // Atualizar FAQs se solicitado e plugin disponível if (updateFaqs && post.faq !== undefined && pluginInfo.status === 'active') { await this.faqService.updatePostFaqs(postId, post.faq); } // Atualizar CTA se solicitado e plugin disponível if (updateCta && post.cta !== undefined && pluginInfo.status === 'active') { await this.ctaService.updatePostCta(postId, post.cta); } // Processar imagem destacada se solicitado if (updateFeaturedImage) { await this.processFeaturedImage(postId, post); } } catch (error) { throw new ErrorUtils_1.WordPressError(`Failed to process metadata for post/page with ID ${postId}: ${error.message}`, ErrorUtils_1.WordPressErrorType.SERVER, undefined, error); } } /** * Processa a imagem destacada de um post/página * @param postId ID do post/página * @param post Dados do post/página * @returns void */ async processFeaturedImage(postId, post) { try { const contentType = post.type === 'page' ? 'page' : 'post'; if (post.featured_image_url) { // Definir metadados da imagem const metadata = { alt_text: post.featured_image_alt, title: post.featured_image_title, caption: post.featured_image_caption, description: post.featured_image_description }; // Fazer upload e definir a imagem destacada a partir da URL await this.featuredImageService.setFeaturedImageFromUrl(postId, contentType, post.featured_image_url, { metadata }); } else if (post.featured_image_id) { // Definir imagem destacada a partir do ID existente await this.featuredImageService.setFeaturedImage(postId, contentType, post.featured_image_id); } else if (post.featured_media) { // Definir imagem destacada a partir do ID de mídia await this.featuredImageService.setFeaturedImage(postId, contentType, post.featured_media); } else if (post.featured_media === 0) { // Remover imagem destacada await this.featuredImageService.removeFeaturedImage(postId, contentType); } } catch (error) { // Logar erro mas não interromper a operação principal console.error('Erro ao processar imagem destacada:', error); } } /** * Formata um post/página para o formato esperado pela API WordPress * @param post Dados do post * @param type Tipo de conteúdo (post ou page) * @returns Payload formatado para a API */ formatPostForApi(post, type) { // Converter para o formato esperado pela API WordPress const payload = { title: post.title, content: post.content, status: post.status || 'draft', }; // Note: Categories and tags are handled separately via TaxonomyService // to ensure proper format validation and ID conversion return payload; } /** * Obtém um post/página completo com metadados * @param id ID do post/página * @param type Tipo de conteúdo (post ou page) * @param options Opções da operação * @returns Post/página completo */ async getCompletePost(id, type, options) { var _a, _b, _c; const { updateSeoMetadata = false, updateFaqs = false, updateCta = false } = options; // Obter post/página da API WordPress const endpoint = type === 'post' ? `posts/${id}` : `pages/${id}`; const response = await this.client.get(endpoint); // Formatar a resposta const formattedPost = { id: response.id, title: ((_a = response.title) === null || _a === void 0 ? void 0 : _a.rendered) || '', content: ((_b = response.content) === null || _b === void 0 ? void 0 : _b.rendered) || '', status: response.status || 'draft', categories: type === 'post' ? response.categories || [] : [], tags: type === 'post' ? response.tags || [] : [], image_url: response.featured_media && typeof response.featured_media === 'number' ? '' // Não temos acesso direto à URL da imagem pelo ID : ((_c = response.featured_media) === null || _c === void 0 ? void 0 : _c.source_url) || '', }; // Verificar disponibilidade do plugin const pluginInfo = await this.seoService.checkPluginAvailability(); // Se o plugin não está disponível, retorna o post sem metadados if (pluginInfo.status !== 'active') { return formattedPost; } // Adicionar metadados SEO se solicitado if (updateSeoMetadata) { try { const seoMetadata = await this.seoService.getPostSeoMetadata(id); if (seoMetadata) { formattedPost.meta_title = seoMetadata.meta_title; formattedPost.meta_description = seoMetadata.meta_description; formattedPost.meta_keywords = seoMetadata.meta_keywords; } } catch (error) { // Ignorar erros ao buscar metadados SEO } } // Adicionar FAQs se solicitado if (updateFaqs) { try { const faqs = await this.faqService.getPostFaqs(id); if (faqs && faqs.length > 0) { formattedPost.faq = faqs; } } catch (error) { // Ignorar erros ao buscar FAQs } } // Adicionar CTA se solicitado if (updateCta) { try { const cta = await this.ctaService.getPostCta(id); if (cta) { formattedPost.cta = cta; } } catch (error) { // Ignorar erros ao buscar CTA } } return formattedPost; } /** * Verifica se um post/página existe no WordPress * @param id ID do post/página * @param type Tipo de conteúdo (post ou page) * @returns O post/página se existir * @throws WordPressError se não existir */ async validateResourceExists(id, type) { try { const endpoint = type === 'post' ? `posts/${id}` : `pages/${id}`; const response = await this.client.get(endpoint); if (!response || !response.id) { throw new ErrorUtils_1.WordPressError(`${type === 'post' ? 'Post' : 'Page'} with ID ${id} not found`, ErrorUtils_1.WordPressErrorType.NOT_FOUND); } return response; } catch (error) { // Se já é um WordPressError do tipo NOT_FOUND, apenas relançar if (error instanceof ErrorUtils_1.WordPressError && error.type === ErrorUtils_1.WordPressErrorType.NOT_FOUND) { throw error; } // Se é um erro de outra natureza if (error instanceof ErrorUtils_1.WordPressError) { throw error; } // Erro genérico throw new ErrorUtils_1.WordPressError(`Error validating ${type} with ID ${id}: ${error.message}`, ErrorUtils_1.WordPressErrorType.UNKNOWN, undefined, error); } } } exports.PostCreateService = PostCreateService; //# sourceMappingURL=PostCreateService.js.map