194 lines
5.5 KiB
TypeScript
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
|
|
};
|
|
};
|