Files
Content-Hunter_BE/src/modules/seo/seo.service.ts
Harun CAN fc88faddb9
All checks were successful
Backend Deploy 🚀 / build-and-deploy (push) Successful in 2m1s
main
2026-02-10 12:27:14 +03:00

191 lines
6.1 KiB
TypeScript

// 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);
}
}