ALwrity Version 0.5.0 (Fastapi + React )

This commit is contained in:
ajaysi
2025-08-06 12:48:02 +05:30
parent f28a919caa
commit 32f97fa6b3
476 changed files with 115544 additions and 28747 deletions

View File

@@ -0,0 +1,92 @@
import axios from 'axios';
// Create a shared axios instance for all API calls
export const apiClient = axios.create({
baseURL: 'http://localhost:8000',
timeout: 60000, // Increased to 60 seconds for regular API calls
headers: {
'Content-Type': 'application/json',
},
});
// Create a specialized client for AI operations with extended timeout
export const aiApiClient = axios.create({
baseURL: 'http://localhost:8000',
timeout: 180000, // 3 minutes timeout for AI operations (matching 20-25 second responses)
headers: {
'Content-Type': 'application/json',
},
});
// Create a specialized client for long-running operations like SEO analysis
export const longRunningApiClient = axios.create({
baseURL: 'http://localhost:8000',
timeout: 300000, // 5 minutes timeout for SEO analysis
headers: {
'Content-Type': 'application/json',
},
});
// Add request interceptor for logging (optional)
apiClient.interceptors.request.use(
(config) => {
console.log(`Making ${config.method?.toUpperCase()} request to ${config.url}`);
return config;
},
(error) => {
return Promise.reject(error);
}
);
// Add response interceptor for error handling (optional)
apiClient.interceptors.response.use(
(response) => {
return response;
},
(error) => {
console.error('API Error:', error.response?.status, error.response?.data);
return Promise.reject(error);
}
);
// Add interceptors for AI client
aiApiClient.interceptors.request.use(
(config) => {
console.log(`Making AI ${config.method?.toUpperCase()} request to ${config.url}`);
return config;
},
(error) => {
return Promise.reject(error);
}
);
aiApiClient.interceptors.response.use(
(response) => {
return response;
},
(error) => {
console.error('AI API Error:', error.response?.status, error.response?.data);
return Promise.reject(error);
}
);
// Add interceptors for long-running client
longRunningApiClient.interceptors.request.use(
(config) => {
console.log(`Making long-running ${config.method?.toUpperCase()} request to ${config.url}`);
return config;
},
(error) => {
return Promise.reject(error);
}
);
longRunningApiClient.interceptors.response.use(
(response) => {
return response;
},
(error) => {
console.error('Long-running API Error:', error.response?.status, error.response?.data);
return Promise.reject(error);
}
);

View File

