Skip to content
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
18 changes: 9 additions & 9 deletions backend/main.go
Original file line number Diff line number Diff line change
Expand Up @@ -191,10 +191,10 @@ func (c *catalogCache) fetchLocked() ([]byte, error) {
// raw.githubusercontent.com). The host UI enforces a `default-src 'self'`
// CSP that blocks those cross-origin image loads, so the backend rewrites
// every absolute icon URL in the catalog/summary responses to a *relative*
// path (e.g. `api/icon/<key>`). The frontend then prepends the plugin's
// runtime mount prefix (`/v1/plugins/<pluginName>`) — derived from the
// SDK-supplied pluginName — so the URL is always correct regardless of the
// release name the chart was installed under.
// path (e.g. `api/icon/<key>`). The frontend then prepends the host-provided
// proxy base (the SDK's `api.basePath`, i.e.
// `/v1/clusters/<cluster>/plugins/<pluginName>`) so the URL is always correct
// regardless of the cluster or the release name the chart was installed under.
//
// Keys are SHA-256 of the upstream URL: stable, opaque, and content-addressed
// by URL (no caller-supplied URL parameter, no SSRF surface). Only URLs that
Expand Down Expand Up @@ -463,8 +463,8 @@ func makeCatalogHandler(cache *catalogCache, icons *iconProxy) http.HandlerFunc
// - GET /v1/clusters/{cluster}/providers — Kubernetes-style list, one item
// per installed Provider CR. The catalog name lives at metadata.name,
// the chart version at metadata.labels["app.kubernetes.io/version"].
// - GET /v1/plugins — flat list of host-registered generic plugins. Catalog
// name lives at .name, version at .version.
// - GET /v1/clusters/{cluster}/plugins — flat list of host-registered generic
// plugins. Catalog name lives at .name, version at .version.
//
// We surface a non-fatal error if either call fails so the UI can still show
// partial state. A 404 on either endpoint is treated as "feature absent" and
Expand All @@ -482,7 +482,7 @@ func fetchInstalled(apiBase, cluster, authHeader string) ([]installedExtension,
} else {
all = append(all, providers...)
}
if plugins, err := fetchPlugins(apiBase, authHeader); err != nil {
if plugins, err := fetchPlugins(apiBase, cluster, authHeader); err != nil {
errs = append(errs, "plugins: "+err.Error())
} else {
all = append(all, plugins...)
Expand Down Expand Up @@ -524,8 +524,8 @@ func fetchProviders(apiBase, cluster, authHeader string) ([]installedExtension,
return out, nil
}

func fetchPlugins(apiBase, authHeader string) ([]installedExtension, error) {
body, err := everestGET(apiBase+"/v1/plugins", authHeader)
func fetchPlugins(apiBase, cluster, authHeader string) ([]installedExtension, error) {
body, err := everestGET(apiBase+"/v1/clusters/"+url.PathEscape(cluster)+"/plugins", authHeader)
if err != nil {
return nil, err
}
Expand Down
2 changes: 1 addition & 1 deletion package.json
Original file line number Diff line number Diff line change
Expand Up @@ -13,7 +13,7 @@
"react-dom": "^18.2.0"
},
"devDependencies": {
"@openeverest/plugin-sdk": "^0.1.0",
"@openeverest/plugin-sdk": "^0.3.0",
"@types/react": "^18.3.28",
"@vitejs/plugin-react-swc": "^4.3.3",
"typescript": "^5.2.2",
Expand Down
6 changes: 3 additions & 3 deletions src/components/Drawer.ts
Original file line number Diff line number Diff line change
Expand Up @@ -91,8 +91,8 @@ function useAppBarOffset(): number {
return offset;
}

export function Drawer(props: { entry: CatalogEntry; pluginName: string; onClose: () => void }): any {
const { entry, pluginName, onClose } = props;
export function Drawer(props: { entry: CatalogEntry; onClose: () => void }): any {
const { entry, onClose } = props;
const isGated = entry.access === 'gated';
const version = isGated ? null : defaultChannelVersion(entry);
const install = isGated ? null : helmInstallCommand(entry);
Expand All @@ -115,7 +115,7 @@ export function Drawer(props: { entry: CatalogEntry; pluginName: string; onClose
'div',
{ style: styles.drawerHeader },
h(IconImg, {
src: resolveIconSrc(entry.icon, pluginName),
src: resolveIconSrc(entry.icon),
style: { width: 40, height: 40 },
}),
h(
Expand Down
5 changes: 2 additions & 3 deletions src/components/Row.ts
Original file line number Diff line number Diff line change
Expand Up @@ -6,10 +6,9 @@ import type { CatalogEntry } from '../types';

export function Row(props: {
entry: CatalogEntry;
pluginName: string;
onSelect: (e: CatalogEntry) => void;
}): any {
const { entry, pluginName, onSelect } = props;
const { entry, onSelect } = props;
const version = defaultChannelVersion(entry);
return h(
'tr',
Expand All @@ -22,7 +21,7 @@ export function Row(props: {
'td',
{ style: { ...styles.td, ...styles.iconCell } },
h(IconImg, {
src: resolveIconSrc(entry.icon, pluginName),
src: resolveIconSrc(entry.icon),
style: styles.iconImg,
}),
),
Expand Down
15 changes: 7 additions & 8 deletions src/icons.ts
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
// Icon resolution + a self-healing <img> that falls back to an inline
// placeholder when an upstream icon fails to load.
import { h } from './runtime';
import { h, pluginBasePath } from './runtime';

export const ICON_FALLBACK_DATA_URI =
"data:image/svg+xml;utf8," +
Expand All @@ -13,11 +13,10 @@ export const ICON_FALLBACK_DATA_URI =
const failedIconSrcs = new Set<string>();

// resolveIconSrc converts whatever the backend put into entry.icon into a
// usable <img src>. The backend now emits *relative* paths (e.g.
// 'api/icon/<key>') for proxied icons; we prepend the plugin's runtime
// mount prefix here so the URL is correct regardless of the release name
// the chart was installed under.
export function resolveIconSrc(rawIcon: string | undefined, pluginName: string): string {
// usable <img src>. The backend emits *relative* paths (e.g. 'api/icon/<key>')
// for proxied icons; we prepend the host-provided proxy base so the URL routes
// through the OpenEverest proxy for the current cluster.
export function resolveIconSrc(rawIcon: string | undefined): string {
if (!rawIcon) return ICON_FALLBACK_DATA_URI;
if (
rawIcon.startsWith('data:') ||
Expand All @@ -27,8 +26,8 @@ export function resolveIconSrc(rawIcon: string | undefined, pluginName: string):
) {
return rawIcon;
}
if (!pluginName) return ICON_FALLBACK_DATA_URI;
return `/v1/plugins/${pluginName}/${rawIcon}`;
if (!pluginBasePath) return ICON_FALLBACK_DATA_URI;
return `${pluginBasePath}/${rawIcon}`;
}

export function IconImg(props: { src: string; alt?: string; style?: any }): any {
Expand Down
7 changes: 3 additions & 4 deletions src/main.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -15,7 +15,6 @@
import type {
PluginRegisterFn,
PluginApi,
PluginRouteProps,
} from '@openeverest/plugin-sdk';

import { React, h, initRuntime } from './runtime';
Expand All @@ -31,7 +30,7 @@ import type { CatalogEntry, FilterState, SummaryResponse } from './types';
// Page
// ---------------------------------------------------------------------------

const HubPage = (props: PluginRouteProps): any => {
const HubPage = (): any => {
const [data, setData] = React.useState<SummaryResponse | null>(null);
const [installedKeys, setInstalledKeys] = React.useState<Set<string> | null>(null);
const [installedError, setInstalledError] = React.useState<string | null>(null);
Expand Down Expand Up @@ -195,10 +194,10 @@ const HubPage = (props: PluginRouteProps): any => {
h('th', { style: styles.th }, 'Maturity'),
),
),
h('tbody', null, ...filtered.map((entry) => Row({ entry, pluginName: props.pluginName, onSelect: setSelected }))),
h('tbody', null, ...filtered.map((entry) => Row({ entry, onSelect: setSelected }))),
),

selected ? h(Drawer, { entry: selected, pluginName: props.pluginName, onClose: () => setSelected(null) }) : null,
selected ? h(Drawer, { entry: selected, onClose: () => setSelected(null) }) : null,
);
};

Expand Down
2 changes: 2 additions & 0 deletions src/runtime.ts
Original file line number Diff line number Diff line change
Expand Up @@ -7,10 +7,12 @@ import type { PluginApi } from '@openeverest/plugin-sdk';

export let React: PluginApi['React'];
export let pluginFetch: PluginApi['fetch'];
export let pluginBasePath: PluginApi['basePath'];

export function initRuntime(api: PluginApi): void {
React = api.React;
pluginFetch = api.fetch.bind(api);
pluginBasePath = api.basePath;
}

// h is a thin React.createElement wrapper so components can be authored
Expand Down
Loading