From f8b9dc5e4af59413e419a2ecfd5d5d7aed7f73cd Mon Sep 17 00:00:00 2001 From: lingyun14 Date: Sat, 16 May 2026 16:27:13 +0800 Subject: [PATCH 1/4] =?UTF-8?q?fix:=20=E4=BF=AE=E5=A4=8D=E6=8F=92=E4=BB=B6?= =?UTF-8?q?=20repo=20=E5=AD=97=E6=AE=B5=E7=B1=BB=E5=9E=8B=E5=AF=BC?= =?UTF-8?q?=E8=87=B4=E5=89=8D=E7=AB=AF=E6=8A=A5=E9=94=99?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- astrbot/dashboard/routes/plugin.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/astrbot/dashboard/routes/plugin.py b/astrbot/dashboard/routes/plugin.py index 10d87eabea..e2eb24c45e 100644 --- a/astrbot/dashboard/routes/plugin.py +++ b/astrbot/dashboard/routes/plugin.py @@ -1270,7 +1270,7 @@ async def get_plugins(self): logo_url = await self.get_plugin_logo_token(plugin.logo_path) _t = { "name": plugin.name, - "repo": "" if plugin.repo is None else plugin.repo, + "repo": "" if plugin.repo is None else str(plugin.repo), "author": plugin.author, "desc": plugin.desc, "version": plugin.version, @@ -1320,7 +1320,7 @@ async def get_plugin_detail(self): .ok( { "name": plugin.name, - "repo": "" if plugin.repo is None else plugin.repo, + "repo": "" if plugin.repo is None else str(plugin.repo), "author": plugin.author, "desc": plugin.desc, "version": plugin.version, From 9d2d63486ab43520ff045f781f6e468c573a1f18 Mon Sep 17 00:00:00 2001 From: lingyun14 Date: Sat, 16 May 2026 16:29:59 +0800 Subject: [PATCH 2/4] =?UTF-8?q?fix:=20=E4=BF=AE=E5=A4=8D=E6=8F=92=E4=BB=B6?= =?UTF-8?q?=20repo=20=E5=AD=97=E6=AE=B5=E7=B1=BB=E5=9E=8B=E5=AF=BC?= =?UTF-8?q?=E8=87=B4=E5=89=8D=E7=AB=AF=E6=8A=A5=E9=94=99?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../src/views/extension/useExtensionPage.js | 19 +++++++++++-------- 1 file changed, 11 insertions(+), 8 deletions(-) diff --git a/dashboard/src/views/extension/useExtensionPage.js b/dashboard/src/views/extension/useExtensionPage.js index e23716acdc..95cc54c222 100644 --- a/dashboard/src/views/extension/useExtensionPage.js +++ b/dashboard/src/views/extension/useExtensionPage.js @@ -14,6 +14,9 @@ import { getValidHashTab, replaceTabRoute } from "@/utils/hashRouteTabs.mjs"; import { ref, computed, onMounted, onUnmounted, reactive, watch } from "vue"; import { useRoute, useRouter } from "vue-router"; +// 安全地将 repo 字段转为小写字符串,防止非字符串类型导致 toLowerCase 报错 +const safeRepoStr = (repo) => (typeof repo === "string" ? repo : String(repo ?? "")).trim(); + const buildFailedPluginItems = (raw) => { return Object.entries(raw || {}).map(([dirName, info]) => { const detail = info && typeof info === "object" ? info : {}; @@ -619,12 +622,12 @@ export const useExtensionPage = () => { const findMarketPluginForExtension = (extension) => { if (!extension) return null; - const repo = normalizeInstallUrl(extension.repo).toLowerCase(); + const repo = safeRepoStr(normalizeInstallUrl(extension.repo)).toLowerCase(); return ( pluginMarketData.value.find( (plugin) => repo && - normalizeInstallUrl(plugin?.repo).toLowerCase() === repo, + safeRepoStr(normalizeInstallUrl(plugin?.repo)).toLowerCase() === repo, ) || pluginMarketData.value.find((plugin) => plugin.name === extension.name) || null @@ -640,14 +643,14 @@ export const useExtensionPage = () => { pluginMarketData.value.forEach((plugin) => { if (plugin.repo) { - onlinePluginsMap.set(plugin.repo.toLowerCase(), plugin); + onlinePluginsMap.set(safeRepoStr(plugin.repo).toLowerCase(), plugin); } onlinePluginsNameMap.set(plugin.name, plugin); }); const data = Array.isArray(extension_data?.data) ? extension_data.data : []; data.forEach((extension) => { - const repoKey = extension.repo?.toLowerCase(); + const repoKey = extension.repo ? safeRepoStr(extension.repo).toLowerCase() : undefined; const onlinePlugin = repoKey ? onlinePluginsMap.get(repoKey) : null; const onlinePluginByName = onlinePluginsNameMap.get(extension.name); const matchedPlugin = onlinePlugin || onlinePluginByName; @@ -1233,21 +1236,21 @@ export const useExtensionPage = () => { const checkAlreadyInstalled = () => { const data = Array.isArray(extension_data?.data) ? extension_data.data : []; - const installedRepos = new Set(data.map((ext) => ext.repo?.toLowerCase())); + const installedRepos = new Set(data.map((ext) => ext.repo ? safeRepoStr(ext.repo).toLowerCase() : undefined)); const installedNames = new Set( data.map((ext) => normalizeStr(ext.name).replace(/_/g, "-")), ); //统一格式,以防下面的匹配不生效 const installedByRepo = new Map( data .filter((ext) => ext.repo) - .map((ext) => [ext.repo.toLowerCase(), ext]), + .map((ext) => [safeRepoStr(ext.repo).toLowerCase(), ext]), ); const installedByName = new Map(data.map((ext) => [ext.name, ext])); for (let i = 0; i < pluginMarketData.value.length; i++) { const plugin = pluginMarketData.value[i]; const matchedInstalled = - (plugin.repo && installedByRepo.get(plugin.repo.toLowerCase())) || + (plugin.repo && installedByRepo.get(safeRepoStr(plugin.repo).toLowerCase())) || installedByName.get(plugin.name); // 兜底:市场源未提供字段时,回填本地已安装插件中的元数据,便于在市场页直接展示 @@ -1265,7 +1268,7 @@ export const useExtensionPage = () => { } plugin.installed = - installedRepos.has(plugin.repo?.toLowerCase()) || + installedRepos.has(plugin.repo ? safeRepoStr(plugin.repo).toLowerCase() : undefined) || installedNames.has(normalizeStr(plugin.name).replace(/_/g, "-")); //统一格式,防止匹配失败 } From c1e421b5d9696405d6e08ee4060fd508ae0136fa Mon Sep 17 00:00:00 2001 From: lingyun14 Date: Sat, 16 May 2026 16:46:00 +0800 Subject: [PATCH 3/4] =?UTF-8?q?fix:=20=E4=BD=BF=E7=94=A8=20normalizeInstal?= =?UTF-8?q?lUrl=20=E7=BB=9F=E4=B8=80=20repo=20=E5=AD=97=E6=AE=B5=E5=A4=84?= =?UTF-8?q?=E7=90=86?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../src/views/extension/useExtensionPage.js | 22 ++++++++++--------- 1 file changed, 12 insertions(+), 10 deletions(-) diff --git a/dashboard/src/views/extension/useExtensionPage.js b/dashboard/src/views/extension/useExtensionPage.js index 95cc54c222..73ad849770 100644 --- a/dashboard/src/views/extension/useExtensionPage.js +++ b/dashboard/src/views/extension/useExtensionPage.js @@ -14,8 +14,6 @@ import { getValidHashTab, replaceTabRoute } from "@/utils/hashRouteTabs.mjs"; import { ref, computed, onMounted, onUnmounted, reactive, watch } from "vue"; import { useRoute, useRouter } from "vue-router"; -// 安全地将 repo 字段转为小写字符串,防止非字符串类型导致 toLowerCase 报错 -const safeRepoStr = (repo) => (typeof repo === "string" ? repo : String(repo ?? "")).trim(); const buildFailedPluginItems = (raw) => { return Object.entries(raw || {}).map(([dirName, info]) => { @@ -622,12 +620,12 @@ export const useExtensionPage = () => { const findMarketPluginForExtension = (extension) => { if (!extension) return null; - const repo = safeRepoStr(normalizeInstallUrl(extension.repo)).toLowerCase(); + const repo = normalizeInstallUrl(extension.repo).toLowerCase(); return ( pluginMarketData.value.find( (plugin) => repo && - safeRepoStr(normalizeInstallUrl(plugin?.repo)).toLowerCase() === repo, + normalizeInstallUrl(plugin?.repo).toLowerCase() === repo, ) || pluginMarketData.value.find((plugin) => plugin.name === extension.name) || null @@ -643,14 +641,14 @@ export const useExtensionPage = () => { pluginMarketData.value.forEach((plugin) => { if (plugin.repo) { - onlinePluginsMap.set(safeRepoStr(plugin.repo).toLowerCase(), plugin); + onlinePluginsMap.set(normalizeInstallUrl(plugin.repo).toLowerCase(), plugin); } onlinePluginsNameMap.set(plugin.name, plugin); }); const data = Array.isArray(extension_data?.data) ? extension_data.data : []; data.forEach((extension) => { - const repoKey = extension.repo ? safeRepoStr(extension.repo).toLowerCase() : undefined; + const repoKey = extension.repo ? normalizeInstallUrl(extension.repo).toLowerCase() : undefined; const onlinePlugin = repoKey ? onlinePluginsMap.get(repoKey) : null; const onlinePluginByName = onlinePluginsNameMap.get(extension.name); const matchedPlugin = onlinePlugin || onlinePluginByName; @@ -1236,21 +1234,25 @@ export const useExtensionPage = () => { const checkAlreadyInstalled = () => { const data = Array.isArray(extension_data?.data) ? extension_data.data : []; - const installedRepos = new Set(data.map((ext) => ext.repo ? safeRepoStr(ext.repo).toLowerCase() : undefined)); + const installedRepos = new Set( + data + .filter((ext) => ext.repo) + .map((ext) => normalizeInstallUrl(ext.repo).toLowerCase()), + ); const installedNames = new Set( data.map((ext) => normalizeStr(ext.name).replace(/_/g, "-")), ); //统一格式,以防下面的匹配不生效 const installedByRepo = new Map( data .filter((ext) => ext.repo) - .map((ext) => [safeRepoStr(ext.repo).toLowerCase(), ext]), + .map((ext) => [normalizeInstallUrl(ext.repo).toLowerCase(), ext]), ); const installedByName = new Map(data.map((ext) => [ext.name, ext])); for (let i = 0; i < pluginMarketData.value.length; i++) { const plugin = pluginMarketData.value[i]; const matchedInstalled = - (plugin.repo && installedByRepo.get(safeRepoStr(plugin.repo).toLowerCase())) || + (plugin.repo && installedByRepo.get(normalizeInstallUrl(plugin.repo).toLowerCase())) || installedByName.get(plugin.name); // 兜底:市场源未提供字段时,回填本地已安装插件中的元数据,便于在市场页直接展示 @@ -1268,7 +1270,7 @@ export const useExtensionPage = () => { } plugin.installed = - installedRepos.has(plugin.repo ? safeRepoStr(plugin.repo).toLowerCase() : undefined) || + (plugin.repo && installedRepos.has(normalizeInstallUrl(plugin.repo).toLowerCase())) || installedNames.has(normalizeStr(plugin.name).replace(/_/g, "-")); //统一格式,防止匹配失败 } From 52e87443a5f66524c182c328e2d0dcd5bae58514 Mon Sep 17 00:00:00 2001 From: lingyun14 Date: Sat, 16 May 2026 17:06:44 +0800 Subject: [PATCH 4/4] =?UTF-8?q?fix:=E4=BC=98=E5=8C=96=20installedRepos=20?= =?UTF-8?q?=E6=9E=84=E5=BB=BA=E6=96=B9=E5=BC=8F?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- dashboard/src/views/extension/useExtensionPage.js | 11 +++-------- 1 file changed, 3 insertions(+), 8 deletions(-) diff --git a/dashboard/src/views/extension/useExtensionPage.js b/dashboard/src/views/extension/useExtensionPage.js index 73ad849770..8db1a9c33c 100644 --- a/dashboard/src/views/extension/useExtensionPage.js +++ b/dashboard/src/views/extension/useExtensionPage.js @@ -14,7 +14,6 @@ import { getValidHashTab, replaceTabRoute } from "@/utils/hashRouteTabs.mjs"; import { ref, computed, onMounted, onUnmounted, reactive, watch } from "vue"; import { useRoute, useRouter } from "vue-router"; - const buildFailedPluginItems = (raw) => { return Object.entries(raw || {}).map(([dirName, info]) => { const detail = info && typeof info === "object" ? info : {}; @@ -1234,19 +1233,15 @@ export const useExtensionPage = () => { const checkAlreadyInstalled = () => { const data = Array.isArray(extension_data?.data) ? extension_data.data : []; - const installedRepos = new Set( + const installedByRepo = new Map( data .filter((ext) => ext.repo) - .map((ext) => normalizeInstallUrl(ext.repo).toLowerCase()), + .map((ext) => [normalizeInstallUrl(ext.repo).toLowerCase(), ext]), ); + const installedRepos = new Set(installedByRepo.keys()); const installedNames = new Set( data.map((ext) => normalizeStr(ext.name).replace(/_/g, "-")), ); //统一格式,以防下面的匹配不生效 - const installedByRepo = new Map( - data - .filter((ext) => ext.repo) - .map((ext) => [normalizeInstallUrl(ext.repo).toLowerCase(), ext]), - ); const installedByName = new Map(data.map((ext) => [ext.name, ext])); for (let i = 0; i < pluginMarketData.value.length; i++) {