diff --git a/apps/docs/src/components/workshop/DetailPage.tsx b/apps/docs/src/components/workshop/DetailPage.tsx index 137e22a19..6eba7aa54 100644 --- a/apps/docs/src/components/workshop/DetailPage.tsx +++ b/apps/docs/src/components/workshop/DetailPage.tsx @@ -32,6 +32,7 @@ import type { WorkshopSubmission, WorkshopVote } from "@site/src/lib/pocketbase" import { getWorkshopBackend } from "@site/src/lib/pocketbase"; import { getRuntimeWorkshopApiUrl } from "@site/src/lib/runtime-config"; import type { SubmissionType } from "@site/src/lib/workshop-schema"; +import { integrationDefs } from "@homarr/definitions"; import type { HomarrCustomWidgetV2 } from "@homarr/custom-widgets/core"; import { githubAvatarUrl, @@ -96,7 +97,11 @@ const isNotFound = (caught: unknown) => const avatarFallback = (name: string) => name.trim().slice(0, 1).toUpperCase() || "?"; -const sourceHost = (baseUrl: string) => { +type WidgetSource = HomarrCustomWidgetV2["sources"][string]; + +const sourceHost = (source: WidgetSource) => { + if (source.type === "integration") return `${integrationDefs[source.integrationKind].name} integration`; + const { baseUrl } = source; try { return new URL(baseUrl).host; } catch { @@ -104,26 +109,30 @@ const sourceHost = (baseUrl: string) => { } }; -const sourceAuthLabel = (auth: HomarrCustomWidgetV2["sources"][string]["auth"]) => { - if (typeof auth === "string") - return auth === "none" ? "No credentials" : auth === "basic" ? "Basic auth" : "Bearer token"; - return auth.type === "apiKeyHeader" ? `API key header · ${auth.name}` : `API key query · ${auth.name}`; +const sourceAuthLabel = (source: WidgetSource) => { + if (source.type === "integration") return "Uses stored integration credentials"; + const { auth } = source; + if (auth === "none") return "No credentials"; + if (auth === "basic") return "Basic auth"; + if (auth === "bearer") return "Bearer token"; + if (auth.type === "apiKeyHeader") return `API key header · ${auth.name}`; + return `API key query · ${auth.name}`; }; -const sourceNeedsSecret = (auth: HomarrCustomWidgetV2["sources"][string]["auth"]) => auth !== "none"; +const sourceNeedsSecret = (source: WidgetSource) => source.type === "integration" || source.auth !== "none"; const WidgetSafetySummary = ({ widget }: { widget: HomarrCustomWidgetV2 }) => { const sources = Object.entries(widget.sources); const requests = Object.values(widget.requests); - const protectedSources = sources.filter(([, source]) => sourceNeedsSecret(source.auth)); + const protectedSources = sources.filter(([, source]) => sourceNeedsSecret(source)); const queryCount = requests.filter((request) => request.kind === "query").length; const actionCount = requests.filter((request) => request.kind === "action").length; const credentialLabel = protectedSources.length === 0 ? "None required" : protectedSources.length === 1 - ? sourceAuthLabel(protectedSources[0][1].auth) - : `${protectedSources.length} credentials required`; + ? sourceAuthLabel(protectedSources[0][1]) + : `${protectedSources.length} authenticated sources`; return (
{
{sources.slice(0, 2).map(([id, source]) => ( - {sourceHost(source.baseUrl)} + {sourceHost(source)} - {source.networkScope} + {source.networkScope ?? "integration"} ))} @@ -536,7 +545,7 @@ const MarketplaceDetail = ({ workshopUrl }: { workshopUrl: string }) => { const sources = widgetDefinition ? Object.entries(widgetDefinition.sources) : []; const requests = widgetDefinition ? Object.entries(widgetDefinition.requests) : []; const options = widgetDefinition ? Object.entries(widgetDefinition.options) : []; - const protectedSources = sources.filter(([, source]) => sourceNeedsSecret(source.auth)); + const protectedSources = sources.filter(([, source]) => sourceNeedsSecret(source)); const displayedContent = formatWorkshopContent(submission.type, submission.content); const socialTitle = `${submission.title} · Homarr Workshop`; const socialDescription = `${typeSocialLabels[submission.type]} for Homarr. ${submission.description}`.trim(); @@ -779,11 +788,11 @@ const MarketplaceDetail = ({ workshopUrl }: { workshopUrl: string }) => {
{source.name || id} - {source.networkScope} + {source.networkScope ?? "integration"}
-

{sourceHost(source.baseUrl)}

-

{sourceAuthLabel(source.auth)}

+

{sourceHost(source)}

+

{sourceAuthLabel(source)}

))}