|
| 1 | +/** |
| 2 | + * Used by node |
| 3 | + */ |
| 4 | +import * as https from "https"; |
| 5 | +import * as os from "os"; |
| 6 | + |
| 7 | +export const defaultClient = "filler"; |
| 8 | + |
| 9 | +export class TelemetryClient { |
| 10 | + public channel = { |
| 11 | + setUseDiskRetryCaching: (): void => undefined, |
| 12 | + }; |
| 13 | + |
| 14 | + public constructor() { |
| 15 | + // |
| 16 | + } |
| 17 | + |
| 18 | + public trackEvent(options: { |
| 19 | + name: string; |
| 20 | + properties: object; |
| 21 | + measurements: object; |
| 22 | + }): void { |
| 23 | + if (!options.properties) { |
| 24 | + options.properties = {}; |
| 25 | + } |
| 26 | + if (!options.measurements) { |
| 27 | + options.measurements = {}; |
| 28 | + } |
| 29 | + |
| 30 | + try { |
| 31 | + const cpus = os.cpus(); |
| 32 | + // tslint:disable-next-line:no-any |
| 33 | + (options.measurements as any).cpu = { |
| 34 | + model: cpus[0].model, |
| 35 | + cores: cpus.length, |
| 36 | + }; |
| 37 | + } catch (ex) { |
| 38 | + // Nothin |
| 39 | + } |
| 40 | + |
| 41 | + try { |
| 42 | + // tslint:disable-next-line:no-any |
| 43 | + (options.measurements as any).memory = { |
| 44 | + virtual_free: os.freemem(), |
| 45 | + virtual_used: os.totalmem(), |
| 46 | + }; |
| 47 | + } catch (ex) { |
| 48 | + // |
| 49 | + } |
| 50 | + |
| 51 | + try { |
| 52 | + // tslint:disable:no-any |
| 53 | + (options.properties as any)["common.shell"] = os.userInfo().shell; |
| 54 | + (options.properties as any)["common.release"] = os.release(); |
| 55 | + (options.properties as any)["common.arch"] = os.arch(); |
| 56 | + // tslint:enable:no-any |
| 57 | + } catch (ex) { |
| 58 | + // |
| 59 | + } |
| 60 | + |
| 61 | + try { |
| 62 | + // tslint:disable-next-line:no-any |
| 63 | + (options.properties as any)["common.machineId"] = machineIdSync(); |
| 64 | + } catch (ex) { |
| 65 | + // |
| 66 | + } |
| 67 | + |
| 68 | + try { |
| 69 | + const request = https.request({ |
| 70 | + host: "v1.telemetry.coder.com", |
| 71 | + port: 443, |
| 72 | + path: "/track", |
| 73 | + method: "POST", |
| 74 | + headers: { |
| 75 | + "Content-Type": "application/json", |
| 76 | + }, |
| 77 | + }); |
| 78 | + request.on("error", () => { |
| 79 | + // Do nothing, we don"t really care |
| 80 | + }); |
| 81 | + request.write(JSON.stringify(options)); |
| 82 | + request.end(); |
| 83 | + } catch (ex) { |
| 84 | + // Suppress all errs |
| 85 | + } |
| 86 | + } |
| 87 | + |
| 88 | + public flush(options: { |
| 89 | + readonly callback: () => void; |
| 90 | + }): void { |
| 91 | + options.callback(); |
| 92 | + } |
| 93 | +} |
| 94 | + |
| 95 | +// Taken from https://github.com/automation-stack/node-machine-id |
| 96 | +import { exec, execSync } from "child_process"; |
| 97 | +import { createHash } from "crypto"; |
| 98 | + |
| 99 | +const isWindowsProcessMixedOrNativeArchitecture = (): "" | "mixed" | "native" => { |
| 100 | + // detect if the node binary is the same arch as the Windows OS. |
| 101 | + // or if this is 32 bit node on 64 bit windows. |
| 102 | + if (process.platform !== "win32") { |
| 103 | + return ""; |
| 104 | + } |
| 105 | + if (process.arch === "ia32" && process.env.hasOwnProperty("PROCESSOR_ARCHITEW6432")) { |
| 106 | + return "mixed"; |
| 107 | + } |
| 108 | + |
| 109 | + return "native"; |
| 110 | +}; |
| 111 | + |
| 112 | +let { platform } = process, |
| 113 | + win32RegBinPath = { |
| 114 | + native: "%windir%\\System32", |
| 115 | + mixed: "%windir%\\sysnative\\cmd.exe /c %windir%\\System32", |
| 116 | + "": "", |
| 117 | + }, |
| 118 | + guid = { |
| 119 | + darwin: "ioreg -rd1 -c IOPlatformExpertDevice", |
| 120 | + win32: `${win32RegBinPath[isWindowsProcessMixedOrNativeArchitecture()]}\\REG ` + |
| 121 | + "QUERY HKEY_LOCAL_MACHINE\\SOFTWARE\\Microsoft\\Cryptography " + |
| 122 | + "/v MachineGuid", |
| 123 | + linux: "( cat /var/lib/dbus/machine-id /etc/machine-id 2> /dev/null || hostname ) | head -n 1 || :", |
| 124 | + freebsd: "kenv -q smbios.system.uuid || sysctl -n kern.hostuuid", |
| 125 | + // tslint:disable-next-line:no-any |
| 126 | + } as any; |
| 127 | + |
| 128 | +const hash = (guid: string): string => { |
| 129 | + return createHash("sha256").update(guid).digest("hex"); |
| 130 | +}; |
| 131 | + |
| 132 | +const expose = (result: string): string => { |
| 133 | + switch (platform) { |
| 134 | + case "darwin": |
| 135 | + return result |
| 136 | + .split("IOPlatformUUID")[1] |
| 137 | + .split("\n")[0].replace(/\=|\s+|\"/ig, "") |
| 138 | + .toLowerCase(); |
| 139 | + case "win32": |
| 140 | + return result |
| 141 | + .toString() |
| 142 | + .split("REG_SZ")[1] |
| 143 | + .replace(/\r+|\n+|\s+/ig, "") |
| 144 | + .toLowerCase(); |
| 145 | + case "linux": |
| 146 | + return result |
| 147 | + .toString() |
| 148 | + .replace(/\r+|\n+|\s+/ig, "") |
| 149 | + .toLowerCase(); |
| 150 | + case "freebsd": |
| 151 | + return result |
| 152 | + .toString() |
| 153 | + .replace(/\r+|\n+|\s+/ig, "") |
| 154 | + .toLowerCase(); |
| 155 | + default: |
| 156 | + throw new Error(`Unsupported platform: ${process.platform}`); |
| 157 | + } |
| 158 | +}; |
| 159 | + |
| 160 | +let cachedMachineId: string | undefined; |
| 161 | + |
| 162 | +const machineIdSync = (): string => { |
| 163 | + if (cachedMachineId) { |
| 164 | + return cachedMachineId; |
| 165 | + } |
| 166 | + let id: string = expose(execSync(guid[platform]).toString()); |
| 167 | + cachedMachineId = hash(id); |
| 168 | + |
| 169 | + return cachedMachineId; |
| 170 | +}; |
0 commit comments