Files
ALwrity/frontend/src/hooks/useWordPressOAuth.ts

194 lines
5.5 KiB
TypeScript

/**
* WordPress OAuth Connection Hook
* Manages WordPress.com OAuth2 authentication flow.
*/
import { useState, useEffect, useCallback } from 'react';
import { wordpressOAuthAPI, WordPressOAuthStatus, WordPressOAuthSite } from '../api/wordpressOAuth';
import { useAuth } from '@clerk/clerk-react';
export interface UseWordPressOAuthReturn {
// Connection state
connected: boolean;
sites: WordPressOAuthSite[];
totalSites: number;
isLoading: boolean;
// OAuth actions
startOAuthFlow: () => Promise<void>;
disconnectSite: (tokenId: number) => Promise<boolean>;
refreshStatus: () => Promise<void>;
}
export const useWordPressOAuth = (): UseWordPressOAuthReturn => {
const { getToken } = useAuth();
const [connected, setConnected] = useState(false);
const [sites, setSites] = useState<WordPressOAuthSite[]>([]);
const [totalSites, setTotalSites] = useState(0);
const [isLoading, setIsLoading] = useState(false);
const [lastStatusCheck, setLastStatusCheck] = useState<number>(0);
// Set up authentication
useEffect(() => {
const setupAuth = async () => {
try {
wordpressOAuthAPI.setAuthTokenGetter(async () => {
try {
return await getToken();
} catch (e) {
return null;
}
});
} catch (error) {
console.error('Error setting up WordPress OAuth API auth:', error);
}
};
setupAuth();
}, [getToken]);
const checkStatus = useCallback(async () => {
// Throttle status checks to prevent excessive API calls
const now = Date.now();
const THROTTLE_MS = 10000; // 10 seconds - status doesn't change frequently
if (now - lastStatusCheck < THROTTLE_MS) {
return;
}
try {
setIsLoading(true);
setLastStatusCheck(now);
const status: WordPressOAuthStatus = await wordpressOAuthAPI.getStatus();
setConnected(status.connected);
setSites(status.sites || []);
setTotalSites(status.total_sites);
} catch (error) {
console.error('Error checking WordPress OAuth status:', error);
setConnected(false);
setSites([]);
setTotalSites(0);
} finally {
setIsLoading(false);
}
}, [lastStatusCheck]);
// Check connection status on mount
useEffect(() => {
checkStatus();
}, [checkStatus]);
const startOAuthFlow = async () => {
try {
setIsLoading(true);
const authData = await wordpressOAuthAPI.getAuthUrl();
if (authData && authData.auth_url) {
// Open OAuth popup window
const popup = window.open(
authData.auth_url,
'wordpress-oauth',
'width=600,height=700,scrollbars=yes,resizable=yes'
);
if (!popup) {
throw new Error('Popup blocked. Please allow popups for this site.');
}
// Listen for popup completion and messages
const messageHandler = (event: MessageEvent) => {
// Accept messages only from the popup we opened and from trusted origins
const ngrokOrigin = process.env.REACT_APP_NGROK_ORIGIN || 'https://littery-sonny-unscrutinisingly.ngrok-free.dev';
const productionOrigin = 'https://alwrity-ai.vercel.app';
const trustedOrigins = [window.location.origin, ngrokOrigin, productionOrigin];
if (event.source !== popup) return;
if (!trustedOrigins.includes(event.origin)) {
return;
}
if (event.data.type === 'WPCOM_OAUTH_SUCCESS') {
popup.close();
clearInterval(checkClosed);
// Refresh status after OAuth completion
setTimeout(() => {
checkStatus();
}, 1000);
} else if (event.data.type === 'WPCOM_OAUTH_ERROR') {
popup.close();
clearInterval(checkClosed);
// Refresh status to show disconnected state
setTimeout(() => {
checkStatus();
}, 1000);
}
};
window.addEventListener('message', messageHandler);
const checkClosed = setInterval(() => {
if (popup.closed) {
clearInterval(checkClosed);
window.removeEventListener('message', messageHandler);
// Refresh status after OAuth completion
setTimeout(() => {
checkStatus();
}, 1000);
}
}, 1000);
} else {
throw new Error('Failed to get WordPress OAuth URL');
}
} catch (error) {
console.error('Error starting WordPress OAuth flow:', error);
throw error;
} finally {
setIsLoading(false);
}
};
const disconnectSite = async (tokenId: number): Promise<boolean> => {
try {
setIsLoading(true);
const result = await wordpressOAuthAPI.disconnectSite(tokenId);
if (result.success) {
// Refresh status after disconnection
await checkStatus();
console.log('WordPress site disconnected successfully');
return true;
}
return false;
} catch (error) {
console.error('Error disconnecting WordPress site:', error);
return false;
} finally {
setIsLoading(false);
}
};
const refreshStatus = useCallback(async (): Promise<void> => {
await checkStatus();
}, [checkStatus]);
return {
// Connection state
connected,
sites,
totalSites,
isLoading,
// OAuth actions
startOAuthFlow,
disconnectSite,
refreshStatus
};
};