homebridge-config-ui-x
Version:
A web based management, configuration and control platform for Homebridge.
249 lines • 12.6 kB
JavaScript
"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 __importDefault = (this && this.__importDefault) || function (mod) {
return (mod && mod.__esModule) ? mod : { "default": mod };
};
Object.defineProperty(exports, "__esModule", { value: true });
exports.ConfigService = void 0;
const node_crypto_1 = require("node:crypto");
const node_os_1 = require("node:os");
const node_path_1 = require("node:path");
const node_process_1 = __importDefault(require("node:process"));
const common_1 = require("@nestjs/common");
const fs_extra_1 = require("fs-extra");
const lodash_1 = require("lodash");
const semver_1 = require("semver");
let ConfigService = class ConfigService {
constructor() {
this.name = 'homebridge-config-ui-x';
this.configPath = node_process_1.default.env.UIX_CONFIG_PATH || (0, node_path_1.resolve)((0, node_os_1.homedir)(), '.homebridge/config.json');
this.storagePath = node_process_1.default.env.UIX_STORAGE_PATH || (0, node_path_1.resolve)((0, node_os_1.homedir)(), '.homebridge');
this.customPluginPath = node_process_1.default.env.UIX_CUSTOM_PLUGIN_PATH;
this.strictPluginResolution = (node_process_1.default.env.UIX_STRICT_PLUGIN_RESOLUTION === '1');
this.secretPath = (0, node_path_1.resolve)(this.storagePath, '.uix-secrets');
this.authPath = (0, node_path_1.resolve)(this.storagePath, 'auth.json');
this.accessoryLayoutPath = (0, node_path_1.resolve)(this.storagePath, 'accessories', 'uiAccessoriesLayout.json');
this.configBackupPath = (0, node_path_1.resolve)(this.storagePath, 'backups/config-backups');
this.instanceBackupPath = (0, node_path_1.resolve)(this.storagePath, 'backups/instance-backups');
this.homebridgeInsecureMode = Boolean(node_process_1.default.env.UIX_INSECURE_MODE === '1');
this.minimumNodeVersion = '14.15.0';
this.serviceMode = (node_process_1.default.env.UIX_SERVICE_MODE === '1');
this.runningInDocker = Boolean(node_process_1.default.env.HOMEBRIDGE_CONFIG_UI === '1');
this.runningInSynologyPackage = Boolean(node_process_1.default.env.HOMEBRIDGE_SYNOLOGY_PACKAGE === '1');
this.runningInPackageMode = Boolean(node_process_1.default.env.HOMEBRIDGE_APT_PACKAGE === '1');
this.runningInLinux = (!this.runningInDocker && !this.runningInSynologyPackage && !this.runningInPackageMode && (0, node_os_1.platform)() === 'linux');
this.runningInFreeBSD = ((0, node_os_1.platform)() === 'freebsd');
this.canShutdownRestartHost = (this.runningInLinux || node_process_1.default.env.UIX_CAN_SHUTDOWN_RESTART_HOST === '1');
this.enableTerminalAccess = (this.runningInDocker && node_process_1.default.env.HOMEBRIDGE_CONFIG_UI_TERMINAL !== '0')
|| (this.runningInPackageMode && node_process_1.default.env.HOMEBRIDGE_CONFIG_UI_TERMINAL !== '0')
|| this.runningInSynologyPackage
|| Boolean(node_process_1.default.env.HOMEBRIDGE_CONFIG_UI_TERMINAL === '1');
this.usePnpm = (node_process_1.default.env.UIX_USE_PNPM === '1');
this.usePluginBundles = (node_process_1.default.env.UIX_USE_PLUGIN_BUNDLES === '1');
this.recommendChildBridges = ((0, node_os_1.totalmem)() > 2e+9);
this.runningOnRaspberryPi = false;
this.startupScript = (0, node_path_1.resolve)(this.storagePath, 'startup.sh');
this.dockerOfflineUpdate = this.runningInDocker && (0, semver_1.satisfies)(node_process_1.default.env.CONFIG_UI_VERSION, '>=4.6.2 <=4.44.1', { includePrerelease: true });
this.package = (0, fs_extra_1.readJsonSync)((0, node_path_1.resolve)(node_process_1.default.env.UIX_BASE_PATH, 'package.json'));
this.setupWizardComplete = true;
this.hbServiceUiRestartRequired = false;
const homebridgeConfig = (0, fs_extra_1.readJSONSync)(this.configPath);
this.parseConfig(homebridgeConfig);
this.checkIfRunningOnRaspberryPi();
}
parseConfig(homebridgeConfig) {
this.homebridgeConfig = homebridgeConfig;
if (!this.homebridgeConfig.bridge) {
this.homebridgeConfig.bridge = {};
}
this.ui = Array.isArray(this.homebridgeConfig.platforms) ? this.homebridgeConfig.platforms.find(x => x.platform === 'config') : undefined;
if (!this.ui) {
this.ui = {
name: 'Config',
};
}
node_process_1.default.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 = this.ui.auth === 'none' ? 1296000 : 28800;
}
if (this.ui.scheduledBackupPath) {
this.instanceBackupPath = this.ui.scheduledBackupPath;
}
else {
this.instanceBackupPath = (0, node_path_1.resolve)(this.storagePath, 'backups/instance-backups');
}
this.secrets = this.getSecrets();
this.instanceId = this.getInstanceId();
this.freezeUiSettings();
this.getCustomWallpaperHash();
}
uiSettings(authorized = false) {
const toReturn = {
env: {
canShutdownRestartHost: this.canShutdownRestartHost,
customWallpaperHash: this.customWallpaperHash,
dockerOfflineUpdate: this.dockerOfflineUpdate,
homebridgeVersion: this.homebridgeVersion || null,
homebridgeInstanceName: this.homebridgeConfig.bridge.name,
instanceId: this.instanceId,
lang: this.ui.lang === 'auto' ? null : this.ui.lang,
packageName: this.package.name,
packageVersion: this.package.version,
platform: (0, node_os_1.platform)(),
port: this.ui.port,
serviceMode: this.serviceMode,
setupWizardComplete: this.setupWizardComplete,
},
formAuth: Boolean(this.ui.auth !== 'none'),
lightingMode: this.ui.lightingMode || 'auto',
serverTimestamp: new Date().toISOString(),
theme: this.ui.theme || 'orange',
menuMode: this.ui.menuMode || 'default',
};
if (!authorized) {
return toReturn;
}
return {
...toReturn,
env: {
...toReturn.env,
enableAccessories: this.homebridgeInsecureMode,
enableTerminalAccess: this.enableTerminalAccess,
nodeVersion: node_process_1.default.version,
recommendChildBridges: this.recommendChildBridges,
runningInDocker: this.runningInDocker,
runningInSynologyPackage: this.runningInSynologyPackage,
runningInPackageMode: this.runningInPackageMode,
runningInLinux: this.runningInLinux,
runningInFreeBSD: this.runningInFreeBSD,
runningOnRaspberryPi: this.runningOnRaspberryPi,
temperatureUnits: this.ui.tempUnits || 'c',
usePnpm: this.usePnpm,
},
wallpaper: this.ui.wallpaper,
};
}
async uiRestartRequired() {
if (this.hbServiceUiRestartRequired) {
return true;
}
const currentPackage = await (0, fs_extra_1.readJson)((0, node_path_1.resolve)(node_process_1.default.env.UIX_BASE_PATH, 'package.json'));
if (currentPackage.version !== this.package.version) {
return true;
}
return !((0, lodash_1.isEqual)(this.ui, this.uiFreeze) && (0, lodash_1.isEqual)(this.homebridgeConfig.bridge, this.bridgeFreeze));
}
freezeUiSettings() {
if (!this.uiFreeze) {
this.uiFreeze = {};
Object.assign(this.uiFreeze, this.ui);
}
if (!this.bridgeFreeze) {
this.bridgeFreeze = {};
Object.assign(this.bridgeFreeze, this.homebridgeConfig.bridge);
}
}
setConfigForDocker() {
this.ui.restart = 'killall -15 homebridge; sleep 5.1; killall -9 homebridge; kill -9 $(pidof homebridge-config-ui-x);';
this.homebridgeInsecureMode = Boolean(node_process_1.default.env.HOMEBRIDGE_INSECURE === '1');
this.ui.sudo = false;
this.ui.log = {
method: 'file',
path: '/homebridge/logs/homebridge.log',
};
if (!this.ui.port && node_process_1.default.env.HOMEBRIDGE_CONFIG_UI_PORT) {
this.ui.port = Number.parseInt(node_process_1.default.env.HOMEBRIDGE_CONFIG_UI_PORT, 10);
}
this.ui.theme = this.ui.theme || node_process_1.default.env.HOMEBRIDGE_CONFIG_UI_THEME || 'orange';
this.ui.auth = this.ui.auth || node_process_1.default.env.HOMEBRIDGE_CONFIG_UI_AUTH || 'form';
this.ui.wallpaper = this.ui.wallpaper || node_process_1.default.env.HOMEBRIDGE_CONFIG_UI_LOGIN_WALLPAPER || undefined;
}
setConfigForServiceMode() {
this.homebridgeInsecureMode = Boolean(node_process_1.default.env.UIX_INSECURE_MODE === '1');
this.ui.restart = undefined;
this.ui.sudo = ((0, node_os_1.platform)() === 'linux' && !this.runningInDocker && !this.runningInSynologyPackage && !this.runningInPackageMode) || (0, node_os_1.platform)() === 'freebsd';
this.ui.log = {
method: 'native',
path: (0, node_path_1.resolve)(this.storagePath, 'homebridge.log'),
};
}
getSecrets() {
if ((0, fs_extra_1.pathExistsSync)(this.secretPath)) {
try {
const secrets = (0, fs_extra_1.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: (0, node_crypto_1.randomBytes)(32).toString('hex'),
};
(0, fs_extra_1.writeJsonSync)(this.secretPath, secrets);
return secrets;
}
getInstanceId() {
return (0, node_crypto_1.createHash)('sha256').update(this.secrets.secretKey).digest('hex');
}
async getCustomWallpaperHash() {
try {
if (this.ui.wallpaper) {
const filePath = (0, node_path_1.resolve)(this.storagePath, this.ui.wallpaper);
const fileStat = await (0, fs_extra_1.stat)(filePath);
const hash = (0, node_crypto_1.createHash)('sha256');
hash.update(`${fileStat.birthtime}${fileStat.ctime}${fileStat.size}${fileStat.blocks}`);
this.customWallpaperHash = `${hash.digest('hex')}.jpg`;
}
}
catch (e) {
}
}
removeWallpaperCache() {
this.customWallpaperHash = undefined;
}
streamCustomWallpaper() {
return (0, fs_extra_1.createReadStream)((0, node_path_1.resolve)(this.storagePath, this.ui.wallpaper));
}
async checkIfRunningOnRaspberryPi() {
try {
this.runningOnRaspberryPi = await (0, fs_extra_1.pathExists)('/usr/bin/vcgencmd') && await (0, fs_extra_1.pathExists)('/usr/bin/raspi-config');
}
catch (e) {
this.runningOnRaspberryPi = false;
}
}
};
exports.ConfigService = ConfigService;
exports.ConfigService = ConfigService = __decorate([
(0, common_1.Injectable)(),
__metadata("design:paramtypes", [])
], ConfigService);
//# sourceMappingURL=config.service.js.map