/** * Plugin Page Renderer - Renders plugin pages with security isolation */ "use client" import React, { useState, useEffect, useRef } from 'react'; import { Alert, AlertDescription } from '@/components/ui/alert'; import { Card, CardContent } from '@/components/ui/card'; import { Skeleton } from '@/components/ui/skeleton'; import { AlertCircle, Loader2 } from 'lucide-react'; import { useAuth } from '@/components/providers/auth-provider'; import { tokenManager } from '@/lib/token-manager'; import { usePlugin, type PluginInfo } from '../../contexts/PluginContext'; import { config } from '../../lib/config'; interface PluginPageRendererProps { pluginId: string; pagePath: string; componentName?: string; } interface PluginIframeProps { pluginId: string; pagePath: string; token: string; onLoad?: () => void; onError?: (error: string) => void; } const PluginIframe: React.FC = ({ pluginId, pagePath, token, onLoad, onError }) => { const iframeRef = useRef(null); const [loading, setLoading] = useState(true); useEffect(() => { const iframe = iframeRef.current; if (!iframe) return; // Set up iframe communication const handleMessage = (event: MessageEvent) => { // Only accept messages from our iframe if (event.source !== iframe.contentWindow) return; // Validate origin - should be from our backend const allowedOrigins = [ window.location.origin, config.API_BASE_URL, config.API_BASE_URL ].filter(Boolean); if (!allowedOrigins.some(origin => event.origin.startsWith(origin))) { return; } try { const message = event.data; switch (message.type) { case 'plugin_loaded': setLoading(false); onLoad?.(); break; case 'plugin_error': setLoading(false); onError?.(message.error || 'Plugin failed to load'); break; case 'plugin_resize': if (message.height && iframe) { iframe.style.height = `${message.height}px`; } break; case 'plugin_navigate': // Handle navigation within plugin if (message.path) { // Update URL without reload const newUrl = `/plugins/${pluginId}${message.path}`; window.history.pushState(null, '', newUrl); } break; } } catch (err) { } }; window.addEventListener('message', handleMessage); return () => { window.removeEventListener('message', handleMessage); }; }, [pluginId, onLoad, onError]); const iframeUrl = `/api-internal/v1/plugins/${pluginId}/ui${pagePath}?token=${encodeURIComponent(token)}`; return (
{loading && (
Loading plugin...
)}