import { useState } from 'react'; import { Puzzle, Search, Download, RefreshCw, Star, Users, Trash2, Zap, Palette, Code, Globe, Database, ShieldCheck } from 'lucide-react'; import { toast } from 'sonner'; import { useQuery, useMutation, useQueryClient } from '@tanstack/react-query'; import { api } from '@/lib/api'; type TabType = 'installed' | 'available' | 'updates' | 'advanced'; const categoryColors: Record = { '部署': '#0969da', '构建': '#1a7f37', '主题': '#8250df', '扫描': '#9a6700', '安全': '#cf222e', '运维': '#6e7781', '通知': '#bf8700', }; const iconMap: Record = { '部署': Globe, '构建': Code, '主题': Palette, '扫描': ShieldCheck, '安全': ShieldCheck, '运维': Database, '通知': Zap, 'general': Puzzle, }; export default function PluginManager() { const [activeTab, setActiveTab] = useState('installed'); const [search, setSearch] = useState(''); const queryClient = useQueryClient(); const { data: installed = [] } = useQuery({ queryKey: ['plugins'], queryFn: () => api('/api/plugins') }); const { data: available = [] } = useQuery({ queryKey: ['plugins', 'available'], queryFn: () => api('/api/plugins/available') }); const invalidate = () => queryClient.invalidateQueries({ queryKey: ['plugins'] }); const installMutation = useMutation({ mutationFn: (body: any) => api('/api/plugins/install', { method: 'POST', body: JSON.stringify(body) }), onSuccess: () => { toast.success('插件安装成功'); invalidate(); }, onError: (err: Error) => toast.error(err.message), }); const uninstallMutation = useMutation({ mutationFn: (id: number) => api(`/api/plugins/${id}`, { method: 'DELETE' }), onSuccess: () => { toast.success('插件已卸载'); invalidate(); }, onError: (err: Error) => toast.error(err.message), }); const toggleMutation = useMutation({ mutationFn: (input: { id: number; enabled: boolean }) => api(`/api/plugins/${input.id}/toggle`, { method: 'POST', body: JSON.stringify({ enabled: input.enabled }) }), onSuccess: () => invalidate(), onError: (err: Error) => toast.error(err.message), }); const installedPlugins = installed.map((p: any) => ({ id: String(p.id), name: p.name, description: p.description || '', version: p.version, latestVersion: p.latestVersion || p.version, author: p.author || 'Unknown', installs: p.installs || 0, rating: p.rating || null, category: p.category || 'general', installed: true, hasUpdate: p.latestVersion && p.latestVersion !== p.version, enabled: p.enabled, icon: iconMap[p.category || 'general'] || Puzzle, })); const availablePlugins = available.map((p: any) => ({ id: p.pluginId, name: p.name, description: p.description || '', version: p.version, latestVersion: p.version, author: p.author || 'Unknown', installs: p.installs || 0, rating: p.rating || null, category: p.category || 'general', installed: false, hasUpdate: false, enabled: false, icon: iconMap[p.category || 'general'] || Puzzle, })); const updatePlugins = installedPlugins.filter((p: any) => p.hasUpdate); const allPlugins = [...installedPlugins, ...availablePlugins]; const filteredPlugins = () => { const list = activeTab === 'installed' ? installedPlugins : activeTab === 'available' ? availablePlugins : activeTab === 'updates' ? updatePlugins : allPlugins; return list.filter((p: any) => p.name.toLowerCase().includes(search.toLowerCase()) || p.description.toLowerCase().includes(search.toLowerCase())); }; const handleInstall = (plugin: any) => { installMutation.mutate({ pluginId: plugin.id, name: plugin.name, description: plugin.description, version: plugin.version, author: plugin.author, category: plugin.category, }); }; const handleUninstall = (id: string) => { const numId = Number(id); if (!isNaN(numId)) uninstallMutation.mutate(numId); }; const handleToggle = (id: string) => { const numId = Number(id); const plugin = installedPlugins.find((p: any) => p.id === id); if (!isNaN(numId) && plugin) { toggleMutation.mutate({ id: numId, enabled: !plugin.enabled }); } }; const tabs: { id: TabType; label: string; count: number }[] = [ { id: 'installed', label: '已安装', count: installedPlugins.length }, { id: 'available', label: '可安装', count: availablePlugins.length }, { id: 'updates', label: '更新', count: updatePlugins.length }, { id: 'advanced', label: '高级设置', count: 0 }, ]; return (
{/* Header */}

插件管理

{installedPlugins.length} 已安装 / {availablePlugins.length} 可安装
setSearch(e.target.value)} placeholder="搜索插件..." className="px-2 py-[5px] text-[12px] outline-none w-48" style={{ background: 'transparent', color: 'var(--text-primary)' }} />
{/* Tabs */}
{tabs.map(tab => ( ))}
{/* Content */}
{activeTab === 'advanced' ? (

插件安装源

{['https://plugins.opsx.dev/stable', 'https://plugins.opsx.dev/beta', 'https://plugins.opsx.dev/community'].map((url, idx) => (
{url}
{idx === 0 ? '默认' : '备用'}
))}

代理设置

其他设置

) : (
{filteredPlugins().map(plugin => { const Icon = plugin.icon; return (
{plugin.name} {plugin.category} {plugin.installed && ( 已安装 )}

{plugin.description}

{plugin.rating != null && {plugin.rating}} {plugin.installs > 0 && {plugin.installs.toLocaleString()} 次安装} 作者: {plugin.author} v{plugin.version} {plugin.hasUpdate && v{plugin.latestVersion} 可用}
{plugin.installed ? ( <> {plugin.hasUpdate && ( )} ) : ( )}
); })}
)}
); }