@@ -0,0 +1,177 @@
// Component Logic API integration
import { AxiosResponse } from 'axios';
import { apiClient } from './client';
// AI Research Interfaces
export interface UserInfoRequest {
full_name: string;
email: string;
company: string;
role: string;
}
export interface UserInfoResponse {
valid: boolean;
user_info?: any;
errors: string[];
}
export interface ResearchPreferencesRequest {
research_depth: string;
content_types: string[];
auto_research: boolean;
factual_content: boolean;
}
export interface ResearchPreferencesResponse {
valid: boolean;
preferences?: any;
errors: string[];
}
export interface ResearchRequest {
topic: string;
preferences: ResearchPreferencesRequest;
}
export interface ResearchResponse {
success: boolean;
topic: string;
results?: any;
error?: string;
}
// Personalization Interfaces
export interface ContentStyleRequest {
writing_style: string;
tone: string;
content_length: string;
}
export interface ContentStyleResponse {
valid: boolean;
style_config?: any;
errors: string[];
}
export interface BrandVoiceRequest {
personality_traits: string[];
voice_description?: string;
keywords?: string;
}
export interface BrandVoiceResponse {
valid: boolean;
brand_config?: any;
errors: string[];
}
export interface AdvancedSettingsRequest {
seo_optimization: boolean;
readability_level: string;
content_structure: string[];
}
export interface PersonalizationSettingsRequest {
content_style: ContentStyleRequest;
brand_voice: BrandVoiceRequest;
advanced_settings: AdvancedSettingsRequest;
}
export interface PersonalizationSettingsResponse {
valid: boolean;
settings?: any;
errors: string[];
}
// Research Utilities Interfaces
export interface ResearchTopicRequest {
topic: string;
api_keys: Record<string, string>;
}
export interface ResearchResultResponse {
success: boolean;
topic: string;
data?: any;
error?: string;
metadata?: any;
}
// AI Research API Functions
export async function validateUserInfo(request: UserInfoRequest): Promise<UserInfoResponse> {
const res: AxiosResponse<UserInfoResponse> = await apiClient.post('/api/onboarding/ai-research/validate-user', request);
return res.data;
}
export async function configureResearchPreferences(request: ResearchPreferencesRequest): Promise<ResearchPreferencesResponse> {
const res: AxiosResponse<ResearchPreferencesResponse> = await apiClient.post('/api/onboarding/ai-research/configure-preferences', request);
return res.data;
}
export async function processResearchRequest(request: ResearchRequest): Promise<ResearchResponse> {
const res: AxiosResponse<ResearchResponse> = await apiClient.post('/api/onboarding/ai-research/process-research', request);
return res.data;
}
export async function getResearchConfigurationOptions(): Promise<any> {
const res: AxiosResponse<any> = await apiClient.get('/api/onboarding/ai-research/configuration-options');
return res.data;
}
export async function getResearchPreferences(): Promise<ResearchPreferencesResponse> {
const res: AxiosResponse<ResearchPreferencesResponse> = await apiClient.get('/api/onboarding/ai-research/preferences');
return res.data;
}
// Personalization API Functions
export async function validateContentStyle(request: ContentStyleRequest): Promise<ContentStyleResponse> {
const res: AxiosResponse<ContentStyleResponse> = await apiClient.post('/api/onboarding/personalization/validate-style', request);
return res.data;
}
export async function configureBrandVoice(request: BrandVoiceRequest): Promise<BrandVoiceResponse> {
const res: AxiosResponse<BrandVoiceResponse> = await apiClient.post('/api/onboarding/personalization/configure-brand', request);
return res.data;
}
export async function processPersonalizationSettings(request: PersonalizationSettingsRequest): Promise<PersonalizationSettingsResponse> {
const res: AxiosResponse<PersonalizationSettingsResponse> = await apiClient.post('/api/onboarding/personalization/process-settings', request);
return res.data;
}
export async function getPersonalizationConfigurationOptions(): Promise<any> {
const res: AxiosResponse<any> = await apiClient.get('/api/onboarding/personalization/configuration-options');
return res.data;
}
export async function generateContentGuidelines(settings: any): Promise<any> {
const res: AxiosResponse<any> = await apiClient.post('/api/onboarding/personalization/generate-guidelines', settings);
return res.data;
}
// Research Utilities API Functions
export async function processResearchTopic(request: ResearchTopicRequest): Promise<ResearchResultResponse> {
const res: AxiosResponse<ResearchResultResponse> = await apiClient.post('/api/onboarding/research/process-topic', request);
return res.data;
}
export async function processResearchResults(results: any): Promise<any> {
const res: AxiosResponse<any> = await apiClient.post('/api/onboarding/research/process-results', results);
return res.data;
}
export async function validateResearchRequest(topic: string, api_keys: Record<string, string>): Promise<any> {
const res: AxiosResponse<any> = await apiClient.post('/api/onboarding/research/validate-request', { topic, api_keys });
return res.data;
}
export async function getResearchProvidersInfo(): Promise<any> {
const res: AxiosResponse<any> = await apiClient.get('/api/onboarding/research/providers-info');
return res.data;
}
export async function generateResearchReport(results: any): Promise<any> {
const res: AxiosResponse<any> = await apiClient.post('/api/onboarding/research/generate-report', results);
return res.data;
}

View File

