ALwrity + Wordpress + Wix + GSC integration

This commit is contained in:
ajaysi
2025-10-08 10:13:14 +05:30
parent 14dfb2e5c0
commit 3bab3450dc
147 changed files with 19815 additions and 17053 deletions

View File

@@ -1,7 +1,6 @@
/** Google Search Console API client for ALwrity frontend. */
import { apiClient } from './client';
import { useAuth } from '@clerk/clerk-react';
export interface GSCSite {
siteUrl: string;
@@ -76,11 +75,9 @@ class GSCAPI {
* Get Google Search Console OAuth authorization URL
*/
async getAuthUrl(): Promise<{ auth_url: string }> {
console.log('GSC API: Getting OAuth authorization URL');
try {
const client = await this.getAuthenticatedClient();
const response = await client.get(`${this.baseUrl}/auth/url`);
console.log('GSC API: OAuth URL retrieved successfully');
return response.data;
} catch (error) {
console.error('GSC API: Error getting OAuth URL:', error);
@@ -92,13 +89,11 @@ class GSCAPI {
* Handle OAuth callback (typically called from popup)
*/
async handleCallback(code: string, state: string): Promise<{ success: boolean; message: string }> {
console.log('GSC API: Handling OAuth callback');
try {
const client = await this.getAuthenticatedClient();
const response = await client.get(`${this.baseUrl}/callback`, {
params: { code, state }
});
console.log('GSC API: OAuth callback handled successfully');
return response.data;
} catch (error) {
console.error('GSC API: Error handling OAuth callback:', error);
@@ -110,11 +105,9 @@ class GSCAPI {
* Get user's Google Search Console sites
*/
async getSites(): Promise<{ sites: GSCSite[] }> {
console.log('GSC API: Getting user sites');
try {
const client = await this.getAuthenticatedClient();
const response = await client.get(`${this.baseUrl}/sites`);
console.log(`GSC API: Retrieved ${response.data.sites.length} sites`);
return response.data;
} catch (error) {
console.error('GSC API: Error getting sites:', error);
@@ -126,11 +119,9 @@ class GSCAPI {
* Get search analytics data
*/
async getAnalytics(request: GSCAnalyticsRequest): Promise<GSCAnalyticsResponse> {
console.log('GSC API: Getting analytics data for site:', request.site_url);
try {
const client = await this.getAuthenticatedClient();
const response = await client.post(`${this.baseUrl}/analytics`, request);
console.log('GSC API: Analytics data retrieved successfully');
return response.data;
} catch (error) {
console.error('GSC API: Error getting analytics:', error);
@@ -142,11 +133,9 @@ class GSCAPI {
* Get sitemaps for a specific site
*/
async getSitemaps(siteUrl: string): Promise<{ sitemaps: GSCSitemap[] }> {
console.log('GSC API: Getting sitemaps for site:', siteUrl);
try {
const client = await this.getAuthenticatedClient();
const response = await client.get(`${this.baseUrl}/sitemaps/${encodeURIComponent(siteUrl)}`);
console.log(`GSC API: Retrieved ${response.data.sitemaps.length} sitemaps`);
return response.data;
} catch (error) {
console.error('GSC API: Error getting sitemaps:', error);
@@ -158,11 +147,9 @@ class GSCAPI {
* Get GSC connection status
*/
async getStatus(): Promise<GSCStatusResponse> {
console.log('GSC API: Getting connection status');
try {
const client = await this.getAuthenticatedClient();
const response = await client.get(`${this.baseUrl}/status`);
console.log('GSC API: Status retrieved, connected:', response.data.connected);
return response.data;
} catch (error) {
console.error('GSC API: Error getting status:', error);
@@ -170,15 +157,27 @@ class GSCAPI {
}
}
/**
* Clear incomplete GSC credentials
*/
async clearIncomplete(): Promise<{ success: boolean; message: string }> {
try {
const client = await this.getAuthenticatedClient();
const response = await client.post(`${this.baseUrl}/clear-incomplete`);
return response.data;
} catch (error) {
console.error('GSC API: Error clearing incomplete credentials:', error);
throw error;
}
}
/**
* Disconnect GSC account
*/
async disconnect(): Promise<{ success: boolean; message: string }> {
console.log('GSC API: Disconnecting GSC account');
try {
const client = await this.getAuthenticatedClient();
const response = await client.delete(`${this.baseUrl}/disconnect`);
console.log('GSC API: Account disconnected successfully');
return response.data;
} catch (error) {
console.error('GSC API: Error disconnecting account:', error);
@@ -190,10 +189,8 @@ class GSCAPI {
* Health check
*/
async healthCheck(): Promise<{ status: string; service: string; timestamp: string }> {
console.log('GSC API: Performing health check');
try {
const response = await apiClient.get(`${this.baseUrl}/health`);
console.log('GSC API: Health check passed');
return response.data;
} catch (error) {
console.error('GSC API: Health check failed:', error);

View File

@@ -0,0 +1,158 @@
/**
* Persona API Client
* Handles communication with the persona generation backend services.
*/
import { apiClient } from './client';
export interface PersonaGenerationRequest {
onboarding_data: {
websiteAnalysis?: any;
competitorResearch?: any;
sitemapAnalysis?: any;
businessData?: any;
};
selected_platforms: string[];
user_preferences?: any;
}
export interface PersonaGenerationResponse {
success: boolean;
core_persona?: any;
platform_personas?: Record<string, any>;
quality_metrics?: any;
error?: string;
}
export interface PersonaQualityRequest {
core_persona: any;
platform_personas: Record<string, any>;
user_feedback?: any;
}
export interface PersonaQualityResponse {
success: boolean;
quality_metrics?: any;
recommendations?: string[];
error?: string;
}
export interface PersonaOptions {
success: boolean;
available_platforms: Array<{
id: string;
name: string;
description: string;
}>;
persona_types: string[];
quality_metrics: string[];
}
/**
* Generate AI writing personas using the sophisticated persona system.
*/
export const generateWritingPersonas = async (
request: PersonaGenerationRequest
): Promise<PersonaGenerationResponse> => {
try {
const response = await apiClient.post('/api/onboarding/step4/generate-personas', request);
return response.data;
} catch (error: any) {
console.error('Error generating personas:', error);
return {
success: false,
error: error.response?.data?.detail || error.message || 'Failed to generate personas'
};
}
};
/**
* Assess the quality of generated personas.
*/
export const assessPersonaQuality = async (
request: PersonaQualityRequest
): Promise<PersonaQualityResponse> => {
try {
const response = await apiClient.post('/api/onboarding/step4/assess-quality', request);
return response.data;
} catch (error: any) {
console.error('Error assessing persona quality:', error);
return {
success: false,
error: error.response?.data?.detail || error.message || 'Failed to assess persona quality'
};
}
};
/**
* Regenerate persona with different parameters.
*/
export const regeneratePersona = async (
request: PersonaGenerationRequest
): Promise<PersonaGenerationResponse> => {
try {
const response = await apiClient.post('/api/onboarding/step4/regenerate-persona', request);
return response.data;
} catch (error: any) {
console.error('Error regenerating persona:', error);
return {
success: false,
error: error.response?.data?.detail || error.message || 'Failed to regenerate persona'
};
}
};
/**
* Get available options for persona generation.
*/
export const getPersonaGenerationOptions = async (): Promise<PersonaOptions> => {
try {
const response = await apiClient.get('/api/onboarding/step4/persona-options');
return response.data;
} catch (error: any) {
console.error('Error getting persona options:', error);
return {
success: false,
available_platforms: [],
persona_types: [],
quality_metrics: []
};
}
};
/**
* Utility function to prepare onboarding data for persona generation.
*/
export const prepareOnboardingData = (stepData: any) => {
return {
websiteAnalysis: stepData?.analysis || null,
competitorResearch: {
competitors: stepData?.competitors || [],
researchSummary: stepData?.researchSummary || null,
socialMediaAccounts: stepData?.socialMediaAccounts || {}
},
sitemapAnalysis: stepData?.sitemapAnalysis || null,
businessData: stepData?.businessData || null
};
};
/**
* Utility function to validate persona generation request.
*/
export const validatePersonaRequest = (request: PersonaGenerationRequest): string[] => {
const errors: string[] = [];
if (!request.onboarding_data) {
errors.push('Onboarding data is required');
}
if (!request.selected_platforms || request.selected_platforms.length === 0) {
errors.push('At least one platform must be selected');
}
if (request.selected_platforms && request.selected_platforms.length > 5) {
errors.push('Maximum 5 platforms can be selected');
}
return errors;
};

83
frontend/src/api/wix.ts Normal file
View File

@@ -0,0 +1,83 @@
/**
* Wix API Client
* Handles Wix connection status and operations
*/
import { apiClient } from './client';
export interface WixStatus {
connected: boolean;
sites: Array<{
id: string;
blog_url: string;
blog_id: string;
created_at: string;
scope: string;
}>;
total_sites: number;
error?: string;
}
class WixAPI {
private baseUrl = '/api/wix';
private getAuthToken: (() => Promise<string | null>) | null = null;
/**
* Set the auth token getter function
*/
setAuthTokenGetter(getToken: () => Promise<string | null>) {
this.getAuthToken = getToken;
}
/**
* Get authenticated API client with auth token
*/
private async getAuthenticatedClient() {
const token = this.getAuthToken ? await this.getAuthToken() : null;
if (!token) {
throw new Error('No authentication token available');
}
return apiClient.create({
headers: {
'Authorization': `Bearer ${token}`
}
});
}
/**
* Get Wix connection status
*/
async getStatus(): Promise<WixStatus> {
try {
const client = await this.getAuthenticatedClient();
const response = await client.get(`${this.baseUrl}/status`);
return response.data;
} catch (error: any) {
console.error('Wix API: Error getting status:', error);
return {
connected: false,
sites: [],
total_sites: 0,
error: error.response?.data?.detail || error.message
};
}
}
/**
* Health check for Wix service
*/
async healthCheck(): Promise<boolean> {
try {
const client = await this.getAuthenticatedClient();
await client.get(`${this.baseUrl}/connection/status`);
return true;
} catch (error) {
console.error('Wix API: Health check failed:', error);
return false;
}
}
}
export const wixAPI = new WixAPI();

View File

@@ -0,0 +1,276 @@
/**
* WordPress API client for ALwrity frontend.
* Handles WordPress site connections, content publishing, and management.
*/
import { apiClient } from './client';
export interface WordPressSite {
id: number;
site_url: string;
site_name: string;
username: string;
is_active: boolean;
created_at: string;
updated_at: string;
}
export interface WordPressSiteRequest {
site_url: string;
site_name: string;
username: string;
app_password: string;
}
export interface WordPressPublishRequest {
site_id: number;
title: string;
content: string;
excerpt?: string;
featured_image_path?: string;
categories?: string[];
tags?: string[];
status?: 'draft' | 'publish' | 'private';
meta_description?: string;
}
export interface WordPressPublishResponse {
success: boolean;
post_id?: number;
post_url?: string;
error?: string;
}
export interface WordPressPost {
id: number;
wp_post_id: number;
title: string;
status: string;
published_at?: string;
created_at: string;
site_name: string;
site_url: string;
}
export interface WordPressStatusResponse {
connected: boolean;
sites?: WordPressSite[];
total_sites: number;
}
export interface WordPressHealthResponse {
status: string;
service: string;
timestamp: string;
version: string;
}
class WordPressAPI {
private baseUrl = '/wordpress';
private getAuthToken: (() => Promise<string | null>) | null = null;
/**
* Set authentication token getter
*/
setAuthTokenGetter(getter: () => Promise<string | null>) {
this.getAuthToken = getter;
}
/**
* Get authenticated client with token
*/
private async getAuthenticatedClient() {
if (this.getAuthToken) {
const token = await this.getAuthToken();
if (token) {
// Create a new client instance with the auth header
return apiClient.create({
headers: {
'Authorization': `Bearer ${token}`
}
});
}
}
return apiClient;
}
/**
* Get WordPress connection status
*/
async getStatus(): Promise<WordPressStatusResponse> {
try {
const client = await this.getAuthenticatedClient();
const response = await client.get(`${this.baseUrl}/status`);
return response.data;
} catch (error) {
console.error('WordPress API: Error getting status:', error);
throw error;
}
}
/**
* Add a new WordPress site connection
*/
async addSite(siteData: WordPressSiteRequest): Promise<WordPressSite> {
try {
const client = await this.getAuthenticatedClient();
const response = await client.post(`${this.baseUrl}/sites`, siteData);
return response.data;
} catch (error) {
console.error('WordPress API: Error adding site:', error);
throw error;
}
}
/**
* Get all WordPress sites for the current user
*/
async getSites(): Promise<WordPressSite[]> {
try {
const client = await this.getAuthenticatedClient();
const response = await client.get(`${this.baseUrl}/sites`);
return response.data;
} catch (error) {
console.error('WordPress API: Error getting sites:', error);
throw error;
}
}
/**
* Disconnect a WordPress site
*/
async disconnectSite(siteId: number): Promise<{ success: boolean; message: string }> {
try {
const client = await this.getAuthenticatedClient();
const response = await client.delete(`${this.baseUrl}/sites/${siteId}`);
return response.data;
} catch (error) {
console.error('WordPress API: Error disconnecting site:', error);
throw error;
}
}
/**
* Publish content to WordPress
*/
async publishContent(publishData: WordPressPublishRequest): Promise<WordPressPublishResponse> {
try {
const client = await this.getAuthenticatedClient();
const response = await client.post(`${this.baseUrl}/publish`, publishData);
return response.data;
} catch (error) {
console.error('WordPress API: Error publishing content:', error);
throw error;
}
}
/**
* Get published posts from WordPress sites
*/
async getPosts(siteId?: number): Promise<WordPressPost[]> {
try {
const client = await this.getAuthenticatedClient();
const params = siteId ? { site_id: siteId } : {};
const response = await client.get(`${this.baseUrl}/posts`, { params });
return response.data;
} catch (error) {
console.error('WordPress API: Error getting posts:', error);
throw error;
}
}
/**
* Update post status (draft/publish/private)
*/
async updatePostStatus(postId: number, status: string): Promise<{ success: boolean; message: string }> {
try {
const client = await this.getAuthenticatedClient();
const response = await client.put(`${this.baseUrl}/posts/${postId}/status`, null, {
params: { status }
});
return response.data;
} catch (error) {
console.error('WordPress API: Error updating post status:', error);
throw error;
}
}
/**
* Delete a WordPress post
*/
async deletePost(postId: number, force: boolean = false): Promise<{ success: boolean; message: string }> {
try {
const client = await this.getAuthenticatedClient();
const response = await client.delete(`${this.baseUrl}/posts/${postId}`, {
params: { force }
});
return response.data;
} catch (error) {
console.error('WordPress API: Error deleting post:', error);
throw error;
}
}
/**
* Test WordPress site connection
*/
async testConnection(siteData: WordPressSiteRequest): Promise<boolean> {
try {
// This would typically be a separate endpoint for testing connections
// For now, we'll try to add the site and see if it succeeds
await this.addSite(siteData);
return true;
} catch (error) {
console.error('WordPress API: Connection test failed:', error);
return false;
}
}
/**
* Health check
*/
async healthCheck(): Promise<WordPressHealthResponse> {
try {
const response = await apiClient.get(`${this.baseUrl}/health`);
return response.data;
} catch (error) {
console.error('WordPress API: Health check failed:', error);
throw error;
}
}
/**
* Validate WordPress site URL
*/
validateSiteUrl(url: string): boolean {
try {
// Remove protocol if present
const cleanUrl = url.replace(/^https?:\/\//, '');
// Basic URL validation
const urlPattern = /^[a-zA-Z0-9][a-zA-Z0-9-]*[a-zA-Z0-9]*\.([a-zA-Z]{2,}|[a-zA-Z]{2,}\.[a-zA-Z]{2,})$/;
return urlPattern.test(cleanUrl) || cleanUrl.includes('localhost') || cleanUrl.includes('127.0.0.1');
} catch (error) {
return false;
}
}
/**
* Format WordPress site URL
*/
formatSiteUrl(url: string): string {
if (!url) return '';
// Add protocol if missing
if (!url.startsWith('http://') && !url.startsWith('https://')) {
return `https://${url}`;
}
return url;
}
}
// Export singleton instance
export const wordpressAPI = new WordPressAPI();
export default wordpressAPI;

View File

@@ -0,0 +1,113 @@
/**
* WordPress OAuth2 API client for ALwrity frontend.
* Handles WordPress.com OAuth2 authentication flow.
*/
import { apiClient } from './client';
export interface WordPressOAuthResponse {
auth_url: string;
state: string;
}
export interface WordPressOAuthStatus {
connected: boolean;
sites: WordPressOAuthSite[];
total_sites: number;
}
export interface WordPressOAuthSite {
id: number;
blog_id: string;
blog_url: string;
scope: string;
created_at: string;
}
class WordPressOAuthAPI {
private baseUrl = '/wp';
private getAuthToken: (() => Promise<string | null>) | null = null;
/**
* Set authentication token getter
*/
setAuthTokenGetter(getter: () => Promise<string | null>) {
this.getAuthToken = getter;
}
/**
* Get authenticated client with token
*/
private async getAuthenticatedClient() {
const token = this.getAuthToken ? await this.getAuthToken() : null;
if (!token) {
throw new Error('No authentication token available');
}
return apiClient.create({
headers: {
'Authorization': `Bearer ${token}`
}
});
}
/**
* Get WordPress OAuth2 authorization URL
*/
async getAuthUrl(): Promise<WordPressOAuthResponse> {
try {
const client = await this.getAuthenticatedClient();
const response = await client.get(`${this.baseUrl}/auth/url`);
return response.data;
} catch (error) {
console.error('WordPress OAuth API: Error getting auth URL:', error);
throw error;
}
}
/**
* Get WordPress OAuth connection status
*/
async getStatus(): Promise<WordPressOAuthStatus> {
try {
const client = await this.getAuthenticatedClient();
const response = await client.get(`${this.baseUrl}/status`);
return response.data;
} catch (error) {
console.error('WordPress OAuth API: Error getting status:', error);
throw error;
}
}
/**
* Disconnect a WordPress site
*/
async disconnectSite(tokenId: number): Promise<{ success: boolean; message: string }> {
try {
const client = await this.getAuthenticatedClient();
const response = await client.delete(`${this.baseUrl}/disconnect/${tokenId}`);
return response.data;
} catch (error) {
console.error('WordPress OAuth API: Error disconnecting site:', error);
throw error;
}
}
/**
* Health check
*/
async healthCheck(): Promise<{ status: string; service: string; timestamp: string; version: string }> {
try {
const response = await apiClient.get(`${this.baseUrl}/health`);
return response.data;
} catch (error) {
console.error('WordPress OAuth API: Health check failed:', error);
throw error;
}
}
}
// Export singleton instance
export const wordpressOAuthAPI = new WordPressOAuthAPI();
export default wordpressOAuthAPI;