Add plugin config management to backend and frontend

Introduces a unified plugin configuration schema and API in the backend, with endpoints for getting and setting plugin config. Updates the frontend to support plugin config modals, including a UI for editing plugin settings. Also adds support for uninstalling plugins with optional data cleanup, and updates dependencies to use napcat-types@0.0.5.
This commit is contained in:
手瓜一十雪
2026-01-28 13:56:40 +08:00
parent 40e11b9d29
commit 89bac8f6e3
10 changed files with 482 additions and 44 deletions

View File

@@ -3,7 +3,7 @@ import { Switch } from '@heroui/switch';
import { Chip } from '@heroui/chip';
import { useState } from 'react';
import { MdDeleteForever, MdPublishedWithChanges } from 'react-icons/md';
import { MdDeleteForever, MdPublishedWithChanges, MdSettings } from 'react-icons/md';
import DisplayCardContainer from './container';
import { PluginItem } from '@/controllers/plugin_manager';
@@ -13,6 +13,8 @@ export interface PluginDisplayCardProps {
onReload: () => Promise<void>;
onToggleStatus: () => Promise<void>;
onUninstall: () => Promise<void>;
onConfig?: () => void;
hasConfig?: boolean;
}
const PluginDisplayCard: React.FC<PluginDisplayCardProps> = ({
@@ -20,6 +22,8 @@ const PluginDisplayCard: React.FC<PluginDisplayCardProps> = ({
onReload,
onToggleStatus,
onUninstall,
onConfig,
hasConfig = false,
}) => {
const { name, version, author, description, status } = data;
const isEnabled = status !== 'disabled';
@@ -44,32 +48,47 @@ const PluginDisplayCard: React.FC<PluginDisplayCardProps> = ({
<DisplayCardContainer
className='w-full max-w-[420px]'
action={
<div className='flex gap-2 w-full'>
<Button
fullWidth
radius='full'
size='sm'
variant='flat'
className='flex-1 bg-default-100 dark:bg-default-50 text-default-600 font-medium hover:bg-primary/20 hover:text-primary transition-colors'
startContent={<MdPublishedWithChanges size={16} />}
onPress={handleReload}
isDisabled={!isEnabled || processing}
>
</Button>
<div className='flex flex-col gap-2 w-full'>
<div className='flex gap-2 w-full'>
<Button
fullWidth
radius='full'
size='sm'
variant='flat'
className='flex-1 bg-default-100 dark:bg-default-50 text-default-600 font-medium hover:bg-primary/20 hover:text-primary transition-colors'
startContent={<MdPublishedWithChanges size={16} />}
onPress={handleReload}
isDisabled={!isEnabled || processing}
>
</Button>
<Button
fullWidth
radius='full'
size='sm'
variant='flat'
className='flex-1 bg-default-100 dark:bg-default-50 text-default-600 font-medium hover:bg-danger/20 hover:text-danger transition-colors'
startContent={<MdDeleteForever size={16} />}
onPress={handleUninstall}
isDisabled={processing}
>
</Button>
<Button
fullWidth
radius='full'
size='sm'
variant='flat'
className='flex-1 bg-default-100 dark:bg-default-50 text-default-600 font-medium hover:bg-danger/20 hover:text-danger transition-colors'
startContent={<MdDeleteForever size={16} />}
onPress={handleUninstall}
isDisabled={processing}
>
</Button>
</div>
{hasConfig && (
<Button
fullWidth
radius='full'
size='sm'
variant='flat'
className='bg-default-100 dark:bg-default-50 text-default-600 font-medium hover:bg-secondary/20 hover:text-secondary transition-colors'
startContent={<MdSettings size={16} />}
onPress={onConfig}
>
</Button>
)}
</div>
}
enableSwitch={