@@ -0,0 +1,163 @@
// Make sure to install axios: npm install axios
import { AxiosResponse } from 'axios';
import { apiClient } from './client';
export interface APIKeyRequest {
provider: string;
api_key: string;
description?: string;
}
export interface APIKeyResponse {
provider: string;
api_key: string;
description?: string;
}
export interface OnboardingStepResponse {
step: number;
data?: any;
validation_errors?: string[];
}
export interface OnboardingSessionResponse {
id: number;
user_id: number;
current_step: number;
progress: number;
}
export interface OnboardingProgressResponse {
progress: number;
current_step: number;
total_steps: number;
completion_percentage: number;
}
export async function startOnboarding() {
const res: AxiosResponse<OnboardingSessionResponse> = await apiClient.post('/api/onboarding/start');
return res.data;
}
export async function getCurrentStep() {
// Get the current step from the onboarding status
console.log('getCurrentStep: Calling /api/onboarding/status');
const res: AxiosResponse<any> = await apiClient.get('/api/onboarding/status');
console.log('getCurrentStep: Backend returned:', res.data);
return { step: res.data.current_step || 1 };
}
export async function setCurrentStep(step: number) {
// Complete the current step to move to the next one
console.log('setCurrentStep: Completing step', step);
const res: AxiosResponse<OnboardingStepResponse> = await apiClient.post(`/api/onboarding/step/${step}/complete`, {
data: {},
validation_errors: []
});
console.log('setCurrentStep: Backend response:', res.data);
return { step };
}
export async function getApiKeys() {
const maxRetries = 3;
let lastError: any;
console.log('getApiKeys: Starting API call to /api/onboarding/api-keys');
for (let attempt = 0; attempt < maxRetries; attempt++) {
try {
console.log(`getApiKeys: Attempt ${attempt + 1}/${maxRetries}`);
const res: AxiosResponse<Record<string, string>> = await apiClient.get('/api/onboarding/api-keys');
console.log('getApiKeys: API call successful');
return res.data;
} catch (error: any) {
lastError = error;
console.log(`getApiKeys: Attempt ${attempt + 1} failed:`, error.response?.status, error.message);
// If it's a rate limit error (429), wait and retry
if (error.response?.status === 429) {
const retryAfter = error.response?.data?.retry_after || 60;
const delay = Math.min(retryAfter * 1000, 5000); // Max 5 seconds
console.log(`getApiKeys: Rate limited, retrying in ${delay}ms (attempt ${attempt + 1}/${maxRetries})`);
await new Promise(resolve => setTimeout(resolve, delay));
continue;
}
// For other errors, don't retry
console.log('getApiKeys: Non-rate-limit error, not retrying');
throw error;
}
}
// If we've exhausted all retries, throw the last error
console.log('getApiKeys: All retries exhausted');
throw lastError;
}
export async function saveApiKey(provider: string, api_key: string, description?: string) {
const res: AxiosResponse<APIKeyResponse> = await apiClient.post('/api/onboarding/api-keys', {
provider,
api_key,
description
});
return res.data;
}
export async function getProgress() {
const res: AxiosResponse<OnboardingProgressResponse> = await apiClient.get('/api/onboarding/progress');
return { progress: res.data.completion_percentage || 0 };
}
export async function setProgress(progress: number) {
// Progress is managed automatically by the backend
// This function is kept for compatibility but doesn't make a backend call
return { progress };
}
// Additional functions for better integration
export async function getOnboardingConfig() {
const res: AxiosResponse<any> = await apiClient.get('/api/onboarding/config');
return res.data;
}
export async function getStepData(stepNumber: number) {
const res: AxiosResponse<any> = await apiClient.get(`/api/onboarding/step/${stepNumber}`);
return res.data;
}
export async function skipStep(stepNumber: number) {
const res: AxiosResponse<any> = await apiClient.post(`/api/onboarding/step/${stepNumber}/skip`);
return res.data;
}
export async function validateApiKeys() {
const res: AxiosResponse<any> = await apiClient.post('/api/onboarding/api-keys/validate');
return res.data;
}
export async function completeOnboarding() {
const res: AxiosResponse<any> = await apiClient.post('/api/onboarding/complete');
return res.data;
}
export async function resetOnboarding() {
const res: AxiosResponse<any> = await apiClient.post('/api/onboarding/reset');
return res.data;
}
// New functions for FinalStep data loading
export async function getOnboardingSummary() {
const res: AxiosResponse<any> = await apiClient.get('/api/onboarding/summary');
return res.data;
}
export async function getWebsiteAnalysisData() {
const res: AxiosResponse<any> = await apiClient.get('/api/onboarding/website-analysis');
return res.data;
}
export async function getResearchPreferencesData() {
const res: AxiosResponse<any> = await apiClient.get('/api/onboarding/research-preferences');
return res.data;
}

View File

