feat(plugins): richer admin panel + registry detail view

Admin list: flush-left header like Addons, type/reviewed badges, runtime
health as a dot on the icon tile (text badge only for problem states),
description + source-repo link on installed cards, manifest icon.

Browse: cards show the plugin screenshot (docs/screenshot.png at the pinned
commit) and open a detail dialog fed by GET /api/admin/plugins/registry/:id —
live-manifest permissions in plain language, egress hosts, setup preview,
repo/homepage links. Manifest fetched server-side through safeDownload at the
reviewed commit, cached per plugin for 30 min and only when a detail opens.

Also fixes the update flow (restart the running child around the install,
keep the admin's enabled intent instead of force-activating disabled
plugins), guards the icon lookup against Object.prototype names, reserves
ids that would shadow static admin routes, stops negative-caching failed
manifest fetches, and makes the version compare prerelease-safe.
This commit is contained in:
Maurice
2026-07-03 23:31:28 +02:00
committed by Maurice
parent 17f3c86967
commit 1127700219
9 changed files with 526 additions and 66 deletions
+1
View File
@@ -462,6 +462,7 @@ export const adminApi = {
updateAddon: (id: number | string, data: Record<string, unknown>) => apiClient.put(`/admin/addons/${id}`, data).then(r => r.data),
plugins: () => apiClient.get('/admin/plugins').then(r => r.data),
pluginBrowse: () => apiClient.get('/admin/plugins/registry').then(r => r.data),
pluginDetail: (id: string) => apiClient.get(`/admin/plugins/registry/${encodeURIComponent(id)}`).then(r => r.data),
pluginInstall: (id: string, version?: string) => apiClient.post('/admin/plugins/install', { id, version }).then(r => r.data),
pluginActivate: (id: string) => apiClient.post(`/admin/plugins/${id}/activate`).then(r => r.data),
pluginDeactivate: (id: string) => apiClient.post(`/admin/plugins/${id}/deactivate`).then(r => r.data),