171 lines
3.8 KiB
TypeScript
171 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;
|
||
|
};
|