// SEO Service - Main orchestration service // Path: src/modules/seo/seo.service.ts import { Injectable, Logger } from '@nestjs/common'; import { KeywordResearchService, Keyword, KeywordCluster } from './services/keyword-research.service'; import { ContentOptimizationService, SeoScore, OptimizedMeta } from './services/content-optimization.service'; import { CompetitorAnalysisService, ContentGap, CompetitorProfile } from './services/competitor-analysis.service'; export interface FullSeoAnalysis { content: { score: SeoScore; meta: OptimizedMeta; }; keywords: { main: Keyword; related: Keyword[]; clusters: KeywordCluster[]; longTail: ReturnType; }; competitors: { gaps: ContentGap[]; }; } @Injectable() export class SeoService { private readonly logger = new Logger(SeoService.name); constructor( private readonly keywordService: KeywordResearchService, private readonly optimizationService: ContentOptimizationService, private readonly competitorService: CompetitorAnalysisService, ) { } /** * Full SEO analysis for content */ async analyzeFull( content: string, targetKeyword: string, options?: { title?: string; metaDescription?: string; url?: string; competitorDomains?: string[]; }, ): Promise { // Analyze content const score = this.optimizationService.analyze(content, { targetKeyword, title: options?.title, metaDescription: options?.metaDescription, url: options?.url, }); // Generate optimized meta const meta = this.optimizationService.generateMeta(content, targetKeyword); // Research keywords const keywordData = await this.keywordService.suggestKeywords(targetKeyword, { count: 20, includeQuestions: true, includeLongTail: true, }); // Cluster keywords const allKeywords = [targetKeyword, ...keywordData.related.map((k) => k.term)]; const clusters = this.keywordService.clusterKeywords(allKeywords); // Long-tail variations const longTail = this.keywordService.generateLongTail(targetKeyword, 15); // Content gaps (if competitors provided) let gaps: ContentGap[] = []; if (options?.competitorDomains?.length) { gaps = await this.competitorService.findContentGaps( allKeywords, options.competitorDomains, ); } return { content: { score, meta }, keywords: { main: keywordData.main, related: keywordData.related, clusters, longTail, }, competitors: { gaps }, }; } /** * Quick SEO score check */ quickScore(content: string, targetKeyword?: string): number { const analysis = this.optimizationService.analyze(content, { targetKeyword }); return analysis.overall; } /** * Generate SEO-optimized content outline */ async generateOutline( keyword: string, options?: { competitorDomains?: string[]; contentType?: string; }, ): Promise<{ title: string; description: string; headings: string[]; keywords: string[]; estimatedWordCount: number; differentiators: string[]; }> { // Get keyword data const keywordData = await this.keywordService.suggestKeywords(keyword); // Get title variations const titles = this.optimizationService.generateTitleVariations(keyword, 1); const descriptions = this.optimizationService.generateDescriptionVariations(keyword, '', 1); // Generate outline if competitors provided let headings: string[] = []; let differentiators: string[] = []; if (options?.competitorDomains?.length) { const competitorContent = await this.competitorService.analyzeTopContent(keyword, 5); const blueprint = this.competitorService.generateContentBlueprint(keyword, competitorContent); headings = blueprint.suggestedHeadings; differentiators = blueprint.differentiators; } else { headings = [ `What is ${keyword}?`, `Why ${keyword} is Important`, `How to Use ${keyword}`, `${keyword} Best Practices`, `Common ${keyword} Mistakes`, `${keyword} Tools & Resources`, `FAQs about ${keyword}`, ]; differentiators = [ 'Include original research or data', 'Add expert insights', 'Provide actionable steps', 'Include real examples', ]; } return { title: titles[0] || `Complete Guide to ${keyword}`, description: descriptions[0] || `Learn everything about ${keyword} in this comprehensive guide.`, headings, keywords: [keyword, ...keywordData.related.slice(0, 5).map((k) => k.term)], estimatedWordCount: 1500, differentiators, }; } /** * Get LSI keywords for semantic SEO */ getLSIKeywords(keyword: string, count: number = 10): string[] { return this.keywordService.generateLSIKeywords(keyword, count); } /** * Analyze keyword difficulty */ analyzeKeywordDifficulty(keyword: string) { return this.keywordService.analyzeKeywordDifficulty(keyword); } /** * Check for keyword cannibalization */ checkCannibalization(newKeyword: string, existingKeywords: string[]) { return this.optimizationService.checkCannibalization(newKeyword, existingKeywords); } /** * Get competitor insights */ async getCompetitorInsights(domain: string): Promise { return this.competitorService.analyzeCompetitor(domain); } }