UNPKG

homebridge-config-ui-x

Version:

A web based management, configuration and control platform for Homebridge

1,454 lines (1,310 loc) 1.43 MB
(function(e, a) { for(var i in a) e[i] = a[i]; }(exports, /******/ (function(modules) { // webpackBootstrap /******/ // The module cache /******/ var installedModules = {}; /******/ /******/ // The require function /******/ function __webpack_require__(moduleId) { /******/ /******/ // Check if module is in cache /******/ if(installedModules[moduleId]) { /******/ return installedModules[moduleId].exports; /******/ } /******/ // Create a new module (and put it into the cache) /******/ var module = installedModules[moduleId] = { /******/ i: moduleId, /******/ l: false, /******/ exports: {} /******/ }; /******/ /******/ // Execute the module function /******/ modules[moduleId].call(module.exports, module, module.exports, __webpack_require__); /******/ /******/ // Flag the module as loaded /******/ module.l = true; /******/ /******/ // Return the exports of the module /******/ return module.exports; /******/ } /******/ /******/ /******/ // expose the modules object (__webpack_modules__) /******/ __webpack_require__.m = modules; /******/ /******/ // expose the module cache /******/ __webpack_require__.c = installedModules; /******/ /******/ // define getter function for harmony exports /******/ __webpack_require__.d = function(exports, name, getter) { /******/ if(!__webpack_require__.o(exports, name)) { /******/ Object.defineProperty(exports, name, { enumerable: true, get: getter }); /******/ } /******/ }; /******/ /******/ // define __esModule on exports /******/ __webpack_require__.r = function(exports) { /******/ if(typeof Symbol !== 'undefined' && Symbol.toStringTag) { /******/ Object.defineProperty(exports, Symbol.toStringTag, { value: 'Module' }); /******/ } /******/ Object.defineProperty(exports, '__esModule', { value: true }); /******/ }; /******/ /******/ // create a fake namespace object /******/ // mode & 1: value is a module id, require it /******/ // mode & 2: merge all properties of value into the ns /******/ // mode & 4: return value when already ns object /******/ // mode & 8|1: behave like require /******/ __webpack_require__.t = function(value, mode) { /******/ if(mode & 1) value = __webpack_require__(value); /******/ if(mode & 8) return value; /******/ if((mode & 4) && typeof value === 'object' && value && value.__esModule) return value; /******/ var ns = Object.create(null); /******/ __webpack_require__.r(ns); /******/ Object.defineProperty(ns, 'default', { enumerable: true, value: value }); /******/ if(mode & 2 && typeof value != 'string') for(var key in value) __webpack_require__.d(ns, key, function(key) { return value[key]; }.bind(null, key)); /******/ return ns; /******/ }; /******/ /******/ // getDefaultExport function for compatibility with non-harmony modules /******/ __webpack_require__.n = function(module) { /******/ var getter = module && module.__esModule ? /******/ function getDefault() { return module['default']; } : /******/ function getModuleExports() { return module; }; /******/ __webpack_require__.d(getter, 'a', getter); /******/ return getter; /******/ }; /******/ /******/ // Object.prototype.hasOwnProperty.call /******/ __webpack_require__.o = function(object, property) { return Object.prototype.hasOwnProperty.call(object, property); }; /******/ /******/ // __webpack_public_path__ /******/ __webpack_require__.p = ""; /******/ /******/ /******/ // Load entry module and return exports /******/ return __webpack_require__(__webpack_require__.s = 71); /******/ }) /************************************************************************/ /******/ ([ /* 0 */ /***/ (function(module, exports) { module.exports = require("@nestjs/common"); /***/ }), /* 1 */ /***/ (function(module, exports, __webpack_require__) { "use strict"; var __decorate = (this && this.__decorate) || function (decorators, target, key, desc) { var c = arguments.length, r = c < 3 ? target : desc === null ? desc = Object.getOwnPropertyDescriptor(target, key) : desc, d; if (typeof Reflect === "object" && typeof Reflect.decorate === "function") r = Reflect.decorate(decorators, target, key, desc); else for (var i = decorators.length - 1; i >= 0; i--) if (d = decorators[i]) r = (c < 3 ? d(r) : c > 3 ? d(target, key, r) : d(target, key)) || r; return c > 3 && r && Object.defineProperty(target, key, r), r; }; var __metadata = (this && this.__metadata) || function (k, v) { if (typeof Reflect === "object" && typeof Reflect.metadata === "function") return Reflect.metadata(k, v); }; Object.defineProperty(exports, "__esModule", { value: true }); const common_1 = __webpack_require__(0); const os = __webpack_require__(8); const path = __webpack_require__(7); const fs = __webpack_require__(4); const crypto = __webpack_require__(20); const semver = __webpack_require__(26); let ConfigService = class ConfigService { constructor() { this.name = 'homebridge-config-ui-x'; this.configPath = process.env.UIX_CONFIG_PATH || path.resolve(os.homedir(), '.homebridge/config.json'); this.storagePath = process.env.UIX_STORAGE_PATH || path.resolve(os.homedir(), '.homebridge'); this.customPluginPath = process.env.UIX_CUSTOM_PLUGIN_PATH; this.secretPath = path.resolve(this.storagePath, '.uix-secrets'); this.authPath = path.resolve(this.storagePath, 'auth.json'); this.accessoryLayoutPath = path.resolve(this.storagePath, 'accessories', 'uiAccessoriesLayout.json'); this.homebridgeInsecureMode = Boolean(process.env.UIX_INSECURE_MODE === '1'); this.homebridgeNoTimestamps = Boolean(process.env.UIX_LOG_NO_TIMESTAMPS === '1'); this.minimumNodeVersion = '8.15.1'; this.serviceMode = (process.env.UIX_SERVICE_MODE === '1'); this.runningInDocker = Boolean(process.env.HOMEBRIDGE_CONFIG_UI === '1'); this.runningInLinux = (!this.runningInDocker && os.platform() === 'linux'); this.ableToConfigureSelf = (!this.runningInDocker || semver.satisfies(process.env.CONFIG_UI_VERSION, '>=3.5.5'), { includePrerelease: true }); this.enableTerminalAccess = this.runningInDocker || Boolean(process.env.HOMEBRIDGE_CONFIG_UI_TERMINAL === '1'); this.branding = process.env.CONFIG_UI_BRANDING || false; this.startupScript = path.resolve(this.storagePath, 'startup.sh'); this.dockerEnvFile = path.resolve(this.storagePath, '.docker.env'); this.dockerOfflineUpdate = this.runningInDocker && semver.satisfies(process.env.CONFIG_UI_VERSION, '>=4.6.2', { includePrerelease: true }); this.package = fs.readJsonSync(path.resolve(process.env.UIX_BASE_PATH, 'package.json')); this.homebridgeConfig = fs.readJSONSync(this.configPath); this.ui = Array.isArray(this.homebridgeConfig.platforms) ? this.homebridgeConfig.platforms.find(x => x.platform === 'config') : undefined; if (!this.ui) { this.ui = { name: 'Config', }; } process.env.UIX_PLUGIN_NAME = this.ui.name || 'homebridge-config-ui-x'; if (this.runningInDocker) { this.setConfigForDocker(); } if (this.serviceMode) { this.setConfigForServiceMode(); } if (!this.ui.port) { this.ui.port = 8080; } if (!this.ui.sessionTimeout) { this.ui.sessionTimeout = 28800; } this.secrets = this.getSecrets(); this.instanceId = this.getInstanceId(); } uiSettings() { return { env: { ableToConfigureSelf: this.ableToConfigureSelf, enableAccessories: this.homebridgeInsecureMode, enableTerminalAccess: this.enableTerminalAccess, homebridgeInstanceName: this.homebridgeConfig.bridge.name, nodeVersion: process.version, packageName: this.package.name, packageVersion: this.package.version, runningInDocker: this.runningInDocker, runningInLinux: this.runningInLinux, dockerOfflineUpdate: this.dockerOfflineUpdate, temperatureUnits: this.ui.tempUnits || 'c', websocketCompatibilityMode: this.ui.websocketCompatibilityMode || false, branding: this.branding, instanceId: this.instanceId, }, formAuth: Boolean(this.ui.auth !== 'none'), theme: this.ui.theme || 'auto', serverTimestamp: new Date().toISOString(), }; } setConfigForDocker() { this.ui.restart = 'killall -9 homebridge && killall -9 homebridge-config-ui-x'; this.homebridgeInsecureMode = Boolean(process.env.HOMEBRIDGE_INSECURE === '1'); this.ui.sudo = false; this.ui.log = { method: 'file', path: '/homebridge/logs/homebridge.log', }; if (!this.ui.port && process.env.HOMEBRIDGE_CONFIG_UI_PORT) { this.ui.port = parseInt(process.env.HOMEBRIDGE_CONFIG_UI_PORT, 10); } this.ui.theme = this.ui.theme || process.env.HOMEBRIDGE_CONFIG_UI_THEME || 'teal'; this.ui.auth = this.ui.auth || process.env.HOMEBRIDGE_CONFIG_UI_AUTH || 'form'; this.ui.temp = this.ui.temp || process.env.HOMEBRIDGE_CONFIG_UI_TEMP || undefined; this.ui.loginWallpaper = this.ui.loginWallpaper || process.env.HOMEBRIDGE_CONFIG_UI_LOGIN_WALLPAPER || undefined; } setConfigForServiceMode() { this.ui.restart = undefined; this.homebridgeInsecureMode = true; this.ui.log = { method: 'file', path: path.resolve(this.storagePath, 'homebridge.log'), }; } getSecrets() { if (fs.pathExistsSync(this.secretPath)) { try { const secrets = fs.readJsonSync(this.secretPath); if (!secrets.secretKey) { return this.generateSecretToken(); } else { return secrets; } } catch (e) { return this.generateSecretToken(); } } else { return this.generateSecretToken(); } } generateSecretToken() { const secrets = { secretKey: crypto.randomBytes(32).toString('hex'), }; fs.writeJsonSync(this.secretPath, secrets); return secrets; } getInstanceId() { return crypto.createHash('sha256').update(this.secrets.secretKey).digest('hex'); } }; ConfigService = __decorate([ common_1.Injectable(), __metadata("design:paramtypes", []) ], ConfigService); exports.ConfigService = ConfigService; /***/ }), /* 2 */ /***/ (function(module, exports, __webpack_require__) { "use strict"; function __export(m) { for (var p in m) if (!exports.hasOwnProperty(p)) exports[p] = m[p]; } exports.__esModule = true; __export(__webpack_require__(78)); /***/ }), /* 3 */ /***/ (function(module, exports, __webpack_require__) { "use strict"; Object.defineProperty(exports, "__esModule", { value: true }); const color = __webpack_require__(14); class Logger { constructor() { this.pluginName = (process.env.UIX_PLUGIN_NAME || 'homebridge-config-ui-x'); this.useTimestamps = (process.env.UIX_LOG_NO_TIMESTAMPS !== '1'); } get prefix() { if (this.useTimestamps) { return color.white(`[${new Date().toLocaleString()}] `) + color.cyan(`[${this.pluginName}]`); } else { return color.cyan(`[${this.pluginName}]`); } } log(args) { console.log(this.prefix, args); } error(args) { console.error(this.prefix, color.red(args)); } warn(args) { console.warn(this.prefix, color.yellow(args)); } debug(args) { console.debug(this.prefix, args); } verbose(args) { console.debug(this.prefix, args); } } exports.Logger = Logger; /***/ }), /* 4 */ /***/ (function(module, exports) { module.exports = require("fs-extra"); /***/ }), /* 5 */ /***/ (function(module, exports, __webpack_require__) { "use strict"; var __decorate = (this && this.__decorate) || function (decorators, target, key, desc) { var c = arguments.length, r = c < 3 ? target : desc === null ? desc = Object.getOwnPropertyDescriptor(target, key) : desc, d; if (typeof Reflect === "object" && typeof Reflect.decorate === "function") r = Reflect.decorate(decorators, target, key, desc); else for (var i = decorators.length - 1; i >= 0; i--) if (d = decorators[i]) r = (c < 3 ? d(r) : c > 3 ? d(target, key, r) : d(target, key)) || r; return c > 3 && r && Object.defineProperty(target, key, r), r; }; Object.defineProperty(exports, "__esModule", { value: true }); const common_1 = __webpack_require__(0); const logger_service_1 = __webpack_require__(3); let LoggerModule = class LoggerModule { }; LoggerModule = __decorate([ common_1.Module({ providers: [logger_service_1.Logger], exports: [logger_service_1.Logger], }) ], LoggerModule); exports.LoggerModule = LoggerModule; /***/ }), /* 6 */ /***/ (function(module, exports, __webpack_require__) { "use strict"; var __decorate = (this && this.__decorate) || function (decorators, target, key, desc) { var c = arguments.length, r = c < 3 ? target : desc === null ? desc = Object.getOwnPropertyDescriptor(target, key) : desc, d; if (typeof Reflect === "object" && typeof Reflect.decorate === "function") r = Reflect.decorate(decorators, target, key, desc); else for (var i = decorators.length - 1; i >= 0; i--) if (d = decorators[i]) r = (c < 3 ? d(r) : c > 3 ? d(target, key, r) : d(target, key)) || r; return c > 3 && r && Object.defineProperty(target, key, r), r; }; Object.defineProperty(exports, "__esModule", { value: true }); const common_1 = __webpack_require__(0); const config_service_1 = __webpack_require__(1); let ConfigModule = class ConfigModule { }; ConfigModule = __decorate([ common_1.Module({ providers: [config_service_1.ConfigService], exports: [config_service_1.ConfigService], }) ], ConfigModule); exports.ConfigModule = ConfigModule; /***/ }), /* 7 */ /***/ (function(module, exports) { module.exports = require("path"); /***/ }), /* 8 */ /***/ (function(module, exports) { module.exports = require("os"); /***/ }), /* 9 */ /***/ (function(module, exports) { module.exports = require("util"); /***/ }), /* 10 */ /***/ (function(module, exports, __webpack_require__) { "use strict"; var __decorate = (this && this.__decorate) || function (decorators, target, key, desc) { var c = arguments.length, r = c < 3 ? target : desc === null ? desc = Object.getOwnPropertyDescriptor(target, key) : desc, d; if (typeof Reflect === "object" && typeof Reflect.decorate === "function") r = Reflect.decorate(decorators, target, key, desc); else for (var i = decorators.length - 1; i >= 0; i--) if (d = decorators[i]) r = (c < 3 ? d(r) : c > 3 ? d(target, key, r) : d(target, key)) || r; return c > 3 && r && Object.defineProperty(target, key, r), r; }; var __metadata = (this && this.__metadata) || function (k, v) { if (typeof Reflect === "object" && typeof Reflect.metadata === "function") return Reflect.metadata(k, v); }; Object.defineProperty(exports, "__esModule", { value: true }); const common_1 = __webpack_require__(0); let AdminGuard = class AdminGuard { constructor() { } canActivate(context) { const request = context.switchToHttp().getRequest(); return request.user.admin; } }; AdminGuard = __decorate([ common_1.Injectable(), __metadata("design:paramtypes", []) ], AdminGuard); exports.AdminGuard = AdminGuard; /***/ }), /* 11 */ /***/ (function(module, exports) { module.exports = require("buffer"); /***/ }), /* 12 */ /***/ (function(module, exports) { module.exports = require("@nestjs/websockets"); /***/ }), /* 13 */ /***/ (function(module, exports, __webpack_require__) { /* eslint-disable node/no-deprecated-api */ var buffer = __webpack_require__(11) var Buffer = buffer.Buffer // alternative to using Object.keys for old browsers function copyProps (src, dst) { for (var key in src) { dst[key] = src[key] } } if (Buffer.from && Buffer.alloc && Buffer.allocUnsafe && Buffer.allocUnsafeSlow) { module.exports = buffer } else { // Copy properties from require('buffer') copyProps(buffer, exports) exports.Buffer = SafeBuffer } function SafeBuffer (arg, encodingOrOffset, length) { return Buffer(arg, encodingOrOffset, length) } // Copy static methods from Buffer copyProps(Buffer, SafeBuffer) SafeBuffer.from = function (arg, encodingOrOffset, length) { if (typeof arg === 'number') { throw new TypeError('Argument must not be a number') } return Buffer(arg, encodingOrOffset, length) } SafeBuffer.alloc = function (size, fill, encoding) { if (typeof size !== 'number') { throw new TypeError('Argument must be a number') } var buf = Buffer(size) if (fill !== undefined) { if (typeof encoding === 'string') { buf.fill(fill, encoding) } else { buf.fill(fill) } } else { buf.fill(0) } return buf } SafeBuffer.allocUnsafe = function (size) { if (typeof size !== 'number') { throw new TypeError('Argument must be a number') } return Buffer(size) } SafeBuffer.allocUnsafeSlow = function (size) { if (typeof size !== 'number') { throw new TypeError('Argument must be a number') } return buffer.SlowBuffer(size) } /***/ }), /* 14 */ /***/ (function(module, exports) { var bash_codes = exports.bash_codes = { "BLACK" : { "text" : "\033[0;30m", "underline": "\033[4;30m", "background": "\033[40m", "bold":"\033[1;30m", "hi_text":"\033[0;90m", "hi_bold" : "\033[1;90m", "hi_background" : "\033[0;100m" }, "RED" : { "text" : "\033[0;31m", "bold":"\033[1;31m", "underline": "\033[4;31m", "background": "\033[41m", "hi_text":"\033[0;91m", "hi_bold" : "\033[1;91m", "hi_background" : "\033[0;101m" }, "GREEN" : { "text" : "\033[0;32m", "bold":"\033[1;32m", "underline": "\033[4;32m", "background": "\033[42m", "hi_text":"\033[0;92m", "hi_bold" : "\033[1;92m", "hi_background" : "\033[0;102m" }, "YELLOW" : { "text" : "\033[0;33m", "bold":"\033[1;33m", "underline": "\033[4;33m", "background": "\033[43m", "hi_text":"\033[0;93m", "hi_bold" : "\033[1;93m", "hi_background" : "\033[0;103m" }, "BLUE" : { "text" : "\033[0;34m", "bold":"\033[1;34m", "underline": "\033[4;34m", "background": "\033[44m", "hi_text":"\033[0;94m", "hi_bold" : "\033[1;94m", "hi_background" : "\033[0;104m" }, "PURPLE" : { "text" : "\033[0;35m", "bold":"\033[1;35m", "underline": "\033[4;35m", "background": "\033[45m", "hi_text":"\033[0;95m", "hi_bold" : "\033[1;95m", "hi_background" : "\033[0;105m" }, "CYAN" : { "text" : "\033[0;36m", "bold":"\033[1;36m", "underline": "\033[4;36m", "background": "\033[46m", "hi_text":"\033[0;96m", "hi_bold" : "\033[1;96m", "hi_background" : "\033[0;106m" }, "WHITE" : { "text" : "\033[0;37m", "bold":"\033[1;37m", "underline": "\033[4;37m", "background": "\033[47m", "hi_text":"\033[0;97m", "hi_bold" : "\033[1;97m", "hi_background" : "\033[0;107m" } }; exports.colors = { BLACK: "BLACK", RED: "RED", GREEN: "GREEN", YELLOW: "YELLOW", BLUE: "BLUE", PURPLE: "PURPLE", CYAN: "CYAN", WHITE: "WHITE" }; var styles = exports.styles = { bold: "bold", underline: "underline", background: "background", hi_text: "hi_text", hi_bold: "hi_bold", hi_background: "hi_background" }; var REMOVE_COLOR = exports.REMOVE_COLOR = "\033[0m"; // various logical inconsistencies in the code below - renderColor and wrap seem like they should be combined, but I'm letting wrap basically stand on its own // in case anyone wants access to explicitly handle background or underline stuff. I feel like those are a bit more special-casey, and generally speakign // users are going to want to quickly turn a word or phrase into a single color without worrying about background or underline. So the .colorName methods // are just syntactic sugar. exports.wrap = function(str, color, style) { var c = bash_codes[color.toUpperCase()]; var s = styles[style] || "text"; return render(c[s], str); }; exports.black = function(str, hi) { return renderColor(str, bash_codes.BLACK, hi); }; exports.red = function(str, hi) { return renderColor(str, bash_codes.RED, hi); }; exports.green = function(str, hi) { return renderColor(str, bash_codes.GREEN, hi); }; exports.yellow = function(str, hi) { return renderColor(str, bash_codes.YELLOW, hi); }; exports.blue = function(str, hi) { return renderColor(str, bash_codes.BLUE, hi); }; exports.purple = function(str, hi) { return renderColor(str, bash_codes.PURPLE, hi); }; exports.cyan = function(str, hi) { return renderColor(str, bash_codes.CYAN, hi); }; exports.white = function(str, hi) { return renderColor(str, bash_codes.WHITE, hi); }; function renderColor(str, color, hi) { return render(hi ? color.hi_text : color.text, str); } function render(code, str) { return code + str + REMOVE_COLOR; } /***/ }), /* 15 */ /***/ (function(module, exports) { module.exports = require("child_process"); /***/ }), /* 16 */ /***/ (function(module, exports, __webpack_require__) { module.exports = { decode: __webpack_require__(45), verify: __webpack_require__(103), sign: __webpack_require__(105), JsonWebTokenError: __webpack_require__(23), NotBeforeError: __webpack_require__(49), TokenExpiredError: __webpack_require__(50), }; /***/ }), /* 17 */ /***/ (function(module, exports) { /* -*- Mode: js; js-indent-level: 2; -*- */ /* * Copyright 2011 Mozilla Foundation and contributors * Licensed under the New BSD license. See LICENSE or: * http://opensource.org/licenses/BSD-3-Clause */ /** * This is a helper function for getting values from parameter/options * objects. * * @param args The object we are extracting values from * @param name The name of the property we are getting. * @param defaultValue An optional value to return if the property is missing * from the object. If this is not specified and the property is missing, an * error will be thrown. */ function getArg(aArgs, aName, aDefaultValue) { if (aName in aArgs) { return aArgs[aName]; } else if (arguments.length === 3) { return aDefaultValue; } else { throw new Error('"' + aName + '" is a required argument.'); } } exports.getArg = getArg; var urlRegexp = /^(?:([\w+\-.]+):)?\/\/(?:(\w+:\w+)@)?([\w.-]*)(?::(\d+))?(.*)$/; var dataUrlRegexp = /^data:.+\,.+$/; function urlParse(aUrl) { var match = aUrl.match(urlRegexp); if (!match) { return null; } return { scheme: match[1], auth: match[2], host: match[3], port: match[4], path: match[5] }; } exports.urlParse = urlParse; function urlGenerate(aParsedUrl) { var url = ''; if (aParsedUrl.scheme) { url += aParsedUrl.scheme + ':'; } url += '//'; if (aParsedUrl.auth) { url += aParsedUrl.auth + '@'; } if (aParsedUrl.host) { url += aParsedUrl.host; } if (aParsedUrl.port) { url += ":" + aParsedUrl.port } if (aParsedUrl.path) { url += aParsedUrl.path; } return url; } exports.urlGenerate = urlGenerate; /** * Normalizes a path, or the path portion of a URL: * * - Replaces consecutive slashes with one slash. * - Removes unnecessary '.' parts. * - Removes unnecessary '<dir>/..' parts. * * Based on code in the Node.js 'path' core module. * * @param aPath The path or url to normalize. */ function normalize(aPath) { var path = aPath; var url = urlParse(aPath); if (url) { if (!url.path) { return aPath; } path = url.path; } var isAbsolute = exports.isAbsolute(path); var parts = path.split(/\/+/); for (var part, up = 0, i = parts.length - 1; i >= 0; i--) { part = parts[i]; if (part === '.') { parts.splice(i, 1); } else if (part === '..') { up++; } else if (up > 0) { if (part === '') { // The first part is blank if the path is absolute. Trying to go // above the root is a no-op. Therefore we can remove all '..' parts // directly after the root. parts.splice(i + 1, up); up = 0; } else { parts.splice(i, 2); up--; } } } path = parts.join('/'); if (path === '') { path = isAbsolute ? '/' : '.'; } if (url) { url.path = path; return urlGenerate(url); } return path; } exports.normalize = normalize; /** * Joins two paths/URLs. * * @param aRoot The root path or URL. * @param aPath The path or URL to be joined with the root. * * - If aPath is a URL or a data URI, aPath is returned, unless aPath is a * scheme-relative URL: Then the scheme of aRoot, if any, is prepended * first. * - Otherwise aPath is a path. If aRoot is a URL, then its path portion * is updated with the result and aRoot is returned. Otherwise the result * is returned. * - If aPath is absolute, the result is aPath. * - Otherwise the two paths are joined with a slash. * - Joining for example 'http://' and 'www.example.com' is also supported. */ function join(aRoot, aPath) { if (aRoot === "") { aRoot = "."; } if (aPath === "") { aPath = "."; } var aPathUrl = urlParse(aPath); var aRootUrl = urlParse(aRoot); if (aRootUrl) { aRoot = aRootUrl.path || '/'; } // `join(foo, '//www.example.org')` if (aPathUrl && !aPathUrl.scheme) { if (aRootUrl) { aPathUrl.scheme = aRootUrl.scheme; } return urlGenerate(aPathUrl); } if (aPathUrl || aPath.match(dataUrlRegexp)) { return aPath; } // `join('http://', 'www.example.com')` if (aRootUrl && !aRootUrl.host && !aRootUrl.path) { aRootUrl.host = aPath; return urlGenerate(aRootUrl); } var joined = aPath.charAt(0) === '/' ? aPath : normalize(aRoot.replace(/\/+$/, '') + '/' + aPath); if (aRootUrl) { aRootUrl.path = joined; return urlGenerate(aRootUrl); } return joined; } exports.join = join; exports.isAbsolute = function (aPath) { return aPath.charAt(0) === '/' || urlRegexp.test(aPath); }; /** * Make a path relative to a URL or another path. * * @param aRoot The root path or URL. * @param aPath The path or URL to be made relative to aRoot. */ function relative(aRoot, aPath) { if (aRoot === "") { aRoot = "."; } aRoot = aRoot.replace(/\/$/, ''); // It is possible for the path to be above the root. In this case, simply // checking whether the root is a prefix of the path won't work. Instead, we // need to remove components from the root one by one, until either we find // a prefix that fits, or we run out of components to remove. var level = 0; while (aPath.indexOf(aRoot + '/') !== 0) { var index = aRoot.lastIndexOf("/"); if (index < 0) { return aPath; } // If the only part of the root that is left is the scheme (i.e. http://, // file:///, etc.), one or more slashes (/), or simply nothing at all, we // have exhausted all components, so the path is not relative to the root. aRoot = aRoot.slice(0, index); if (aRoot.match(/^([^\/]+:\/)?\/*$/)) { return aPath; } ++level; } // Make sure we add a "../" for each component we removed from the root. return Array(level + 1).join("../") + aPath.substr(aRoot.length + 1); } exports.relative = relative; var supportsNullProto = (function () { var obj = Object.create(null); return !('__proto__' in obj); }()); function identity (s) { return s; } /** * Because behavior goes wacky when you set `__proto__` on objects, we * have to prefix all the strings in our set with an arbitrary character. * * See https://github.com/mozilla/source-map/pull/31 and * https://github.com/mozilla/source-map/issues/30 * * @param String aStr */ function toSetString(aStr) { if (isProtoString(aStr)) { return '$' + aStr; } return aStr; } exports.toSetString = supportsNullProto ? identity : toSetString; function fromSetString(aStr) { if (isProtoString(aStr)) { return aStr.slice(1); } return aStr; } exports.fromSetString = supportsNullProto ? identity : fromSetString; function isProtoString(s) { if (!s) { return false; } var length = s.length; if (length < 9 /* "__proto__".length */) { return false; } if (s.charCodeAt(length - 1) !== 95 /* '_' */ || s.charCodeAt(length - 2) !== 95 /* '_' */ || s.charCodeAt(length - 3) !== 111 /* 'o' */ || s.charCodeAt(length - 4) !== 116 /* 't' */ || s.charCodeAt(length - 5) !== 111 /* 'o' */ || s.charCodeAt(length - 6) !== 114 /* 'r' */ || s.charCodeAt(length - 7) !== 112 /* 'p' */ || s.charCodeAt(length - 8) !== 95 /* '_' */ || s.charCodeAt(length - 9) !== 95 /* '_' */) { return false; } for (var i = length - 10; i >= 0; i--) { if (s.charCodeAt(i) !== 36 /* '$' */) { return false; } } return true; } /** * Comparator between two mappings where the original positions are compared. * * Optionally pass in `true` as `onlyCompareGenerated` to consider two * mappings with the same original source/line/column, but different generated * line and column the same. Useful when searching for a mapping with a * stubbed out mapping. */ function compareByOriginalPositions(mappingA, mappingB, onlyCompareOriginal) { var cmp = strcmp(mappingA.source, mappingB.source); if (cmp !== 0) { return cmp; } cmp = mappingA.originalLine - mappingB.originalLine; if (cmp !== 0) { return cmp; } cmp = mappingA.originalColumn - mappingB.originalColumn; if (cmp !== 0 || onlyCompareOriginal) { return cmp; } cmp = mappingA.generatedColumn - mappingB.generatedColumn; if (cmp !== 0) { return cmp; } cmp = mappingA.generatedLine - mappingB.generatedLine; if (cmp !== 0) { return cmp; } return strcmp(mappingA.name, mappingB.name); } exports.compareByOriginalPositions = compareByOriginalPositions; /** * Comparator between two mappings with deflated source and name indices where * the generated positions are compared. * * Optionally pass in `true` as `onlyCompareGenerated` to consider two * mappings with the same generated line and column, but different * source/name/original line and column the same. Useful when searching for a * mapping with a stubbed out mapping. */ function compareByGeneratedPositionsDeflated(mappingA, mappingB, onlyCompareGenerated) { var cmp = mappingA.generatedLine - mappingB.generatedLine; if (cmp !== 0) { return cmp; } cmp = mappingA.generatedColumn - mappingB.generatedColumn; if (cmp !== 0 || onlyCompareGenerated) { return cmp; } cmp = strcmp(mappingA.source, mappingB.source); if (cmp !== 0) { return cmp; } cmp = mappingA.originalLine - mappingB.originalLine; if (cmp !== 0) { return cmp; } cmp = mappingA.originalColumn - mappingB.originalColumn; if (cmp !== 0) { return cmp; } return strcmp(mappingA.name, mappingB.name); } exports.compareByGeneratedPositionsDeflated = compareByGeneratedPositionsDeflated; function strcmp(aStr1, aStr2) { if (aStr1 === aStr2) { return 0; } if (aStr1 === null) { return 1; // aStr2 !== null } if (aStr2 === null) { return -1; // aStr1 !== null } if (aStr1 > aStr2) { return 1; } return -1; } /** * Comparator between two mappings with inflated source and name strings where * the generated positions are compared. */ function compareByGeneratedPositionsInflated(mappingA, mappingB) { var cmp = mappingA.generatedLine - mappingB.generatedLine; if (cmp !== 0) { return cmp; } cmp = mappingA.generatedColumn - mappingB.generatedColumn; if (cmp !== 0) { return cmp; } cmp = strcmp(mappingA.source, mappingB.source); if (cmp !== 0) { return cmp; } cmp = mappingA.originalLine - mappingB.originalLine; if (cmp !== 0) { return cmp; } cmp = mappingA.originalColumn - mappingB.originalColumn; if (cmp !== 0) { return cmp; } return strcmp(mappingA.name, mappingB.name); } exports.compareByGeneratedPositionsInflated = compareByGeneratedPositionsInflated; /** * Strip any JSON XSSI avoidance prefix from the string (as documented * in the source maps specification), and then parse the string as * JSON. */ function parseSourceMapInput(str) { return JSON.parse(str.replace(/^\)]}'[^\n]*\n/, '')); } exports.parseSourceMapInput = parseSourceMapInput; /** * Compute the URL of a source given the the source root, the source's * URL, and the source map's URL. */ function computeSourceURL(sourceRoot, sourceURL, sourceMapURL) { sourceURL = sourceURL || ''; if (sourceRoot) { // This follows what Chrome does. if (sourceRoot[sourceRoot.length - 1] !== '/' && sourceURL[0] !== '/') { sourceRoot += '/'; } // The spec says: // Line 4: An optional source root, useful for relocating source // files on a server or removing repeated values in the // “sources” entry. This value is prepended to the individual // entries in the “source” field. sourceURL = sourceRoot + sourceURL; } // Historically, SourceMapConsumer did not take the sourceMapURL as // a parameter. This mode is still somewhat supported, which is why // this code block is conditional. However, it's preferable to pass // the source map URL to SourceMapConsumer, so that this function // can implement the source URL resolution algorithm as outlined in // the spec. This block is basically the equivalent of: // new URL(sourceURL, sourceMapURL).toString() // ... except it avoids using URL, which wasn't available in the // older releases of node still supported by this library. // // The spec says: // If the sources are not absolute URLs after prepending of the // “sourceRoot”, the sources are resolved relative to the // SourceMap (like resolving script src in a html document). if (sourceMapURL) { var parsed = urlParse(sourceMapURL); if (!parsed) { throw new Error("sourceMapURL could not be parsed"); } if (parsed.path) { // Strip the last path component, but keep the "/". var index = parsed.path.lastIndexOf('/'); if (index >= 0) { parsed.path = parsed.path.substring(0, index + 1); } } sourceURL = join(urlGenerate(parsed), sourceURL); } return normalize(sourceURL); } exports.computeSourceURL = computeSourceURL; /***/ }), /* 18 */ /***/ (function(module, exports) { module.exports = require("events"); /***/ }), /* 19 */ /***/ (function(module, exports) { module.exports = require("node-pty-prebuilt-multiarch"); /***/ }), /* 20 */ /***/ (function(module, exports) { module.exports = require("crypto"); /***/ }), /* 21 */ /***/ (function(module, exports, __webpack_require__) { "use strict"; var __decorate = (this && this.__decorate) || function (decorators, target, key, desc) { var c = arguments.length, r = c < 3 ? target : desc === null ? desc = Object.getOwnPropertyDescriptor(target, key) : desc, d; if (typeof Reflect === "object" && typeof Reflect.decorate === "function") r = Reflect.decorate(decorators, target, key, desc); else for (var i = decorators.length - 1; i >= 0; i--) if (d = decorators[i]) r = (c < 3 ? d(r) : c > 3 ? d(target, key, r) : d(target, key)) || r; return c > 3 && r && Object.defineProperty(target, key, r), r; }; var __metadata = (this && this.__metadata) || function (k, v) { if (typeof Reflect === "object" && typeof Reflect.metadata === "function") return Reflect.metadata(k, v); }; var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, P, generator) { function adopt(value) { return value instanceof P ? value : new P(function (resolve) { resolve(value); }); } return new (P || (P = Promise))(function (resolve, reject) { function fulfilled(value) { try { step(generator.next(value)); } catch (e) { reject(e); } } function rejected(value) { try { step(generator["throw"](value)); } catch (e) { reject(e); } } function step(result) { result.done ? resolve(result.value) : adopt(result.value).then(fulfilled, rejected); } step((generator = generator.apply(thisArg, _arguments || [])).next()); }); }; Object.defineProperty(exports, "__esModule", { value: true }); const common_1 = __webpack_require__(0); const os = __webpack_require__(8); const _ = __webpack_require__(94); const path = __webpack_require__(7); const fs = __webpack_require__(4); const child_process = __webpack_require__(15); const semver = __webpack_require__(26); const rp = __webpack_require__(30); const color = __webpack_require__(14); const pty = __webpack_require__(19); const logger_service_1 = __webpack_require__(3); const config_service_1 = __webpack_require__(1); let PluginsService = class PluginsService { constructor(configService, logger) { this.configService = configService; this.logger = logger; this.npm = this.getNpmPath(); this.paths = this.getBasePaths(); this.rp = rp.defaults({ json: true, headers: { 'User-Agent': this.configService.package.name, }, timeout: 5000, }); } getInstalledPlugins() { return __awaiter(this, void 0, void 0, function* () { const plugins = []; const modules = yield this.getInstalledModules(); const homebridgePlugins = modules .filter(module => (module.name.indexOf('homebridge-') === 0)) .filter((module) => __awaiter(this, void 0, void 0, function* () { return (yield fs.pathExists(path.join(module.installPath, 'package.json')).catch(x => null)); })) .filter(x => x); yield Promise.all(homebridgePlugins.map((pkg) => __awaiter(this, void 0, void 0, function* () { try { const pjson = yield fs.readJson(path.join(pkg.installPath, 'package.json')); if (pjson.keywords && pjson.keywords.includes('homebridge-plugin')) { const plugin = yield this.parsePackageJson(pjson, pkg.path); if (!plugins.find(x => plugin.name === x.name)) { plugins.push(plugin); } else if (!plugin.globalInstall && plugins.find(x => plugin.name === x.name && x.globalInstall === true)) { const index = plugins.findIndex(x => plugin.name === x.name && x.globalInstall === true); plugins[index] = plugin; } } } catch (e) { this.logger.error(`Failed to parse plugin "${pkg.name}": ${e.message}`); } }))); this.installedPlugins = plugins; return _.orderBy(plugins, ['updateAvailable', 'name'], ['desc', 'asc']); }); } getOutOfDatePlugins() { return __awaiter(this, void 0, void 0, function* () { const plugins = yield this.getInstalledPlugins(); return plugins.filter(x => x.updateAvailable); }); } searchNpmRegistry(query) { return __awaiter(this, void 0, void 0, function* () { if (!this.installedPlugins) { yield this.getInstalledPlugins(); } const q = ((!query || !query.length) ? '' : query + '+') + 'keywords:homebridge-plugin+not:deprecated&size=30'; const searchResults = yield this.rp.get(`https://registry.npmjs.org/-/v1/search?text=${q}`); const result = searchResults.objects .filter(x => x.package.name.indexOf('homebridge-') === 0) .map((pkg) => { let plugin = { name: pkg.package.name, }; const isInstalled = this.installedPlugins.find(x => x.name === plugin.name); if (isInstalled) { plugin = isInstalled; return plugin; } plugin.publicPackage = true; plugin.installedVersion = null; plugin.latestVersion = pkg.package.version; plugin.lastUpdated = pkg.package.date; plugin.description = (pkg.package.description) ? pkg.package.description.replace(/(?:https?|ftp):\/\/[\n\S]+/g, '').trim() : pkg.package.name; plugin.links = pkg.package.links; plugin.author = (pkg.package.publisher) ? pkg.package.publisher.username : null; plugin.certifiedPlugin = (pkg.package.name.indexOf('@homebridge/homebridge-') === 0); return plugin; }); if (!result.length && query.indexOf('homebridge-') === 0) { return yield this.searchNpmRegistrySingle(query); } return result; }); } searchNpmRegistrySingle(query) { return __awaiter(this, void 0, void 0, function* () { try { const pkg = yield this.rp.get(`https://registry.npmjs.org/${encodeURIComponent(query).replace('%40', '@')}`); if (!pkg.keywords || !pkg.keywords.includes('homebridge-plugin')) { return []; } let plugin; const isInstalled = this.installedPlugins.find(x => x.name === pkg.name); if (isInstalled) { plugin = isInstalled; return [plugin]; } plugin = { name: pkg.name, description: (pkg.description) ? pkg.description.replace(/(?:https?|ftp):\/\/[\n\S]+/g, '').trim() : pkg.name, certifiedPlugin: (pkg.name.indexOf('@homebridge/homebridge-') === 0), }; plugin.publicPackage = true; plugin.latestVersion = pkg['dist-tags'].latest; plugin.lastUpdated = pkg.package.date; plugin.updateAvailable = false; plugin.links = { npm: `https://www.npmjs.com/package/${plugin.name}`, homepage: pkg.homepage, bugs: (pkg.bugs) ? pkg.bugs.url : null, }; plugin.author = (pkg.maintainers.length) ? pkg.maintainers[0].name : null; plugin.certifiedPlugin = (pkg.name.indexOf('@homebridge/homebridge-') === 0); return [plugin]; } catch (e) { if (e.statusCode !== 404) { this.logger.error('Failed to search npm registry'); this.logger.error(e.message); } return []; } }); } installPlugin(pluginName, client) { return __awaiter(this, void 0, void 0, function* () { yield this.getInstalledPlugins(); let installPath = (this.configService.customPluginPath) ? this.configService.customPluginPath : this.installedPlugins.find(x => x.name === this.configService.name).installPath; const installOptions = []; if (installPath === this.configService.customPluginPath && (yield fs.pathExists(path.resolve(installPath, '../package.json')))) { installOptions.push('--save'); } installPath = path.resolve(installPath, '../'); yield this.runNpmCommand([...this.npm, 'install', '--unsafe-perm', ...installOptions, `${pluginName}@latest`], installPath, client); return true; }); } uninstallPlugin(pluginName, client) { return __awaiter(this, void 0, void 0, function* () { yield this.getInstalledPlugins(); const plugin = this.installedPlugins.find(x => x.name === pluginName); if (!plugin) { throw new Error(`Plugin "${pluginName}" Not Found`); } let installPath = plugin.installPath; const installOptions = []; if (installPath === this.configService.customPluginPath && (yield fs.pathExists(path.resolve(installPath, '../package.json')))) { installOptions.push('--save'); } installPath = path.resolve(installPath, '../'); yield this.runNpmCommand([...this.npm, 'uninstall', '--unsafe-perm', ...installOptions, pluginName], installPath, client); yield this.ensureCustomPluginDirExists(); return true; }); } updatePlugin(pluginName, client) { return __awaiter(this, void 0, void 0, function* () { if (pluginName === this.configService.name && this.configService.dockerOfflineUpdate) { yield this.updateSelfOffline(client); return true; } yield this.getInstalledPlugins(); const plugin = this.installedPlugins.find(x => x.name === pluginName); if (!plugin) { throw new Error(`Plugin "${pluginName}" Not Found`); } let installPath = plugin.installPath; const installOptions = []; if (installPath === this.configService.customPluginPath && (yield fs.pathExists(path.resolve(installPath, '../package.json')))) { installOptions.push('--save'); } installPath = path.resolve(installPath, '../'); yield this.runNpmCommand([...this.npm, 'install', '--unsafe-perm', ...installOptions, `${pluginName}@latest`], installPath, client); return true; }); } getHomebridgePackage() { return __awaiter(this, void 0, void 0, function* () { if (this.configService.ui.homebridgePackagePath) { const pjsonPath = path.join(this.configService.ui.homebridgePackagePath, 'package.json'); if (yield fs.pathExists(pjsonPath)) { return yield this.parsePackageJson(yield fs.readJson(pjsonPath), this.configService.ui.homebridgePackagePath); } else { this.logger.error(`"homebridgePath" (${this.configService.ui.homebridgePackagePath}) does not exist`); } } const modules = yield this.getInstalledModules(); const homebridgeInstalls = modules.filter(x => x.name === 'homebridge'); if (homebridgeInstalls.length > 1) { this.logger.warn('Multiple Instances Of Homebridge Found Installed'); homebridgeInstalls.forEach((instance) => { this.logger.warn(instance.installPath); }); } if (!homebridgeInstalls.length) { this.logger.error('Unable To Find Homebridge Installation'); throw new Error('Unable To Find Homebridge Installation'); } const homebridgeModule = homebridgeInstalls[0]; const pjson = yield fs.readJson(path.join(homebridgeModule.installPath, 'package.json')); const homebridge = yield this.parsePackageJson(pjson, homebridgeModule.path); return homebridge; }); } updateHomebridgePackage(client) { return __awaiter(this, void 0, void 0, function* () { const homebridge = yield this.getHomebridgePackage(); let installPath = homebridge.installPath; const installOptions = []; if (installPath === this.configService.customPluginPath && (yield fs.pathExists(path.resolve(installPath, '../package.json')))) { installOptions.push('--save'); } installPath = path.resolve(installPath, '../'); yield this.runNpmCommand([...this.npm, 'install', '--unsafe-perm', ...installOptions, `${homebridge.name}@latest`], installPath, client); return true; }); } updateSelfOffline(client) { return __awaiter(this, void 0, void 0, function* () { client.emit('stdout', color.yellow(`${this.configService.name} has been scheduled to update on the next container restart.\n\r\n\r`)); yield new Promise(resolve => setTimeout(resolve, 800)); client.emit('stdout', color.yellow(`The Docker container will now try and restart.\n\r\n\r`)); yield new Promise(resolve => setTimeout(resolve, 800)); client.emit('stdout', color.yellow(`If you have not started the Docker container with `) + color.red('--restart=always') + color.yellow(` you may\n\rneed to manually start the container again.\n\r\n\r`)); yield new Promise(resolve => setTimeout(resolve, 800)); client.emit('stdout', color.yellow(`This process may take several minutes. Please be patient.\n\r`)); yield new Promise(resolve => setTimeout(resolve, 10000)); yield fs.createFile('/homebridge/.uix-upgrade-on-restart'); }); } getPluginConfigSchema(pluginName) { return __awaiter(this, void 0, void 0, function* () { if (!this.installedPlugins) yield this.getInstalledPlugins(); const plugin = this.installedPlugins.find(x => x.name === pluginName); if (!plugin) { throw new common_1.NotFoundException(); } if (!plugin.settingsSchema) { throw new common_1.NotFoundException(); } const schemaPath = path.resolve(plugin.installPath, pluginName, 'config.schema.json'); co