@@ -0,0 +1,85 @@
import { longRunningApiClient } from './client';
import { SEOAnalysisData } from '../components/shared/types';
// SEO Analysis API functions
export const seoAnalysisAPI = {
async analyzeURL(url: string, targetKeywords?: string[]): Promise<SEOAnalysisData | null> {
try {
console.log(`Starting SEO analysis for URL: ${url}`);
console.log(`Target keywords:`, targetKeywords);
const requestData = {
url,
target_keywords: targetKeywords
};
console.log('Request data:', requestData);
const response = await longRunningApiClient.post('/api/seo-dashboard/analyze-comprehensive', requestData);
console.log('Response received:', response);
console.log('Response data:', response.data);
if (response.data.success) {
console.log(`SEO analysis completed for ${url}`);
console.log('Analysis result:', response.data);
return response.data;
} else {
console.error('Analysis failed:', response.data.message);
throw new Error(response.data.message || 'Analysis failed');
}
} catch (error: any) {
console.error('Error analyzing URL:', error);
console.error('Error details:', {
message: error.message,
status: error.response?.status,
data: error.response?.data
});
throw error;
}
},
async getDetailedMetrics(url: string): Promise<any> {
try {
console.log(`Getting detailed metrics for URL: ${url}`);
const response = await longRunningApiClient.get(`/api/seo-dashboard/metrics/${encodeURIComponent(url)}`);
console.log(`Detailed metrics retrieved for ${url}`);
return response.data;
} catch (error) {
console.error('Error getting detailed metrics:', error);
throw error;
}
},
async getAnalysisSummary(): Promise<any> {
try {
console.log('Getting analysis summary');
const response = await longRunningApiClient.get('/api/seo-dashboard/summary');
console.log('Analysis summary retrieved');
return response.data;
} catch (error) {
console.error('Error getting analysis summary:', error);
throw error;
}
},
async batchAnalyzeURLs(urls: string[]): Promise<any[]> {
try {
console.log(`Starting batch analysis for ${urls.length} URLs`);
const response = await longRunningApiClient.post('/api/seo-dashboard/batch-analyze', { urls });
console.log(`Batch analysis completed for ${urls.length} URLs`);
return response.data;
} catch (error) {
console.error('Error in batch analysis:', error);
throw error;
}
},
async healthCheck(): Promise<boolean> {
try {
const response = await longRunningApiClient.get('/api/seo-dashboard/health');
return response.status === 200;
} catch (error) {
console.error('Health check failed:', error);
return false;
}
}
};

View File

@@ -0,0 +1,112 @@
import { apiClient } from './client';
export interface SEOHealthScore {
score: number;
change: number;
trend: string;
label: string;
color: string;
}
export interface SEOMetric {
value: number;
change: number;
trend: string;
description: string;
color: string;
}
export interface PlatformStatus {
status: string;
connected: boolean;
last_sync?: string;
data_points?: number;
}
export interface AIInsight {
insight: string;
priority: string;
category: string;
action_required: boolean;
tool_path?: string;
}
export interface SEODashboardData {
health_score: SEOHealthScore;
key_insight: string;
priority_alert: string;
metrics: Record<string, SEOMetric>;
platforms: Record<string, PlatformStatus>;
ai_insights: AIInsight[];
last_updated: string;
website_url?: string; // User's website URL from onboarding
}
// SEO Dashboard API functions
export const seoDashboardAPI = {
// Get complete dashboard data
async getDashboardData(): Promise<SEODashboardData> {
try {
const response = await apiClient.get('/api/seo-dashboard/data');
return response.data;
} catch (error) {
console.error('Error fetching SEO dashboard data:', error);
throw error;
}
},
// Get health score only
async getHealthScore(): Promise<SEOHealthScore> {
try {
const response = await apiClient.get('/api/seo-dashboard/health-score');
return response.data;
} catch (error) {
console.error('Error fetching SEO health score:', error);
throw error;
}
},
// Get metrics only
async getMetrics(): Promise<Record<string, SEOMetric>> {
try {
const response = await apiClient.get('/api/seo-dashboard/metrics');
return response.data;
} catch (error) {
console.error('Error fetching SEO metrics:', error);
throw error;
}
},
// Get platform status
async getPlatformStatus(): Promise<Record<string, PlatformStatus>> {
try {
const response = await apiClient.get('/api/seo-dashboard/platforms');
return response.data;
} catch (error) {
console.error('Error fetching platform status:', error);
throw error;
}
},
// Get AI insights
async getAIInsights(): Promise<AIInsight[]> {
try {
const response = await apiClient.get('/api/seo-dashboard/insights');
return response.data;
} catch (error) {
console.error('Error fetching AI insights:', error);
throw error;
}
},
// Health check
async healthCheck(): Promise<any> {
try {
const response = await apiClient.get('/api/seo-dashboard/health');
return response.data;
} catch (error) {
console.error('Error checking SEO dashboard health:', error);
throw error;
}
}
};

