@homebridge-plugins/homebridge-cloudflared-tunnel
Version:
The Cloudflared Tunnel plugin allows you to run a Cloudflare-Tunnel for exposing your homebridge instance for remote access.
123 lines • 3.82 kB
JavaScript
/* Copyright(C) 2023-2024, donavanbecker (https://github.com/donavanbecker). All rights reserved.
*
* cloudflared-tunnel.ts: @homebridge-plugins/homebridge-cloudflared-tunnel.
*/
import * as childProcess from 'node:child_process';
import * as process from 'node:process';
import { sync as commandExistsSync } from 'command-exists';
class CloudflaredTunnel {
cloudflaredPath;
_token;
url;
hostname;
running;
childProcess;
change;
error;
constructor(cloudflaredPath = 'cloudflared') {
this.cloudflaredPath = cloudflaredPath;
this.url = 'http://localhost:80';
this.hostname = '';
this.running = false;
this.childProcess = null;
}
get token() {
return this._token;
}
set token(token) {
if (token && typeof token === 'string') {
token = token.trim();
// try to strip out "cloudflared.exe service install"
const array = token.split(' ');
if (array.length > 1) {
for (let i = 0; i < array.length - 1; i++) {
if (array[i] === 'install') {
token = array[i + 1];
}
}
}
}
this._token = token;
}
checkInstalled() {
return commandExistsSync(this.cloudflaredPath);
}
emitChange(msg, code) {
if (this.change) {
this.change(this.running, msg, code);
}
}
emitError(msg) {
if (this.error) {
this.error(msg);
}
}
start() {
if (this.childProcess) {
this.emitError('Already started');
return;
}
if (!this.checkInstalled()) {
this.emitError(`Cloudflared error: ${this.cloudflaredPath} is not found`);
return;
}
if (!this.token) {
this.emitError('Cloudflared error: Token is not set');
return;
}
const args = [
'tunnel',
'--no-autoupdate',
];
if (this.hostname) {
args.push('--hostname');
args.push(this.hostname);
}
if (this.url) {
args.push('--url');
args.push(this.url);
}
args.push('run');
args.push('--token');
args.push(this.token);
this.running = true;
this.emitChange('Starting cloudflared');
this.childProcess = childProcess.spawn(this.cloudflaredPath, args);
if (this.childProcess && this.childProcess.stdout) {
this.childProcess.stdout.pipe(process.stdout);
}
if (this.childProcess && this.childProcess.stderr) {
this.childProcess.stderr.pipe(process.stderr);
}
this.childProcess.on('close', (code) => {
this.running = false;
this.childProcess = null;
this.emitChange('Stopped cloudflared', code ?? undefined);
});
this.childProcess.on('error', (err) => {
if (err.code === 'ENOENT') {
this.emitError(`Cloudflared error: ${this.cloudflaredPath} is not found`);
}
else {
this.emitError(err.message);
}
});
if (this.childProcess && this.childProcess.stderr) {
this.childProcess.stderr.on('data', (data) => {
this.emitError(data.toString());
});
}
}
stop() {
this.emitChange('Stopping cloudflared');
if (this.childProcess) {
this.childProcess.kill('SIGINT');
this.childProcess = null;
}
}
setChildProcess(childProcess) {
this.childProcess = childProcess;
}
}
export { CloudflaredTunnel };
//# sourceMappingURL=cloudflared-tunnel.js.map