Files
code-server/lib/vscode/src/vs/server/browser/client.ts
Asher a48ac5080b Share common util code with VS Code
This lets us re-use the normalized base path so when we expire/clear the
cookie we use the same base path.
2021-05-04 13:29:40 -05:00

199 lines
6.7 KiB
TypeScript

import * as path from 'vs/base/common/path';
import { Options } from 'vs/ipc';
import { localize } from 'vs/nls';
import { MenuId, MenuRegistry } from 'vs/platform/actions/common/actions';
import { CommandsRegistry } from 'vs/platform/commands/common/commands';
import { Extensions, IConfigurationRegistry } from 'vs/platform/configuration/common/configurationRegistry';
import { ContextKeyExpr, IContextKeyService } from 'vs/platform/contextkey/common/contextkey';
import { registerSingleton } from 'vs/platform/instantiation/common/extensions';
import { ServiceCollection } from 'vs/platform/instantiation/common/serviceCollection';
import { ILogService } from 'vs/platform/log/common/log';
import { INotificationService, Severity } from 'vs/platform/notification/common/notification';
import { Registry } from 'vs/platform/registry/common/platform';
import { IStorageService, StorageScope, StorageTarget } from 'vs/platform/storage/common/storage';
import { ITelemetryService } from 'vs/platform/telemetry/common/telemetry';
import { TelemetryChannelClient } from 'vs/server/common/telemetry';
import { getOptions } from 'vs/server/common/util';
import 'vs/workbench/contrib/localizations/browser/localizations.contribution';
import 'vs/workbench/services/localizations/browser/localizationsService';
import { IRemoteAgentService } from 'vs/workbench/services/remote/common/remoteAgentService';
/**
* All client-side customization to VS Code should live in this file when
* possible.
*/
const options = getOptions<Options>();
class TelemetryService extends TelemetryChannelClient {
public constructor(
@IRemoteAgentService remoteAgentService: IRemoteAgentService,
) {
super(remoteAgentService.getConnection()!.getChannel('telemetry'));
}
}
const TELEMETRY_SECTION_ID = 'telemetry';
Registry.as<IConfigurationRegistry>(Extensions.Configuration).registerConfiguration({
'id': TELEMETRY_SECTION_ID,
'order': 110,
'type': 'object',
'title': localize('telemetryConfigurationTitle', 'Telemetry'),
'properties': {
'telemetry.enableTelemetry': {
'type': 'boolean',
'description': localize('telemetry.enableTelemetry', 'Enable usage data and errors to be sent to a Microsoft online service.'),
'default': !options.disableTelemetry,
'tags': ['usesOnlineServices']
}
}
});
registerSingleton(ITelemetryService, TelemetryService);
/**
* This is called by vs/workbench/browser/web.main.ts after the workbench has
* been initialized so we can initialize our own client-side code.
*/
export const initialize = async (services: ServiceCollection): Promise<void> => {
const event = new CustomEvent('ide-ready');
window.dispatchEvent(event);
if (parent) {
// Tell the parent loading has completed.
parent.postMessage({ event: 'loaded' }, '*');
// Proxy or stop proxing events as requested by the parent.
const listeners = new Map<string, (event: Event) => void>();
window.addEventListener('message', (parentEvent) => {
const eventName = parentEvent.data.bind || parentEvent.data.unbind;
if (eventName) {
const oldListener = listeners.get(eventName);
if (oldListener) {
document.removeEventListener(eventName, oldListener);
}
}
if (parentEvent.data.bind && parentEvent.data.prop) {
const listener = (event: Event) => {
parent.postMessage({
event: parentEvent.data.event,
[parentEvent.data.prop]: event[parentEvent.data.prop as keyof Event]
}, window.location.origin);
};
listeners.set(parentEvent.data.bind, listener);
document.addEventListener(parentEvent.data.bind, listener);
}
});
}
if (!window.isSecureContext) {
(services.get(INotificationService) as INotificationService).notify({
severity: Severity.Warning,
message: 'code-server is being accessed over an insecure domain. Web views, the clipboard, and other functionality will not work as expected.',
actions: {
primary: [{
id: 'understand',
label: 'I understand',
tooltip: '',
class: undefined,
enabled: true,
checked: true,
dispose: () => undefined,
run: () => {
return Promise.resolve();
}
}],
}
});
}
const logService = (services.get(ILogService) as ILogService);
const storageService = (services.get(IStorageService) as IStorageService);
const updateCheckEndpoint = path.join(options.base, '/update/check');
const getUpdate = async (): Promise<void> => {
logService.debug('Checking for update...');
const response = await fetch(updateCheckEndpoint, {
headers: { 'Accept': 'application/json' },
});
if (!response.ok) {
throw new Error(response.statusText);
}
const json = await response.json();
if (json.error) {
throw new Error(json.error);
}
if (json.isLatest) {
return;
}
const lastNoti = storageService.getNumber('csLastUpdateNotification', StorageScope.GLOBAL);
if (lastNoti) {
// Only remind them again after 1 week.
const timeout = 1000*60*60*24*7;
const threshold = lastNoti + timeout;
if (Date.now() < threshold) {
return;
}
}
storageService.store('csLastUpdateNotification', Date.now(), StorageScope.GLOBAL, StorageTarget.MACHINE);
(services.get(INotificationService) as INotificationService).notify({
severity: Severity.Info,
message: `[code-server v${json.latest}](https://github.com/cdr/code-server/releases/tag/v${json.latest}) has been released!`,
});
};
const updateLoop = (): void => {
getUpdate().catch((error) => {
logService.debug(`failed to check for update: ${error}`);
}).finally(() => {
// Check again every 6 hours.
setTimeout(updateLoop, 1000*60*60*6);
});
};
if (!options.disableUpdateCheck) {
updateLoop();
}
// This will be used to set the background color while VS Code loads.
const theme = storageService.get('colorThemeData', StorageScope.GLOBAL);
if (theme) {
localStorage.setItem('colorThemeData', theme);
}
// Use to show or hide logout commands and menu options.
const contextKeyService = (services.get(IContextKeyService) as IContextKeyService);
contextKeyService.createKey('code-server.authed', options.authed);
// Add a logout command.
const logoutEndpoint = path.join(options.base, '/logout') + `?base=${options.base}`;
const LOGOUT_COMMAND_ID = 'code-server.logout';
CommandsRegistry.registerCommand(
LOGOUT_COMMAND_ID,
() => {
window.location.href = logoutEndpoint;
},
);
// Add logout to command palette.
MenuRegistry.appendMenuItem(MenuId.CommandPalette, {
command: {
id: LOGOUT_COMMAND_ID,
title: localize('logout', "Log out")
},
when: ContextKeyExpr.has('code-server.authed')
});
// Add logout to the (web-only) home menu.
MenuRegistry.appendMenuItem(MenuId.MenubarHomeMenu, {
command: {
id: LOGOUT_COMMAND_ID,
title: localize('logout', "Log out")
},
when: ContextKeyExpr.has('code-server.authed')
});
};