feat: use app wildcards for apps if configured (#4263)

* feat: use app wildcards for apps if configured

* feat: relative_path -> subdomain

- rename relative_path -> subdomain when referring to apps
    - migrate workspace_apps.relative_path to workspace_apps.subdomain
- upgrade coder/coder terraform module to 0.5.0
This commit is contained in:
Dean Sheather
2022-10-05 19:23:01 +00:00
committed by GitHub
parent 4f3958c831
commit 2a66395fb7
57 changed files with 577 additions and 505 deletions
+1
View File
@@ -603,6 +603,7 @@ export interface WorkspaceApp {
readonly name: string
readonly command?: string
readonly icon?: string
readonly subdomain: boolean
readonly healthcheck: Healthcheck
readonly health: WorkspaceAppHealth
}
@@ -11,7 +11,7 @@ const Template: Story<AppLinkProps> = (args) => <AppLink {...args} />
export const WithIcon = Template.bind({})
WithIcon.args = {
userName: "developer",
username: "developer",
workspaceName: MockWorkspace.name,
appName: "code-server",
appIcon: "/icon/code.svg",
@@ -20,7 +20,7 @@ WithIcon.args = {
export const WithoutIcon = Template.bind({})
WithoutIcon.args = {
userName: "developer",
username: "developer",
workspaceName: MockWorkspace.name,
appName: "code-server",
health: "healthy",
@@ -28,7 +28,7 @@ WithoutIcon.args = {
export const HealthDisabled = Template.bind({})
HealthDisabled.args = {
userName: "developer",
username: "developer",
workspaceName: MockWorkspace.name,
appName: "code-server",
health: "disabled",
@@ -36,7 +36,7 @@ HealthDisabled.args = {
export const HealthInitializing = Template.bind({})
HealthInitializing.args = {
userName: "developer",
username: "developer",
workspaceName: MockWorkspace.name,
appName: "code-server",
health: "initializing",
@@ -44,7 +44,7 @@ HealthInitializing.args = {
export const HealthUnhealthy = Template.bind({})
HealthUnhealthy.args = {
userName: "developer",
username: "developer",
workspaceName: MockWorkspace.name,
appName: "code-server",
health: "unhealthy",
+54 -25
View File
@@ -2,6 +2,7 @@ import Button from "@material-ui/core/Button"
import CircularProgress from "@material-ui/core/CircularProgress"
import Link from "@material-ui/core/Link"
import { makeStyles } from "@material-ui/core/styles"
import Tooltip from "@material-ui/core/Tooltip"
import ComputerIcon from "@material-ui/icons/Computer"
import ErrorOutlineIcon from "@material-ui/icons/ErrorOutline"
import { FC, PropsWithChildren } from "react"
@@ -13,68 +14,92 @@ export const Language = {
}
export interface AppLinkProps {
userName: TypesGen.User["username"]
appsHost?: string
username: TypesGen.User["username"]
workspaceName: TypesGen.Workspace["name"]
agentName: TypesGen.WorkspaceAgent["name"]
appName: TypesGen.WorkspaceApp["name"]
appIcon?: TypesGen.WorkspaceApp["icon"]
appCommand?: TypesGen.WorkspaceApp["command"]
appSubdomain: TypesGen.WorkspaceApp["subdomain"]
health: TypesGen.WorkspaceApp["health"]
}
export const AppLink: FC<PropsWithChildren<AppLinkProps>> = ({
userName,
appsHost,
username,
workspaceName,
agentName,
appName,
appIcon,
appCommand,
appSubdomain,
health,
}) => {
const styles = useStyles()
// The backend redirects if the trailing slash isn't included, so we add it
// here to avoid extra roundtrips.
let href = `/@${userName}/${workspaceName}.${agentName}/apps/${encodeURIComponent(appName)}/`
let href = `/@${username}/${workspaceName}.${agentName}/apps/${encodeURIComponent(appName)}/`
if (appCommand) {
href = `/@${userName}/${workspaceName}.${agentName}/terminal?command=${encodeURIComponent(
href = `/@${username}/${workspaceName}.${agentName}/terminal?command=${encodeURIComponent(
appCommand,
)}`
}
if (appsHost && appSubdomain) {
const subdomain = `${appName}--${agentName}--${workspaceName}--${username}`
href = `${window.location.protocol}//${subdomain}.${appsHost}/`
}
let canClick = true
let icon = appIcon ? <img alt={`${appName} Icon`} src={appIcon} /> : <ComputerIcon />
let tooltip = ""
if (health === "initializing") {
canClick = false
icon = <CircularProgress size={16} />
tooltip = "Initializing..."
}
if (health === "unhealthy") {
canClick = false
icon = <ErrorOutlineIcon className={styles.unhealthyIcon} />
tooltip = "Unhealthy"
}
if (!appsHost && appSubdomain) {
canClick = false
icon = <ErrorOutlineIcon className={styles.notConfiguredIcon} />
tooltip = "Your admin has not configured subdomain application access"
}
const button = (
<Button size="small" startIcon={icon} className={styles.button} disabled={!canClick}>
{appName}
</Button>
)
return (
<Link
href={href}
target="_blank"
className={canClick ? styles.link : styles.disabledLink}
onClick={
canClick
? (event) => {
event.preventDefault()
window.open(
href,
Language.appTitle(appName, generateRandomString(12)),
"width=900,height=600",
)
}
: undefined
}
>
<Button size="small" startIcon={icon} className={styles.button} disabled={!canClick}>
{appName}
</Button>
</Link>
<Tooltip title={tooltip}>
<span>
<Link
href={href}
target="_blank"
className={canClick ? styles.link : styles.disabledLink}
onClick={
canClick
? (event) => {
event.preventDefault()
window.open(
href,
Language.appTitle(appName, generateRandomString(12)),
"width=900,height=600",
)
}
: undefined
}
>
{button}
</Link>
</span>
</Tooltip>
)
}
@@ -95,4 +120,8 @@ const useStyles = makeStyles((theme) => ({
unhealthyIcon: {
color: theme.palette.warning.light,
},
notConfiguredIcon: {
color: theme.palette.grey[300],
},
}))
+3 -1
View File
@@ -175,10 +175,12 @@ export const Resources: FC<React.PropsWithChildren<ResourcesProps>> = ({
{agent.apps.map((app) => (
<AppLink
key={app.name}
appsHost={applicationsHost}
appIcon={app.icon}
appName={app.name}
appCommand={app.command}
userName={workspace.owner_name}
appSubdomain={app.subdomain}
username={workspace.owner_name}
workspaceName={workspace.name}
agentName={agent.name}
health={app.health}
+1
View File
@@ -193,6 +193,7 @@ export const MockWorkspaceApp: TypesGen.WorkspaceApp = {
id: "test-app",
name: "test-app",
icon: "",
subdomain: false,
health: "disabled",
healthcheck: {
url: "",