Skip to content

Commit 92ee158

Browse files
authored
v0.4.0 (#9)
1 parent 0e626cb commit 92ee158

File tree

8 files changed

+119
-110
lines changed

8 files changed

+119
-110
lines changed

.github/workflows/test.yml

Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -14,6 +14,7 @@ jobs:
1414
- uses: actions/setup-node@v4
1515
with:
1616
node-version: 24.x
17+
- run: sudo chmod 440 /etc/sudoers.d/runner
1718
- run: sudo npm install -g matterbridge
1819
- run: npm ci
1920
- run: npm run build
@@ -29,6 +30,11 @@ jobs:
2930
- run: sleep 10
3031
- run: curl http://localhost:8283/
3132
- run: sudo mb-service uninstall
33+
- run: matterbridge -factoryreset
34+
- run: sudo mb-service install --frontend 8383
35+
- run: sleep 10
36+
- run: curl http://localhost:8383/
37+
- run: sudo mb-service uninstall
3238
test-macos:
3339
runs-on: macos-latest
3440
steps:
@@ -57,3 +63,8 @@ jobs:
5763
- run: sleep 10
5864
- run: curl http://localhost:8283/
5965
- run: sudo mb-service uninstall
66+
- run: matterbridge -factoryreset
67+
- run: sudo mb-service install --frontend 8383
68+
- run: sleep 10
69+
- run: curl http://localhost:8383/
70+
- run: sudo mb-service uninstall

README.md

Lines changed: 5 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -7,8 +7,6 @@
77

88
A service management command-line utility for [Matterbridge](https://github.com/Luligu/matterbridge/), inspired by [Homebridge's](https://github.com/homebridge/homebridge-config-ui-x/wiki/Homebridge-Service-Command) `hb-service`.
99

10-
_This is currently experimental and only supports Linux & macOS with a default configuration at the moment!_
11-
1210
```
1311
% npm install -g matterbridge mb-service
1412
@@ -24,10 +22,14 @@ Commands:
2422
pid Get the process id of the Matterbridge service
2523
tail Tail the Matterbridge log file
2624
27-
Options:
25+
Global Options:
2826
-h, --help
2927
-v, --version
3028
29+
Install Options:
30+
--frontend <port>
31+
--ssl
32+
3133
% sudo mb-service install
3234
Matterbridge Service Installed!
3335
Starting Matterbridge Service...

package-lock.json

Lines changed: 2 additions & 2 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

package.json

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,7 @@
11
{
22
"name": "mb-service",
33
"displayName": "Matterbridge Service Command",
4-
"version": "0.3.0",
4+
"version": "0.4.0",
55
"description": "A service management command-line utility for Matterbridge, inspired by Homebridge's 'hb-service'.",
66
"keywords": [
77
"matter",

src/linux.ts

Lines changed: 17 additions & 53 deletions
Original file line numberDiff line numberDiff line change
@@ -1,24 +1,17 @@
1-
import { execFileSync, execSync } from 'node:child_process';
2-
import { chownSync, existsSync, mkdirSync, readFileSync, unlinkSync, writeFileSync } from 'node:fs';
3-
import { UserInfo, userInfo } from 'node:os';
4-
import { resolve } from 'node:path';
1+
import { execFileSync } from 'node:child_process';
2+
import { chmodSync, existsSync, unlinkSync, writeFileSync } from 'node:fs';
3+
import { UserInfo } from 'node:os';
4+
55
import { PlatformCommands } from './platform.js';
66

77
export class LinuxPlatform extends PlatformCommands {
88
#systemdService = '/etc/systemd/system/matterbridge.service';
99

10-
install(): void {
10+
install(args: string[]): void {
1111
this.checkRoot();
12-
const matterbridgePath = this.checkMatterbridgeInstalled();
13-
const userInfo = this.#getUserInfo();
14-
15-
// Create Matterbridge Plugin and Storage directories
16-
const matterbridgePluginPath = resolve(userInfo.homedir, 'Matterbridge');
17-
this.#mkdirPath(matterbridgePluginPath, userInfo);
18-
19-
const matterbridgeStoragePath = resolve(userInfo.homedir, '.matterbridge');
20-
this.#mkdirPath(matterbridgeStoragePath, userInfo);
21-
12+
const matterbridgeBinPath = this.checkMatterbridgeInstalled();
13+
const matterbridgeStoragePath = this.mkdirMatterbridgePaths();
14+
const userInfo = this.getUserInfo();
2215
this.#configSudoers(userInfo);
2316

2417
const systemdServiceFileContents = [
@@ -28,7 +21,7 @@ export class LinuxPlatform extends PlatformCommands {
2821
'',
2922
'[Service]',
3023
'Type=simple',
31-
`ExecStart=${matterbridgePath} -service`,
24+
`ExecStart=${matterbridgeBinPath} ${args.join(' ')}`,
3225
`WorkingDirectory=${matterbridgeStoragePath}`,
3326
'StandardOutput=inherit',
3427
'StandardError=inherit',
@@ -48,7 +41,7 @@ export class LinuxPlatform extends PlatformCommands {
4841
execFileSync('systemctl', ['enable', 'matterbridge']);
4942

5043
this.start();
51-
this.postinstall();
44+
this.postinstall(args);
5245
}
5346

5447
uninstall(): void {
@@ -99,44 +92,15 @@ export class LinuxPlatform extends PlatformCommands {
9992
}
10093

10194
#checkServiceInstalled(): void {
102-
this.checkServiceInstalled(this.#systemdService);
103-
}
104-
105-
#getUserInfo(): UserInfo<string> {
106-
if (process.env.SUDO_USER && process.env.SUDO_UID && process.env.SUDO_GID) {
107-
return {
108-
username: process.env.SUDO_USER,
109-
uid: Number.parseInt(process.env.SUDO_UID),
110-
gid: Number.parseInt(process.env.SUDO_GID),
111-
shell: null,
112-
homedir: execSync(`eval echo ~"${process.env.SUDO_USER}"`).toString().trim()
113-
};
114-
}
115-
116-
return userInfo();
117-
}
118-
119-
#mkdirPath(path: string, userInfo: UserInfo<string>): void {
120-
mkdirSync(path, { recursive: true });
121-
chownSync(path, userInfo.uid, userInfo.gid);
95+
super.checkServiceInstalled(this.#systemdService);
12296
}
12397

12498
#configSudoers(userInfo: UserInfo<string>) {
125-
try {
126-
const npmPath = execSync('which npm').toString().trim();
127-
const sudoersEntry = `${userInfo.username} ALL=(ALL) NOPASSWD:SETENV: ${npmPath}, /usr/bin/npm, /usr/local/bin/npm`;
128-
129-
const sudoers = readFileSync('/etc/sudoers', 'utf-8');
130-
if (sudoers.includes(sudoersEntry)) {
131-
return;
132-
}
133-
134-
execSync(`echo '${sudoersEntry}' | sudo EDITOR='tee -a' visudo`);
135-
}
136-
catch (error: unknown) {
137-
if (error instanceof Error) {
138-
console.error('Failed to update /etc/sudoers:', error.message);
139-
}
140-
}
99+
const npmPath = execFileSync('which', ['npm']).toString().trim();
100+
const sudoersPath = '/etc/sudoers.d/matterbridge';
101+
const sudoersEntry = `${userInfo.username} ALL=(ALL) NOPASSWD: ${npmPath}\n`;
102+
writeFileSync(sudoersPath, sudoersEntry);
103+
chmodSync(sudoersPath, 0o440);
104+
execFileSync('visudo', ['-c']);
141105
}
142106
}

src/mac.ts

Lines changed: 14 additions & 39 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,5 @@
1-
import { execFileSync, execSync } from 'node:child_process';
2-
import { chownSync, existsSync, mkdirSync, unlinkSync, writeFileSync } from 'node:fs';
3-
import { UserInfo, userInfo } from 'node:os';
1+
import { execFileSync } from 'node:child_process';
2+
import { existsSync, unlinkSync, writeFileSync } from 'node:fs';
43
import { resolve } from 'node:path';
54
import process from 'node:process';
65

@@ -9,29 +8,24 @@ import { PlatformCommands } from './platform.js';
98
export class MacPlatform extends PlatformCommands {
109
#plist = '/Library/LaunchDaemons/com.matterbridge.plist';
1110

12-
install(): void {
11+
install(args: string[]): void {
1312
this.checkRoot();
14-
const matterbridgePath = this.checkMatterbridgeInstalled();
15-
const userInfo = this.#getUserInfo();
16-
17-
// Create Matterbridge Plugin and Storage directories
18-
const matterbridgePluginPath = resolve(userInfo.homedir, 'Matterbridge');
19-
this.#mkdirPath(matterbridgePluginPath, userInfo);
20-
21-
const matterbridgeStoragePath = resolve(userInfo.homedir, '.matterbridge');
22-
this.#mkdirPath(matterbridgeStoragePath, userInfo);
13+
const matterbridgeBinPath = this.checkMatterbridgeInstalled();
14+
const matterbridgeStoragePath = this.mkdirMatterbridgePaths();
15+
const userInfo = this.getUserInfo();
2316

2417
// Check NPM global modules path permissions and change if necessary
25-
const npmGlobalModulesPath = execSync('eval echo "$(npm prefix -g --silent)/lib/node_modules"').toString().trim();
18+
const npmGlobalPrefix = execFileSync('npm', ['prefix', '-g', '--silent']).toString().trim();
19+
const npmGlobalModulesPath = resolve(npmGlobalPrefix, 'lib', 'node_modules');
2620
try {
27-
execSync(`eval test -w "${npmGlobalModulesPath}"`, {
21+
execFileSync('test', ['-w', npmGlobalModulesPath], {
2822
uid: userInfo.uid,
2923
gid: userInfo.gid
3024
});
3125
}
3226
catch {
3327
try {
34-
execSync(`chown -R ${userInfo.uid}:${userInfo.gid} "${npmGlobalModulesPath}"`);
28+
execFileSync('chown', ['-R', `${userInfo.uid}:${userInfo.gid}`, npmGlobalModulesPath]);
3529
}
3630
catch {
3731
console.error('User not able to write to the NPM Global Modules Path!');
@@ -50,8 +44,8 @@ export class MacPlatform extends PlatformCommands {
5044
` <string>com.matterbridge</string>`,
5145
' <key>ProgramArguments</key>',
5246
' <array>',
53-
` <string>${matterbridgePath}</string>`,
54-
` <string>-service</string>`,
47+
` <string>${matterbridgeBinPath}</string>`,
48+
...args.map(arg => ` <string>${arg}</string>`),
5549
' </array>',
5650
' <key>KeepAlive</key>',
5751
' <true/>',
@@ -77,7 +71,7 @@ export class MacPlatform extends PlatformCommands {
7771
writeFileSync(this.#plist, plistFileContents);
7872
console.info('Matterbridge Service Installed!');
7973
this.start();
80-
this.postinstall();
74+
this.postinstall(args);
8175
}
8276

8377
uninstall(): void {
@@ -129,30 +123,11 @@ export class MacPlatform extends PlatformCommands {
129123
}
130124

131125
tail(): void {
132-
const matterbridgeStoragePath = resolve(this.#getUserInfo().homedir, '.matterbridge');
126+
const matterbridgeStoragePath = resolve(this.getUserInfo().homedir, '.matterbridge');
133127
execFileSync('tail', ['-f', '-n', '32', `${matterbridgeStoragePath}/matterbridge.log`], { stdio: 'inherit' });
134128
}
135129

136130
#checkServiceInstalled(): void {
137131
super.checkServiceInstalled(this.#plist);
138132
}
139-
140-
#getUserInfo(): UserInfo<string> {
141-
if (process.env.SUDO_USER && process.env.SUDO_UID && process.env.SUDO_GID) {
142-
return {
143-
username: process.env.SUDO_USER,
144-
uid: Number.parseInt(process.env.SUDO_UID),
145-
gid: Number.parseInt(process.env.SUDO_GID),
146-
shell: null,
147-
homedir: execSync(`eval echo ~"${process.env.SUDO_USER}"`).toString().trim()
148-
};
149-
}
150-
151-
return userInfo();
152-
}
153-
154-
#mkdirPath(path: string, userInfo: UserInfo<string>): void {
155-
mkdirSync(path, { recursive: true });
156-
chownSync(path, userInfo.uid, userInfo.gid);
157-
}
158133
}

src/mb-service.ts

Lines changed: 27 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -12,7 +12,10 @@ async function main() {
1212
args: process.argv.slice(2),
1313
options: {
1414
help: { type: 'boolean', short: 'h' },
15-
version: { type: 'boolean', short: 'v' } },
15+
version: { type: 'boolean', short: 'v' },
16+
frontend: { type: 'string' },
17+
ssl: { type: 'boolean' }
18+
},
1619
strict: false,
1720
allowPositionals: true
1821
});
@@ -42,10 +45,27 @@ async function main() {
4245
process.exit(2);
4346
}
4447

48+
const installArgs: string[] = ['-service'];
49+
50+
if (args.values.frontend) {
51+
if (typeof args.values.frontend === 'string' && /^\d+$/.test(args.values.frontend)) {
52+
installArgs.push('-frontend', args.values.frontend);
53+
}
54+
else {
55+
console.error(`Specify a valid number in --frontend <port>`);
56+
help();
57+
process.exit(1);
58+
}
59+
}
60+
61+
if (args.values.ssl) {
62+
installArgs.push('-ssl');
63+
}
64+
4565
const command = args.positionals[0];
4666
switch (command) {
4767
case 'install':
48-
platformCommands.install();
68+
platformCommands.install(installArgs);
4969
break;
5070
case 'uninstall':
5171
platformCommands.uninstall();
@@ -95,10 +115,14 @@ function help() {
95115
console.log(' pid Get the process id of the Matterbridge service');
96116
console.log(' tail Tail the Matterbridge log file');
97117
console.log('');
98-
console.log('Options:');
118+
console.log('Global Options:');
99119
console.log(' -h, --help');
100120
console.log(' -v, --version');
101121
console.log('');
122+
console.log('Install Options:');
123+
console.log(' --frontend <port>');
124+
console.log(' --ssl');
125+
console.log('');
102126
}
103127

104128
main().catch(err => console.error(err));

0 commit comments

Comments
 (0)