mirror of
https://github.com/coder/coder.git
synced 2026-09-24 15:04:27 +08:00
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:
@@ -9,6 +9,7 @@ import {
|
|||||||
import { ExternalImage } from "components/ExternalImage/ExternalImage";
|
import { ExternalImage } from "components/ExternalImage/ExternalImage";
|
||||||
import { InfoTooltip } from "components/InfoTooltip/InfoTooltip";
|
import { InfoTooltip } from "components/InfoTooltip/InfoTooltip";
|
||||||
import { Link } from "components/Link/Link";
|
import { Link } from "components/Link/Link";
|
||||||
|
import { useProxy } from "contexts/ProxyContext";
|
||||||
import { ChevronDownIcon, LayoutGridIcon } from "lucide-react";
|
import { ChevronDownIcon, LayoutGridIcon } from "lucide-react";
|
||||||
import { useAppLink } from "modules/apps/useAppLink";
|
import { useAppLink } from "modules/apps/useAppLink";
|
||||||
import type { Task } from "modules/tasks/tasks";
|
import type { Task } from "modules/tasks/tasks";
|
||||||
@@ -18,6 +19,7 @@ import { Link as RouterLink } from "react-router";
|
|||||||
import { cn } from "utils/cn";
|
import { cn } from "utils/cn";
|
||||||
import { docs } from "utils/docs";
|
import { docs } from "utils/docs";
|
||||||
import { TaskAppIFrame } from "./TaskAppIframe";
|
import { TaskAppIFrame } from "./TaskAppIframe";
|
||||||
|
import { TaskWildcardWarning } from "./TaskWildcardWarning";
|
||||||
|
|
||||||
type TaskAppsProps = {
|
type TaskAppsProps = {
|
||||||
task: Task;
|
task: Task;
|
||||||
@@ -29,6 +31,8 @@ type AppWithAgent = {
|
|||||||
};
|
};
|
||||||
|
|
||||||
export const TaskApps: FC<TaskAppsProps> = ({ task }) => {
|
export const TaskApps: FC<TaskAppsProps> = ({ task }) => {
|
||||||
|
const { proxy } = useProxy();
|
||||||
|
|
||||||
const agents = task.workspace.latest_build.resources
|
const agents = task.workspace.latest_build.resources
|
||||||
.flatMap((r) => r.agents)
|
.flatMap((r) => r.agents)
|
||||||
.filter((a) => !!a);
|
.filter((a) => !!a);
|
||||||
@@ -54,6 +58,10 @@ export const TaskApps: FC<TaskAppsProps> = ({ task }) => {
|
|||||||
embeddedApps[0]?.app.id,
|
embeddedApps[0]?.app.id,
|
||||||
);
|
);
|
||||||
|
|
||||||
|
const activeApp = embeddedApps.find(({ app }) => app.id === activeAppId)?.app;
|
||||||
|
const shouldDisplayWildcardWarning =
|
||||||
|
activeApp?.subdomain && !proxy.proxy?.wildcard_hostname;
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<main className="flex flex-col">
|
<main className="flex flex-col">
|
||||||
<div className="w-full flex items-center border-0 border-b border-border border-solid">
|
<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>
|
</div>
|
||||||
|
|
||||||
{embeddedApps.length > 0 ? (
|
{embeddedApps.length > 0 ? (
|
||||||
<div className="flex-1">
|
shouldDisplayWildcardWarning ? (
|
||||||
{embeddedApps.map(({ app }) => {
|
<div className="flex-1 flex flex-col items-center justify-center pb-4">
|
||||||
return (
|
<TaskWildcardWarning className="max-w-xl" />
|
||||||
|
</div>
|
||||||
|
) : (
|
||||||
|
<div className="flex-1">
|
||||||
|
{embeddedApps.map(({ app }) => (
|
||||||
<TaskAppIFrame
|
<TaskAppIFrame
|
||||||
key={app.id}
|
key={app.id}
|
||||||
active={activeAppId === app.id}
|
active={activeAppId === app.id}
|
||||||
app={app}
|
app={app}
|
||||||
task={task}
|
task={task}
|
||||||
/>
|
/>
|
||||||
);
|
))}
|
||||||
})}
|
</div>
|
||||||
</div>
|
)
|
||||||
) : (
|
) : (
|
||||||
<div className="mx-auto my-auto flex flex-col items-center">
|
<div className="mx-auto my-auto flex flex-col items-center">
|
||||||
<h3 className="font-medium text-content-primary text-base">
|
<h3 className="font-medium text-content-primary text-base">
|
||||||
|
|||||||
@@ -1,8 +1,10 @@
|
|||||||
import type { WorkspaceApp } from "api/typesGenerated";
|
import type { WorkspaceApp } from "api/typesGenerated";
|
||||||
import { Spinner } from "components/Spinner/Spinner";
|
import { Spinner } from "components/Spinner/Spinner";
|
||||||
|
import { useProxy } from "contexts/ProxyContext";
|
||||||
import type { Task } from "modules/tasks/tasks";
|
import type { Task } from "modules/tasks/tasks";
|
||||||
import type { FC } from "react";
|
import type { FC } from "react";
|
||||||
import { TaskAppIFrame } from "./TaskAppIframe";
|
import { TaskAppIFrame } from "./TaskAppIframe";
|
||||||
|
import { TaskWildcardWarning } from "./TaskWildcardWarning";
|
||||||
|
|
||||||
type TaskSidebarProps = {
|
type TaskSidebarProps = {
|
||||||
task: Task;
|
task: Task;
|
||||||
@@ -64,21 +66,29 @@ const getSidebarApp = (task: Task): [WorkspaceApp | null, SidebarAppStatus] => {
|
|||||||
};
|
};
|
||||||
|
|
||||||
export const TaskSidebar: FC<TaskSidebarProps> = ({ task }) => {
|
export const TaskSidebar: FC<TaskSidebarProps> = ({ task }) => {
|
||||||
|
const proxy = useProxy();
|
||||||
|
|
||||||
const [sidebarApp, sidebarAppStatus] = getSidebarApp(task);
|
const [sidebarApp, sidebarAppStatus] = getSidebarApp(task);
|
||||||
|
const shouldDisplayWildcardWarning =
|
||||||
|
sidebarApp?.subdomain && proxy.proxy?.preferredWildcardHostname === "";
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<aside className="flex flex-col h-full shrink-0 w-full">
|
<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
|
<TaskAppIFrame
|
||||||
active
|
active
|
||||||
key={sidebarApp.id}
|
key={sidebarApp.id}
|
||||||
app={sidebarApp}
|
app={sidebarApp}
|
||||||
task={task}
|
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">
|
<div className="flex-1 flex flex-col items-center justify-center">
|
||||||
<h3 className="m-0 font-medium text-content-primary text-base">
|
<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>
|
||||||
|
);
|
||||||
|
};
|
||||||
Reference in New Issue
Block a user