UNPKG

zwave-js-ui

Version:

Z-Wave Control Panel and MQTT Gateway

375 lines (374 loc) 12.6 kB
"use strict"; var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) { if (k2 === undefined) k2 = k; var desc = Object.getOwnPropertyDescriptor(m, k); if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) { desc = { enumerable: true, get: function() { return m[k]; } }; } Object.defineProperty(o, k2, desc); }) : (function(o, m, k, k2) { if (k2 === undefined) k2 = k; o[k2] = m[k]; })); var __setModuleDefault = (this && this.__setModuleDefault) || (Object.create ? (function(o, v) { Object.defineProperty(o, "default", { enumerable: true, value: v }); }) : function(o, v) { o["default"] = v; }); var __importStar = (this && this.__importStar) || function (mod) { if (mod && mod.__esModule) return mod; var result = {}; if (mod != null) for (var k in mod) if (k !== "default" && Object.prototype.hasOwnProperty.call(mod, k)) __createBinding(result, mod, k); __setModuleDefault(result, mod); return result; }; var __importDefault = (this && this.__importDefault) || function (mod) { return (mod && mod.__esModule) ? mod : { "default": mod }; }; Object.defineProperty(exports, "__esModule", { value: true }); exports.isDocker = exports.parseSecurityKeys = exports.stringifyJSON = exports.parseJSON = exports.allSettled = exports.buffer2hex = exports.bufferFromHex = exports.isBufferAsHex = exports.verifyPsw = exports.hashPsw = exports.humanSize = exports.hasProperty = exports.removeSlash = exports.sanitizeTopic = exports.getVersion = exports.num2hex = exports.copy = exports.isValueId = exports.joinProps = exports.joinPath = exports.getPath = exports.basePath = exports.fileDate = exports.deepEqual = exports.padNumber = exports.applyMixin = exports.pkgJson = void 0; const path_1 = __importStar(require("path")); const crypto_1 = __importDefault(require("crypto")); const fs_1 = require("fs"); const types_1 = require("util/types"); // don't use import here, it will break the build // eslint-disable-next-line @typescript-eslint/no-var-requires exports.pkgJson = require('../../package.json'); let VERSION; function applyMixin(target, mixin, includeConstructor = false) { // Figure out the inheritance chain of the mixin const inheritanceChain = [mixin]; // eslint-disable-next-line no-constant-condition while (true) { const current = inheritanceChain[0]; const base = Object.getPrototypeOf(current); if (base?.prototype) { inheritanceChain.unshift(base); } else { break; } } for (const ctor of inheritanceChain) { for (const prop of Object.getOwnPropertyNames(ctor.prototype)) { // Do not override the constructor if (includeConstructor || prop !== 'constructor') { Object.defineProperty(target.prototype, prop, Object.getOwnPropertyDescriptor(ctor.prototype, prop) ?? Object.create(null)); } } } } exports.applyMixin = applyMixin; function padNumber(num, digits) { return num ? num.toString().padStart(digits, '0') : 'unknown'; } exports.padNumber = padNumber; function deepEqual(a, b) { return JSON.stringify(a) === JSON.stringify(b); } exports.deepEqual = deepEqual; function fileDate(date) { date = date || new Date(); return date.toISOString().slice(-24).replace(/\D/g, '').slice(0, 14); } exports.fileDate = fileDate; /** Where package.json is */ exports.basePath = __filename.endsWith('index.js') ? (0, path_1.resolve)(__dirname) // esbuild bundle : (0, path_1.resolve)(__dirname, '..', '..'); /** * Get the base root path to application directory. When we are in a `pkg` environment * the path of the snapshot is not writable */ function getPath(write) { if (write && hasProperty(process, 'pkg')) return process.cwd(); else return exports.basePath; } exports.getPath = getPath; /** * path.join wrapper, the first option can be a boolean and it will automatically fetch the root path * passing the boolean to getPath */ function joinPath(write, ...paths) { if (typeof write === 'boolean') { write = getPath(write); } return path_1.default.join(write, ...paths); } exports.joinPath = joinPath; /** * Join props with a `_` and skips undefined props */ function joinProps(...props) { props = props || []; let ret = props[0].toString() || ''; for (let i = 1; i < props.length; i++) { const p = props[i]; if (p !== null && p !== undefined && p !== '') { ret += '_' + (typeof p === 'number' ? p.toString() : p); } } return ret; } exports.joinProps = joinProps; /** * Checks if an object is a valueId, returns error otherwise */ function isValueId(v) { if (typeof v.commandClass !== 'number' || v.commandClass < 0) { return 'invalid `commandClass`'; } if (v.endpoint !== undefined && v.endpoint < 0) { return 'invalid `endpoint`'; } if (v.property === undefined || (typeof v.property !== 'string' && typeof v.property !== 'number')) { return 'invalid `property`'; } if (v.propertyKey !== undefined && typeof v.propertyKey !== 'string' && typeof v.propertyKey !== 'number') { return 'invalid `propertyKey`'; } return true; } exports.isValueId = isValueId; /** * Deep copy of an object */ function copy(obj) { return JSON.parse(JSON.stringify(obj)); } exports.copy = copy; /** * Converts a decimal to an hex number of 4 digits and `0x` as prefix */ function num2hex(num) { const hex = num >= 0 ? num.toString(16) : 'XXXX'; return '0x' + '0'.repeat(4 - hex.length) + hex; } exports.num2hex = num2hex; /** * Gets the actual package.json version with also the git revision number at the end of it */ function getVersion() { if (!VERSION) { try { // try to get short sha of last commit let rev = (0, fs_1.readFileSync)('.git/HEAD').toString().trim(); if (rev.indexOf(':') !== -1) { rev = (0, fs_1.readFileSync)('.git/' + rev.substring(5)) .toString() .trim(); } VERSION = `${exports.pkgJson.version}${rev ? '.' + rev.substring(0, 7) : ''}`; } catch { VERSION = exports.pkgJson.version; } } return VERSION; } exports.getVersion = getVersion; /** * Sanitize chars of a string to use in a topic * */ function sanitizeTopic(str, sanitizeSlash = false) { if (typeof str === 'number' || !str) return str.toString(); if (sanitizeSlash) { str = removeSlash(str); } // replace spaces with '_' str = str.replace(/\s/g, '_'); // remove special chars return str.replace(/[^A-Za-z0-9-_À-ÖØ-öø-ÿ/]/g, ''); } exports.sanitizeTopic = sanitizeTopic; /** * Removes `/` chars from strings */ function removeSlash(str) { return typeof str === 'number' ? str.toString() : str.replace(/\//g, '-'); } exports.removeSlash = removeSlash; /** * Check if an object has a property */ // eslint-disable-next-line @typescript-eslint/ban-types function hasProperty(obj, prop) { return Object.prototype.hasOwnProperty.call(obj, prop); } exports.hasProperty = hasProperty; /** * Gets the size in a human readable form starting from bytes */ function humanSize(bytes) { const sizes = ['Bytes', 'KB', 'MB', 'GB', 'TB']; if (bytes === 0) { return 'n/a'; } const i = Math.floor(Math.log(bytes) / Math.log(1024)); if (i === 0) { return bytes + ' ' + sizes[i]; } return (bytes / Math.pow(1024, i)).toFixed(1) + ' ' + sizes[i]; } exports.humanSize = humanSize; async function hashPsw(password) { return new Promise((resolve, reject) => { const salt = crypto_1.default.randomBytes(8).toString('hex'); crypto_1.default.scrypt(password, salt, 64, (err, derivedKey) => { if (err) reject(err); resolve(salt + ':' + derivedKey.toString('hex')); }); }); } exports.hashPsw = hashPsw; async function verifyPsw(password, hash) { return new Promise((resolve, reject) => { const [salt, key] = hash.split(':'); crypto_1.default.scrypt(password, salt, 64, (err, derivedKey) => { if (err) reject(err); resolve(key === derivedKey.toString('hex')); }); }); } exports.verifyPsw = verifyPsw; /** * Checks if a string is a hex buffer */ function isBufferAsHex(str) { return /^0x([a-fA-F0-9]{2})+$/.test(str); } exports.isBufferAsHex = isBufferAsHex; /** * Parses a buffer from a string has the form 0x[a-f0-9]+ */ function bufferFromHex(hex) { return Buffer.from(hex.substr(2), 'hex'); } exports.bufferFromHex = bufferFromHex; /** * Converts a buffer to an hex string */ function buffer2hex(buffer) { if (buffer.length === 0) return ''; return `0x${Buffer.from(buffer).toString('hex')}`; } exports.buffer2hex = buffer2hex; function allSettled(promises) { const wrappedPromises = promises.map((p) => Promise.resolve(p).then((val) => ({ status: 'fulfilled', value: val }), (err) => ({ status: 'rejected', reason: err }))); return Promise.all(wrappedPromises); } exports.allSettled = allSettled; /** Parses a string to json with buffer decode support */ function parseJSON(str) { return JSON.parse(str, (k, v) => { if (v !== null && typeof v === 'object' && 'type' in v && v.type === 'Buffer' && 'data' in v && Array.isArray(v.data)) { return Buffer.from(v.data); } return v; }); } exports.parseJSON = parseJSON; /** * Correctly stringify a JSON object with uint8array support */ function stringifyJSON(obj) { return JSON.stringify(obj, (k, v) => { if ((0, types_1.isUint8Array)(v)) { return { type: 'Buffer', data: Array.from(v.values()), }; } return v; }); } exports.stringifyJSON = stringifyJSON; function parseSecurityKeys(config, options) { config.securityKeys = config.securityKeys || {}; if (process.env.NETWORK_KEY) { config.securityKeys.S0_Legacy = process.env.NETWORK_KEY; } const availableKeys = [ 'S2_Unauthenticated', 'S2_Authenticated', 'S2_AccessControl', 'S0_Legacy', ]; const availableLongRangeKeys = ['S2_Authenticated', 'S2_AccessControl']; const envKeys = Object.keys(process.env) .filter((k) => k?.startsWith('KEY_')) .map((k) => k.substring(4)); const longRangeEnvKeys = Object.keys(process.env) .filter((k) => k?.startsWith('KEY_LR_')) .map((k) => k.substring(7)); // load security keys from env for (const k of envKeys) { if (availableKeys.includes(k)) { config.securityKeys[k] = process.env[`KEY_${k}`]; } } // load long range security keys from env for (const k of longRangeEnvKeys) { if (availableLongRangeKeys.includes(k)) { config.securityKeysLongRange[k] = process.env[`KEY_LR_${k}`]; } } options.securityKeys = {}; options.securityKeysLongRange = {}; // convert security keys to buffer for (const key in config.securityKeys) { if (availableKeys.includes(key) && config.securityKeys[key].length === 32) { options.securityKeys[key] = Buffer.from(config.securityKeys[key], 'hex'); } } config.securityKeysLongRange = config.securityKeysLongRange || {}; // convert long range security keys to buffer for (const key in config.securityKeysLongRange) { if (availableLongRangeKeys.includes(key) && config.securityKeysLongRange[key].length === 32) { options.securityKeysLongRange[key] = Buffer.from(config.securityKeysLongRange[key], 'hex'); } } } exports.parseSecurityKeys = parseSecurityKeys; function hasDockerEnv() { try { (0, fs_1.statSync)('/.dockerenv'); return true; } catch { return false; } } function hasDockerCGroup() { try { return (0, fs_1.readFileSync)('/proc/self/cgroup', 'utf8').includes('docker'); } catch { return false; } } let isDockerCached; function isDocker() { // TODO: Use `??=` when targeting Node.js 16. isDockerCached ??= hasDockerEnv() || hasDockerCGroup(); return isDockerCached; } exports.isDocker = isDocker;