(
app: WorkspaceApp,
{ path, token, workspace, agent, host }: GetAppHrefParams,
)
| 113 | }; |
| 114 | |
| 115 | export const getAppHref = ( |
| 116 | app: WorkspaceApp, |
| 117 | { path, token, workspace, agent, host }: GetAppHrefParams, |
| 118 | ): string => { |
| 119 | if (isExternalApp(app)) { |
| 120 | const appProtocol = new URL(app.url).protocol; |
| 121 | const isAllowedProtocol = |
| 122 | ALLOWED_EXTERNAL_APP_PROTOCOLS.includes(appProtocol); |
| 123 | |
| 124 | return needsSessionToken(app) && isAllowedProtocol |
| 125 | ? app.url.replaceAll(SESSION_TOKEN_PLACEHOLDER, token ?? "") |
| 126 | : app.url; |
| 127 | } |
| 128 | |
| 129 | if (app.command) { |
| 130 | // Pass the app slug instead of the raw command. The terminal |
| 131 | // page resolves the command from the workspace agent's app |
| 132 | // list, which avoids exposing the command in the URL and |
| 133 | // lets us skip the confirmation dialog for trusted, |
| 134 | // admin-configured template apps. |
| 135 | return `/@${workspace.owner_name}/${workspace.name}.${ |
| 136 | agent.name |
| 137 | }/terminal?app=${encodeURIComponent(app.slug)}`; |
| 138 | } |
| 139 | |
| 140 | if (host && app.subdomain && app.subdomain_name) { |
| 141 | const baseUrl = `${location.protocol}//${host.replace(/\*/g, app.subdomain_name)}`; |
| 142 | const url = new URL(baseUrl); |
| 143 | url.pathname = "/"; |
| 144 | return url.toString(); |
| 145 | } |
| 146 | |
| 147 | // The backend redirects if the trailing slash isn't included, so we add it |
| 148 | // here to avoid extra roundtrips. |
| 149 | return `${path}/@${workspace.owner_name}/${workspace.name}.${ |
| 150 | agent.name |
| 151 | }/apps/${encodeURIComponent(app.slug)}/`; |
| 152 | }; |
| 153 | |
| 154 | type ExternalWorkspaceApp = WorkspaceApp & { |
| 155 | external: true; |
no test coverage detected