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:
@@ -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;
|
||||
|
||||
@@ -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();
|
||||
|
||||
409
frontend/src/api/enterpriseSeoApi.ts
Normal file
409
frontend/src/api/enterpriseSeoApi.ts
Normal 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;
|
||||
}
|
||||
},
|
||||
};
|
||||
410
frontend/src/api/llmInsightsGenerator.ts
Normal file
410
frontend/src/api/llmInsightsGenerator.ts
Normal 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 };
|
||||
@@ -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
|
||||
|
||||
Reference in New Issue
Block a user