Skip to content

fix: resolve plugin market detail mix-up for same-named plugins - #9925

Open
Hayston1001 wants to merge 1 commit into
AstrBotDevs:masterfrom
Hayston1001:fix/9924-market-detail-same-name
Open

fix: resolve plugin market detail mix-up for same-named plugins#9925
Hayston1001 wants to merge 1 commit into
AstrBotDevs:masterfrom
Hayston1001:fix/9924-market-detail-same-name

Conversation

@Hayston1001

@Hayston1001 Hayston1001 commented Sep 2, 2026

Copy link
Copy Markdown

Motivation

Fixes #9924.

In the plugin market (WebUI), two entries can share the same metadata name (typically plugins published by different authors). The detail flow, however, used name as the unique key, so both entries resolved to the same detail page showing the first entry's info (version / author / repo / README / changelog), and duplicate v-for keys made the market list render incorrectly as well. Related but distinct from #9026 (installed-vs-market name collision) — this issue is market-entry vs market-entry.

Modifications

  • dashboard/src/views/extension/useExtensionPage.js
    • Add getMarketPluginKey(plugin) = market_plugin_id || repo || name as the unique identity of a market entry (three-level fallback so legacy registry entries without an explicit id still get a stable key), and expose it via the page state.
    • The random-plugins section now keys its sampling Map by the unique key instead of name, so same-named entries no longer collapse into one.
  • dashboard/src/views/extension/MarketPluginsTab.vue
    • openMarketPluginDetail() navigates with the unique key as pluginId instead of plugin.name.
    • Both v-for :keys (paginated list and random list) use the unique key, eliminating duplicate Vue keys.
  • dashboard/src/views/ExtensionPage.vue

Notes:

  • Route params containing / (e.g. author/name) are safe: vue-router 4.2.4 encodes / as %2F when building the path and decodes it back on parse (verified against the installed version at runtime).

  • No backend API/schema changes, so no API client regeneration is needed.

  • This is NOT a breaking change. / 这不是一个破坏性变更。

