feat(site): display warnings in tasks page when wildcard is not configured (#19780)

Closes #19739 

This PR adds warnings to Task page to alert users when wildcard is not
configured.

Admin:
<img width="1871" height="1209" alt="image"
src="https://github.com/user-attachments/assets/01beb885-5493-41ed-a266-58609bee5b8b"
/>

Member:
<img width="1713" height="1204" alt="image"
src="https://github.com/user-attachments/assets/53c2bae5-5e0f-458c-b761-633399bdf2f0"
/>
This commit is contained in:
Kacper Sawicki
2025-09-19 15:49:34 +02:00
committed by GitHub
parent eb74d902bf
commit 0601cc8fa6
3 changed files with 82 additions and 11 deletions
+18 -6
View File
@@ -9,6 +9,7 @@ import {
import { ExternalImage } from "components/ExternalImage/ExternalImage";
import { InfoTooltip } from "components/InfoTooltip/InfoTooltip";
import { Link } from "components/Link/Link";
import { useProxy } from "contexts/ProxyContext";
import { ChevronDownIcon, LayoutGridIcon } from "lucide-react";
import { useAppLink } from "modules/apps/useAppLink";
import type { Task } from "modules/tasks/tasks";
@@ -18,6 +19,7 @@ import { Link as RouterLink } from "react-router";
import { cn } from "utils/cn";
import { docs } from "utils/docs";
import { TaskAppIFrame } from "./TaskAppIframe";
import { TaskWildcardWarning } from "./TaskWildcardWarning";
type TaskAppsProps = {
task: Task;
@@ -29,6 +31,8 @@ type AppWithAgent = {
};
export const TaskApps: FC<TaskAppsProps> = ({ task }) => {
const { proxy } = useProxy();
const agents = task.workspace.latest_build.resources
.flatMap((r) => r.agents)
.filter((a) => !!a);
@@ -54,6 +58,10 @@ export const TaskApps: FC<TaskAppsProps> = ({ task }) => {
embeddedApps[0]?.app.id,
);
const activeApp = embeddedApps.find(({ app }) => app.id === activeAppId)?.app;
const shouldDisplayWildcardWarning =
activeApp?.subdomain && !proxy.proxy?.wildcard_hostname;
return (
<main className="flex flex-col">
<div className="w-full flex items-center border-0 border-b border-border border-solid">
@@ -83,18 +91,22 @@ export const TaskApps: FC<TaskAppsProps> = ({ task }) => {
</div>
{embeddedApps.length > 0 ? (
<div className="flex-1">
{embeddedApps.map(({ app }) => {
return (
shouldDisplayWildcardWarning ? (
<div className="flex-1 flex flex-col items-center justify-center pb-4">
<TaskWildcardWarning className="max-w-xl" />
</div>
) : (
<div className="flex-1">
{embeddedApps.map(({ app }) => (
<TaskAppIFrame
key={app.id}
active={activeAppId === app.id}
app={app}
task={task}
/>
);
})}
</div>
))}
</div>
)
) : (
<div className="mx-auto my-auto flex flex-col items-center">
<h3 className="font-medium text-content-primary text-base">
+15 -5
View File
@@ -1,8 +1,10 @@
import type { WorkspaceApp } from "api/typesGenerated";
import { Spinner } from "components/Spinner/Spinner";
import { useProxy } from "contexts/ProxyContext";
import type { Task } from "modules/tasks/tasks";
import type { FC } from "react";
import { TaskAppIFrame } from "./TaskAppIframe";
import { TaskWildcardWarning } from "./TaskWildcardWarning";
type TaskSidebarProps = {
task: Task;
@@ -64,21 +66,29 @@ const getSidebarApp = (task: Task): [WorkspaceApp | null, SidebarAppStatus] => {
};
export const TaskSidebar: FC<TaskSidebarProps> = ({ task }) => {
const proxy = useProxy();
const [sidebarApp, sidebarAppStatus] = getSidebarApp(task);
const shouldDisplayWildcardWarning =
sidebarApp?.subdomain && proxy.proxy?.preferredWildcardHostname === "";
return (
<aside className="flex flex-col h-full shrink-0 w-full">
{sidebarAppStatus === "healthy" && sidebarApp ? (
{sidebarAppStatus === "loading" ? (
<div className="flex-1 flex flex-col items-center justify-center pb-4">
<Spinner loading />
</div>
) : shouldDisplayWildcardWarning ? (
<div className="flex-1 flex flex-col items-center justify-center pb-4">
<TaskWildcardWarning className="max-w-xl" />
</div>
) : sidebarAppStatus === "healthy" && sidebarApp ? (
<TaskAppIFrame
active
key={sidebarApp.id}
app={sidebarApp}
task={task}
/>
) : sidebarAppStatus === "loading" ? (
<div className="flex-1 flex flex-col items-center justify-center">
<Spinner loading className="mb-4" />
</div>
) : (
<div className="flex-1 flex flex-col items-center justify-center">
<h3 className="m-0 font-medium text-content-primary text-base">
@@ -0,0 +1,49 @@
import { Button } from "components/Button/Button";
import { useAuthenticated } from "hooks/useAuthenticated";
import { SquareArrowOutUpRightIcon } from "lucide-react";
import { Link as RouterLink } from "react-router";
import { cn } from "utils/cn";
import { docs } from "utils/docs";
type TaskWildcardWarningProps = {
className?: string;
};
export const TaskWildcardWarning = ({
className,
}: TaskWildcardWarningProps) => {
const { permissions } = useAuthenticated();
const canEditDeploymentConfig = Boolean(permissions.editDeploymentConfig);
return (
<div className={cn("text-center", className)}>
<h3 className="font-medium text-content-primary text-base mb-3">Error</h3>
<div className="text-content-secondary text-sm flex flex-col gap-3 items-center">
<div className="px-4">
This application has{" "}
<code className="py-px px-1 bg-surface-tertiary rounded-sm text-content-primary">
subdomain = true
</code>
{canEditDeploymentConfig ? (
<>
, but subdomain applications are not configured. This application
won't be accessible until you configure the{" "}
<code className="py-px px-1 bg-surface-tertiary rounded-sm text-content-primary whitespace-nowrap">
--wildcard-access-url
</code>{" "}
flag when starting the Coder server.
</>
) : (
", which requires a Coder deployment with a Wildcard Access URL configured. Please contact your administrator."
)}
</div>
<Button size="sm" variant="outline" asChild>
<RouterLink to={docs("/admin/networking/wildcard-access-url")}>
<SquareArrowOutUpRightIcon />
Learn more about wildcard access URL
</RouterLink>
</Button>
</div>
</div>
);
};