mirror of
https://github.com/coder/code-server.git
synced 2026-05-05 03:55:18 +02:00
This lets us re-use the normalized base path so when we expire/clear the cookie we use the same base path.
199 lines
6.7 KiB
TypeScript
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')
|
|
});
|
|
};
|