From 9322218880d5f29d33e32318da68b52541d5b1c0 Mon Sep 17 00:00:00 2001 From: clown145 Date: Tue, 6 Jan 2026 12:53:14 +0800 Subject: [PATCH] feat: supports to display plugin CHANGELOG.md (#4337) * feat: optimize plugin update changelog feature, refactor to reuse ReadmeDialog and support independent view entry * fix: distinguish error state from empty state in ReadmeDialog --- astrbot/dashboard/routes/plugin.py | 50 + .../src/components/shared/ExtensionCard.vue | 258 ++- .../src/components/shared/ReadmeDialog.vue | 351 ++-- .../src/i18n/locales/en-US/core/common.json | 8 + .../locales/en-US/features/extension.json | 5 +- .../src/i18n/locales/zh-CN/core/common.json | 8 + .../locales/zh-CN/features/extension.json | 3 + dashboard/src/views/ExtensionPage.vue | 1645 ++++++++++++----- 8 files changed, 1656 insertions(+), 672 deletions(-) diff --git a/astrbot/dashboard/routes/plugin.py b/astrbot/dashboard/routes/plugin.py index fd808c6c9..e6c03fe89 100644 --- a/astrbot/dashboard/routes/plugin.py +++ b/astrbot/dashboard/routes/plugin.py @@ -55,6 +55,7 @@ class PluginRoute(Route): "/plugin/on": ("POST", self.on_plugin), "/plugin/reload": ("POST", self.reload_plugins), "/plugin/readme": ("GET", self.get_plugin_readme), + "/plugin/changelog": ("GET", self.get_plugin_changelog), "/plugin/source/get": ("GET", self.get_custom_source), "/plugin/source/save": ("POST", self.save_custom_source), } @@ -615,6 +616,55 @@ class PluginRoute(Route): logger.error(f"/api/plugin/readme: {traceback.format_exc()}") return Response().error(f"读取README文件失败: {e!s}").__dict__ + async def get_plugin_changelog(self): + """获取插件更新日志 + + 读取插件目录下的 CHANGELOG.md 文件内容。 + """ + plugin_name = request.args.get("name") + logger.debug(f"正在获取插件 {plugin_name} 的更新日志") + + if not plugin_name: + return Response().error("插件名称不能为空").__dict__ + + # 查找插件 + plugin_obj = None + for plugin in self.plugin_manager.context.get_all_stars(): + if plugin.name == plugin_name: + plugin_obj = plugin + break + + if not plugin_obj: + return Response().error(f"插件 {plugin_name} 不存在").__dict__ + + if not plugin_obj.root_dir_name: + return Response().error(f"插件 {plugin_name} 目录不存在").__dict__ + + plugin_dir = os.path.join( + self.plugin_manager.plugin_store_path, + plugin_obj.root_dir_name, + ) + + # 尝试多种可能的文件名 + changelog_names = ["CHANGELOG.md", "changelog.md", "CHANGELOG", "changelog"] + for name in changelog_names: + changelog_path = os.path.join(plugin_dir, name) + if os.path.isfile(changelog_path): + try: + with open(changelog_path, encoding="utf-8") as f: + changelog_content = f.read() + return ( + Response() + .ok({"content": changelog_content}, "成功获取更新日志") + .__dict__ + ) + except Exception as e: + logger.error(f"/api/plugin/changelog: {traceback.format_exc()}") + return Response().error(f"读取更新日志失败: {e!s}").__dict__ + + # 没有找到 changelog 文件,返回 ok 但 content 为 null + return Response().ok({"content": None}, "该插件没有更新日志文件").__dict__ + async def get_custom_source(self): """获取自定义插件源""" sources = await sp.global_get("custom_plugin_sources", []) diff --git a/dashboard/src/components/shared/ExtensionCard.vue b/dashboard/src/components/shared/ExtensionCard.vue index 3c23d1f8f..3ad621b29 100644 --- a/dashboard/src/components/shared/ExtensionCard.vue +++ b/dashboard/src/components/shared/ExtensionCard.vue @@ -1,8 +1,8 @@