diff --git a/pkg/api/http/controller/groups/plugins.py b/pkg/api/http/controller/groups/plugins.py index 7825c251..670f13ed 100644 --- a/pkg/api/http/controller/groups/plugins.py +++ b/pkg/api/http/controller/groups/plugins.py @@ -17,17 +17,6 @@ class PluginsRouterGroup(group.RouterGroup): return self.success(data={'plugins': plugins}) - @self.route( - '///toggle', - methods=['PUT'], - auth_type=group.AuthType.USER_TOKEN, - ) - async def _(author: str, plugin_name: str) -> str: - data = await quart.request.json - target_enabled = data.get('target_enabled') - await self.ap.plugin_mgr.update_plugin_switch(plugin_name, target_enabled) - return self.success() - @self.route( '///upgrade', methods=['POST'], @@ -76,21 +65,16 @@ class PluginsRouterGroup(group.RouterGroup): plugin = await self.ap.plugin_connector.get_plugin_info(author, plugin_name) if plugin is None: return self.http_status(404, -1, 'plugin not found') + if quart.request.method == 'GET': return self.success(data={'config': plugin['plugin_config']}) elif quart.request.method == 'PUT': data = await quart.request.json - await self.ap.plugin_mgr.set_plugin_config(plugin, data) + await self.ap.plugin_connector.set_plugin_config(author, plugin_name, data) return self.success(data={}) - @self.route('/reorder', methods=['PUT'], auth_type=group.AuthType.USER_TOKEN) - async def _() -> str: - data = await quart.request.json - await self.ap.plugin_mgr.reorder_plugins(data.get('plugins')) - return self.success() - @self.route('/install/github', methods=['POST'], auth_type=group.AuthType.USER_TOKEN) async def _() -> str: data = await quart.request.json diff --git a/pkg/plugin/connector.py b/pkg/plugin/connector.py index 8c2a91ad..dcab8b8c 100644 --- a/pkg/plugin/connector.py +++ b/pkg/plugin/connector.py @@ -150,6 +150,9 @@ class PluginRuntimeConnector: async def get_plugin_info(self, author: str, plugin_name: str) -> dict[str, Any]: return await self.handler.get_plugin_info(author, plugin_name) + async def set_plugin_config(self, plugin_author: str, plugin_name: str, config: dict[str, Any]) -> dict[str, Any]: + return await self.handler.set_plugin_config(plugin_author, plugin_name, config) + async def emit_event( self, event: events.BaseEventModel, diff --git a/pkg/plugin/handler.py b/pkg/plugin/handler.py index 4095ea86..5ec36e68 100644 --- a/pkg/plugin/handler.py +++ b/pkg/plugin/handler.py @@ -486,6 +486,29 @@ class RuntimeConnectionHandler(handler.Handler): ) return result['plugin'] + async def set_plugin_config(self, plugin_author: str, plugin_name: str, config: dict[str, Any]) -> dict[str, Any]: + """Set plugin config""" + # update plugin setting + await self.ap.persistence_mgr.execute_async( + sqlalchemy.update(persistence_plugin.PluginSetting) + .where(persistence_plugin.PluginSetting.plugin_author == plugin_author) + .where(persistence_plugin.PluginSetting.plugin_name == plugin_name) + .values(config=config) + ) + + # restart plugin + gen = self.call_action_generator( + LangBotToRuntimeAction.RESTART_PLUGIN, + { + 'plugin_author': plugin_author, + 'plugin_name': plugin_name, + }, + ) + async for ret in gen: + pass + + return {} + async def emit_event( self, event_context: dict[str, Any], diff --git a/web/src/app/home/plugins/plugin-installed/PluginInstalledComponent.tsx b/web/src/app/home/plugins/plugin-installed/PluginInstalledComponent.tsx index 0f13618f..52c49d7a 100644 --- a/web/src/app/home/plugins/plugin-installed/PluginInstalledComponent.tsx +++ b/web/src/app/home/plugins/plugin-installed/PluginInstalledComponent.tsx @@ -271,9 +271,15 @@ const PluginInstalledComponent = forwardRef( { + onFormSubmit={(timeout?: number) => { setModalOpen(false); - getPluginList(); + if (timeout) { + setTimeout(() => { + getPluginList(); + }, timeout); + } else { + getPluginList(); + } }} onFormCancel={() => { setModalOpen(false); diff --git a/web/src/app/home/plugins/plugin-installed/plugin-card/PluginCardComponent.tsx b/web/src/app/home/plugins/plugin-installed/plugin-card/PluginCardComponent.tsx index 9b6152d0..5772a3f6 100644 --- a/web/src/app/home/plugins/plugin-installed/plugin-card/PluginCardComponent.tsx +++ b/web/src/app/home/plugins/plugin-installed/plugin-card/PluginCardComponent.tsx @@ -1,9 +1,6 @@ import { PluginCardVO } from '@/app/home/plugins/plugin-installed/PluginCardVO'; import { useState } from 'react'; -import { httpClient } from '@/app/infra/http/HttpClient'; import { Badge } from '@/components/ui/badge'; -import { Switch } from '@/components/ui/switch'; -import { toast } from 'sonner'; import { useTranslation } from 'react-i18next'; import { TFunction } from 'i18next'; import { @@ -81,26 +78,8 @@ export default function PluginCardComponent({ onUpgradeClick: (cardVO: PluginCardVO) => void; }) { const { t } = useTranslation(); - const [enabled, setEnabled] = useState(cardVO.enabled); - const [switchEnable, setSwitchEnable] = useState(true); const [dropdownOpen, setDropdownOpen] = useState(false); - function handleEnable(e: React.MouseEvent) { - e.stopPropagation(); // 阻止事件冒泡 - setSwitchEnable(false); - httpClient - .togglePlugin(cardVO.author, cardVO.name, !enabled) - .then(() => { - setEnabled(!enabled); - }) - .catch((err) => { - toast.error(t('plugins.modifyFailed') + err.message); - }) - .finally(() => { - setSwitchEnable(true); - }); - } - return ( <>
-
- handleEnable(e)} - disabled={!switchEnable} - /> -
+
diff --git a/web/src/app/home/plugins/plugin-installed/plugin-form/PluginForm.tsx b/web/src/app/home/plugins/plugin-installed/plugin-form/PluginForm.tsx index 82c1a5e8..09a79d2f 100644 --- a/web/src/app/home/plugins/plugin-installed/plugin-form/PluginForm.tsx +++ b/web/src/app/home/plugins/plugin-installed/plugin-form/PluginForm.tsx @@ -16,7 +16,7 @@ export default function PluginForm({ }: { pluginAuthor: string; pluginName: string; - onFormSubmit: () => void; + onFormSubmit: (timeout?: number) => void; onFormCancel: () => void; }) { const { t } = useTranslation(); @@ -37,14 +37,19 @@ export default function PluginForm({ const handleSubmit = async (values: object) => { setIsLoading(true); + const isDebugPlugin = pluginInfo?.debug; httpClient .updatePluginConfig(pluginAuthor, pluginName, values) .then(() => { - onFormSubmit(); - toast.success('保存成功'); + toast.success( + isDebugPlugin + ? t('plugins.saveConfigSuccessDebugPlugin') + : t('plugins.saveConfigSuccessNormal'), + ); + onFormSubmit(1000); }) .catch((error) => { - toast.error('保存失败:' + error.message); + toast.error(t('plugins.saveConfigError') + error.message); }) .finally(() => { setIsLoading(false); diff --git a/web/src/app/infra/http/BackendClient.ts b/web/src/app/infra/http/BackendClient.ts index 675a5094..a74cfd76 100644 --- a/web/src/app/infra/http/BackendClient.ts +++ b/web/src/app/infra/http/BackendClient.ts @@ -15,7 +15,6 @@ import { ApiRespPlugins, ApiRespPlugin, ApiRespPluginConfig, - PluginReorderElement, AsyncTaskCreatedResp, ApiRespSystemInfo, ApiRespAsyncTasks, @@ -226,20 +225,6 @@ export class BackendClient extends BaseHttpClient { return this.put(`/api/v1/plugins/${author}/${name}/config`, config); } - public togglePlugin( - author: string, - name: string, - target_enabled: boolean, - ): Promise { - return this.put(`/api/v1/plugins/${author}/${name}/toggle`, { - target_enabled, - }); - } - - public reorderPlugins(plugins: PluginReorderElement[]): Promise { - return this.put('/api/v1/plugins/reorder', { plugins }); - } - public installPluginFromGithub( source: string, ): Promise { diff --git a/web/src/i18n/locales/en-US.ts b/web/src/i18n/locales/en-US.ts index c0d74898..8b697df4 100644 --- a/web/src/i18n/locales/en-US.ts +++ b/web/src/i18n/locales/en-US.ts @@ -170,7 +170,7 @@ const enUS = { saveConfig: 'Save Config', saving: 'Saving...', confirmDeletePlugin: - 'Are you sure you want to delete the plugin ({{author}}/{{name}})?', + 'Are you sure you want to delete the plugin ({{author}}/{{name}})? This will also delete the plugin configuration.', confirmDelete: 'Confirm Delete', deleteError: 'Delete failed: ', close: 'Close', @@ -205,6 +205,10 @@ const enUS = { updating: 'Updating...', updateSuccess: 'Plugin updated successfully', updateError: 'Update failed: ', + saveConfigSuccessNormal: 'Configuration saved successfully', + saveConfigSuccessDebugPlugin: + 'Configuration saved successfully, please manually restart the plugin', + saveConfigError: 'Configuration save failed: ', }, market: { searchPlaceholder: 'Search plugins...', diff --git a/web/src/i18n/locales/ja-JP.ts b/web/src/i18n/locales/ja-JP.ts index 596ce4eb..a4a02af3 100644 --- a/web/src/i18n/locales/ja-JP.ts +++ b/web/src/i18n/locales/ja-JP.ts @@ -170,7 +170,7 @@ const jaJP = { saveConfig: '設定を保存', saving: '保存中...', confirmDeletePlugin: - 'プラグイン「{{author}}/{{name}}」を削除してもよろしいですか?', + 'プラグイン「{{author}}/{{name}}」を削除してもよろしいですか?この操作により、プラグインの設定も削除されます。', confirmDelete: '削除を確認', deleteError: '削除に失敗しました:', close: '閉じる', @@ -205,6 +205,10 @@ const jaJP = { updating: '更新中...', updateSuccess: 'プラグインの更新に成功しました', updateError: '更新に失敗しました:', + saveConfigSuccessNormal: '設定を保存しました', + saveConfigSuccessDebugPlugin: + '設定を保存しました。手動でプラグインを再起動してください', + saveConfigError: '設定の保存に失敗しました:', }, market: { searchPlaceholder: 'プラグインを検索...', diff --git a/web/src/i18n/locales/zh-Hans.ts b/web/src/i18n/locales/zh-Hans.ts index 2c85d45d..ce37ccc5 100644 --- a/web/src/i18n/locales/zh-Hans.ts +++ b/web/src/i18n/locales/zh-Hans.ts @@ -166,7 +166,8 @@ const zhHans = { cancel: '取消', saveConfig: '保存配置', saving: '保存中...', - confirmDeletePlugin: '你确定要删除插件({{author}}/{{name}})吗?', + confirmDeletePlugin: + '你确定要删除插件({{author}}/{{name}})吗?这将同时删除插件的配置。', confirmDelete: '确认删除', deleteError: '删除失败:', close: '关闭', @@ -199,6 +200,9 @@ const zhHans = { updating: '更新中...', updateSuccess: '插件更新成功', updateError: '更新失败:', + saveConfigSuccessNormal: '保存配置成功', + saveConfigSuccessDebugPlugin: '保存配置成功,请手动重启插件', + saveConfigError: '保存配置失败:', }, market: { searchPlaceholder: '搜索插件...',