Skip to content

Commit a9442d2

Browse files
committed
fix(plugin): address remaining README/changelog review findings
- Guard fetchChangelog with a request id + plugin identity check so a slow changelog response for plugin A can no longer overwrite plugin B's page (same race class that was fixed for the README fetch) - Skip the duplicate README fetch triggered when the plugin-switch watcher resets readmeLocale back to the default - Return the originally declared locale key (e.g. zh-CN) as resolved_locale instead of the normalized lowercase tag, keeping it consistent with available_locales and the documented format - Also catch ValueError when resolving declared README paths so an entry with an embedded NUL byte degrades gracefully instead of failing the request
1 parent 51ecf5c commit a9442d2

2 files changed

Lines changed: 36 additions & 6 deletions

File tree

‎astrbot/dashboard/services/plugin_service.py‎

Lines changed: 7 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1978,7 +1978,8 @@ def _resolve_declared_readme(plugin_dir: Path, filename: str) -> Path | None:
19781978
try:
19791979
resolved = (plugin_dir / relative).resolve(strict=True)
19801980
root = plugin_dir.resolve(strict=True)
1981-
except (OSError, RuntimeError):
1981+
except (OSError, RuntimeError, ValueError):
1982+
# ValueError covers paths with embedded NUL bytes.
19821983
return None
19831984
if not resolved.is_file():
19841985
return None
@@ -2018,8 +2019,10 @@ def get_plugin_readme(
20182019
declared = self._get_plugin_readmes(plugin_name)
20192020
available_locales = [key for key in declared if key != "default"]
20202021
requested = self._normalize_locale(locale)
2022+
# Map the normalized tag to the originally declared locale key so the
2023+
# resolved locale matches the spelling used in available_locales.
20212024
locale_paths = {
2022-
self._normalize_locale(key): value
2025+
self._normalize_locale(key): (key, value)
20232026
for key, value in declared.items()
20242027
if key != "default"
20252028
}
@@ -2034,7 +2037,8 @@ def get_plugin_readme(
20342037
if base_language in locale_paths:
20352038
matched_locale = base_language
20362039
if matched_locale:
2037-
candidates.append((matched_locale, locale_paths[matched_locale]))
2040+
declared_locale, path = locale_paths[matched_locale]
2041+
candidates.append((declared_locale, path))
20382042
candidates.append((None, declared.get("default") or "README.md"))
20392043

20402044
readme_path = None

‎dashboard/src/views/extension/PluginDetailPage.vue‎

Lines changed: 29 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -98,6 +98,10 @@ const readmeLocale = ref("");
9898
const readmeAvailableLocales = ref([]);
9999
let readmeRequestId = 0;
100100
let detailRequestId = 0;
101+
let changelogRequestId = 0;
102+
// Set when the plugin-switch watcher resets readmeLocale, so the locale
103+
// watcher skips the fetch that the plugin-switch flow already performs.
104+
let skipNextLocaleWatch = false;
101105
const readmeLocaleItems = computed(() => [
102106
{ title: `${tm("detail.readmeLanguageAuto")} (${locale.value})`, value: "" },
103107
...readmeAvailableLocales.value.map((item) => ({ title: item, value: item })),
@@ -805,7 +809,14 @@ const fetchReadme = async () => {
805809
806810
const fetchChangelog = async () => {
807811
const plugin = pluginData.value || {};
808-
if (!plugin?.name) return;
812+
const pluginName = plugin.name;
813+
if (!pluginName) return;
814+
815+
const requestId = ++changelogRequestId;
816+
// A response is stale when a newer changelog request started or the user
817+
// has switched to another plugin since this request began.
818+
const isCurrent = () =>
819+
requestId === changelogRequestId && props.plugin?.name === pluginName;
809820
810821
changelogLoading.value = true;
811822
changelogError.value = "";
@@ -822,21 +833,26 @@ const fetchChangelog = async () => {
822833
823834
try {
824835
const content = await fetchRemoteMarkdown(changelogUrl);
836+
if (!isCurrent()) return;
825837
if (!content.trim()) {
826838
changelogEmpty.value = true;
827839
return;
828840
}
829841
renderedChangelog.value = renderMarkdown(content);
830842
} catch (err) {
843+
if (!isCurrent()) return;
831844
changelogError.value = err?.message || String(err);
832845
} finally {
833-
changelogLoading.value = false;
846+
if (isCurrent()) {
847+
changelogLoading.value = false;
848+
}
834849
}
835850
return;
836851
}
837852
838853
try {
839854
const res = await pluginApi.changelog(plugin.name);
855+
if (!isCurrent()) return;
840856
841857
if (res.data.status !== "ok") {
842858
changelogError.value = res.data.message || tm("messages.operationFailed");
@@ -851,9 +867,12 @@ const fetchChangelog = async () => {
851867
852868
renderedChangelog.value = renderMarkdown(content);
853869
} catch (err) {
870+
if (!isCurrent()) return;
854871
changelogError.value = err?.message || String(err);
855872
} finally {
856-
changelogLoading.value = false;
873+
if (isCurrent()) {
874+
changelogLoading.value = false;
875+
}
857876
}
858877
};
859878
@@ -874,6 +893,9 @@ watch(
874893
],
875894
async () => {
876895
logoLoadFailed.value = false;
896+
// The locale watcher below would fire on this reset and duplicate the
897+
// README fetch that this flow performs after the detail request.
898+
skipNextLocaleWatch = readmeLocale.value !== "";
877899
readmeLocale.value = "";
878900
const isCurrent = await fetchPluginDetail();
879901
// The plugin selection changed while the detail request was pending;
@@ -888,6 +910,10 @@ watch(
888910
889911
// Follow the WebUI language until the user picks a README variant explicitly.
890912
watch([() => locale.value, readmeLocale], () => {
913+
if (skipNextLocaleWatch) {
914+
skipNextLocaleWatch = false;
915+
return;
916+
}
891917
fetchReadme();
892918
});
893919

0 commit comments

Comments
 (0)