Screenshots or Test Results / 运行截图或测试结果

  • vite build passes (exit 0).
  • node --test tests/*.test.mjs → 39/39 pass.
  • Runtime round-trip test against vue-router 4.2.4: router.push({ params: { pluginId: "authorA/astrbot_plugin_foo" } }) → URL /extension/plugins/market/authorA%2Fastrbot_plugin_fooroute.params.pluginId === "authorA/astrbot_plugin_foo" (also for a second author authorB/... → distinct routes).

Verification steps for reviewers:

  1. Open the plugin market with a registry containing two entries with the same name but different author;
  2. Click each card → each detail page now shows its own entry's version/author/repo/README;
  3. Detail URLs are distinct (.../market/<authorA>%2F<name> vs .../market/<authorB>%2F<name>);
  4. Legacy deep link /extension/<name>#market still resolves to a same-named entry (name fallback).

Checklist / 检查清单

  • 😊 If there are new features added in the PR, I have discussed it with the authors through issues/emails, etc.
    / 如果 PR 中有新加入的功能,已经通过 Issue / 邮件等方式和作者讨论过。

  • 👀 My changes have been well-tested, and "Verification Steps" and "Screenshots" have been provided above.
    / 我的更改经过了良好的测试,并已在上方提供了"验证步骤"和"运行截图"

  • 🤓 I have ensured that no new dependencies are introduced, OR if new dependencies are introduced, they have been added to the appropriate locations in requirements.txt and pyproject.toml.
    / 我确保没有引入新依赖库,或者引入了新依赖库的同时将其添加到 requirements.txtpyproject.toml 文件相应位置。

  • 😮 My changes do not introduce malicious code.
    / 我的更改没有引入恶意代码。

Summary by Sourcery

Fix market plugin identity handling so entries with the same name remain distinct throughout listing, navigation, and detail views.

Bug Fixes:

  • Resolve market plugin details using a unique market-entry identity so same-named plugins from different authors display the correct information.
  • Preserve legacy name-based market deep links while keeping installed-plugin resolution behavior unchanged.

Enhancements:

  • Use unique market-entry identities for random plugin selection and Vue list keys to prevent same-named entries from collapsing or rendering with duplicate keys.

Tests:

  • Verify the dashboard build, automated tests, and route parameter round trips for distinct same-named market plugins.

Two market entries can share the same metadata `name` (different
authors), but the detail flow used `name` as the unique key:

- `openMarketPluginDetail` navigated with `plugin.name` as the route
  param, so both entries resolved to the same detail route;
- `selectedMarketPlugin` matched `item.name === pluginId`, always
  returning the first same-named entry (wrong version/author/repo/README);
- duplicate `v-for` keys (`plugin.name`) caused Vue to reuse wrong
  component instances in the market list;
- the random-plugins section keyed its sampling Map by `name`, collapsing
  same-named entries.

Introduce `getMarketPluginKey(plugin)` (`market_plugin_id || repo ||
name`) as the unique market identity, use it for navigation, list keys,
detail lookup (name kept as fallback for legacy deep links), and the
random-plugins sampling.

Fixes AstrBotDevs#9924

@sourcery-ai sourcery-ai Bot left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Hey - I've found 1 issue

Prompt for AI Agents
Please address the comments from this code review:

## Individual Comments

### Comment 1
<location path="dashboard/src/views/extension/useExtensionPage.js" line_range="691-700" />
<code_context>
     return String(plugin?.market_plugin_id || "").trim();
   };

+  // Unique identity for a market entry. Metadata `name` alone is not unique —
+  // different authors can publish plugins with the same name — so fall back to
+  // repo and finally name for legacy registry entries without an explicit id.
+  const getMarketPluginKey = (plugin) => {
+    return (
+      getMarketPluginId(plugin) ||
+      String(plugin?.repo || "").trim() ||
+      String(plugin?.name || "").trim()
+    );
+  };
+
   const getMarketInstallSourcePayload = () => {
</code_context>
<issue_to_address>
**issue (bug_risk):** `market_plugin_id || repo || name` is not a globally unique key because values from different fallback levels share the same namespace; an entry whose `repo` equals another entry's `market_plugin_id` or `name` produces the same key. Duplicate or empty fallback values also collapse entries in the random-plugin `Map`, create duplicate Vue keys, and cause detail lookup to select the first entry.

**Triggers:** When a registry contains entries with colliding fallback values, duplicate repository values, or entries missing all three identity fields.

**Suggested fix:** Prefix the selected identity with its source field (for example, `id:`, `repo:`, or `name:`), and define a deterministic handling strategy for entries whose resulting identity is still not unique.
</issue_to_address>

Sourcery assessment

Approval pending. 1 finding to address first.

Blocking findings: dashboard/src/views/extension/useExtensionPage.js:700


Sourcery is free for open source - if you like our reviews please consider sharing them ✨
Help me be more useful! Please click 👍 or 👎 on each comment and I'll use the feedback to improve your reviews.

Comment on lines +691 to +700
// Unique identity for a market entry. Metadata `name` alone is not unique —
// different authors can publish plugins with the same name — so fall back to
// repo and finally name for legacy registry entries without an explicit id.
const getMarketPluginKey = (plugin) => {
return (
getMarketPluginId(plugin) ||
String(plugin?.repo || "").trim() ||
String(plugin?.name || "").trim()
);
};

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

issue (bug_risk): market_plugin_id || repo || name is not a globally unique key because values from different fallback levels share the same namespace; an entry whose repo equals another entry's market_plugin_id or name produces the same key. Duplicate or empty fallback values also collapse entries in the random-plugin Map, create duplicate Vue keys, and cause detail lookup to select the first entry.

Triggers: When a registry contains entries with colliding fallback values, duplicate repository values, or entries missing all three identity fields.

Suggested fix: Prefix the selected identity with its source field (for example, id:, repo:, or name:), and define a deterministic handling strategy for entries whose resulting identity is still not unique.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

[Bug] 插件市场中两个 metadata name 相同的插件,详情页信息互相错乱

1 participant