homebridge-config-ui-x
Version:
A web based management, configuration and control platform for Homebridge.
621 lines • 30.3 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.BackupService = void 0;
const node_child_process_1 = require("node:child_process");
const node_events_1 = require("node:events");
const node_os_1 = require("node:os");
const node_path_1 = require("node:path");
const node_process_1 = __importDefault(require("node:process"));
const node_stream_1 = require("node:stream");
const node_util_1 = require("node:util");
const common_1 = require("@nestjs/common");
const bash_color_1 = require("bash-color");
const dayjs_1 = __importDefault(require("dayjs"));
const fs_extra_1 = require("fs-extra");
const systeminformation_1 = require("systeminformation");
const tar_1 = require("tar");
const unzipper_1 = require("unzipper");
const config_service_1 = require("../../core/config/config.service");
const homebridge_ipc_service_1 = require("../../core/homebridge-ipc/homebridge-ipc.service");
const logger_service_1 = require("../../core/logger/logger.service");
const scheduler_service_1 = require("../../core/scheduler/scheduler.service");
const plugins_service_1 = require("../plugins/plugins.service");
const pump = (0, node_util_1.promisify)(node_stream_1.pipeline);
let BackupService = class BackupService {
constructor(configService, pluginsService, schedulerService, homebridgeIpcService, logger) {
this.configService = configService;
this.pluginsService = pluginsService;
this.schedulerService = schedulerService;
this.homebridgeIpcService = homebridgeIpcService;
this.logger = logger;
this.scheduleInstanceBackups();
}
scheduleInstanceBackups() {
if (this.configService.ui.scheduledBackupDisable === true) {
this.logger.debug('Scheduled backups disabled.');
return;
}
const scheduleRule = new this.schedulerService.RecurrenceRule();
scheduleRule.hour = Math.floor(Math.random() * 7);
scheduleRule.minute = Math.floor(Math.random() * 59);
scheduleRule.second = Math.floor(Math.random() * 59);
this.schedulerService.scheduleJob('instance-backup', scheduleRule, () => {
this.logger.log('Running scheduled instance backup...');
this.runScheduledBackupJob();
});
}
async createBackup() {
const instanceId = this.configService.homebridgeConfig.bridge.username.replace(/:/g, '');
const backupDir = await (0, fs_extra_1.mkdtemp)((0, node_path_1.join)((0, node_os_1.tmpdir)(), 'homebridge-backup-'));
const backupFileName = `homebridge-backup-${instanceId}.${new Date().getTime().toString()}.tar.gz`;
const backupPath = (0, node_path_1.resolve)(backupDir, backupFileName);
this.logger.log(`Creating temporary backup archive at ${backupPath}.`);
try {
const storagePath = await (0, fs_extra_1.realpath)(this.configService.storagePath);
await (0, fs_extra_1.copy)(storagePath, (0, node_path_1.resolve)(backupDir, 'storage'), {
filter: async (filePath) => {
if ([
'instance-backups',
'nssm.exe',
'homebridge.log',
'logs',
'node_modules',
'startup.sh',
'.docker.env',
'docker-compose.yml',
'pnpm-lock.yaml',
'package.json',
'package-lock.json',
'.npmrc',
'.npm',
'FFmpeg',
'fdk-aac',
'.git',
'recordings',
'.homebridge.sock',
'#recycle',
'@eaDir',
'.venv',
'.cache',
].includes((0, node_path_1.basename)(filePath))) {
return false;
}
try {
const stat = await (0, fs_extra_1.lstat)(filePath);
return (stat.isDirectory() || stat.isFile());
}
catch (e) {
return false;
}
},
});
const installedPlugins = await this.pluginsService.getInstalledPlugins();
await (0, fs_extra_1.writeJson)((0, node_path_1.resolve)(backupDir, 'plugins.json'), installedPlugins);
await (0, fs_extra_1.writeJson)((0, node_path_1.resolve)(backupDir, 'info.json'), {
timestamp: new Date().toISOString(),
platform: (0, node_os_1.platform)(),
uix: this.configService.package.version,
node: node_process_1.default.version,
});
await (0, tar_1.create)({
portable: true,
gzip: true,
file: backupPath,
cwd: backupDir,
filter: (filePath, stat) => {
if (stat.size > globalThis.backup.maxBackupFileSize) {
this.logger.warn(`Backup is skipping ${filePath} because it is larger than ${globalThis.backup.maxBackupFileSizeText}.`);
return false;
}
return true;
},
}, [
'storage',
'plugins.json',
'info.json',
]);
if ((0, fs_extra_1.statSync)(backupPath).size > globalThis.backup.maxBackupSize) {
this.logger.error(`Backup file exceeds maximum restore file size (${globalThis.backup.maxBackupSizeText}) ${((0, fs_extra_1.statSync)(backupPath).size / (1024 * 1024)).toFixed(1)}MB.`);
}
}
catch (e) {
this.logger.log(`Backup failed, removing ${backupDir}.`);
await (0, fs_extra_1.remove)((0, node_path_1.resolve)(backupDir));
throw e;
}
return {
instanceId,
backupDir,
backupPath,
backupFileName,
};
}
async ensureScheduledBackupPath() {
if (this.configService.ui.scheduledBackupPath) {
if (!await (0, fs_extra_1.pathExists)(this.configService.instanceBackupPath)) {
throw new Error('Custom instance backup path does not exist');
}
try {
await (0, fs_extra_1.access)(this.configService.instanceBackupPath, fs_extra_1.constants.W_OK | fs_extra_1.constants.R_OK);
}
catch (e) {
throw new Error(`Custom instance backup path is not writable / readable by service: ${e.message}`);
}
}
else {
return await (0, fs_extra_1.ensureDir)(this.configService.instanceBackupPath);
}
}
async runScheduledBackupJob() {
try {
await this.ensureScheduledBackupPath();
}
catch (e) {
this.logger.warn(`Could not run scheduled backup as ${e.message}.`);
return;
}
try {
const { backupDir, backupPath, instanceId } = await this.createBackup();
await (0, fs_extra_1.copy)(backupPath, (0, node_path_1.resolve)(this.configService.instanceBackupPath, `homebridge-backup-${instanceId}.${new Date().getTime().toString()}.tar.gz`));
await (0, fs_extra_1.remove)((0, node_path_1.resolve)(backupDir));
}
catch (e) {
this.logger.warn(`Failed to create scheduled instance backup as ${e.message}.`);
}
try {
const backups = await this.listScheduledBackups();
for (const backup of backups) {
if ((0, dayjs_1.default)().diff((0, dayjs_1.default)(backup.timestamp), 'day') >= 7) {
await (0, fs_extra_1.remove)((0, node_path_1.resolve)(this.configService.instanceBackupPath, backup.fileName));
}
}
}
catch (e) {
this.logger.warn(`Failed to remove old backups as ${e.message}.`);
}
}
async getNextBackupTime() {
if (this.configService.ui.scheduledBackupDisable === true) {
return {
next: false,
};
}
else {
return {
next: this.schedulerService.scheduledJobs['instance-backup']?.nextInvocation() || false,
};
}
}
async listScheduledBackups() {
try {
await this.ensureScheduledBackupPath();
const dirContents = await (0, fs_extra_1.readdir)(this.configService.instanceBackupPath, { withFileTypes: true });
return dirContents
.filter(x => x.isFile() && x.name.match(/^homebridge-backup-[0-9A-Za-z]{12}.\d{09,15}.tar.gz/))
.map((x) => {
const split = x.name.split('.');
const instanceId = split[0].split('-')[2];
if (split.length === 4 && !Number.isNaN(split[1])) {
return {
id: `${instanceId}.${split[1]}`,
instanceId: split[0].split('-')[2],
timestamp: new Date(Number.parseInt(split[1], 10)),
fileName: x.name,
size: ((0, fs_extra_1.statSync)(`${this.configService.instanceBackupPath}/${x.name}`).size / (1024 * 1024)).toFixed(1),
maxBackupSize: globalThis.backup.maxBackupSize / (1024 * 1024),
maxBackupSizeText: globalThis.backup.maxBackupSizeText,
};
}
else {
return null;
}
})
.filter(x => x !== null)
.sort((a, b) => {
if (a.id > b.id) {
return -1;
}
else if (a.id < b.id) {
return -2;
}
else {
return 0;
}
});
}
catch (e) {
this.logger.warn(`Could not get scheduled backups as ${e.message}.`);
throw new common_1.InternalServerErrorException(e.message);
}
}
async getScheduledBackup(backupId) {
const backupPath = (0, node_path_1.resolve)(this.configService.instanceBackupPath, `homebridge-backup-${backupId}.tar.gz`);
if (!await (0, fs_extra_1.pathExists)(backupPath)) {
throw new common_1.NotFoundException();
}
return new common_1.StreamableFile((0, fs_extra_1.createReadStream)(backupPath));
}
async deleteScheduledBackup(backupId) {
const backupPath = (0, node_path_1.resolve)(this.configService.instanceBackupPath, `homebridge-backup-${backupId}.tar.gz`);
if (!await (0, fs_extra_1.pathExists)(backupPath)) {
throw new common_1.NotFoundException();
}
try {
await (0, fs_extra_1.remove)(backupPath);
this.logger.warn(`Scheduled backup ${backupId} deleted by request.`);
}
catch (e) {
this.logger.warn(`Failed to delete scheduled backup by request as ${e.message}.`);
throw new common_1.InternalServerErrorException(e.message);
}
}
async restoreScheduledBackup(backupId) {
const backupPath = (0, node_path_1.resolve)(this.configService.instanceBackupPath, `homebridge-backup-${backupId}.tar.gz`);
if (!await (0, fs_extra_1.pathExists)(backupPath)) {
throw new common_1.NotFoundException();
}
this.restoreDirectory = undefined;
const restoreDir = await (0, fs_extra_1.mkdtemp)((0, node_path_1.join)((0, node_os_1.tmpdir)(), 'homebridge-backup-'));
await pump((0, fs_extra_1.createReadStream)(backupPath), (0, tar_1.extract)({
cwd: restoreDir,
}));
this.restoreDirectory = restoreDir;
}
async downloadBackup(reply) {
const { backupDir, backupPath, backupFileName } = await this.createBackup();
async function cleanup() {
await (0, fs_extra_1.remove)((0, node_path_1.resolve)(backupDir));
this.logger.log(`Backup complete, removing ${backupDir}.`);
}
reply.raw.setHeader('Content-type', 'application/octet-stream');
reply.raw.setHeader('Content-disposition', `attachment; filename=${backupFileName}`);
reply.raw.setHeader('File-Name', backupFileName);
if (reply.request.hostname === 'localhost:8080') {
reply.raw.setHeader('access-control-allow-origin', 'http://localhost:4200');
}
return new common_1.StreamableFile((0, fs_extra_1.createReadStream)(backupPath).on('close', cleanup.bind(this)));
}
async uploadBackupRestore(data) {
this.restoreDirectory = undefined;
const backupDir = await (0, fs_extra_1.mkdtemp)((0, node_path_1.join)((0, node_os_1.tmpdir)(), 'homebridge-backup-'));
await pump(data.file, (0, tar_1.extract)({
cwd: backupDir,
}));
this.restoreDirectory = backupDir;
}
async removeRestoreDirectory() {
if (this.restoreDirectory) {
return await (0, fs_extra_1.remove)(this.restoreDirectory);
}
}
async triggerHeadlessRestore() {
if (!await (0, fs_extra_1.pathExists)(this.restoreDirectory)) {
throw new common_1.BadRequestException('No backup file uploaded');
}
const client = new node_events_1.EventEmitter();
client.on('stdout', (data) => {
this.logger.log(data);
});
client.on('stderr', (data) => {
this.logger.log(data);
});
await this.restoreFromBackup(client, true);
return { status: 0 };
}
async restoreFromBackup(client, autoRestart = false) {
if (!this.restoreDirectory) {
throw new common_1.BadRequestException();
}
if (!await (0, fs_extra_1.pathExists)((0, node_path_1.resolve)(this.restoreDirectory, 'info.json'))) {
await this.removeRestoreDirectory();
throw new Error('Uploaded file is not a valid Homebridge Backup Archive.');
}
if (!await (0, fs_extra_1.pathExists)((0, node_path_1.resolve)(this.restoreDirectory, 'plugins.json'))) {
await this.removeRestoreDirectory();
throw new Error('Uploaded file is not a valid Homebridge Backup Archive.');
}
if (!await (0, fs_extra_1.pathExists)((0, node_path_1.resolve)(this.restoreDirectory, 'storage'))) {
await this.removeRestoreDirectory();
throw new Error('Uploaded file is not a valid Homebridge Backup Archive.');
}
const backupInfo = await (0, fs_extra_1.readJson)((0, node_path_1.resolve)(this.restoreDirectory, 'info.json'));
client.emit('stdout', (0, bash_color_1.cyan)('Backup Archive Information\r\n'));
client.emit('stdout', `Source Node.js Version: ${backupInfo.node}\r\n`);
client.emit('stdout', `Source Homebridge UI Version: v${backupInfo.uix}\r\n`);
client.emit('stdout', `Source Platform: ${backupInfo.platform}\r\n`);
client.emit('stdout', `Created: ${backupInfo.timestamp}\r\n`);
this.logger.warn('Starting backup restore...');
client.emit('stdout', (0, bash_color_1.cyan)('\r\nRestoring backup...\r\n\r\n'));
await new Promise(res => setTimeout(res, 1000));
const restoreFilter = [
(0, node_path_1.join)(this.restoreDirectory, 'storage', 'package.json'),
(0, node_path_1.join)(this.restoreDirectory, 'storage', 'package-lock.json'),
(0, node_path_1.join)(this.restoreDirectory, 'storage', '.npmrc'),
(0, node_path_1.join)(this.restoreDirectory, 'storage', 'docker-compose.yml'),
];
const storagePath = await (0, fs_extra_1.realpath)(this.configService.storagePath);
client.emit('stdout', (0, bash_color_1.yellow)(`Restoring Homebridge storage to ${storagePath}\r\n`));
await new Promise(res => setTimeout(res, 100));
await (0, fs_extra_1.copy)((0, node_path_1.resolve)(this.restoreDirectory, 'storage'), storagePath, {
filter: async (filePath) => {
if (restoreFilter.includes(filePath)) {
client.emit('stdout', `Skipping ${(0, node_path_1.basename)(filePath)}\r\n`);
return false;
}
try {
const stat = await (0, fs_extra_1.lstat)(filePath);
if (stat.isDirectory() || stat.isFile()) {
client.emit('stdout', `Restoring ${(0, node_path_1.basename)(filePath)}\r\n`);
return true;
}
else {
client.emit('stdout', `Skipping ${(0, node_path_1.basename)(filePath)}\r\n`);
return false;
}
}
catch (e) {
client.emit('stdout', `Skipping ${(0, node_path_1.basename)(filePath)}\r\n`);
return false;
}
},
});
client.emit('stdout', (0, bash_color_1.yellow)('File restore complete.\r\n'));
await new Promise(res => setTimeout(res, 1000));
client.emit('stdout', (0, bash_color_1.cyan)('\r\nRestoring plugins...\r\n'));
const plugins = (await (0, fs_extra_1.readJson)((0, node_path_1.resolve)(this.restoreDirectory, 'plugins.json')))
.filter((x) => ![
'homebridge-config-ui-x',
].includes(x.name) && x.publicPackage);
for (const plugin of plugins) {
try {
client.emit('stdout', (0, bash_color_1.yellow)(`\r\nInstalling ${plugin.name}...\r\n`));
await this.pluginsService.managePlugin('install', { name: plugin.name, version: plugin.installedVersion }, client);
}
catch (e) {
client.emit('stdout', (0, bash_color_1.red)(`Failed to install ${plugin.name}.\r\n`));
}
}
const restoredConfig = await (0, fs_extra_1.readJson)(this.configService.configPath);
if (restoredConfig.bridge) {
restoredConfig.bridge.port = this.configService.homebridgeConfig.bridge.port;
}
if (restoredConfig.bridge.bind) {
this.checkBridgeBindConfig(restoredConfig);
}
if (!Array.isArray(restoredConfig.platforms)) {
restoredConfig.platforms = [];
}
const uiConfigBlock = restoredConfig.platforms.find(x => x.platform === 'config');
if (uiConfigBlock) {
uiConfigBlock.port = this.configService.ui.port;
if (this.configService.serviceMode || this.configService.runningInDocker) {
delete uiConfigBlock.restart;
delete uiConfigBlock.sudo;
delete uiConfigBlock.log;
}
}
else {
restoredConfig.platforms.push({
name: 'Config',
port: this.configService.ui.port,
platform: 'config',
});
}
await (0, fs_extra_1.writeJson)(this.configService.configPath, restoredConfig, { spaces: 4 });
await this.removeRestoreDirectory();
client.emit('stdout', (0, bash_color_1.green)('\r\nRestore Complete!\r\n'));
this.configService.hbServiceUiRestartRequired = true;
if (autoRestart) {
this.postBackupRestoreRestart();
}
return { status: 0 };
}
async uploadHbfxRestore(data) {
this.restoreDirectory = undefined;
const backupDir = await (0, fs_extra_1.mkdtemp)((0, node_path_1.join)((0, node_os_1.tmpdir)(), 'homebridge-backup-'));
this.logger.log(`Extracting .hbfx file to ${backupDir}.`);
await pump(data.file, (0, unzipper_1.Extract)({
path: backupDir,
}));
this.restoreDirectory = backupDir;
}
async restoreHbfxBackup(client) {
if (!this.restoreDirectory) {
throw new common_1.BadRequestException();
}
if (!await (0, fs_extra_1.pathExists)((0, node_path_1.resolve)(this.restoreDirectory, 'package.json'))) {
await this.removeRestoreDirectory();
throw new Error('Uploaded file is not a valid HBFX Backup Archive.');
}
if (!await (0, fs_extra_1.pathExists)((0, node_path_1.resolve)(this.restoreDirectory, 'etc', 'config.json'))) {
await this.removeRestoreDirectory();
throw new Error('Uploaded file is not a valid HBFX Backup Archive.');
}
const backupInfo = await (0, fs_extra_1.readJson)((0, node_path_1.resolve)(this.restoreDirectory, 'package.json'));
client.emit('stdout', (0, bash_color_1.cyan)('Backup Archive Information\r\n'));
client.emit('stdout', `Backup Source: ${backupInfo.name}\r\n`);
client.emit('stdout', `Version: v${backupInfo.version}\r\n`);
this.logger.warn('Starting hbfx restore...');
client.emit('stdout', (0, bash_color_1.cyan)('\r\nRestoring hbfx backup...\r\n\r\n'));
await new Promise(res => setTimeout(res, 1000));
const storagePath = await (0, fs_extra_1.realpath)(this.configService.storagePath);
client.emit('stdout', (0, bash_color_1.yellow)(`Restoring Homebridge storage to ${storagePath}\r\n`));
await (0, fs_extra_1.copy)((0, node_path_1.resolve)(this.restoreDirectory, 'etc'), (0, node_path_1.resolve)(storagePath), {
filter: (filePath) => {
if ([
'access.json',
'dashboard.json',
'layout.json',
'config.json',
].includes((0, node_path_1.basename)(filePath))) {
return false;
}
client.emit('stdout', `Restoring ${(0, node_path_1.basename)(filePath)}\r\n`);
return true;
},
});
const sourceAccessoriesPath = (0, node_path_1.resolve)(this.restoreDirectory, 'etc', 'accessories');
const targetAccessoriesPath = (0, node_path_1.resolve)(storagePath, 'accessories');
if (await (0, fs_extra_1.pathExists)(sourceAccessoriesPath)) {
await (0, fs_extra_1.copy)(sourceAccessoriesPath, targetAccessoriesPath, {
filter: (filePath) => {
client.emit('stdout', `Restoring ${(0, node_path_1.basename)(filePath)}\r\n`);
return true;
},
});
}
const sourceConfig = await (0, fs_extra_1.readJson)((0, node_path_1.resolve)(this.restoreDirectory, 'etc', 'config.json'));
const pluginMap = {
'hue': 'homebridge-hue',
'chamberlain': 'homebridge-chamberlain',
'google-home': 'homebridge-gsh',
'ikea-tradfri': 'homebridge-ikea-tradfri-gateway',
'nest': 'homebridge-nest',
'ring': 'homebridge-ring',
'roborock': 'homebridge-roborock',
'shelly': 'homebridge-shelly',
'wink': 'homebridge-wink3',
'homebridge-tuya-web': '@milo526/homebridge-tuya-web',
};
if (sourceConfig.plugins?.length) {
for (let plugin of sourceConfig.plugins) {
if (plugin in pluginMap) {
plugin = pluginMap[plugin];
}
try {
client.emit('stdout', (0, bash_color_1.yellow)(`\r\nInstalling ${plugin}...\r\n`));
await this.pluginsService.managePlugin('install', { name: plugin, version: 'latest' }, client);
}
catch (e) {
client.emit('stdout', (0, bash_color_1.red)(`Failed to install ${plugin}.\r\n`));
}
}
}
const targetConfig = JSON.parse(JSON.stringify({
bridge: sourceConfig.bridge,
accessories: sourceConfig.accessories?.map((x) => {
delete x.plugin_map;
return x;
}) || [],
platforms: sourceConfig.platforms?.map((x) => {
if (x.platform === 'google-home') {
x.platform = 'google-smarthome';
x.notice = 'Keep your token a secret!';
}
delete x.plugin_map;
return x;
}) || [],
}));
targetConfig.bridge.name = `Homebridge ${targetConfig.bridge.username.substring(targetConfig.bridge.username.length - 5).replace(/:/g, '')}`;
if (targetConfig.bridge.bind) {
this.checkBridgeBindConfig(targetConfig);
}
targetConfig.platforms.push(this.configService.ui);
await (0, fs_extra_1.writeJson)(this.configService.configPath, targetConfig, { spaces: 4 });
await this.removeRestoreDirectory();
client.emit('stdout', (0, bash_color_1.green)('\r\nRestore Complete!\r\n'));
this.configService.hbServiceUiRestartRequired = true;
return { status: 0 };
}
postBackupRestoreRestart() {
setTimeout(() => {
if (this.configService.serviceMode) {
this.homebridgeIpcService.killHomebridge();
setTimeout(() => {
node_process_1.default.kill(node_process_1.default.pid, 'SIGKILL');
}, 500);
return;
}
if (this.configService.runningInDocker) {
try {
return (0, node_child_process_1.execSync)('killall -9 homebridge; kill -9 $(pidof homebridge-config-ui-x);');
}
catch (e) {
this.logger.error(`Failed to restart Homebridge as ${e.message}.`);
this.logger.error(e);
}
}
if (node_process_1.default.connected) {
node_process_1.default.kill(node_process_1.default.ppid, 'SIGKILL');
node_process_1.default.kill(node_process_1.default.pid, 'SIGKILL');
}
if (this.configService.ui.noFork) {
return node_process_1.default.kill(node_process_1.default.pid, 'SIGKILL');
}
if ((0, node_os_1.platform)() === 'linux' && this.configService.ui.standalone) {
try {
const getPidByPort = (port) => {
try {
return Number.parseInt((0, node_child_process_1.execSync)(`fuser ${port}/tcp 2>/dev/null`).toString('utf8').trim(), 10);
}
catch (e) {
return null;
}
};
const getPidByName = () => {
try {
return Number.parseInt((0, node_child_process_1.execSync)('pidof homebridge').toString('utf8').trim(), 10);
}
catch (e) {
return null;
}
};
const homebridgePid = getPidByPort(this.configService.homebridgeConfig.bridge.port) || getPidByName();
if (homebridgePid) {
node_process_1.default.kill(homebridgePid, 'SIGKILL');
return node_process_1.default.kill(node_process_1.default.pid, 'SIGKILL');
}
}
catch (e) {
}
}
if (this.configService.ui.restart) {
return (0, node_child_process_1.exec)(this.configService.ui.restart, (err) => {
if (err) {
this.logger.log('Restart command exited with an error, failed to restart Homebridge.');
}
});
}
return node_process_1.default.kill(node_process_1.default.pid, 'SIGKILL');
}, 500);
return { status: 0 };
}
checkBridgeBindConfig(restoredConfig) {
if (restoredConfig.bridge.bind) {
if (typeof restoredConfig.bridge.bind === 'string') {
restoredConfig.bridge.bind = [restoredConfig.bridge.bind];
}
if (!Array.isArray(restoredConfig.bridge.bind)) {
delete restoredConfig.bridge.bind;
return;
}
const interfaces = (0, systeminformation_1.networkInterfaces)();
restoredConfig.bridge.bind = restoredConfig.bridge.bind.filter(x => interfaces[x]);
if (!restoredConfig.bridge.bind) {
delete restoredConfig.bridge.bind;
}
}
}
};
exports.BackupService = BackupService;
exports.BackupService = BackupService = __decorate([
(0, common_1.Injectable)(),
__metadata("design:paramtypes", [config_service_1.ConfigService,
plugins_service_1.PluginsService,
scheduler_service_1.SchedulerService,
homebridge_ipc_service_1.HomebridgeIpcService,
logger_service_1.Logger])
], BackupService);
//# sourceMappingURL=backup.service.js.map