@fastybird/smart-panel 0.1.0-alpha.5
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +98 -0
- package/bin/smart-panel-service.js +1074 -0
- package/bin/smart-panel.js +43 -0
- package/dist/index.d.ts +6 -0
- package/dist/index.d.ts.map +1 -0
- package/dist/index.js +6 -0
- package/dist/index.js.map +1 -0
- package/dist/installers/base.d.ts +78 -0
- package/dist/installers/base.d.ts.map +1 -0
- package/dist/installers/base.js +5 -0
- package/dist/installers/base.js.map +1 -0
- package/dist/installers/index.d.ts +8 -0
- package/dist/installers/index.d.ts.map +1 -0
- package/dist/installers/index.js +16 -0
- package/dist/installers/index.js.map +1 -0
- package/dist/installers/linux.d.ts +32 -0
- package/dist/installers/linux.d.ts.map +1 -0
- package/dist/installers/linux.js +406 -0
- package/dist/installers/linux.js.map +1 -0
- package/dist/utils/index.d.ts +5 -0
- package/dist/utils/index.d.ts.map +1 -0
- package/dist/utils/index.js +5 -0
- package/dist/utils/index.js.map +1 -0
- package/dist/utils/logger.d.ts +17 -0
- package/dist/utils/logger.d.ts.map +1 -0
- package/dist/utils/logger.js +55 -0
- package/dist/utils/logger.js.map +1 -0
- package/dist/utils/paths.d.ts +26 -0
- package/dist/utils/paths.d.ts.map +1 -0
- package/dist/utils/paths.js +57 -0
- package/dist/utils/paths.js.map +1 -0
- package/dist/utils/system.d.ts +87 -0
- package/dist/utils/system.d.ts.map +1 -0
- package/dist/utils/system.js +211 -0
- package/dist/utils/system.js.map +1 -0
- package/dist/utils/version.d.ts +11 -0
- package/dist/utils/version.d.ts.map +1 -0
- package/dist/utils/version.js +75 -0
- package/dist/utils/version.js.map +1 -0
- package/package.json +72 -0
- package/templates/environment.template +28 -0
- package/templates/systemd/smart-panel.service +31 -0
|
@@ -0,0 +1,43 @@
|
|
|
1
|
+
#!/usr/bin/env node
|
|
2
|
+
|
|
3
|
+
/**
|
|
4
|
+
* Smart Panel CLI - Main entry point
|
|
5
|
+
*
|
|
6
|
+
* This CLI provides access to the backend CLI commands.
|
|
7
|
+
* For service management, use smart-panel-service instead.
|
|
8
|
+
*/
|
|
9
|
+
|
|
10
|
+
import { spawn } from 'node:child_process';
|
|
11
|
+
import { existsSync } from 'node:fs';
|
|
12
|
+
import { dirname, join } from 'node:path';
|
|
13
|
+
import { fileURLToPath } from 'node:url';
|
|
14
|
+
|
|
15
|
+
const __filename = fileURLToPath(import.meta.url);
|
|
16
|
+
const __dirname = dirname(__filename);
|
|
17
|
+
|
|
18
|
+
// Find the backend CLI
|
|
19
|
+
const packageRoot = join(__dirname, '..');
|
|
20
|
+
const backendCliPath = join(packageRoot, 'node_modules', '@fastybird', 'smart-panel-backend', 'dist', 'cli.js');
|
|
21
|
+
|
|
22
|
+
if (!existsSync(backendCliPath)) {
|
|
23
|
+
console.error('Error: Backend CLI not found at', backendCliPath);
|
|
24
|
+
console.error('Make sure @fastybird/smart-panel-backend is installed.');
|
|
25
|
+
process.exit(1);
|
|
26
|
+
}
|
|
27
|
+
|
|
28
|
+
// Forward all arguments to the backend CLI
|
|
29
|
+
const args = process.argv.slice(2);
|
|
30
|
+
|
|
31
|
+
const child = spawn(process.execPath, [backendCliPath, ...args], {
|
|
32
|
+
stdio: 'inherit',
|
|
33
|
+
env: process.env,
|
|
34
|
+
});
|
|
35
|
+
|
|
36
|
+
child.on('close', (code) => {
|
|
37
|
+
process.exit(code ?? 0);
|
|
38
|
+
});
|
|
39
|
+
|
|
40
|
+
child.on('error', (err) => {
|
|
41
|
+
console.error('Failed to start backend CLI:', err.message);
|
|
42
|
+
process.exit(1);
|
|
43
|
+
});
|
package/dist/index.d.ts
ADDED
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA;;GAEG;AAEH,cAAc,uBAAuB,CAAC;AACtC,cAAc,kBAAkB,CAAC"}
|
package/dist/index.js
ADDED
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"index.js","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA;;GAEG;AAEH,cAAc,uBAAuB,CAAC;AACtC,cAAc,kBAAkB,CAAC"}
|
|
@@ -0,0 +1,78 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Base installer interface for platform-specific implementations
|
|
3
|
+
*/
|
|
4
|
+
export interface InstallOptions {
|
|
5
|
+
/** Service user name */
|
|
6
|
+
user: string;
|
|
7
|
+
/** Data directory path */
|
|
8
|
+
dataDir: string;
|
|
9
|
+
/** HTTP port for the backend */
|
|
10
|
+
port: number;
|
|
11
|
+
/** Skip starting the service after install */
|
|
12
|
+
noStart: boolean;
|
|
13
|
+
/** Admin username for initial setup */
|
|
14
|
+
adminUsername?: string;
|
|
15
|
+
/** Admin password for initial setup */
|
|
16
|
+
adminPassword?: string;
|
|
17
|
+
}
|
|
18
|
+
export interface UninstallOptions {
|
|
19
|
+
/** Keep data directory */
|
|
20
|
+
keepData: boolean;
|
|
21
|
+
/** Skip confirmation prompts */
|
|
22
|
+
force: boolean;
|
|
23
|
+
}
|
|
24
|
+
export interface ServiceStatus {
|
|
25
|
+
/** Whether the service is installed */
|
|
26
|
+
installed: boolean;
|
|
27
|
+
/** Whether the service is currently running */
|
|
28
|
+
running: boolean;
|
|
29
|
+
/** Whether the service is enabled to start on boot */
|
|
30
|
+
enabled: boolean;
|
|
31
|
+
/** Process ID if running */
|
|
32
|
+
pid?: number;
|
|
33
|
+
/** Uptime in seconds if running */
|
|
34
|
+
uptime?: number;
|
|
35
|
+
/** Memory usage in MB if running */
|
|
36
|
+
memoryMB?: number;
|
|
37
|
+
/** Additional status message */
|
|
38
|
+
message?: string;
|
|
39
|
+
}
|
|
40
|
+
export interface InstalledConfig {
|
|
41
|
+
/** Service user name */
|
|
42
|
+
user?: string;
|
|
43
|
+
/** Data directory path */
|
|
44
|
+
dataDir?: string;
|
|
45
|
+
}
|
|
46
|
+
export interface BaseInstaller {
|
|
47
|
+
/** Platform name */
|
|
48
|
+
readonly platform: string;
|
|
49
|
+
/** Check if this installer is compatible with the current system */
|
|
50
|
+
isCompatible(): boolean;
|
|
51
|
+
/** Install the service */
|
|
52
|
+
install(options: InstallOptions): Promise<void>;
|
|
53
|
+
/** Uninstall the service */
|
|
54
|
+
uninstall(options: UninstallOptions): Promise<void>;
|
|
55
|
+
/** Start the service */
|
|
56
|
+
start(): Promise<void>;
|
|
57
|
+
/** Stop the service */
|
|
58
|
+
stop(): Promise<void>;
|
|
59
|
+
/** Restart the service */
|
|
60
|
+
restart(): Promise<void>;
|
|
61
|
+
/** Get service status */
|
|
62
|
+
status(): Promise<ServiceStatus>;
|
|
63
|
+
/** Get service logs */
|
|
64
|
+
logs(options: {
|
|
65
|
+
follow: boolean;
|
|
66
|
+
lines: number;
|
|
67
|
+
since?: string;
|
|
68
|
+
}): Promise<void>;
|
|
69
|
+
/** Run database migrations */
|
|
70
|
+
runMigrations(dataDir: string): Promise<void>;
|
|
71
|
+
/** Create admin user via backend CLI */
|
|
72
|
+
createAdminUser(dataDir: string, username: string, password: string): Promise<void>;
|
|
73
|
+
/** Check prerequisites */
|
|
74
|
+
checkPrerequisites(): Promise<string[]>;
|
|
75
|
+
/** Get installed configuration from environment file */
|
|
76
|
+
getInstalledConfig(): InstalledConfig;
|
|
77
|
+
}
|
|
78
|
+
//# sourceMappingURL=base.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"base.d.ts","sourceRoot":"","sources":["../../src/installers/base.ts"],"names":[],"mappings":"AAAA;;GAEG;AAEH,MAAM,WAAW,cAAc;IAC9B,wBAAwB;IACxB,IAAI,EAAE,MAAM,CAAC;IACb,0BAA0B;IAC1B,OAAO,EAAE,MAAM,CAAC;IAChB,gCAAgC;IAChC,IAAI,EAAE,MAAM,CAAC;IACb,8CAA8C;IAC9C,OAAO,EAAE,OAAO,CAAC;IACjB,uCAAuC;IACvC,aAAa,CAAC,EAAE,MAAM,CAAC;IACvB,uCAAuC;IACvC,aAAa,CAAC,EAAE,MAAM,CAAC;CACvB;AAED,MAAM,WAAW,gBAAgB;IAChC,0BAA0B;IAC1B,QAAQ,EAAE,OAAO,CAAC;IAClB,gCAAgC;IAChC,KAAK,EAAE,OAAO,CAAC;CACf;AAED,MAAM,WAAW,aAAa;IAC7B,uCAAuC;IACvC,SAAS,EAAE,OAAO,CAAC;IACnB,+CAA+C;IAC/C,OAAO,EAAE,OAAO,CAAC;IACjB,sDAAsD;IACtD,OAAO,EAAE,OAAO,CAAC;IACjB,4BAA4B;IAC5B,GAAG,CAAC,EAAE,MAAM,CAAC;IACb,mCAAmC;IACnC,MAAM,CAAC,EAAE,MAAM,CAAC;IAChB,oCAAoC;IACpC,QAAQ,CAAC,EAAE,MAAM,CAAC;IAClB,gCAAgC;IAChC,OAAO,CAAC,EAAE,MAAM,CAAC;CACjB;AAED,MAAM,WAAW,eAAe;IAC/B,wBAAwB;IACxB,IAAI,CAAC,EAAE,MAAM,CAAC;IACd,0BAA0B;IAC1B,OAAO,CAAC,EAAE,MAAM,CAAC;CACjB;AAED,MAAM,WAAW,aAAa;IAC7B,oBAAoB;IACpB,QAAQ,CAAC,QAAQ,EAAE,MAAM,CAAC;IAE1B,oEAAoE;IACpE,YAAY,IAAI,OAAO,CAAC;IAExB,0BAA0B;IAC1B,OAAO,CAAC,OAAO,EAAE,cAAc,GAAG,OAAO,CAAC,IAAI,CAAC,CAAC;IAEhD,4BAA4B;IAC5B,SAAS,CAAC,OAAO,EAAE,gBAAgB,GAAG,OAAO,CAAC,IAAI,CAAC,CAAC;IAEpD,wBAAwB;IACxB,KAAK,IAAI,OAAO,CAAC,IAAI,CAAC,CAAC;IAEvB,uBAAuB;IACvB,IAAI,IAAI,OAAO,CAAC,IAAI,CAAC,CAAC;IAEtB,0BAA0B;IAC1B,OAAO,IAAI,OAAO,CAAC,IAAI,CAAC,CAAC;IAEzB,yBAAyB;IACzB,MAAM,IAAI,OAAO,CAAC,aAAa,CAAC,CAAC;IAEjC,uBAAuB;IACvB,IAAI,CAAC,OAAO,EAAE;QAAE,MAAM,EAAE,OAAO,CAAC;QAAC,KAAK,EAAE,MAAM,CAAC;QAAC,KAAK,CAAC,EAAE,MAAM,CAAA;KAAE,GAAG,OAAO,CAAC,IAAI,CAAC,CAAC;IAEjF,8BAA8B;IAC9B,aAAa,CAAC,OAAO,EAAE,MAAM,GAAG,OAAO,CAAC,IAAI,CAAC,CAAC;IAE9C,wCAAwC;IACxC,eAAe,CAAC,OAAO,EAAE,MAAM,EAAE,QAAQ,EAAE,MAAM,EAAE,QAAQ,EAAE,MAAM,GAAG,OAAO,CAAC,IAAI,CAAC,CAAC;IAEpF,0BAA0B;IAC1B,kBAAkB,IAAI,OAAO,CAAC,MAAM,EAAE,CAAC,CAAC;IAExC,wDAAwD;IACxD,kBAAkB,IAAI,eAAe,CAAC;CACtC"}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"base.js","sourceRoot":"","sources":["../../src/installers/base.ts"],"names":[],"mappings":"AAAA;;GAEG"}
|
|
@@ -0,0 +1,8 @@
|
|
|
1
|
+
export * from './base.js';
|
|
2
|
+
export * from './linux.js';
|
|
3
|
+
import type { BaseInstaller } from './base.js';
|
|
4
|
+
/**
|
|
5
|
+
* Get the appropriate installer for the current platform
|
|
6
|
+
*/
|
|
7
|
+
export declare function getInstaller(): BaseInstaller;
|
|
8
|
+
//# sourceMappingURL=index.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../src/installers/index.ts"],"names":[],"mappings":"AAAA,cAAc,WAAW,CAAC;AAC1B,cAAc,YAAY,CAAC;AAG3B,OAAO,KAAK,EAAE,aAAa,EAAE,MAAM,WAAW,CAAC;AAE/C;;GAEG;AACH,wBAAgB,YAAY,IAAI,aAAa,CAS5C"}
|
|
@@ -0,0 +1,16 @@
|
|
|
1
|
+
export * from './base.js';
|
|
2
|
+
export * from './linux.js';
|
|
3
|
+
import { LinuxInstaller } from './linux.js';
|
|
4
|
+
/**
|
|
5
|
+
* Get the appropriate installer for the current platform
|
|
6
|
+
*/
|
|
7
|
+
export function getInstaller() {
|
|
8
|
+
const platform = process.platform;
|
|
9
|
+
switch (platform) {
|
|
10
|
+
case 'linux':
|
|
11
|
+
return new LinuxInstaller();
|
|
12
|
+
default:
|
|
13
|
+
throw new Error(`Unsupported platform: ${platform}. Only Linux is currently supported.`);
|
|
14
|
+
}
|
|
15
|
+
}
|
|
16
|
+
//# sourceMappingURL=index.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"index.js","sourceRoot":"","sources":["../../src/installers/index.ts"],"names":[],"mappings":"AAAA,cAAc,WAAW,CAAC;AAC1B,cAAc,YAAY,CAAC;AAE3B,OAAO,EAAE,cAAc,EAAE,MAAM,YAAY,CAAC;AAG5C;;GAEG;AACH,MAAM,UAAU,YAAY;IAC3B,MAAM,QAAQ,GAAG,OAAO,CAAC,QAAQ,CAAC;IAElC,QAAQ,QAAQ,EAAE,CAAC;QAClB,KAAK,OAAO;YACX,OAAO,IAAI,cAAc,EAAE,CAAC;QAC7B;YACC,MAAM,IAAI,KAAK,CAAC,yBAAyB,QAAQ,sCAAsC,CAAC,CAAC;IAC3F,CAAC;AACF,CAAC"}
|
|
@@ -0,0 +1,32 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Linux installer using systemd for service management
|
|
3
|
+
*/
|
|
4
|
+
import type { BaseInstaller, InstalledConfig, InstallOptions, ServiceStatus, UninstallOptions } from './base.js';
|
|
5
|
+
export declare class LinuxInstaller implements BaseInstaller {
|
|
6
|
+
readonly platform = "linux";
|
|
7
|
+
private packageRoot;
|
|
8
|
+
constructor();
|
|
9
|
+
isCompatible(): boolean;
|
|
10
|
+
checkPrerequisites(): Promise<string[]>;
|
|
11
|
+
install(options: InstallOptions): Promise<void>;
|
|
12
|
+
uninstall(options: UninstallOptions): Promise<void>;
|
|
13
|
+
/**
|
|
14
|
+
* Read installation config from environment file
|
|
15
|
+
*/
|
|
16
|
+
getInstalledConfig(): InstalledConfig;
|
|
17
|
+
start(): Promise<void>;
|
|
18
|
+
stop(): Promise<void>;
|
|
19
|
+
restart(): Promise<void>;
|
|
20
|
+
status(): Promise<ServiceStatus>;
|
|
21
|
+
logs(options: {
|
|
22
|
+
follow: boolean;
|
|
23
|
+
lines: number;
|
|
24
|
+
since?: string;
|
|
25
|
+
}): Promise<void>;
|
|
26
|
+
runMigrations(dataDir: string): Promise<void>;
|
|
27
|
+
createAdminUser(dataDir: string, username: string, password: string): Promise<void>;
|
|
28
|
+
private createEnvironmentFile;
|
|
29
|
+
private createServiceFile;
|
|
30
|
+
private createPolkitRule;
|
|
31
|
+
}
|
|
32
|
+
//# sourceMappingURL=linux.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"linux.d.ts","sourceRoot":"","sources":["../../src/installers/linux.ts"],"names":[],"mappings":"AAAA;;GAEG;AAOH,OAAO,KAAK,EAAE,aAAa,EAAE,eAAe,EAAE,cAAc,EAAE,aAAa,EAAE,gBAAgB,EAAE,MAAM,WAAW,CAAC;AA4BjH,qBAAa,cAAe,YAAW,aAAa;IACnD,QAAQ,CAAC,QAAQ,WAAW;IAE5B,OAAO,CAAC,WAAW,CAAS;;IAO5B,YAAY,IAAI,OAAO;IAIjB,kBAAkB,IAAI,OAAO,CAAC,MAAM,EAAE,CAAC;IA0CvC,OAAO,CAAC,OAAO,EAAE,cAAc,GAAG,OAAO,CAAC,IAAI,CAAC;IA8C/C,SAAS,CAAC,OAAO,EAAE,gBAAgB,GAAG,OAAO,CAAC,IAAI,CAAC;IAiEzD;;OAEG;IACH,kBAAkB,IAAI,eAAe;IAuB/B,KAAK,IAAI,OAAO,CAAC,IAAI,CAAC;IAItB,IAAI,IAAI,OAAO,CAAC,IAAI,CAAC;IAIrB,OAAO,IAAI,OAAO,CAAC,IAAI,CAAC;IAIxB,MAAM,IAAI,OAAO,CAAC,aAAa,CAAC;IAuFhC,IAAI,CAAC,OAAO,EAAE;QAAE,MAAM,EAAE,OAAO,CAAC;QAAC,KAAK,EAAE,MAAM,CAAC;QAAC,KAAK,CAAC,EAAE,MAAM,CAAA;KAAE,GAAG,OAAO,CAAC,IAAI,CAAC;IAchF,aAAa,CAAC,OAAO,EAAE,MAAM,GAAG,OAAO,CAAC,IAAI,CAAC;IA4B7C,eAAe,CAAC,OAAO,EAAE,MAAM,EAAE,QAAQ,EAAE,MAAM,EAAE,QAAQ,EAAE,MAAM,GAAG,OAAO,CAAC,IAAI,CAAC;IA2BzF,OAAO,CAAC,qBAAqB;IA+C7B,OAAO,CAAC,iBAAiB;IAwCzB,OAAO,CAAC,gBAAgB;CAqBxB"}
|
|
@@ -0,0 +1,406 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Linux installer using systemd for service management
|
|
3
|
+
*/
|
|
4
|
+
import { execFileSync } from 'node:child_process';
|
|
5
|
+
import { existsSync, unlinkSync, rmSync } from 'node:fs';
|
|
6
|
+
import { dirname, join } from 'node:path';
|
|
7
|
+
import { fileURLToPath } from 'node:url';
|
|
8
|
+
import { createDirectory, createSystemUser, deleteSystemUser, exec, execLive, generateSecret, getAdminDistPath, getBackendCliPath, getBackendMainPath, getDataSourcePath, getNodePath, getTypeOrmCliPath, hasSystemd, isRoot, readFile, setOwnership, userExists, writeFile, } from '../utils/index.js';
|
|
9
|
+
const __filename = fileURLToPath(import.meta.url);
|
|
10
|
+
const __dirname = dirname(__filename);
|
|
11
|
+
const SERVICE_NAME = 'smart-panel';
|
|
12
|
+
const SYSTEMD_PATH = `/etc/systemd/system/${SERVICE_NAME}.service`;
|
|
13
|
+
export class LinuxInstaller {
|
|
14
|
+
platform = 'linux';
|
|
15
|
+
packageRoot;
|
|
16
|
+
constructor() {
|
|
17
|
+
// Package root is two levels up from dist/installers/
|
|
18
|
+
this.packageRoot = join(__dirname, '..', '..');
|
|
19
|
+
}
|
|
20
|
+
isCompatible() {
|
|
21
|
+
return process.platform === 'linux' && hasSystemd();
|
|
22
|
+
}
|
|
23
|
+
async checkPrerequisites() {
|
|
24
|
+
const errors = [];
|
|
25
|
+
if (!isRoot()) {
|
|
26
|
+
errors.push('This command must be run as root (use sudo)');
|
|
27
|
+
}
|
|
28
|
+
if (!hasSystemd()) {
|
|
29
|
+
errors.push('systemd is required but not detected');
|
|
30
|
+
}
|
|
31
|
+
if (!existsSync(getNodePath())) {
|
|
32
|
+
errors.push('Node.js executable not found');
|
|
33
|
+
}
|
|
34
|
+
// Check Node.js version (>= 24 required)
|
|
35
|
+
const nodeMajor = parseInt(process.versions.node.split('.')[0], 10);
|
|
36
|
+
if (nodeMajor < 24) {
|
|
37
|
+
errors.push(`Node.js >= 24 is required (found v${process.versions.node})`);
|
|
38
|
+
}
|
|
39
|
+
// Check disk space (need at least 200 MB)
|
|
40
|
+
try {
|
|
41
|
+
const dfOutput = execFileSync('df', ['-BM', '/var/lib'], { encoding: 'utf-8', stdio: ['pipe', 'pipe', 'ignore'] });
|
|
42
|
+
const lines = dfOutput.trim().split('\n');
|
|
43
|
+
if (lines.length >= 2) {
|
|
44
|
+
const parts = lines[1].split(/\s+/);
|
|
45
|
+
const availMB = parseInt(parts[3], 10);
|
|
46
|
+
if (availMB < 200) {
|
|
47
|
+
errors.push(`Insufficient disk space: ${availMB} MB available (200 MB minimum required)`);
|
|
48
|
+
}
|
|
49
|
+
}
|
|
50
|
+
}
|
|
51
|
+
catch {
|
|
52
|
+
// Ignore - non-critical check
|
|
53
|
+
}
|
|
54
|
+
return errors;
|
|
55
|
+
}
|
|
56
|
+
async install(options) {
|
|
57
|
+
const { user, dataDir, port, noStart, adminUsername, adminPassword } = options;
|
|
58
|
+
// Create system user
|
|
59
|
+
createSystemUser(user, dataDir);
|
|
60
|
+
// Create directories
|
|
61
|
+
createDirectory(dataDir, user, 0o755);
|
|
62
|
+
createDirectory(join(dataDir, 'data'), user, 0o755);
|
|
63
|
+
createDirectory(join(dataDir, 'config'), user, 0o755);
|
|
64
|
+
createDirectory('/etc/smart-panel', undefined, 0o755);
|
|
65
|
+
createDirectory('/run/smart-panel', user, 0o755);
|
|
66
|
+
// Generate environment file
|
|
67
|
+
this.createEnvironmentFile(dataDir, port, user);
|
|
68
|
+
// Create systemd service file
|
|
69
|
+
this.createServiceFile(user, dataDir);
|
|
70
|
+
// Install polkit rule for reboot/poweroff authorization
|
|
71
|
+
this.createPolkitRule(user);
|
|
72
|
+
// Reload systemd
|
|
73
|
+
exec('systemctl daemon-reload');
|
|
74
|
+
// Enable service
|
|
75
|
+
exec('systemctl enable smart-panel');
|
|
76
|
+
// Run migrations
|
|
77
|
+
await this.runMigrations(dataDir);
|
|
78
|
+
// Create admin user if credentials provided
|
|
79
|
+
if (adminUsername && adminPassword) {
|
|
80
|
+
await this.createAdminUser(dataDir, adminUsername, adminPassword);
|
|
81
|
+
}
|
|
82
|
+
// Set ownership after all file-creating operations complete
|
|
83
|
+
// This ensures database files created by migrations are owned by the service user
|
|
84
|
+
setOwnership(dataDir, user);
|
|
85
|
+
// Start service if not skipped
|
|
86
|
+
if (!noStart) {
|
|
87
|
+
await this.start();
|
|
88
|
+
}
|
|
89
|
+
}
|
|
90
|
+
async uninstall(options) {
|
|
91
|
+
const { keepData } = options;
|
|
92
|
+
// Read installed config before removing environment file
|
|
93
|
+
const config = this.getInstalledConfig();
|
|
94
|
+
const serviceUser = config.user || 'smart-panel';
|
|
95
|
+
const dataDir = config.dataDir || '/var/lib/smart-panel';
|
|
96
|
+
// Stop and disable service
|
|
97
|
+
try {
|
|
98
|
+
exec('systemctl stop smart-panel', { silent: true });
|
|
99
|
+
}
|
|
100
|
+
catch {
|
|
101
|
+
// Service might not be running
|
|
102
|
+
}
|
|
103
|
+
try {
|
|
104
|
+
exec('systemctl disable smart-panel', { silent: true });
|
|
105
|
+
}
|
|
106
|
+
catch {
|
|
107
|
+
// Service might not be enabled
|
|
108
|
+
}
|
|
109
|
+
// Remove systemd service file
|
|
110
|
+
if (existsSync(SYSTEMD_PATH)) {
|
|
111
|
+
unlinkSync(SYSTEMD_PATH);
|
|
112
|
+
}
|
|
113
|
+
// Reload systemd
|
|
114
|
+
exec('systemctl daemon-reload');
|
|
115
|
+
// Remove polkit rule
|
|
116
|
+
const polkitRule = '/etc/polkit-1/rules.d/50-smart-panel.rules';
|
|
117
|
+
if (existsSync(polkitRule)) {
|
|
118
|
+
unlinkSync(polkitRule);
|
|
119
|
+
}
|
|
120
|
+
// Remove environment file
|
|
121
|
+
if (existsSync('/etc/smart-panel/environment')) {
|
|
122
|
+
unlinkSync('/etc/smart-panel/environment');
|
|
123
|
+
}
|
|
124
|
+
// Remove config directory if empty
|
|
125
|
+
try {
|
|
126
|
+
rmSync('/etc/smart-panel', { recursive: false });
|
|
127
|
+
}
|
|
128
|
+
catch {
|
|
129
|
+
// Directory might not be empty or not exist
|
|
130
|
+
}
|
|
131
|
+
// Remove data directory if not keeping
|
|
132
|
+
if (!keepData && existsSync(dataDir)) {
|
|
133
|
+
rmSync(dataDir, { recursive: true, force: true });
|
|
134
|
+
}
|
|
135
|
+
// Remove run directory
|
|
136
|
+
try {
|
|
137
|
+
rmSync('/run/smart-panel', { recursive: true, force: true });
|
|
138
|
+
}
|
|
139
|
+
catch {
|
|
140
|
+
// Might not exist
|
|
141
|
+
}
|
|
142
|
+
// Delete system user
|
|
143
|
+
if (userExists(serviceUser)) {
|
|
144
|
+
deleteSystemUser(serviceUser);
|
|
145
|
+
}
|
|
146
|
+
}
|
|
147
|
+
/**
|
|
148
|
+
* Read installation config from environment file
|
|
149
|
+
*/
|
|
150
|
+
getInstalledConfig() {
|
|
151
|
+
const envPath = '/etc/smart-panel/environment';
|
|
152
|
+
const content = readFile(envPath);
|
|
153
|
+
if (!content) {
|
|
154
|
+
return {};
|
|
155
|
+
}
|
|
156
|
+
const config = {};
|
|
157
|
+
const userMatch = content.match(/^FB_SERVICE_USER=(.+)$/m);
|
|
158
|
+
if (userMatch?.[1]) {
|
|
159
|
+
config.user = userMatch[1];
|
|
160
|
+
}
|
|
161
|
+
const dataDirMatch = content.match(/^FB_DATA_DIR=(.+)$/m);
|
|
162
|
+
if (dataDirMatch?.[1]) {
|
|
163
|
+
config.dataDir = dataDirMatch[1];
|
|
164
|
+
}
|
|
165
|
+
return config;
|
|
166
|
+
}
|
|
167
|
+
async start() {
|
|
168
|
+
exec('systemctl start smart-panel');
|
|
169
|
+
}
|
|
170
|
+
async stop() {
|
|
171
|
+
exec('systemctl stop smart-panel');
|
|
172
|
+
}
|
|
173
|
+
async restart() {
|
|
174
|
+
exec('systemctl restart smart-panel');
|
|
175
|
+
}
|
|
176
|
+
async status() {
|
|
177
|
+
const installed = existsSync(SYSTEMD_PATH);
|
|
178
|
+
if (!installed) {
|
|
179
|
+
return {
|
|
180
|
+
installed: false,
|
|
181
|
+
running: false,
|
|
182
|
+
enabled: false,
|
|
183
|
+
message: 'Service is not installed',
|
|
184
|
+
};
|
|
185
|
+
}
|
|
186
|
+
let running = false;
|
|
187
|
+
let enabled = false;
|
|
188
|
+
let pid;
|
|
189
|
+
let uptime;
|
|
190
|
+
let memoryMB;
|
|
191
|
+
// Check if running
|
|
192
|
+
try {
|
|
193
|
+
exec('systemctl is-active smart-panel', { silent: true });
|
|
194
|
+
running = true;
|
|
195
|
+
}
|
|
196
|
+
catch {
|
|
197
|
+
running = false;
|
|
198
|
+
}
|
|
199
|
+
// Check if enabled
|
|
200
|
+
try {
|
|
201
|
+
exec('systemctl is-enabled smart-panel', { silent: true });
|
|
202
|
+
enabled = true;
|
|
203
|
+
}
|
|
204
|
+
catch {
|
|
205
|
+
enabled = false;
|
|
206
|
+
}
|
|
207
|
+
// Get PID and resource usage if running
|
|
208
|
+
if (running) {
|
|
209
|
+
try {
|
|
210
|
+
const pidOutput = exec('systemctl show smart-panel --property=MainPID --value', { silent: true });
|
|
211
|
+
pid = parseInt(pidOutput.trim(), 10);
|
|
212
|
+
if (pid && pid > 0) {
|
|
213
|
+
// Get memory usage
|
|
214
|
+
try {
|
|
215
|
+
const memOutput = execFileSync('ps', ['-o', 'rss=', '-p', String(pid)], {
|
|
216
|
+
encoding: 'utf-8',
|
|
217
|
+
stdio: 'pipe',
|
|
218
|
+
});
|
|
219
|
+
const calculatedMemory = Math.round(parseInt(memOutput.trim(), 10) / 1024);
|
|
220
|
+
// Only assign if valid (not NaN and non-negative)
|
|
221
|
+
if (!isNaN(calculatedMemory) && calculatedMemory >= 0) {
|
|
222
|
+
memoryMB = calculatedMemory;
|
|
223
|
+
}
|
|
224
|
+
}
|
|
225
|
+
catch {
|
|
226
|
+
// Ignore
|
|
227
|
+
}
|
|
228
|
+
// Get uptime
|
|
229
|
+
try {
|
|
230
|
+
const startTimeOutput = exec('systemctl show smart-panel --property=ActiveEnterTimestamp --value', { silent: true });
|
|
231
|
+
const startTime = new Date(startTimeOutput.trim());
|
|
232
|
+
const calculatedUptime = Math.floor((Date.now() - startTime.getTime()) / 1000);
|
|
233
|
+
// Only assign if valid (not NaN and non-negative)
|
|
234
|
+
if (!isNaN(calculatedUptime) && calculatedUptime >= 0) {
|
|
235
|
+
uptime = calculatedUptime;
|
|
236
|
+
}
|
|
237
|
+
}
|
|
238
|
+
catch {
|
|
239
|
+
// Ignore
|
|
240
|
+
}
|
|
241
|
+
}
|
|
242
|
+
}
|
|
243
|
+
catch {
|
|
244
|
+
// Ignore
|
|
245
|
+
}
|
|
246
|
+
}
|
|
247
|
+
return {
|
|
248
|
+
installed,
|
|
249
|
+
running,
|
|
250
|
+
enabled,
|
|
251
|
+
pid: pid && pid > 0 ? pid : undefined,
|
|
252
|
+
uptime,
|
|
253
|
+
memoryMB,
|
|
254
|
+
};
|
|
255
|
+
}
|
|
256
|
+
async logs(options) {
|
|
257
|
+
const args = ['journalctl', '-u', 'smart-panel', '--no-pager', '-n', options.lines.toString()];
|
|
258
|
+
if (options.follow) {
|
|
259
|
+
args.push('-f');
|
|
260
|
+
}
|
|
261
|
+
if (options.since) {
|
|
262
|
+
args.push('--since', options.since);
|
|
263
|
+
}
|
|
264
|
+
await execLive(args[0], args.slice(1));
|
|
265
|
+
}
|
|
266
|
+
async runMigrations(dataDir) {
|
|
267
|
+
const nodeModulesPath = join(dataDir, 'node_modules');
|
|
268
|
+
// Check if node_modules exists (for global install, use package's node_modules)
|
|
269
|
+
const actualModulesPath = existsSync(nodeModulesPath) ? dataDir : this.packageRoot;
|
|
270
|
+
const typeormCli = getTypeOrmCliPath(actualModulesPath);
|
|
271
|
+
const dataSource = getDataSourcePath(actualModulesPath);
|
|
272
|
+
if (!existsSync(typeormCli) || !existsSync(dataSource)) {
|
|
273
|
+
throw new Error('TypeORM CLI or data source not found. Make sure dependencies are installed.');
|
|
274
|
+
}
|
|
275
|
+
// Set environment variables for migration
|
|
276
|
+
const env = {
|
|
277
|
+
...process.env,
|
|
278
|
+
FB_DB_PATH: join(dataDir, 'data'),
|
|
279
|
+
FB_CONFIG_PATH: join(dataDir, 'config'),
|
|
280
|
+
NODE_ENV: 'production',
|
|
281
|
+
};
|
|
282
|
+
execFileSync('node', [typeormCli, 'migration:run', '-d', dataSource], {
|
|
283
|
+
cwd: actualModulesPath,
|
|
284
|
+
env,
|
|
285
|
+
stdio: 'inherit',
|
|
286
|
+
});
|
|
287
|
+
}
|
|
288
|
+
async createAdminUser(dataDir, username, password) {
|
|
289
|
+
const nodeModulesPath = join(dataDir, 'node_modules');
|
|
290
|
+
// Check if node_modules exists (for global install, use package's node_modules)
|
|
291
|
+
const actualModulesPath = existsSync(nodeModulesPath) ? dataDir : this.packageRoot;
|
|
292
|
+
const backendCli = getBackendCliPath(actualModulesPath);
|
|
293
|
+
if (!existsSync(backendCli)) {
|
|
294
|
+
throw new Error('Backend CLI not found. Make sure dependencies are installed.');
|
|
295
|
+
}
|
|
296
|
+
// Set environment variables for CLI
|
|
297
|
+
const env = {
|
|
298
|
+
...process.env,
|
|
299
|
+
FB_DB_PATH: join(dataDir, 'data'),
|
|
300
|
+
FB_CONFIG_PATH: join(dataDir, 'config'),
|
|
301
|
+
NODE_ENV: 'production',
|
|
302
|
+
};
|
|
303
|
+
execFileSync('node', [backendCli, 'auth:onboarding', username, password], {
|
|
304
|
+
cwd: actualModulesPath,
|
|
305
|
+
env,
|
|
306
|
+
stdio: 'inherit',
|
|
307
|
+
});
|
|
308
|
+
}
|
|
309
|
+
createEnvironmentFile(dataDir, port, user) {
|
|
310
|
+
const envPath = '/etc/smart-panel/environment';
|
|
311
|
+
const adminDistPath = getAdminDistPath(this.packageRoot);
|
|
312
|
+
// Preserve existing JWT secret if environment file exists
|
|
313
|
+
let jwtSecret;
|
|
314
|
+
const existingContent = readFile(envPath);
|
|
315
|
+
if (existingContent) {
|
|
316
|
+
const match = existingContent.match(/^FB_JWT_SECRET=(.+)$/m);
|
|
317
|
+
if (match?.[1]) {
|
|
318
|
+
jwtSecret = match[1];
|
|
319
|
+
}
|
|
320
|
+
}
|
|
321
|
+
// Generate new secret only if not found
|
|
322
|
+
if (!jwtSecret) {
|
|
323
|
+
jwtSecret = generateSecret(64);
|
|
324
|
+
}
|
|
325
|
+
const content = `# Smart Panel Environment Configuration
|
|
326
|
+
# Generated by smart-panel-service install
|
|
327
|
+
|
|
328
|
+
NODE_ENV=production
|
|
329
|
+
FB_BACKEND_PORT=${port}
|
|
330
|
+
FB_ADMIN_UI_PATH=${adminDistPath}
|
|
331
|
+
FB_DB_PATH=${dataDir}/data
|
|
332
|
+
FB_CONFIG_PATH=${dataDir}/config
|
|
333
|
+
|
|
334
|
+
# JWT secret for authentication (auto-generated)
|
|
335
|
+
FB_JWT_SECRET=${jwtSecret}
|
|
336
|
+
|
|
337
|
+
# Installation settings (used by uninstall/update)
|
|
338
|
+
FB_SERVICE_USER=${user}
|
|
339
|
+
FB_DATA_DIR=${dataDir}
|
|
340
|
+
|
|
341
|
+
# Uncomment and configure as needed:
|
|
342
|
+
# FB_INFLUXDB_URL=http://localhost:8086
|
|
343
|
+
# FB_INFLUXDB_DATABASE=smart_panel
|
|
344
|
+
# FB_MDNS_ENABLED=true
|
|
345
|
+
# FB_MDNS_SERVICE_NAME=FastyBird Smart Panel
|
|
346
|
+
`;
|
|
347
|
+
// Use restrictive permissions for file containing secrets
|
|
348
|
+
writeFile(envPath, content, 0o600);
|
|
349
|
+
}
|
|
350
|
+
createServiceFile(user, dataDir) {
|
|
351
|
+
const nodePath = getNodePath();
|
|
352
|
+
const mainPath = getBackendMainPath(this.packageRoot);
|
|
353
|
+
const content = `[Unit]
|
|
354
|
+
Description=FastyBird Smart Panel
|
|
355
|
+
Documentation=https://smart-panel.fastybird.com
|
|
356
|
+
After=network-online.target
|
|
357
|
+
Wants=network-online.target
|
|
358
|
+
|
|
359
|
+
[Service]
|
|
360
|
+
Type=simple
|
|
361
|
+
User=${user}
|
|
362
|
+
Group=${user}
|
|
363
|
+
WorkingDirectory=${dataDir}
|
|
364
|
+
EnvironmentFile=-/etc/smart-panel/environment
|
|
365
|
+
ExecStart=${nodePath} ${mainPath}
|
|
366
|
+
Restart=on-failure
|
|
367
|
+
RestartSec=5
|
|
368
|
+
StandardOutput=journal
|
|
369
|
+
StandardError=journal
|
|
370
|
+
SyslogIdentifier=smart-panel
|
|
371
|
+
|
|
372
|
+
# Security hardening
|
|
373
|
+
# Note: NoNewPrivileges is NOT set because the app uses sudo for
|
|
374
|
+
# reboot/poweroff. ProtectKernelTunables and ProtectKernelModules
|
|
375
|
+
# are also omitted because they implicitly enable NoNewPrivileges.
|
|
376
|
+
ProtectSystem=strict
|
|
377
|
+
ProtectHome=true
|
|
378
|
+
ReadWritePaths=${dataDir}
|
|
379
|
+
PrivateTmp=true
|
|
380
|
+
ProtectControlGroups=true
|
|
381
|
+
|
|
382
|
+
[Install]
|
|
383
|
+
WantedBy=multi-user.target
|
|
384
|
+
`;
|
|
385
|
+
writeFile(SYSTEMD_PATH, content, 0o644);
|
|
386
|
+
}
|
|
387
|
+
createPolkitRule(user) {
|
|
388
|
+
const rulesDir = '/etc/polkit-1/rules.d';
|
|
389
|
+
createDirectory(rulesDir, undefined, 0o755);
|
|
390
|
+
const content = `// Allow the ${user} service user to reboot and power off
|
|
391
|
+
polkit.addRule(function (action, subject) {
|
|
392
|
+
if (
|
|
393
|
+
(action.id === "org.freedesktop.login1.power-off" ||
|
|
394
|
+
action.id === "org.freedesktop.login1.power-off-multiple-sessions" ||
|
|
395
|
+
action.id === "org.freedesktop.login1.reboot" ||
|
|
396
|
+
action.id === "org.freedesktop.login1.reboot-multiple-sessions") &&
|
|
397
|
+
subject.user === "${user}"
|
|
398
|
+
) {
|
|
399
|
+
return polkit.Result.YES;
|
|
400
|
+
}
|
|
401
|
+
});
|
|
402
|
+
`;
|
|
403
|
+
writeFile(join(rulesDir, '50-smart-panel.rules'), content, 0o644);
|
|
404
|
+
}
|
|
405
|
+
}
|
|
406
|
+
//# sourceMappingURL=linux.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"linux.js","sourceRoot":"","sources":["../../src/installers/linux.ts"],"names":[],"mappings":"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"}
|