fix: credit tracking, voice clone TTL, avatar upload ui, asset serving fallback, OAuth encryption, free plan video renders, backlink outreach sprint

This commit is contained in:
ajaysi
2026-05-25 17:07:35 +05:30
parent 090d69761f
commit 9b3bec698b
99 changed files with 15892 additions and 1278 deletions

View File

@@ -1,5 +1,7 @@
import { apiClient } from './client';
// -- Shared Types --
export interface BacklinkModuleRecord {
identifier: 'backlink' | 'outreach' | 'guest_post' | string;
module_path: string;
@@ -24,6 +26,8 @@ export interface BacklinkQueryTemplatesResponse {
queries: string[];
}
// -- Discovery --
export interface BacklinkDiscoveryRequest {
keyword: string;
max_results?: number;
@@ -36,77 +40,12 @@ export interface BacklinkOpportunity {
confidence_score: number;
}
export interface BacklinkPolicyValidationRequest {
user_id: string;
workspace_id: string;
campaign_id: string;
recipient_email: string;
recipient_domain: string;
recipient_region: string;
legal_basis: string;
approved_by_human: boolean;
unsubscribe_url?: string;
sender_identity: string;
idempotency_key: string;
}
export interface BacklinkPolicyValidationResponse {
allowed: boolean;
reasons: string[];
final_status: string;
}
export interface BacklinkReportingSnapshot {
send_volume: number;
decision_events: number;
response_rate: number;
placement_conversion: number;
}
export interface BacklinkDiscoveryResponse {
keyword: string;
queries: string[];
opportunities: BacklinkOpportunity[];
}
export interface BacklinkCampaignRecord {
campaign_id: string;
name: string;
status: string;
created_at?: string;
}
export interface BacklinkCampaignCreateRequest {
user_id: string;
workspace_id: string;
name: string;
}
export interface BacklinkCampaignCreateResponse {
campaign_id: string;
name: string;
status: string;
}
export interface BacklinkCampaignListResponse {
campaigns: BacklinkCampaignRecord[];
}
export const fetchBacklinkModuleRegistry = async (): Promise<BacklinkModuleRegistryResponse> => (await apiClient.get('/api/backlink-outreach/modules')).data;
export const fetchBacklinkMigrationCoverage = async (): Promise<BacklinkCoverageResponse> => (await apiClient.get('/api/backlink-outreach/migration-coverage')).data;
export const fetchBacklinkQueryTemplates = async (keyword: string): Promise<BacklinkQueryTemplatesResponse> => (await apiClient.get('/api/backlink-outreach/query-templates', { params: { keyword } })).data;
export const discoverBacklinkOpportunities = async (payload: BacklinkDiscoveryRequest): Promise<BacklinkDiscoveryResponse> => (await apiClient.post('/api/backlink-outreach/discover', payload)).data;
export const validateBacklinkPolicy = async (payload: BacklinkPolicyValidationRequest): Promise<BacklinkPolicyValidationResponse> => (await apiClient.post('/api/backlink-outreach/policy-validate', payload)).data;
export const fetchBacklinkReportingSnapshot = async (): Promise<BacklinkReportingSnapshot> => (await apiClient.get('/api/backlink-outreach/reporting')).data;
export const createBacklinkCampaign = async (payload: BacklinkCampaignCreateRequest): Promise<BacklinkCampaignCreateResponse> => (await apiClient.post('/api/backlink-outreach/campaigns', payload)).data;
export const listBacklinkCampaigns = async (user_id: string, workspace_id: string): Promise<BacklinkCampaignListResponse> => (await apiClient.get('/api/backlink-outreach/campaigns', { params: { user_id, workspace_id } })).data;
// -- Deep Discovery --
export interface EnrichedOpportunity {
url: string;
domain: string;
@@ -135,7 +74,58 @@ export interface DeepDiscoveryResponse {
opportunities: EnrichedOpportunity[];
}
export const discoverDeepBacklinkOpportunities = async (payload: DeepDiscoveryRequest): Promise<DeepDiscoveryResponse> => (await apiClient.post('/api/backlink-outreach/discover/deep', payload)).data;
// -- Policy --
export interface BacklinkPolicyValidationRequest {
user_id: string;
workspace_id: string;
campaign_id: string;
recipient_email: string;
recipient_domain: string;
recipient_region: string;
legal_basis: string;
approved_by_human: boolean;
unsubscribe_url?: string;
sender_identity: string;
idempotency_key: string;
}
export interface BacklinkPolicyValidationResponse {
allowed: boolean;
reasons: string[];
final_status: string;
}
export interface BacklinkReportingSnapshot {
send_volume: number;
decision_events: number;
response_rate: number;
placement_conversion: number;
}
// -- Campaigns --
export interface BacklinkCampaignRecord {
campaign_id: string;
name: string;
status: string;
created_at?: string;
}
export interface BacklinkCampaignCreateRequest {
workspace_id: string;
name: string;
}
export interface BacklinkCampaignCreateResponse {
campaign_id: string;
name: string;
status: string;
}
export interface BacklinkCampaignListResponse {
campaigns: BacklinkCampaignRecord[];
}
// -- Leads --
@@ -184,7 +174,248 @@ export interface CampaignDetailResponse {
leads: LeadRecord[];
}
export const fetchCampaignDetail = async (campaign_id: string, user_id: string): Promise<CampaignDetailResponse> => (await apiClient.get(`/api/backlink-outreach/campaigns/${campaign_id}`, { params: { user_id } })).data;
export const fetchCampaignLeads = async (campaign_id: string, user_id: string, status?: string): Promise<LeadListResponse> => (await apiClient.get(`/api/backlink-outreach/campaigns/${campaign_id}/leads`, { params: { user_id, status } })).data;
// -- Outreach Attempts --
export interface SendOutreachRequest {
lead_id: string;
campaign_id: string;
sender_email: string;
subject: string;
body: string;
idempotency_key: string;
template_id?: string;
template_variables?: Record<string, string>;
}
export interface SendOutreachResponse {
attempt_id: string;
status: string;
policy_allowed: boolean;
policy_reasons: string[];
}
export interface OutreachAttemptRecord {
attempt_id: string;
lead_id: string;
campaign_id: string;
idempotency_key: string;
sender_email: string;
subject: string;
status: string;
decision_reason: string | null;
sent_at: string | null;
created_at: string | null;
}
export interface OutreachAttemptListResponse {
attempts: OutreachAttemptRecord[];
total: number;
}
// -- Replies --
export interface OutreachReplyRecord {
reply_id: string;
attempt_id: string;
from_email: string;
subject: string;
received_at: string | null;
classification: string;
body: string;
}
export interface OutreachReplyListResponse {
replies: OutreachReplyRecord[];
total: number;
}
// -- Follow-ups --
export interface ScheduleFollowUpRequest {
attempt_id: string;
scheduled_for: string;
subject?: string;
body?: string;
}
export interface FollowUpScheduleRecord {
schedule_id: string;
attempt_id: string;
subject: string;
scheduled_for: string | null;
sent: boolean;
}
// -- Email Templates --
export interface EmailTemplateRequest {
name: string;
subject_template: string;
body_template: string;
variables?: string[];
}
export interface EmailTemplateRecord {
template_id: string;
user_id: string;
name: string;
subject_template: string;
body_template: string;
variables: string[];
created_at: string | null;
}
export interface GenerateEmailRequest {
topic: string;
target_site?: string;
tone?: 'professional' | 'friendly' | 'casual' | 'formal';
existing_template_id?: string;
}
export interface GeneratedEmailResponse {
subject: string;
body: string;
}
export interface PersonalizeEmailRequest {
lead_name: string;
lead_site: string;
lead_content_topic: string;
pitch_topic: string;
existing_body?: string;
}
export interface SubjectLinesRequest {
body: string;
count?: number;
}
export interface SubjectLinesResponse {
subjects: string[];
}
export interface FollowUpRequest {
original_subject: string;
original_body: string;
days_elapsed?: number;
reply_context?: string;
}
// -- Campaign Analytics --
export interface BulkStatusUpdateRequest {
lead_ids: string[];
status: string;
notes?: string;
}
export interface BulkStatusUpdateResponse {
updated: number;
failed: string[];
}
export interface CampaignVolumePoint {
date: string;
count: number;
}
export interface CampaignVolumeResponse {
campaign_id: string;
days: number;
volume: CampaignVolumePoint[];
}
export interface FunnelStage {
status: string;
count: number;
}
export interface ConversionFunnelResponse {
campaign_id: string;
stages: FunnelStage[];
}
export interface CampaignAnalyticsResponse {
campaign_id: string;
lead_count: number;
send_volume: number;
blocked_count: number;
reply_count: number;
response_rate: number;
placement_rate: number;
reply_classification: Record<string, number>;
}
// ============================================================
// API Functions
// ============================================================
// Discovery
export const fetchBacklinkModuleRegistry = async (): Promise<BacklinkModuleRegistryResponse> => (await apiClient.get('/api/backlink-outreach/modules')).data;
export const fetchBacklinkMigrationCoverage = async (): Promise<BacklinkCoverageResponse> => (await apiClient.get('/api/backlink-outreach/migration-coverage')).data;
export const fetchBacklinkQueryTemplates = async (keyword: string): Promise<BacklinkQueryTemplatesResponse> => (await apiClient.get('/api/backlink-outreach/query-templates', { params: { keyword } })).data;
export const discoverBacklinkOpportunities = async (payload: BacklinkDiscoveryRequest): Promise<BacklinkDiscoveryResponse> => (await apiClient.post('/api/backlink-outreach/discover', payload)).data;
export const discoverDeepBacklinkOpportunities = async (payload: DeepDiscoveryRequest): Promise<DeepDiscoveryResponse> => (await apiClient.post('/api/backlink-outreach/discover/deep', payload)).data;
// Policy & Reporting
export const validateBacklinkPolicy = async (payload: BacklinkPolicyValidationRequest): Promise<BacklinkPolicyValidationResponse> => (await apiClient.post('/api/backlink-outreach/policy-validate', payload)).data;
export const fetchBacklinkReportingSnapshot = async (): Promise<BacklinkReportingSnapshot> => (await apiClient.get('/api/backlink-outreach/reporting')).data;
// Campaigns (auth handled by backend via Clerk)
export const createBacklinkCampaign = async (payload: BacklinkCampaignCreateRequest): Promise<BacklinkCampaignCreateResponse> => (await apiClient.post('/api/backlink-outreach/campaigns', payload)).data;
export const listBacklinkCampaigns = async (workspace_id: string): Promise<BacklinkCampaignListResponse> => (await apiClient.get('/api/backlink-outreach/campaigns', { params: { workspace_id } })).data;
export const fetchCampaignDetail = async (campaign_id: string): Promise<CampaignDetailResponse> => (await apiClient.get(`/api/backlink-outreach/campaigns/${campaign_id}`)).data;
export const fetchCampaignLeads = async (campaign_id: string, status?: string): Promise<LeadListResponse> => (await apiClient.get(`/api/backlink-outreach/campaigns/${campaign_id}/leads`, { params: { status } })).data;
export const addLeadToCampaign = async (campaign_id: string, payload: LeadCreateRequest): Promise<LeadRecord> => (await apiClient.post(`/api/backlink-outreach/campaigns/${campaign_id}/leads`, payload)).data;
export const updateLeadStatus = async (lead_id: string, payload: LeadStatusUpdateRequest): Promise<LeadRecord> => (await apiClient.patch(`/api/backlink-outreach/leads/${lead_id}/status`, payload)).data;
export const bulkUpdateLeadStatus = async (payload: BulkStatusUpdateRequest): Promise<BulkStatusUpdateResponse> => (await apiClient.post('/api/backlink-outreach/leads/bulk-status', payload)).data;
// Outreach
export const sendOutreach = async (payload: SendOutreachRequest): Promise<SendOutreachResponse> => (await apiClient.post('/api/backlink-outreach/send-outreach', payload)).data;
export const fetchCampaignAttempts = async (campaign_id: string): Promise<OutreachAttemptListResponse> => (await apiClient.get(`/api/backlink-outreach/campaigns/${campaign_id}/attempts`)).data;
export const fetchCampaignReplies = async (campaign_id: string): Promise<OutreachReplyListResponse> => (await apiClient.get(`/api/backlink-outreach/campaigns/${campaign_id}/replies`)).data;
export const pollReplies = async (sent_from_email: string): Promise<{ polled: number; stored: number; replies: OutreachReplyRecord[] }> => (await apiClient.post('/api/backlink-outreach/replies/poll', null, { params: { sent_from_email } })).data;
// Follow-ups
export const scheduleFollowUp = async (campaign_id: string, payload: ScheduleFollowUpRequest): Promise<{ campaign_id: string; schedule: FollowUpScheduleRecord }> => (await apiClient.post(`/api/backlink-outreach/campaigns/${campaign_id}/schedule-followup`, payload)).data;
export const fetchFollowUps = async (campaign_id: string): Promise<{ followups: FollowUpScheduleRecord[]; total: number }> => (await apiClient.get(`/api/backlink-outreach/campaigns/${campaign_id}/followups`)).data;
// Email Templates
export const createEmailTemplate = async (payload: EmailTemplateRequest): Promise<EmailTemplateRecord> => (await apiClient.post('/api/backlink-outreach/templates', payload)).data;
export const listEmailTemplates = async (): Promise<{ templates: EmailTemplateRecord[] }> => (await apiClient.get('/api/backlink-outreach/templates')).data;
export const fetchEmailTemplate = async (template_id: string): Promise<EmailTemplateRecord> => (await apiClient.get(`/api/backlink-outreach/templates/${template_id}`)).data;
export const deleteEmailTemplate = async (template_id: string): Promise<{ deleted: boolean }> => (await apiClient.delete(`/api/backlink-outreach/templates/${template_id}`)).data;
export const generateEmailTemplate = async (payload: GenerateEmailRequest): Promise<GeneratedEmailResponse> => (await apiClient.post('/api/backlink-outreach/templates/generate', payload)).data;
export const personalizeEmail = async (payload: PersonalizeEmailRequest): Promise<GeneratedEmailResponse> => (await apiClient.post('/api/backlink-outreach/generate/personalized', payload)).data;
export const generateSubjectLines = async (payload: SubjectLinesRequest): Promise<SubjectLinesResponse> => (await apiClient.post('/api/backlink-outreach/generate/subject-lines', payload)).data;
export const generateFollowUp = async (payload: FollowUpRequest): Promise<GeneratedEmailResponse> => (await apiClient.post('/api/backlink-outreach/generate/follow-up', payload)).data;
// Campaign Analytics
export const fetchCampaignAnalytics = async (campaign_id: string): Promise<CampaignAnalyticsResponse> => (await apiClient.get(`/api/backlink-outreach/campaigns/${campaign_id}/analytics`)).data;
export const fetchCampaignAnalyticsVolume = async (campaign_id: string, days: number = 30): Promise<CampaignVolumeResponse> => (await apiClient.get(`/api/backlink-outreach/campaigns/${campaign_id}/analytics/volume`, { params: { days } })).data;
export const fetchCampaignAnalyticsFunnel = async (campaign_id: string): Promise<ConversionFunnelResponse> => (await apiClient.get(`/api/backlink-outreach/campaigns/${campaign_id}/analytics/funnel`)).data;
async function csvFetch(url: string): Promise<Blob> {
try {
const res = await apiClient.get(url, { responseType: 'blob' });
return res.data;
} catch (err: any) {
if (err?.response?.data instanceof Blob) {
try {
const text = await err.response.data.text();
const json = JSON.parse(text);
throw new Error(json.detail || json.message || 'Export failed');
} catch (parseErr: any) {
if (parseErr.message && parseErr.message !== 'Export failed') throw parseErr;
}
}
throw err;
}
}
export const exportCampaignLeadsCsv = async (campaign_id: string): Promise<Blob> => csvFetch(`/api/backlink-outreach/campaigns/${campaign_id}/export/leads`);
export const exportCampaignAttemptsCsv = async (campaign_id: string): Promise<Blob> => csvFetch(`/api/backlink-outreach/campaigns/${campaign_id}/export/attempts`);
export const exportCampaignRepliesCsv = async (campaign_id: string): Promise<Blob> => csvFetch(`/api/backlink-outreach/campaigns/${campaign_id}/export/replies`);
// Suppression
export const fetchSuppressionList = async (): Promise<{ suppressed: any[] }> => (await apiClient.get('/api/backlink-outreach/suppression')).data;
export const addSuppression = async (email: string, reason?: string): Promise<any> => (await apiClient.post('/api/backlink-outreach/suppression', null, { params: { email, reason } })).data;

View File

@@ -1,4 +1,5 @@
import axios from 'axios';
import { getApiBaseUrl } from '../utils/apiUrl';
const sanitizeUrlForLogging = (url: string | undefined): string => {
if (!url) return '';
@@ -62,26 +63,8 @@ export const getAuthTokenGetter = (): (() => Promise<string | null>) | null => {
return authTokenGetter;
};
// Get API URL from environment variables
export const getApiUrl = () => {
const apiUrl = process.env.REACT_APP_API_URL;
const isProduction = process.env.NODE_ENV === 'production';
// In production, require REACT_APP_API_URL to be set
if (isProduction && !apiUrl) {
console.error('[apiClient] ❌ REACT_APP_API_URL is not set for production! Please configure in Vercel environment variables.');
throw new Error('REACT_APP_API_URL environment variable is required for production. Please set it in your Vercel project settings.');
}
// Always respect REACT_APP_API_URL if explicitly set — behavior is independent of
// whether the browser is on localhost, ngrok, or any other hostname.
if (apiUrl) {
return apiUrl;
}
// Development fallback when no env var is configured
return 'http://localhost:8000';
};
// Get API URL using shared utility that handles localhost vs ngrok detection
export const getApiUrl = getApiBaseUrl;
// Create a shared axios instance for all API calls
const apiBaseUrl = getApiUrl();

View File

@@ -0,0 +1,409 @@
/**
* Enterprise SEO API client for ALwrity frontend
* Handles Phase 2A endpoints: Enterprise Audit and GSC Analysis
*/
import { longRunningApiClient, apiClient } from './client';
// ============================================================================
// Type Definitions
// ============================================================================
export interface AuditIssue {
type: string;
severity: 'critical' | 'high' | 'medium' | 'low';
description: string;
affected_pages?: number;
estimated_impact?: string;
recommendation?: string;
}
export interface TechnicalAuditResult {
status: string;
pages_audited: number;
avg_score: number;
issues: AuditIssue[];
core_web_vitals?: {
lcp: number; // Largest Contentful Paint
fid: number; // First Input Delay
cls: number; // Cumulative Layout Shift
};
}
export interface PagePerformance {
url: string;
score: number;
status: string;
issues_count: number;
priority: string;
}
export interface KeywordAnalysis {
keyword: string;
volume: number;
difficulty: number;
current_ranking: number;
trend: string;
opportunity_score: number;
}
export interface ContentOpportunity {
type: string; // 'low_ctr', 'ready_to_rank', 'long_tail', etc.
keyword: string;
current_position: number;
impressions: number;
clicks: number;
ctr: number;
estimated_traffic_gain: number;
difficulty_score: number;
recommended_action: string;
priority: 'high' | 'medium' | 'low';
}
export interface PerformanceOverview {
clicks: number;
impressions: number;
ctr: number;
avg_position: number;
traffic_trend: string;
top_keywords: KeywordAnalysis[];
}
export interface CompetitiveAnalysis {
competitor_keywords: string[];
content_gaps: string[];
opportunity_score: number;
positioning_strength: string;
recommendations: string[];
}
export interface AIInsight {
category: string;
insight: string;
priority: 'high' | 'medium' | 'low';
action_required: boolean;
estimated_impact: string;
implementation_difficulty: string;
}
export interface ExecutiveSummary {
overall_score: number;
key_findings: string[];
top_opportunities: string[];
critical_issues: string[];
estimated_traffic_potential: string;
timeframe_to_implement: string;
}
export interface EnterpriseAuditResult {
website_url: string;
audit_date: string;
executive_summary: ExecutiveSummary;
technical_audit: TechnicalAuditResult;
on_page_analysis: {
pages_analyzed: number;
avg_score: number;
top_issues: AuditIssue[];
top_performers: PagePerformance[];
};
content_strategy: {
current_strategy: string;
gaps_identified: string[];
recommendations: string[];
content_calendar_suggestion?: string;
};
competitive_analysis: CompetitiveAnalysis;
keyword_research: {
target_keywords: KeywordAnalysis[];
long_tail_opportunities: KeywordAnalysis[];
competitor_keywords: KeywordAnalysis[];
};
ai_insights: AIInsight[];
implementation_roadmap: {
phase1_quick_wins: string[];
phase2_medium_term: string[];
phase3_long_term: string[];
};
metrics_summary: {
current_organic_traffic: number;
estimated_traffic_potential: number;
estimated_growth_percentage: number;
};
}
export interface GSCAnalysisResult {
site_url: string;
analysis_date: string;
analysis_period_days: number;
performance_overview: PerformanceOverview;
page_performance: PagePerformance[];
keyword_analysis: {
top_performers: KeywordAnalysis[];
opportunities: KeywordAnalysis[];
declining_keywords: KeywordAnalysis[];
};
content_opportunities: ContentOpportunity[];
technical_signals: {
core_web_vitals_score: number;
mobile_usability_issues: number;
indexing_issues: number;
security_issues: number;
};
competitive_positioning: CompetitiveAnalysis;
ai_recommendations: AIInsight[];
traffic_potential: {
low_hanging_fruit: string; // Quick wins
medium_term_opportunities: string;
long_term_growth: string;
estimated_additional_traffic: number;
};
}
export interface ContentOpportunitiesReport {
site_url: string;
report_date: string;
analysis_period_days: number;
total_opportunities: number;
opportunities_by_priority: {
high: ContentOpportunity[];
medium: ContentOpportunity[];
low: ContentOpportunity[];
};
phased_roadmap: {
phase1: {
target: string;
opportunities: ContentOpportunity[];
estimated_traffic_gain: number;
timeframe_weeks: number;
};
phase2: {
target: string;
opportunities: ContentOpportunity[];
estimated_traffic_gain: number;
timeframe_weeks: number;
};
phase3: {
target: string;
opportunities: ContentOpportunity[];
estimated_traffic_gain: number;
timeframe_weeks: number;
};
};
implementation_guide: string[];
success_metrics: string[];
}
export interface BaseResponse<T> {
success: boolean;
message: string;
data: T;
execution_time?: number;
}
// ============================================================================
// API Client
// ============================================================================
export const enterpriseSeoAPI = {
/**
* Execute comprehensive enterprise SEO audit
*/
async executeEnterpriseAudit(
websiteUrl: string,
options?: {
competitors?: string[];
targetKeywords?: string[];
includeContentAnalysis?: boolean;
includeCompetitiveAnalysis?: boolean;
generateExecutiveReport?: boolean;
}
): Promise<BaseResponse<EnterpriseAuditResult>> {
try {
const request = {
website_url: websiteUrl,
competitors: options?.competitors || [],
target_keywords: options?.targetKeywords || [],
include_content_analysis: options?.includeContentAnalysis ?? true,
include_competitive_analysis: options?.includeCompetitiveAnalysis ?? true,
generate_executive_report: options?.generateExecutiveReport ?? true,
};
console.log('Starting enterprise audit request:', request);
const response = await longRunningApiClient.post(
'/api/seo-tools/enterprise/complete-audit',
request
);
console.log('Enterprise audit response:', response.data);
return response.data;
} catch (error) {
console.error('Error executing enterprise audit:', error);
throw error;
}
},
/**
* Execute quick enterprise audit (faster version)
*/
async executeQuickAudit(
websiteUrl: string,
options?: {
targetKeywords?: string[];
}
): Promise<BaseResponse<EnterpriseAuditResult>> {
try {
const request = {
website_url: websiteUrl,
target_keywords: options?.targetKeywords || [],
};
console.log('Starting quick audit request:', request);
const response = await longRunningApiClient.post(
'/api/seo-tools/enterprise/quick-audit',
request
);
console.log('Quick audit response:', response.data);
return response.data;
} catch (error) {
console.error('Error executing quick audit:', error);
throw error;
}
},
/**
* Analyze GSC search performance with comprehensive insights
*/
async analyzeGSCSearchPerformance(
siteUrl: string,
options?: {
dateRangeDays?: number;
includeOpportunities?: boolean;
includeCompetitive?: boolean;
}
): Promise<BaseResponse<GSCAnalysisResult>> {
try {
const request = {
site_url: siteUrl,
date_range_days: options?.dateRangeDays || 90,
include_opportunities: options?.includeOpportunities ?? true,
include_competitive: options?.includeCompetitive ?? true,
};
console.log('Starting GSC analysis request:', request);
const response = await longRunningApiClient.post(
'/api/seo-tools/gsc/analyze-search-performance',
request
);
console.log('GSC analysis response:', response.data);
return response.data;
} catch (error) {
console.error('Error analyzing GSC search performance:', error);
throw error;
}
},
/**
* Generate content opportunities report from GSC data
*/
async getContentOpportunitiesReport(
siteUrl: string,
options?: {
minImpressions?: number;
dateRangeDays?: number;
}
): Promise<BaseResponse<ContentOpportunitiesReport>> {
try {
const request = {
site_url: siteUrl,
min_impressions: options?.minImpressions || 100,
date_range_days: options?.dateRangeDays || 90,
};
console.log('Starting content opportunities request:', request);
const response = await longRunningApiClient.post(
'/api/seo-tools/gsc/content-opportunities',
request
);
console.log('Content opportunities response:', response.data);
return response.data;
} catch (error) {
console.error('Error getting content opportunities report:', error);
throw error;
}
},
/**
* Check health of enterprise services
*/
async checkServicesHealth(): Promise<BaseResponse<any>> {
try {
const response = await apiClient.get('/api/seo-tools/enterprise/health');
return response.data;
} catch (error) {
console.error('Error checking enterprise services health:', error);
throw error;
}
},
/**
* Generate LLM-powered actionable insights for audit results
*/
async generateAuditInsights(
auditResult: EnterpriseAuditResult
): Promise<{ insights: AIInsight[]; recommendations: string[] }> {
try {
const response = await apiClient.post('/api/seo-tools/generate-insights', {
audit_data: auditResult,
insight_type: 'enterprise_audit',
});
return response.data;
} catch (error) {
console.error('Error generating audit insights:', error);
throw error;
}
},
/**
* Generate LLM-powered actionable insights for GSC analysis results
*/
async generateGSCInsights(
analysisResult: GSCAnalysisResult
): Promise<{ insights: AIInsight[]; recommendations: string[] }> {
try {
const response = await apiClient.post('/api/seo-tools/generate-insights', {
gsc_data: analysisResult,
insight_type: 'gsc_analysis',
});
return response.data;
} catch (error) {
console.error('Error generating GSC insights:', error);
throw error;
}
},
/**
* Get actionable traffic improvement strategies
*/
async getTrafficImprovementStrategies(
siteUrl: string,
options?: {
currentTraffic?: number;
targetTraffic?: number;
timeframe?: 'month' | 'quarter' | 'year';
}
): Promise<{ strategies: string[]; expected_growth: string; priority_actions: string[] }> {
try {
const request = {
site_url: siteUrl,
current_traffic: options?.currentTraffic,
target_traffic: options?.targetTraffic,
timeframe: options?.timeframe || 'quarter',
};
const response = await apiClient.post('/api/seo-tools/traffic-strategies', request);
return response.data;
} catch (error) {
console.error('Error getting traffic improvement strategies:', error);
throw error;
}
},
};

View File

@@ -0,0 +1,410 @@
/**
* LLM Insights Generator Service
* Generates actionable, business-focused insights from SEO audit and analysis data
* Uses LLM prompts to provide personalized, traffic-focused recommendations
*/
import { apiClient, longRunningApiClient } from './client';
import {
EnterpriseAuditResult,
GSCAnalysisResult,
AIInsight,
ContentOpportunity,
KeywordAnalysis,
} from './enterpriseSeoApi';
export interface ActionableInsight {
title: string;
description: string;
impact: 'high' | 'medium' | 'low';
effort: 'easy' | 'medium' | 'complex';
timeToImplement: string;
estimatedTrafficGain: number;
steps: string[];
tools?: string[];
priority: number; // 1-10, where 10 is highest priority
}
export interface TrafficImprovementStrategy {
phase: 'quick_wins' | 'medium_term' | 'long_term';
title: string;
description: string;
targetKeywords: string[];
estimatedTrafficGain: number;
timeframe: string;
keyActions: string[];
expectedROI: string;
}
export interface InsightGenerationResult {
insights: AIInsight[];
actionableInsights: ActionableInsight[];
trafficStrategies: TrafficImprovementStrategy[];
summary: string;
}
class LLMInsightsGenerator {
/**
* Generate actionable insights from enterprise audit results
* Focuses on traffic improvement and conversion opportunities
*/
async generateEnterpriseAuditInsights(
auditResult: EnterpriseAuditResult,
websiteContext?: {
currentMonthlyTraffic?: number;
targetAudience?: string;
primaryGoal?: string;
budget?: 'startup' | 'small' | 'medium' | 'enterprise';
}
): Promise<InsightGenerationResult> {
try {
const prompt = this.buildAuditInsightPrompt(auditResult, websiteContext);
const response = await apiClient.post('/api/seo-tools/llm/generate-audit-insights', {
audit_data: auditResult,
context: websiteContext,
prompt_template: 'enterprise_audit_insights',
});
return response.data;
} catch (error) {
console.error('Error generating audit insights:', error);
throw error;
}
}
/**
* Generate actionable insights from GSC analysis results
* Focuses on quick wins and keyword optimization
*/
async generateGSCAnalysisInsights(
analysisResult: GSCAnalysisResult,
websiteContext?: {
currentMonthlyTraffic?: number;
targetKeywords?: string[];
primaryGoal?: string;
}
): Promise<InsightGenerationResult> {
try {
const prompt = this.buildGSCInsightPrompt(analysisResult, websiteContext);
const response = await apiClient.post('/api/seo-tools/llm/generate-gsc-insights', {
gsc_data: analysisResult,
context: websiteContext,
prompt_template: 'gsc_analysis_insights',
});
return response.data;
} catch (error) {
console.error('Error generating GSC insights:', error);
throw error;
}
}
/**
* Generate content strategy recommendations
* Provides specific content ideas and gaps to address
*/
async generateContentStrategy(
auditOrAnalysisResult: EnterpriseAuditResult | GSCAnalysisResult,
options?: {
focusArea?: 'keywords' | 'content_gaps' | 'long_tail' | 'featured_snippets';
contentType?: 'blog' | 'guides' | 'product_pages' | 'mixed';
targetTraffic?: number;
}
): Promise<{
contentIdeas: string[];
gapAnalysis: string[];
prioritizedTopics: { topic: string; estimatedTraffic: number; difficulty: string }[];
contentCalendar: {
month: string;
topics: string[];
expectedTraffic: number;
}[];
}> {
try {
const response = await apiClient.post('/api/seo-tools/llm/generate-content-strategy', {
data: auditOrAnalysisResult,
options,
});
return response.data;
} catch (error) {
console.error('Error generating content strategy:', error);
throw error;
}
}
/**
* Generate traffic improvement roadmap
* Provides phased approach to increasing organic traffic
*/
async generateTrafficRoadmap(
auditOrAnalysisResult: EnterpriseAuditResult | GSCAnalysisResult,
targetTraffic: number,
timeframe: 'quarter' | 'semi_annual' | 'annual'
): Promise<{
currentTraffic: number;
targetTraffic: number;
timeframe: string;
phases: TrafficImprovementStrategy[];
keyMetrics: {
metric: string;
baseline: number;
target: number;
unit: string;
}[];
risks: string[];
opportunities: string[];
}> {
try {
const response = await apiClient.post('/api/seo-tools/llm/generate-traffic-roadmap', {
data: auditOrAnalysisResult,
target_traffic: targetTraffic,
timeframe,
});
return response.data;
} catch (error) {
console.error('Error generating traffic roadmap:', error);
throw error;
}
}
/**
* Generate priority-ranked recommendations
* Ranks all possible improvements by impact vs effort
*/
async generatePrioritizedRecommendations(
auditOrAnalysisResult: EnterpriseAuditResult | GSCAnalysisResult
): Promise<ActionableInsight[]> {
try {
const response = await apiClient.post('/api/seo-tools/llm/prioritized-recommendations', {
data: auditOrAnalysisResult,
});
return response.data.recommendations || [];
} catch (error) {
console.error('Error generating prioritized recommendations:', error);
throw error;
}
}
/**
* Generate quick wins recommendations
* Focus on 1-2 week implementation timeline
*/
async generateQuickWins(
auditOrAnalysisResult: EnterpriseAuditResult | GSCAnalysisResult
): Promise<ActionableInsight[]> {
try {
const response = await apiClient.post('/api/seo-tools/llm/quick-wins', {
data: auditOrAnalysisResult,
filter: 'quick_wins',
});
return response.data.insights || [];
} catch (error) {
console.error('Error generating quick wins:', error);
throw error;
}
}
/**
* Generate competitive positioning insights
* Helps understand how to outrank competitors
*/
async generateCompetitiveInsights(
auditOrAnalysisResult: EnterpriseAuditResult | GSCAnalysisResult,
competitors?: string[]
): Promise<{
positioning: string;
whiteSpaceOpportunities: string[];
competitiveAdvantages: string[];
recommendedActions: string[];
}> {
try {
const response = await apiClient.post('/api/seo-tools/llm/competitive-insights', {
data: auditOrAnalysisResult,
competitors,
});
return response.data;
} catch (error) {
console.error('Error generating competitive insights:', error);
throw error;
}
}
/**
* Generate keyword expansion recommendations
* Helps find related keywords and long-tail opportunities
*/
async generateKeywordExpansion(
targetKeywords: string[],
analysisData?: GSCAnalysisResult | EnterpriseAuditResult
): Promise<{
expandedKeywords: KeywordAnalysis[];
longTailVariations: string[];
relatedSearches: string[];
semanticVariations: string[];
recommendedContent: string[];
}> {
try {
const response = await apiClient.post('/api/seo-tools/llm/keyword-expansion', {
target_keywords: targetKeywords,
analysis_data: analysisData,
});
return response.data;
} catch (error) {
console.error('Error generating keyword expansion:', error);
throw error;
}
}
/**
* Generate content optimization recommendations
* Provides specific guidance on improving existing content
*/
async generateContentOptimization(
pageUrl: string,
currentContent: string,
analysisContext?: GSCAnalysisResult | EnterpriseAuditResult
): Promise<{
currentPerformance: string;
optimizationPriorities: string[];
keywordInsertions: { keyword: string; placement: string; context: string }[];
contentExpansionIdeas: string[];
structuredDataRecommendations: string[];
estimatedImpact: string;
}> {
try {
const response = await apiClient.post('/api/seo-tools/llm/content-optimization', {
page_url: pageUrl,
current_content: currentContent,
analysis_context: analysisContext,
});
return response.data;
} catch (error) {
console.error('Error generating content optimization:', error);
throw error;
}
}
/**
* Generate technical SEO improvement plan
* Addresses technical issues with actionable steps
*/
async generateTechnicalImprovementPlan(
auditResult: EnterpriseAuditResult
): Promise<{
criticalFixes: { issue: string; solution: string; timeToFix: string; impact: string }[];
performanceOptimizations: string[];
mobileOptimizations: string[];
implementationSequence: string[];
expectedImpactOnRankings: string;
}> {
try {
const response = await apiClient.post('/api/seo-tools/llm/technical-improvement-plan', {
audit_result: auditResult,
});
return response.data;
} catch (error) {
console.error('Error generating technical improvement plan:', error);
throw error;
}
}
// ============================================================================
// Helper Methods - Prompt Building
// ============================================================================
private buildAuditInsightPrompt(
auditResult: EnterpriseAuditResult,
context?: any
): string {
return `
As an expert SEO strategist, analyze this enterprise audit and provide actionable, traffic-focused insights.
AUDIT DATA:
- Overall Score: ${auditResult.executive_summary.overall_score}/100
- Traffic Potential: ${auditResult.executive_summary.estimated_traffic_potential}
- Critical Issues: ${auditResult.executive_summary.critical_issues.length}
- Top Opportunities: ${auditResult.executive_summary.top_opportunities.join('; ')}
WEBSITE CONTEXT:
- Current Monthly Traffic: ${context?.currentMonthlyTraffic || 'Unknown'}
- Target Audience: ${context?.targetAudience || 'Not specified'}
- Primary Goal: ${context?.primaryGoal || 'Increase organic traffic'}
- Budget Level: ${context?.budget || 'Not specified'}
TASK:
1. Generate 5-7 high-impact, actionable insights (prioritize quick wins first)
2. For each insight, provide:
- Clear title and description
- Expected traffic impact (number or percentage)
- Implementation difficulty (easy/medium/complex)
- Estimated time to implement
- Step-by-step implementation guide
3. Identify the top 3 traffic improvement strategies with specific, measurable outcomes
4. Provide competitive positioning recommendations
5. Highlight any urgent/critical items that need immediate attention
Focus on traffic improvement and revenue impact. Make recommendations specific and actionable, not generic.
Return structured JSON with insights array containing objects with: title, description, impact, effort, timeToImplement, estimatedTraffic, steps[], priority (1-10).
`;
}
private buildGSCInsightPrompt(
analysisResult: GSCAnalysisResult,
context?: any
): string {
return `
As an expert SEO strategist specializing in GSC optimization, analyze this search performance data and provide traffic-focused recommendations.
SEARCH PERFORMANCE DATA:
- Total Clicks: ${analysisResult.performance_overview.clicks}
- Total Impressions: ${analysisResult.performance_overview.impressions}
- Average CTR: ${(analysisResult.performance_overview.ctr * 100).toFixed(2)}%
- Average Position: ${analysisResult.performance_overview.avg_position}
- Content Opportunities: ${analysisResult.content_opportunities.length}
KEYWORD DATA:
- Top Keywords: ${analysisResult.keyword_analysis.top_performers.slice(0, 3).map(k => k.keyword).join(', ')}
- Keywords Ready for Improvement: ${analysisResult.keyword_analysis.opportunities.length}
- Declining Keywords: ${analysisResult.keyword_analysis.declining_keywords.length}
WEBSITE CONTEXT:
- Current Monthly Traffic: ${context?.currentMonthlyTraffic || 'Unknown'}
- Target Keywords: ${context?.targetKeywords?.join(', ') || 'Not specified'}
- Primary Goal: ${context?.primaryGoal || 'Increase click-through rate'}
TASK:
1. Identify 5-10 high-potential opportunities for traffic growth
2. Prioritize by: (a) Current position (rank 4-10), (b) Volume, (c) CTR improvement potential
3. For each top opportunity, provide:
- Keyword and current metrics
- Specific on-page optimization recommendations
- Estimated traffic gain
- Implementation timeframe
4. Generate quick wins (things that can be done in 1-2 weeks)
5. Identify any technical SEO issues affecting CTR or rankings
6. Provide long-tail keyword expansion opportunities
Focus on practical, measurable improvements to clicks and rankings.
Return structured JSON with insights array and trafficStrategies array.
`;
}
}
// Export singleton instance
export const llmInsightsGenerator = new LLMInsightsGenerator();
// For React component usage
export { LLMInsightsGenerator };

View File

@@ -51,8 +51,8 @@ export interface StyleDetectionResponse {
timestamp: string;
}
// Consistent API URL pattern - no hardcoded localhost fallback
const API_BASE_URL = process.env.REACT_APP_API_URL || process.env.REACT_APP_BACKEND_URL || '';
// API URL is handled by the shared apiClient which uses the centralized getApiBaseUrl utility
// so we don't need a separate API_BASE_URL here
/**
* Analyze content style using AI