View File

@@ -0,0 +1,289 @@
/** Style Detection API Integration */
export interface StyleAnalysisRequest {
content: {
main_content: string;
title?: string;
description?: string;
};
analysis_type?: 'comprehensive' | 'patterns';
}
export interface StyleAnalysisResponse {
success: boolean;
analysis?: any;
patterns?: any;
guidelines?: any;
error?: string;
timestamp: string;
}
export interface WebCrawlRequest {
url?: string;
text_sample?: string;
}
export interface WebCrawlResponse {
success: boolean;
content?: any;
metrics?: any;
error?: string;
timestamp: string;
}
export interface StyleDetectionRequest {
url?: string;
text_sample?: string;
include_patterns?: boolean;
include_guidelines?: boolean;
}
export interface StyleDetectionResponse {
success: boolean;
crawl_result?: any;
style_analysis?: any;
style_patterns?: any;
style_guidelines?: any;
error?: string;
warning?: string;
timestamp: string;
}
const API_BASE_URL = process.env.REACT_APP_API_URL || 'http://localhost:8000';
/**
* Analyze content style using AI
*/
export const analyzeContentStyle = async (request: StyleAnalysisRequest): Promise<StyleAnalysisResponse> => {
try {
const response = await fetch(`${API_BASE_URL}/api/onboarding/style-detection/analyze`, {
method: 'POST',
headers: {
'Content-Type': 'application/json',
},
body: JSON.stringify(request),
});
if (!response.ok) {
throw new Error(`HTTP error! status: ${response.status}`);
}
return await response.json();
} catch (error) {
console.error('Error analyzing content style:', error);
return {
success: false,
error: error instanceof Error ? error.message : 'Unknown error',
timestamp: new Date().toISOString(),
};
}
};
/**
* Crawl website content for style analysis
*/
export const crawlWebsiteContent = async (request: WebCrawlRequest): Promise<WebCrawlResponse> => {
try {
const response = await fetch(`${API_BASE_URL}/api/onboarding/style-detection/crawl`, {
method: 'POST',
headers: {
'Content-Type': 'application/json',
},
body: JSON.stringify(request),
});
if (!response.ok) {
throw new Error(`HTTP error! status: ${response.status}`);
}
return await response.json();
} catch (error) {
console.error('Error crawling website content:', error);
return {
success: false,
error: error instanceof Error ? error.message : 'Unknown error',
timestamp: new Date().toISOString(),
};
}
};
/**
* Complete style detection workflow
*/
export const completeStyleDetection = async (request: StyleDetectionRequest): Promise<StyleDetectionResponse> => {
try {
const response = await fetch(`${API_BASE_URL}/api/onboarding/style-detection/complete`, {
method: 'POST',
headers: {
'Content-Type': 'application/json',
},
body: JSON.stringify(request),
});
if (!response.ok) {
throw new Error(`HTTP error! status: ${response.status}`);
}
return await response.json();
} catch (error) {
console.error('Error in complete style detection:', error);
return {
success: false,
error: error instanceof Error ? error.message : 'Unknown error',
timestamp: new Date().toISOString(),
};
}
};
/**
* Get style detection configuration options
*/
export const getStyleDetectionConfiguration = async (): Promise<any> => {
try {
const response = await fetch(`${API_BASE_URL}/api/onboarding/style-detection/configuration-options`, {
method: 'GET',
headers: {
'Content-Type': 'application/json',
},
});
if (!response.ok) {
throw new Error(`HTTP error! status: ${response.status}`);
}
return await response.json();
} catch (error) {
console.error('Error getting style detection configuration:', error);
return {
error: error instanceof Error ? error.message : 'Unknown error',
};
}
};
/**
* Validate style detection request
*/
export const validateStyleDetectionRequest = (request: StyleDetectionRequest): { valid: boolean; errors: string[] } => {
const errors: string[] = [];
if (!request.url && !request.text_sample) {
errors.push('Either URL or text sample is required');
}
if (request.url && !request.url.startsWith('http')) {
errors.push('URL must start with http:// or https://');
}
if (request.text_sample && request.text_sample.length < 50) {
errors.push('Text sample must be at least 50 characters');
}
if (request.text_sample && request.text_sample.length > 10000) {
errors.push('Text sample is too long (max 10,000 characters)');
}
return {
valid: errors.length === 0,
errors,
};
};
/**
* Check if analysis exists for a website URL
*/
export const checkExistingAnalysis = async (websiteUrl: string): Promise<any> => {
try {
const response = await fetch(`${API_BASE_URL}/api/onboarding/style-detection/check-existing/${encodeURIComponent(websiteUrl)}`, {
method: 'GET',
headers: {
'Content-Type': 'application/json',
},
});
if (!response.ok) {
throw new Error(`HTTP error! status: ${response.status}`);
}
return await response.json();
} catch (error) {
console.error('Error checking existing analysis:', error);
return {
error: error instanceof Error ? error.message : 'Unknown error',
};
}
};
/**
* Get analysis by ID
*/
export const getAnalysisById = async (analysisId: number): Promise<any> => {
try {
const response = await fetch(`${API_BASE_URL}/api/onboarding/style-detection/analysis/${analysisId}`, {
method: 'GET',
headers: {
'Content-Type': 'application/json',
},
});
if (!response.ok) {
throw new Error(`HTTP error! status: ${response.status}`);
}
return await response.json();
} catch (error) {
console.error('Error getting analysis by ID:', error);
return {
error: error instanceof Error ? error.message : 'Unknown error',
};
}
};
/**
* Get all analyses for the current session
*/
export const getSessionAnalyses = async (): Promise<any> => {
try {
const response = await fetch(`${API_BASE_URL}/api/onboarding/style-detection/session-analyses`, {
method: 'GET',
headers: {
'Content-Type': 'application/json',
},
});
if (!response.ok) {
throw new Error(`HTTP error! status: ${response.status}`);
}
return await response.json();
} catch (error) {
console.error('Error getting session analyses:', error);
return {
error: error instanceof Error ? error.message : 'Unknown error',
};
}
};
/**
* Delete an analysis
*/
export const deleteAnalysis = async (analysisId: number): Promise<any> => {
try {
const response = await fetch(`${API_BASE_URL}/api/onboarding/style-detection/analysis/${analysisId}`, {
method: 'DELETE',
headers: {
'Content-Type': 'application/json',
},
});
if (!response.ok) {
throw new Error(`HTTP error! status: ${response.status}`);
}
return await response.json();
} catch (error) {
console.error('Error deleting analysis:', error);
return {
error: error instanceof Error ? error.message : 'Unknown error',
};
}
};

