diff --git a/ci/vscode.patch b/ci/vscode.patch index c7e58872b..c3c1acde4 100644 --- a/ci/vscode.patch +++ b/ci/vscode.patch @@ -486,10 +486,10 @@ index eab8591492..26668701f7 100644 options.logService.error(`${logPrefix} socketFactory.connect() failed. Error:`); diff --git a/src/vs/server/browser/client.ts b/src/vs/server/browser/client.ts new file mode 100644 -index 0000000000..3f53907de0 +index 0000000000..4042e32f74 --- /dev/null +++ b/src/vs/server/browser/client.ts -@@ -0,0 +1,227 @@ +@@ -0,0 +1,263 @@ +import { Emitter } from 'vs/base/common/event'; +import { URI } from 'vs/base/common/uri'; +import { localize } from 'vs/nls'; @@ -507,6 +507,7 @@ index 0000000000..3f53907de0 +import 'vs/workbench/contrib/localizations/browser/localizations.contribution'; +import { LocalizationsService } from 'vs/workbench/services/localizations/electron-browser/localizationsService'; +import { IRemoteAgentService } from 'vs/workbench/services/remote/common/remoteAgentService'; ++import { Options } from 'vs/server/ipc.d'; + +class TelemetryService extends TelemetryChannelClient { + public constructor( @@ -516,11 +517,46 @@ index 0000000000..3f53907de0 + } +} + ++/** ++ * Remove extra slashes in a URL. ++ */ ++export const normalize = (url: string, keepTrailing = false): string => { ++ return url.replace(/\/\/+/g, "/").replace(/\/+$/, keepTrailing ? "/" : ""); ++}; ++ ++/** ++ * Get options embedded in the HTML from the server. ++ */ ++export const getOptions = (): T => { ++ if (typeof document === "undefined") { ++ return {} as T; ++ } ++ const el = document.getElementById("coder-options"); ++ try { ++ if (!el) { ++ throw new Error("no options element"); ++ } ++ const value = el.getAttribute("data-settings"); ++ if (!value) { ++ throw new Error("no options value"); ++ } ++ const options = JSON.parse(value); ++ const parts = window.location.pathname.replace(/^\//g, "").split("/"); ++ parts[parts.length - 1] = options.base; ++ const url = new URL(window.location.origin + "/" + parts.join("/")); ++ return { ++ ...options, ++ base: normalize(url.pathname, true), ++ }; ++ } catch (error) { ++ console.warn(error); ++ return {} as T; ++ } ++}; ++ ++const options = getOptions(); ++ +const TELEMETRY_SECTION_ID = 'telemetry'; -+ -+const el = document.getElementById("vscode-disable-telemetry"); -+const disableTelemetry = el && el.getAttribute("data-value") === "true" || false; -+ +Registry.as(Extensions.Configuration).registerConfiguration({ + 'id': TELEMETRY_SECTION_ID, + 'order': 110, @@ -530,7 +566,7 @@ index 0000000000..3f53907de0 + 'telemetry.enableTelemetry': { + 'type': 'boolean', + 'description': localize('telemetry.enableTelemetry', 'Enable usage data and errors to be sent to a Microsoft online service.'), -+ 'default': !disableTelemetry, ++ 'default': !options.disableTelemetry, + 'tags': ['usesOnlineServices'] + } + } @@ -625,7 +661,7 @@ index 0000000000..3f53907de0 + const applyUpdate = async (): Promise => { + (services.get(ILogService) as ILogService).debug("Applying update..."); + -+ const response = await fetch("./update/apply", { ++ const response = await fetch(normalize(`${options.base}/update/apply`), { + headers: { "content-type": "application/json" }, + }); + if (response.status !== 200) { @@ -643,7 +679,7 @@ index 0000000000..3f53907de0 + const getUpdate = async (): Promise => { + (services.get(ILogService) as ILogService).debug("Checking for update..."); + -+ const response = await fetch("./update", { ++ const response = await fetch(normalize(`${options.base}/update`), { + headers: { "content-type": "application/json" }, + }); + if (response.status !== 200) { @@ -1076,14 +1112,20 @@ index 0000000000..56331ff1fc +require('../../bootstrap-amd').load('vs/server/entry'); diff --git a/src/vs/server/ipc.d.ts b/src/vs/server/ipc.d.ts new file mode 100644 -index 0000000000..a0d1d0df54 +index 0000000000..15d2ba55d1 --- /dev/null +++ b/src/vs/server/ipc.d.ts -@@ -0,0 +1,108 @@ +@@ -0,0 +1,114 @@ +/** + * External interfaces for integration into code-server over IPC. No vs imports + * should be made in this file. + */ ++export interface Options { ++ base: string ++ commit: string ++ disableTelemetry: boolean ++ nlsConfiguration: NLSConfiguration ++} + +export interface InitMessage { + type: 'init'; diff --git a/src/browser/pages/vscode.html b/src/browser/pages/vscode.html index aeea05661..688a47f2f 100644 --- a/src/browser/pages/vscode.html +++ b/src/browser/pages/vscode.html @@ -20,10 +20,8 @@ - - @@ -53,9 +51,7 @@ const parts = window.location.pathname.replace(/^\//g, "").split("/") parts[parts.length - 1] = "{{BASE}}" const url = new URL(window.location.origin + "/" + parts.join("/")) - const el = document.getElementById("vscode-remote-commit") - const commit = el ? el.getAttribute("data-settings") : "" - const staticBase = url.href.replace(/\/+$/, "") + "/static/" + commit + "/lib/vscode" + const staticBase = url.href.replace(/\/+$/, "") + "/static/{{COMMIT}}/lib/vscode" let nlsConfig try { nlsConfig = JSON.parse(document.getElementById("vscode-remote-nls-configuration").getAttribute("data-settings")) diff --git a/src/node/app/vscode.ts b/src/node/app/vscode.ts index c31785092..4b3fda72c 100644 --- a/src/node/app/vscode.ts +++ b/src/node/app/vscode.ts @@ -8,6 +8,7 @@ import * as path from "path" import * as url from "url" import { CodeServerMessage, + Options, StartPath, VscodeMessage, VscodeOptions, @@ -205,8 +206,11 @@ export class VscodeHttpProvider extends HttpProvider { .replace(`"{{PRODUCT_CONFIGURATION}}"`, `'${JSON.stringify(options.productConfiguration)}'`) .replace(`"{{WORKBENCH_WEB_CONFIGURATION}}"`, `'${JSON.stringify(options.workbenchWebConfiguration)}'`) .replace(`"{{NLS_CONFIGURATION}}"`, `'${JSON.stringify(options.nlsConfiguration)}'`) - .replace("{{DISABLE_TELEMETRY}}", this.args["disable-telemetry"] ? "true" : "false") - return this.replaceTemplates(route, response) + return this.replaceTemplates(route, response, { + base: this.base(route), + commit: this.options.commit, + disableTelemetry: !!this.args["disable-telemetry"], + }) } /** diff --git a/src/node/http.ts b/src/node/http.ts index a2a5e828c..c4cf25f67 100644 --- a/src/node/http.ts +++ b/src/node/http.ts @@ -185,22 +185,30 @@ export abstract class HttpProvider { /** * Replace common templates strings. */ + protected replaceTemplates(route: Route, response: HttpStringFileResponse, sessionId?: string): HttpStringFileResponse + protected replaceTemplates( + route: Route, + response: HttpStringFileResponse, + options: T, + ): HttpStringFileResponse protected replaceTemplates( route: Route, response: HttpStringFileResponse, - sessionId?: string, + sessionIdOrOptions?: string | object, ): HttpStringFileResponse { - const options: Options = { - base: this.base(route), - commit: this.options.commit, - logLevel: logger.level, - sessionId, + if (typeof sessionIdOrOptions === "undefined" || typeof sessionIdOrOptions === "string") { + sessionIdOrOptions = { + base: this.base(route), + commit: this.options.commit, + logLevel: logger.level, + sessionID: sessionIdOrOptions, + } as Options } response.content = response.content .replace(/{{COMMIT}}/g, this.options.commit) .replace(/{{TO}}/g, Array.isArray(route.query.to) ? route.query.to[0] : route.query.to || "/dashboard") .replace(/{{BASE}}/g, this.base(route)) - .replace(/"{{OPTIONS}}"/, `'${JSON.stringify(options)}'`) + .replace(/"{{OPTIONS}}"/, `'${JSON.stringify(sessionIdOrOptions)}'`) return response }