-
Notifications
You must be signed in to change notification settings - Fork 0
Expand file tree
/
Copy pathsystemd.ts
More file actions
220 lines (185 loc) · 8.29 KB
/
systemd.ts
File metadata and controls
220 lines (185 loc) · 8.29 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
// Systemd setup script for creating a service that automates your app startup
// Run with: bun run systemd.ts
import { writeFileSync } from 'fs';
import { execSync } from 'child_process';
import readline from 'readline';
import { DOMAIN, SUBDOMAIN, FULL_DOMAIN, PORT } from './src/lib/utils';
import path from 'path';
import os from 'os';
const SERVICE_NAME = 'hypernote-elements';
const SYSTEMD_DIR = '/etc/systemd/system';
async function prompt(question: string): Promise<string> {
const rl = readline.createInterface({ input: process.stdin, output: process.stdout });
return new Promise(resolve => rl.question(question, ans => { rl.close(); resolve(ans); }));
}
function getCurrentUser(): string {
try {
return execSync('whoami').toString().trim();
} catch (error) {
console.error('Failed to get current user:', error);
return os.userInfo().username;
}
}
function getCurrentWorkingDirectory(): string {
return process.cwd();
}
function getBunPath(): string {
try {
return execSync('which bun').toString().trim();
} catch (error) {
console.error('Failed to find bun path:', error);
return `/home/${getCurrentUser()}/.bun/bin/bun`;
}
}
function buildSystemdConfig(appName: string, user: string, workingDir: string, bunPath: string): string {
return `[Unit]
Description=${appName}
After=network.target
[Service]
Type=simple
User=${user}
WorkingDirectory=${workingDir}
ExecStart=${bunPath} run start
Restart=on-failure
RestartSec=10
StandardOutput=journal
StandardError=journal
SyslogIdentifier=${appName}
[Install]
WantedBy=multi-user.target
`;
}
function writeServiceFile(serviceName: string, config: string): string {
const filename = `${serviceName}.service`;
writeFileSync(filename, config);
console.log(`✅ Service file created: ${filename}`);
return filename;
}
function checkSudo(): boolean {
try {
execSync('sudo -n true', { stdio: 'ignore' });
return true;
} catch (error) {
return false;
}
}
function installService(serviceFilePath: string, serviceName: string): boolean {
try {
console.log(`📋 Installing service to ${SYSTEMD_DIR}...`);
execSync(`sudo cp ${serviceFilePath} ${SYSTEMD_DIR}/`, { stdio: 'inherit' });
console.log('✅ Service file copied successfully');
console.log('📋 Reloading systemd daemon...');
execSync('sudo systemctl daemon-reload', { stdio: 'inherit' });
console.log('✅ Systemd daemon reloaded');
console.log(`📋 Enabling ${serviceName} service...`);
execSync(`sudo systemctl enable ${serviceName}`, { stdio: 'inherit' });
console.log(`✅ Service ${serviceName} enabled`);
console.log(`📋 Starting ${serviceName} service...`);
execSync(`sudo systemctl start ${serviceName}`, { stdio: 'inherit' });
console.log(`✅ Service ${serviceName} started`);
// Check if service is running successfully
const status = execSync(`systemctl is-active ${serviceName} || echo 'inactive'`).toString().trim();
if (status === 'active') {
console.log(`✅ Service ${serviceName} is running successfully!`);
return true;
} else {
console.warn(`⚠️ Service ${serviceName} may not be running correctly. Check status for details.`);
return false;
}
} catch (error: any) {
console.error('❌ Error during service installation:', error.message);
return false;
}
}
function printInstructions(serviceName: string, serviceFilePath: string) {
console.log('\n================ SYSTEMD SERVICE SETUP GUIDE ================\n');
// Installation instructions
console.log('📋 MANUAL INSTALLATION (if automatic install failed):');
console.log(`1. Copy the service file to the systemd directory:`);
console.log(` sudo cp ${serviceFilePath} /etc/systemd/system/`);
console.log('2. Reload systemd to recognize the new service:');
console.log(' sudo systemctl daemon-reload');
console.log(`3. Enable the service to start on boot:`);
console.log(` sudo systemctl enable ${serviceName}`);
console.log(`4. Start the service:`);
console.log(` sudo systemctl start ${serviceName}`);
// Management instructions
console.log('\n📋 SERVICE MANAGEMENT:');
console.log(`1. Check status of the service:`);
console.log(` sudo systemctl status ${serviceName}`);
console.log('2. View logs:');
console.log(` sudo journalctl -u ${serviceName} -f`);
console.log('3. Restart the service:');
console.log(` sudo systemctl restart ${serviceName}`);
console.log('4. Stop the service:');
console.log(` sudo systemctl stop ${serviceName}`);
console.log('5. Disable service from starting on boot:');
console.log(` sudo systemctl disable ${serviceName}`);
// Troubleshooting
console.log('\n📋 TROUBLESHOOTING:');
console.log('1. If the service fails to start, check the logs:');
console.log(` sudo journalctl -u ${serviceName} -e`);
console.log('2. Make sure the bun executable has the correct permissions');
console.log('3. Ensure the working directory exists and is accessible');
console.log('4. Verify that the user running the service has permission to access all required files');
console.log('\n================================================================\n');
}
async function main() {
console.log('--- Hypernote Elements Systemd Setup ---\n');
console.log('This script will create a systemd service file for your application.');
console.log('You can then install this service to automatically start your app on system boot.');
const proceed = (await prompt('Continue? (y/N): ')).trim().toLowerCase();
if (proceed !== 'y') {
console.log('Aborted.');
process.exit(0);
}
// Get default values
const defaultUser = getCurrentUser();
const defaultWorkingDir = getCurrentWorkingDirectory();
const defaultBunPath = getBunPath();
const defaultAppName = 'Hypernote Elements';
// Prompt for customization
console.log('\nPlease confirm or customize the following settings:');
const appName = (await prompt(`Application name [${defaultAppName}]: `)).trim() || defaultAppName;
const user = (await prompt(`User to run the service [${defaultUser}]: `)).trim() || defaultUser;
const workingDir = (await prompt(`Working directory [${defaultWorkingDir}]: `)).trim() || defaultWorkingDir;
const bunPath = (await prompt(`Path to bun executable [${defaultBunPath}]: `)).trim() || defaultBunPath;
const customServiceName = (await prompt(`Service name [${SERVICE_NAME}]: `)).trim() || SERVICE_NAME;
try {
const config = buildSystemdConfig(appName, user, workingDir, bunPath);
const serviceFilePath = writeServiceFile(customServiceName, config);
// Check if we should attempt automatic installation
const hasSudo = checkSudo();
if (hasSudo) {
const shouldInstall = (await prompt('Do you want to automatically install and start the service? (y/N): ')).trim().toLowerCase() === 'y';
if (shouldInstall) {
const success = installService(serviceFilePath, customServiceName);
if (success) {
console.log(`\n🎉 Service ${customServiceName} has been successfully installed and started!`);
} else {
console.log('\n⚠️ Automatic installation encountered issues. Please refer to the manual instructions below.');
}
} else {
console.log('\n📋 Skipping automatic installation. Please refer to the manual instructions below.');
}
} else {
console.log('\n⚠️ Sudo access is required for automatic installation. Please enter your password when prompted or follow manual instructions.');
const shouldInstall = (await prompt('Do you want to attempt installation with sudo? (y/N): ')).trim().toLowerCase() === 'y';
if (shouldInstall) {
const success = installService(serviceFilePath, customServiceName);
if (success) {
console.log(`\n🎉 Service ${customServiceName} has been successfully installed and started!`);
} else {
console.log('\n⚠️ Automatic installation encountered issues. Please refer to the manual instructions below.');
}
} else {
console.log('\n📋 Skipping automatic installation. Please refer to the manual instructions below.');
}
}
printInstructions(customServiceName, serviceFilePath);
} catch (err: any) {
console.error('❌ Error:', err.message);
process.exit(1);
}
}
main();