View File

@@ -0,0 +1,68 @@
import { apiClient } from './client';
export interface UserData {
website_url?: string;
session?: {
id: number;
current_step: number;
progress: number;
started_at?: string;
updated_at?: string;
};
website_analysis?: {
website_url: string;
industry: string;
target_audience: string;
content_goals: string[];
brand_voice: string;
content_style: string;
};
api_keys?: Array<{
id: number;
provider: string;
description?: string;
}>;
research_preferences?: {
target_keywords: string[];
competitor_urls: string[];
content_topics: string[];
};
}
export const userDataAPI = {
async getUserData(): Promise<UserData | null> {
try {
console.log('Fetching user data from backend...');
const response = await apiClient.get('/api/user-data');
console.log('User data received:', response.data);
return response.data;
} catch (error: any) {
console.error('Error fetching user data:', error);
return null;
}
},
async getWebsiteURL(): Promise<string | null> {
try {
console.log('Fetching website URL...');
const response = await apiClient.get('/api/user-data/website-url');
console.log('Website URL received:', response.data);
return response.data.website_url || null;
} catch (error: any) {
console.error('Error fetching website URL:', error);
return null;
}
},
async getOnboardingData(): Promise<any> {
try {
console.log('Fetching onboarding data...');
const response = await apiClient.get('/api/user-data/onboarding');
console.log('Onboarding data received:', response.data);
return response.data;
} catch (error: any) {
console.error('Error fetching onboarding data:', error);
return null;
}
}
};