code-server/insights.ts
2019-07-18 18:08:11 -05:00

170 lines
3.8 KiB
TypeScript

/**
* Used by node
*/
import * as https from "https";
import * as os from "os";
export const defaultClient = "filler";
export class TelemetryClient {
public channel = {
setUseDiskRetryCaching: (): void => undefined,
};
public constructor() {
//
}
public trackEvent(options: {
name: string;
properties: object;
measurements: object;
}): void {
if (!options.properties) {
options.properties = {};
}
if (!options.measurements) {
options.measurements = {};
}
try {
const cpus = os.cpus();
// tslint:disable-next-line:no-any
(options.measurements as any).cpu = {
model: cpus[0].model,
cores: cpus.length,
};
} catch (ex) {
// Nothin
}
try {
// tslint:disable-next-line:no-any
(options.measurements as any).memory = {
virtual_free: os.freemem(),
virtual_used: os.totalmem(),
};
} catch (ex) {
//
}
try {
// tslint:disable:no-any
(options.properties as any)["common.shell"] = os.userInfo().shell;
(options.properties as any)["common.release"] = os.release();
(options.properties as any)["common.arch"] = os.arch();
// tslint:enable:no-any
} catch (ex) {
//
}
try {
// tslint:disable-next-line:no-any
(options.properties as any)["common.machineId"] = machineIdSync();
} catch (ex) {
//
}
try {
const request = https.request({
host: "v1.telemetry.coder.com",
port: 443,
path: "/track",
method: "POST",
headers: {
"Content-Type": "application/json",
},
});
request.on("error", () => {
// Do nothing, we don"t really care
});
request.write(JSON.stringify(options));
request.end();
} catch (ex) {
// Suppress all errs
}
}
public flush(options: {
readonly callback: () => void;
}): void {
options.callback();
}
}
// Taken from https://github.com/automation-stack/node-machine-id
import { exec, execSync } from "child_process";
import { createHash } from "crypto";
const isWindowsProcessMixedOrNativeArchitecture = (): "" | "mixed" | "native" => {
// detect if the node binary is the same arch as the Windows OS.
// or if this is 32 bit node on 64 bit windows.
if (process.platform !== "win32") {
return "";
}
if (process.arch === "ia32" && process.env.hasOwnProperty("PROCESSOR_ARCHITEW6432")) {
return "mixed";
}
return "native";
};
let { platform } = process,
win32RegBinPath = {
native: "%windir%\\System32",
mixed: "%windir%\\sysnative\\cmd.exe /c %windir%\\System32",
"": "",
},
guid = {
darwin: "ioreg -rd1 -c IOPlatformExpertDevice",
win32: `${win32RegBinPath[isWindowsProcessMixedOrNativeArchitecture()]}\\REG ` +
"QUERY HKEY_LOCAL_MACHINE\\SOFTWARE\\Microsoft\\Cryptography " +
"/v MachineGuid",
linux: "( cat /var/lib/dbus/machine-id /etc/machine-id 2> /dev/null || hostname ) | head -n 1 || :",
freebsd: "kenv -q smbios.system.uuid || sysctl -n kern.hostuuid",
// tslint:disable-next-line:no-any
} as any;
const hash = (guid: string): string => {
return createHash("sha256").update(guid).digest("hex");
};
const expose = (result: string): string => {
switch (platform) {
case "darwin":
return result
.split("IOPlatformUUID")[1]
.split("\n")[0].replace(/\=|\s+|\"/ig, "")
.toLowerCase();
case "win32":
return result
.toString()
.split("REG_SZ")[1]
.replace(/\r+|\n+|\s+/ig, "")
.toLowerCase();
case "linux":
return result
.toString()
.replace(/\r+|\n+|\s+/ig, "")
.toLowerCase();
case "freebsd":
return result
.toString()
.replace(/\r+|\n+|\s+/ig, "")
.toLowerCase();
default:
throw new Error(`Unsupported platform: ${process.platform}`);
}
};
let cachedMachineId: string | undefined;
const machineIdSync = (): string => {
if (cachedMachineId) {
return cachedMachineId;
}
let id: string = expose(execSync(guid[platform]).toString());
cachedMachineId = hash(id);
return cachedMachineId;
};