generated from fahricansecer/boilerplate-be
This commit is contained in:
190
src/modules/seo/seo.service.ts
Normal file
190
src/modules/seo/seo.service.ts
Normal file
@@ -0,0 +1,190 @@
|
||||
// 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<KeywordResearchService['generateLongTail']>;
|
||||
};
|
||||
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<FullSeoAnalysis> {
|
||||
// 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<CompetitorProfile> {
|
||||
return this.competitorService.analyzeCompetitor(domain);
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user