import { RefreshCw, Package, AlertCircle, CheckCircle, XCircle, Info } from 'lucide-react' import { useState } from 'react' import { useTranslation } from 'react-i18next' import { Button, Badge, Alert, EmptyState, Skeleton, Dialog, DialogContent, DialogHeader, DialogTitle, DialogFooter, Switch, Card, } from '../components/ui' import { usePlugins, useEnablePlugin, useDisablePlugin, useReloadPlugins, type PluginInfo, } from '../hooks/usePlugins' import { toast } from '../utils/toast' export default function Plugins() { const { t } = useTranslation() const { data: plugins = [], isLoading, refetch } = usePlugins() const enableMutation = useEnablePlugin() const disableMutation = useDisablePlugin() const reloadMutation = useReloadPlugins() const [selectedPlugin, setSelectedPlugin] = useState(null) const [metadataModalOpen, setMetadataModalOpen] = useState(false) const handleTogglePlugin = async (plugin: PluginInfo) => { if (plugin.is_built_in) { toast.error(t('plugins.cannotDisableBuiltIn', 'Built-in plugins cannot be disabled')) return } try { if (plugin.enabled) { await disableMutation.mutateAsync(plugin.id) toast.success(t('plugins.disableSuccess', 'Plugin disabled successfully')) } else { await enableMutation.mutateAsync(plugin.id) toast.success(t('plugins.enableSuccess', 'Plugin enabled successfully')) } refetch() } catch (error: unknown) { const err = error as { response?: { data?: { error?: string } }; message?: string } const message = err.response?.data?.error || err.message || t('plugins.toggleFailed', 'Failed to toggle plugin') toast.error(message) } } const handleReloadPlugins = async () => { try { const result = await reloadMutation.mutateAsync() toast.success( t('plugins.reloadSuccess', 'Plugins reloaded: {{count}} loaded', { count: result.count }) ) refetch() } catch (error: unknown) { const err = error as { response?: { data?: { error?: string } }; message?: string } const message = err.response?.data?.error || err.message || t('plugins.reloadFailed', 'Failed to reload plugins') toast.error(message) } } const handleViewMetadata = (plugin: PluginInfo) => { setSelectedPlugin(plugin) setMetadataModalOpen(true) } const getStatusBadge = (plugin: PluginInfo) => { if (!plugin.enabled) { return ( {t('plugins.disabled', 'Disabled')} ) } switch (plugin.status) { case 'loaded': return ( {t('plugins.loaded', 'Loaded')} ) case 'error': return ( {t('plugins.error', 'Error')} ) case 'pending': return ( {t('plugins.pending', 'Pending')} ) default: return null } } // Group plugins by type const builtInPlugins = plugins.filter((p) => p.is_built_in) const externalPlugins = plugins.filter((p) => !p.is_built_in) // Header actions const headerActions = ( ) return (
{/* Header with Reload Button */}
{headerActions}
{/* Info Alert */} {t('plugins.note', 'Note')}:{' '} {t( 'plugins.noteText', 'External plugins extend Charon with custom DNS providers. Only install plugins from trusted sources.' )} {/* Loading State */} {isLoading && (
{[1, 2, 3].map((i) => ( ))}
)} {/* Empty State */} {!isLoading && plugins.length === 0 && ( } title={t('plugins.noPlugins', 'No Plugins Found')} description={t( 'plugins.noPluginsDescription', 'No DNS provider plugins are currently installed.' )} /> )} {/* Built-in Plugins Section */} {!isLoading && builtInPlugins.length > 0 && (

{t('plugins.builtInPlugins', 'Built-in Providers')}

{builtInPlugins.map((plugin) => (

{plugin.name}

{plugin.type} {plugin.version && ( v{plugin.version} )}

{plugin.description && (

{plugin.description}

)}
{getStatusBadge(plugin)} {plugin.documentation_url && ( )}
))}
)} {/* External Plugins Section */} {!isLoading && externalPlugins.length > 0 && (

{t('plugins.externalPlugins', 'External Plugins')}

{externalPlugins.map((plugin) => (

{plugin.name}

{plugin.type} {plugin.version && ( v{plugin.version} )} {plugin.author && ( by {plugin.author} )}

{plugin.description && (

{plugin.description}

)} {plugin.error && (

{plugin.error}

)}
{getStatusBadge(plugin)} handleTogglePlugin(plugin)} disabled={enableMutation.isPending || disableMutation.isPending} /> {plugin.documentation_url && ( )}
))}
)} {/* Metadata Modal */} {t('plugins.pluginDetails', 'Plugin Details')}: {selectedPlugin?.name} {selectedPlugin && (

{t('plugins.type', 'Type')}

{selectedPlugin.type}

{t('plugins.status', 'Status')}

{getStatusBadge(selectedPlugin)}
{selectedPlugin.version && (

{t('plugins.version', 'Version')}

{selectedPlugin.version}

)} {selectedPlugin.author && (

{t('plugins.author', 'Author')}

{selectedPlugin.author}

)}

{t('plugins.pluginType', 'Plugin Type')}

{selectedPlugin.is_built_in ? t('plugins.builtIn', 'Built-in') : t('plugins.external', 'External')}

{selectedPlugin.loaded_at && (

{t('plugins.loadedAt', 'Loaded At')}

{new Date(selectedPlugin.loaded_at).toLocaleString()}

)}
{selectedPlugin.description && (

{t('plugins.description', 'Description')}

{selectedPlugin.description}

)} {selectedPlugin.documentation_url && (

{t('plugins.documentation', 'Documentation')}

{selectedPlugin.documentation_url}
)} {selectedPlugin.error && (

{t('plugins.errorDetails', 'Error Details')}

{selectedPlugin.error}

)}
)}
) }