Fix base path
Now it should work whether you have a trailing slash or not.
This commit is contained in:
parent
cc79edb312
commit
bf1be16d11
|
@ -1,3 +1,4 @@
|
||||||
|
import { field, logger } from "@coder/logger"
|
||||||
import { getBasepath, navigate, setBasepath } from "hookrouter"
|
import { getBasepath, navigate, setBasepath } from "hookrouter"
|
||||||
import * as React from "react"
|
import * as React from "react"
|
||||||
import { Application, isExecutableApplication } from "../common/api"
|
import { Application, isExecutableApplication } from "../common/api"
|
||||||
|
@ -14,16 +15,25 @@ interface RedirectedApplication extends Application {
|
||||||
redirected?: boolean
|
redirected?: boolean
|
||||||
}
|
}
|
||||||
|
|
||||||
const origin = typeof window !== "undefined" ? window.location.origin + window.location.pathname : undefined
|
let resolved = false
|
||||||
|
|
||||||
const App: React.FunctionComponent<AppProps> = (props) => {
|
const App: React.FunctionComponent<AppProps> = (props) => {
|
||||||
const [authed, setAuthed] = React.useState<boolean>(props.options.authed)
|
const [authed, setAuthed] = React.useState<boolean>(props.options.authed)
|
||||||
const [app, setApp] = React.useState<RedirectedApplication | undefined>(props.options.app)
|
const [app, setApp] = React.useState<RedirectedApplication | undefined>(props.options.app)
|
||||||
const [error, setError] = React.useState<HttpError | Error | string>()
|
const [error, setError] = React.useState<HttpError | Error | string>()
|
||||||
|
|
||||||
if (typeof window !== "undefined") {
|
if (!resolved && typeof document !== "undefined") {
|
||||||
const url = new URL(origin + props.options.basePath)
|
// Get the base path. We need the full URL for connecting the web socket.
|
||||||
|
// Use the path name plus the provided base path. For example:
|
||||||
|
// foo.com/base + ./ => foo.com/base
|
||||||
|
// foo.com/base/ + ./ => foo.com/base
|
||||||
|
// foo.com/base/bar + ./ => foo.com/base
|
||||||
|
// foo.com/base/bar/ + ./../ => foo.com/base
|
||||||
|
const parts = window.location.pathname.replace(/^\//g, "").split("/")
|
||||||
|
parts[parts.length - 1] = props.options.basePath
|
||||||
|
const url = new URL(window.location.origin + "/" + parts.join("/"))
|
||||||
setBasepath(normalize(url.pathname))
|
setBasepath(normalize(url.pathname))
|
||||||
|
logger.debug("resolved base path", field("base", getBasepath()))
|
||||||
|
resolved = true
|
||||||
|
|
||||||
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
||||||
;(window as any).setAuthed = (a: boolean): void => {
|
;(window as any).setAuthed = (a: boolean): void => {
|
||||||
|
|
|
@ -117,8 +117,9 @@ interface ProviderRoute extends Route {
|
||||||
|
|
||||||
export interface HttpProviderOptions {
|
export interface HttpProviderOptions {
|
||||||
readonly auth: AuthType
|
readonly auth: AuthType
|
||||||
readonly password?: string
|
readonly base: string
|
||||||
readonly commit: string
|
readonly commit: string
|
||||||
|
readonly password?: string
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
@ -150,10 +151,16 @@ export abstract class HttpProvider {
|
||||||
public abstract handleRequest(route: Route, request: http.IncomingMessage): Promise<HttpResponse | undefined>
|
public abstract handleRequest(route: Route, request: http.IncomingMessage): Promise<HttpResponse | undefined>
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Get the base relative to the provided route.
|
* Get the base relative to the provided route. For each slash we need to go
|
||||||
|
* up a directory. For example:
|
||||||
|
* / => ./
|
||||||
|
* /foo => ./
|
||||||
|
* /foo/ => ./../
|
||||||
|
* /foo/bar => ./../
|
||||||
|
* /foo/bar/ => ./../../
|
||||||
*/
|
*/
|
||||||
public base(route: Route): string {
|
public base(route: Route): string {
|
||||||
const depth = ((route.fullPath + "/").match(/\//g) || []).length
|
const depth = (route.originalPath.match(/\//g) || []).length
|
||||||
return normalize("./" + (depth > 1 ? "../".repeat(depth - 1) : ""))
|
return normalize("./" + (depth > 1 ? "../".repeat(depth - 1) : ""))
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -403,6 +410,7 @@ export class HttpServer {
|
||||||
new provider(
|
new provider(
|
||||||
{
|
{
|
||||||
auth: this.options.auth || AuthType.None,
|
auth: this.options.auth || AuthType.None,
|
||||||
|
base: `/${endpoint}`,
|
||||||
commit: this.options.commit,
|
commit: this.options.commit,
|
||||||
password: this.options.password,
|
password: this.options.password,
|
||||||
},
|
},
|
||||||
|
@ -510,11 +518,6 @@ export class HttpServer {
|
||||||
return { redirect: redirect(route.fullPath) }
|
return { redirect: redirect(route.fullPath) }
|
||||||
}
|
}
|
||||||
|
|
||||||
// Redirect our indexes to a trailing slash so relative paths in the served
|
|
||||||
// HTML will operate against the base path properly.
|
|
||||||
if (route.requestPath === "/index.html" && !route.originalPath.endsWith("/") && this.providers.has(route.base)) {
|
|
||||||
return { redirect: redirect(route.fullPath + "/") }
|
|
||||||
}
|
|
||||||
return undefined
|
return undefined
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -572,7 +575,7 @@ export class HttpServer {
|
||||||
}
|
}
|
||||||
|
|
||||||
const parsedUrl = request.url ? url.parse(request.url, true) : { query: {}, pathname: "" }
|
const parsedUrl = request.url ? url.parse(request.url, true) : { query: {}, pathname: "" }
|
||||||
const originalPath = parsedUrl.pathname || ""
|
const originalPath = parsedUrl.pathname || "/"
|
||||||
const fullPath = normalize(originalPath)
|
const fullPath = normalize(originalPath)
|
||||||
const { base, requestPath } = parse(fullPath)
|
const { base, requestPath } = parse(fullPath)
|
||||||
|
|
||||||
|
|
|
@ -199,6 +199,8 @@ export class VscodeHttpProvider extends HttpProvider {
|
||||||
...response,
|
...response,
|
||||||
content: response.content
|
content: response.content
|
||||||
.replace(/{{COMMIT}}/g, options.commit)
|
.replace(/{{COMMIT}}/g, options.commit)
|
||||||
|
.replace(/{{BASE}}/g, this.base(route))
|
||||||
|
.replace(/{{VS_BASE}}/g, this.base(route) + this.options.base)
|
||||||
.replace(`"{{REMOTE_USER_DATA_URI}}"`, `'${JSON.stringify(options.remoteUserDataUri)}'`)
|
.replace(`"{{REMOTE_USER_DATA_URI}}"`, `'${JSON.stringify(options.remoteUserDataUri)}'`)
|
||||||
.replace(`"{{PRODUCT_CONFIGURATION}}"`, `'${JSON.stringify(options.productConfiguration)}'`)
|
.replace(`"{{PRODUCT_CONFIGURATION}}"`, `'${JSON.stringify(options.productConfiguration)}'`)
|
||||||
.replace(`"{{WORKBENCH_WEB_CONFIGURATION}}"`, `'${JSON.stringify(options.workbenchWebConfiguration)}'`)
|
.replace(`"{{WORKBENCH_WEB_CONFIGURATION}}"`, `'${JSON.stringify(options.workbenchWebConfiguration)}'`)
|
||||||
|
|
|
@ -19,28 +19,27 @@
|
||||||
<meta id="vscode-remote-nls-configuration" data-settings="{{NLS_CONFIGURATION}}">
|
<meta id="vscode-remote-nls-configuration" data-settings="{{NLS_CONFIGURATION}}">
|
||||||
|
|
||||||
<!-- Workbench Icon/Manifest/CSS -->
|
<!-- Workbench Icon/Manifest/CSS -->
|
||||||
<link rel="icon" href="../static-{{COMMIT}}/src/browser/media/favicon.ico" type="image/x-icon" />
|
<link rel="icon" href="{{BASE}}/static-{{COMMIT}}/src/browser/media/favicon.ico" type="image/x-icon" />
|
||||||
<link rel="manifest" href="../static-{{COMMIT}}/src/browser/media/manifest.json" crossorigin="use-credentials">
|
<link rel="manifest" href="{{BASE}}/static-{{COMMIT}}/src/browser/media/manifest.json" crossorigin="use-credentials">
|
||||||
<link data-name="vs/workbench/workbench.web.api" rel="stylesheet" href="./static-{{COMMIT}}/out/vs/workbench/workbench.web.api.css">
|
<link data-name="vs/workbench/workbench.web.api" rel="stylesheet" href="{{VS_BASE}}/static-{{COMMIT}}/out/vs/workbench/workbench.web.api.css">
|
||||||
<link rel="apple-touch-icon" href="../static-{{COMMIT}}/src/browser/media/code-server.png" />
|
<link rel="apple-touch-icon" href="{{BASE}}/static-{{COMMIT}}/src/browser/media/code-server.png" />
|
||||||
<meta name="apple-mobile-web-app-capable" content="yes">
|
<meta name="apple-mobile-web-app-capable" content="yes">
|
||||||
|
|
||||||
<!-- Prefetch to avoid waterfall -->
|
<!-- Prefetch to avoid waterfall -->
|
||||||
<link rel="prefetch" href="./static-{{COMMIT}}/node_modules/semver-umd/lib/semver-umd.js">
|
<link rel="prefetch" href="{{BASE}}/static-{{COMMIT}}/node_modules/semver-umd/lib/semver-umd.js">
|
||||||
</head>
|
</head>
|
||||||
|
|
||||||
<body aria-label="">
|
<body aria-label="">
|
||||||
</body>
|
</body>
|
||||||
|
|
||||||
<!-- Startup (do not modify order of script tags!) -->
|
<!-- Startup (do not modify order of script tags!) -->
|
||||||
<!-- NOTE:coder: Modified to work against the current path and use the commit for caching. -->
|
|
||||||
<script>
|
<script>
|
||||||
// NOTE: Changes to inline scripts require update of content security policy
|
const parts = window.location.pathname.replace(/^\//g, "").split("/")
|
||||||
const basePath = window.location.pathname.replace(/\/+$/, '');
|
parts[parts.length - 1] = "{{VS_BASE}}"
|
||||||
const base = window.location.origin + basePath;
|
const url = new URL(window.location.origin + "/" + parts.join("/"))
|
||||||
const el = document.getElementById('vscode-remote-commit');
|
const el = document.getElementById('vscode-remote-commit');
|
||||||
const commit = el ? el.getAttribute('data-settings') : "";
|
const commit = el ? el.getAttribute('data-settings') : "";
|
||||||
const staticBase = base + '/static-' + commit;
|
const staticBase = url.href + '/static-' + commit;
|
||||||
let nlsConfig;
|
let nlsConfig;
|
||||||
try {
|
try {
|
||||||
nlsConfig = JSON.parse(document.getElementById('vscode-remote-nls-configuration').getAttribute('data-settings'));
|
nlsConfig = JSON.parse(document.getElementById('vscode-remote-nls-configuration').getAttribute('data-settings'));
|
||||||
|
@ -54,7 +53,7 @@
|
||||||
// FIXME: Only works if path separators are /.
|
// FIXME: Only works if path separators are /.
|
||||||
const path = nlsConfig._resolvedLanguagePackCoreLocation
|
const path = nlsConfig._resolvedLanguagePackCoreLocation
|
||||||
+ '/' + bundle.replace(/\//g, '!') + '.nls.json';
|
+ '/' + bundle.replace(/\//g, '!') + '.nls.json';
|
||||||
fetch(`${base}/resource/?path=${encodeURIComponent(path)}`)
|
fetch(`${url.href}/resource/?path=${encodeURIComponent(path)}`)
|
||||||
.then((response) => response.json())
|
.then((response) => response.json())
|
||||||
.then((json) => {
|
.then((json) => {
|
||||||
bundles[bundle] = json;
|
bundles[bundle] = json;
|
||||||
|
@ -78,15 +77,14 @@
|
||||||
'vs/nls': nlsConfig,
|
'vs/nls': nlsConfig,
|
||||||
};
|
};
|
||||||
</script>
|
</script>
|
||||||
<script src="./static-{{COMMIT}}/out/vs/loader.js"></script>
|
<script src="{{VS_BASE}}/static-{{COMMIT}}/out/vs/loader.js"></script>
|
||||||
<script src="./static-{{COMMIT}}/out/vs/workbench/workbench.web.api.nls.js"></script>
|
<script src="{{VS_BASE}}/static-{{COMMIT}}/out/vs/workbench/workbench.web.api.nls.js"></script>
|
||||||
<script src="./static-{{COMMIT}}/out/vs/workbench/workbench.web.api.js"></script>
|
<script src="{{VS_BASE}}/static-{{COMMIT}}/out/vs/workbench/workbench.web.api.js"></script>
|
||||||
<!-- TODO@coder: This errors with multiple anonymous define calls (one is
|
<!-- TODO@coder: This errors with multiple anonymous define calls (one is
|
||||||
workbench.js and one is semver-umd.js). For now use the same method found in
|
workbench.js and one is semver-umd.js). For now use the same method found in
|
||||||
workbench-dev.html. Appears related to the timing of the script load events. -->
|
workbench-dev.html. Appears related to the timing of the script load events. -->
|
||||||
<!-- <script src="./static-{{COMMIT}}/out/vs/workbench/workbench.js"></script> -->
|
<!-- <script src="{{VS_BASE}}/static-{{COMMIT}}/out/vs/workbench/workbench.js"></script> -->
|
||||||
<script>
|
<script>
|
||||||
// NOTE: Changes to inline scripts require update of content security policy
|
|
||||||
require(['vs/code/browser/workbench/workbench'], function() {});
|
require(['vs/code/browser/workbench/workbench'], function() {});
|
||||||
</script>
|
</script>
|
||||||
</html>
|
</html>
|
||||||
|
|
|
@ -19,9 +19,9 @@
|
||||||
<meta id="vscode-remote-nls-configuration" data-settings="{{NLS_CONFIGURATION}}">
|
<meta id="vscode-remote-nls-configuration" data-settings="{{NLS_CONFIGURATION}}">
|
||||||
|
|
||||||
<!-- Workbench Icon/Manifest/CSS -->
|
<!-- Workbench Icon/Manifest/CSS -->
|
||||||
<link rel="icon" href="../static/src/browser/media/favicon.ico" type="image/x-icon" />
|
<link rel="icon" href="{{BASE}}/static/src/browser/media/favicon.ico" type="image/x-icon" />
|
||||||
<link rel="manifest" href="../static/src/browser/media/manifest.json" crossorigin="use-credentials">
|
<link rel="manifest" href="{{BASE}}/static/src/browser/media/manifest.json" crossorigin="use-credentials">
|
||||||
<link rel="apple-touch-icon" href="../static/src/browser/media/code-server.png" />
|
<link rel="apple-touch-icon" href="{{BASE}}/static/src/browser/media/code-server.png" />
|
||||||
<meta name="apple-mobile-web-app-capable" content="yes">
|
<meta name="apple-mobile-web-app-capable" content="yes">
|
||||||
</head>
|
</head>
|
||||||
|
|
||||||
|
@ -30,11 +30,12 @@
|
||||||
|
|
||||||
<!-- Startup (do not modify order of script tags!) -->
|
<!-- Startup (do not modify order of script tags!) -->
|
||||||
<script>
|
<script>
|
||||||
const basePath = window.location.pathname.replace(/\/+$/, '');
|
const parts = window.location.pathname.replace(/^\//g, "").split("/")
|
||||||
const base = window.location.origin + basePath;
|
parts[parts.length - 1] = "{{VS_BASE}}"
|
||||||
|
const url = new URL(window.location.origin + "/" + parts.join("/"))
|
||||||
const el = document.getElementById('vscode-remote-commit');
|
const el = document.getElementById('vscode-remote-commit');
|
||||||
const commit = el ? el.getAttribute('data-settings') : "";
|
const commit = el ? el.getAttribute('data-settings') : "";
|
||||||
const staticBase = base + '/static-' + commit;
|
const staticBase = url.href + '/static-' + commit;
|
||||||
self.require = {
|
self.require = {
|
||||||
baseUrl: `${staticBase}/out`,
|
baseUrl: `${staticBase}/out`,
|
||||||
paths: {
|
paths: {
|
||||||
|
@ -48,7 +49,7 @@
|
||||||
},
|
},
|
||||||
};
|
};
|
||||||
</script>
|
</script>
|
||||||
<script src="./static/out/vs/loader.js"></script>
|
<script src="{{VS_BASE}}/static/out/vs/loader.js"></script>
|
||||||
<script>
|
<script>
|
||||||
require(['vs/code/browser/workbench/workbench'], function() {});
|
require(['vs/code/browser/workbench/workbench'], function() {});
|
||||||
</script>
|
</script>
|
||||||
|
|
Loading…
Reference in New Issue