@irsyadulibad/servermon 1.0.4 β 1.1.0
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 +1 -1
- package/cli.ts +138 -17
- package/index.ts +13 -4
- package/package.json +1 -1
package/README.md
CHANGED
package/cli.ts
CHANGED
|
@@ -14,6 +14,97 @@ function banner() {
|
|
|
14
14
|
console.log();
|
|
15
15
|
}
|
|
16
16
|
|
|
17
|
+
// βββββββββββββββββββββββββββββββββββββ
|
|
18
|
+
// systemd autoβsetup
|
|
19
|
+
// βββββββββββββββββββββββββββββββββββββ
|
|
20
|
+
async function setupSystemd(): Promise<void> {
|
|
21
|
+
banner();
|
|
22
|
+
console.log("βοΈ Systemd Service Setup\n");
|
|
23
|
+
|
|
24
|
+
const config = await loadConfig();
|
|
25
|
+
if (!config) {
|
|
26
|
+
console.error("β No config found. Run `servermon` first to set up.");
|
|
27
|
+
console.error(" Then try: servermon --install-service");
|
|
28
|
+
process.exit(1);
|
|
29
|
+
}
|
|
30
|
+
|
|
31
|
+
// Resolve paths
|
|
32
|
+
const bunPath = Bun.which("bun");
|
|
33
|
+
if (!bunPath) {
|
|
34
|
+
console.error("β bun not found in PATH");
|
|
35
|
+
process.exit(1);
|
|
36
|
+
}
|
|
37
|
+
|
|
38
|
+
const servermonPath = Bun.which("servermon");
|
|
39
|
+
if (!servermonPath) {
|
|
40
|
+
console.error("β servermon binary not found.");
|
|
41
|
+
console.error(" Install with: bun i -g @irsyadulibad/servermon");
|
|
42
|
+
process.exit(1);
|
|
43
|
+
}
|
|
44
|
+
|
|
45
|
+
console.log(`π bun: ${bunPath}`);
|
|
46
|
+
console.log(`π servermon: ${servermonPath}`);
|
|
47
|
+
console.log(`π Config: ${configPath()}`);
|
|
48
|
+
console.log();
|
|
49
|
+
|
|
50
|
+
// Create systemd user directory
|
|
51
|
+
const home = process.env.HOME ?? "~";
|
|
52
|
+
const systemdDir = `${home}/.config/systemd/user`;
|
|
53
|
+
await Bun.write(`${systemdDir}/.gitkeep`, ""); // ensure dir exists
|
|
54
|
+
try { await (Bun as any).mkdir?.(systemdDir, { recursive: true }); } catch { /* Bun.mkdir may not exist; fs fallback */ }
|
|
55
|
+
try { await import("node:fs").then(m => m.mkdirSync(systemdDir, { recursive: true })); } catch {}
|
|
56
|
+
|
|
57
|
+
const serviceFile = `${systemdDir}/servermon.service`;
|
|
58
|
+
const serviceContent = `[Unit]
|
|
59
|
+
Description=Server Monitor β Telegram system health reports
|
|
60
|
+
After=network-online.target
|
|
61
|
+
Wants=network-online.target
|
|
62
|
+
|
|
63
|
+
[Service]
|
|
64
|
+
Type=simple
|
|
65
|
+
ExecStart=${servermonPath} start
|
|
66
|
+
Restart=always
|
|
67
|
+
RestartSec=30
|
|
68
|
+
Environment=NODE_ENV=production
|
|
69
|
+
|
|
70
|
+
[Install]
|
|
71
|
+
WantedBy=default.target
|
|
72
|
+
`;
|
|
73
|
+
|
|
74
|
+
await Bun.write(serviceFile, serviceContent);
|
|
75
|
+
console.log(`π Written: ${serviceFile}`);
|
|
76
|
+
console.log();
|
|
77
|
+
|
|
78
|
+
// systemctl commands
|
|
79
|
+
const cmds = [
|
|
80
|
+
["systemctl", "--user", "daemon-reload"],
|
|
81
|
+
["systemctl", "--user", "enable", "servermon.service"],
|
|
82
|
+
["systemctl", "--user", "start", "servermon.service"],
|
|
83
|
+
["systemctl", "--user", "status", "servermon.service", "--no-pager", "-l"],
|
|
84
|
+
];
|
|
85
|
+
|
|
86
|
+
for (const cmd of cmds) {
|
|
87
|
+
console.log(`π ${cmd.join(" ")}`);
|
|
88
|
+
const proc = Bun.spawnSync({ cmd, stdout: "pipe", stderr: "pipe" });
|
|
89
|
+
const out = new TextDecoder().decode(proc.stdout);
|
|
90
|
+
const err = new TextDecoder().decode(proc.stderr);
|
|
91
|
+
if (out) console.log(out);
|
|
92
|
+
if (err && proc.exitCode !== 0) console.error(err);
|
|
93
|
+
}
|
|
94
|
+
|
|
95
|
+
console.log();
|
|
96
|
+
console.log("β
Systemd service installed!");
|
|
97
|
+
console.log();
|
|
98
|
+
console.log("π For auto-start at boot, enable lingering:");
|
|
99
|
+
console.log(` loginctl enable-linger`);
|
|
100
|
+
console.log();
|
|
101
|
+
console.log("π Useful commands:");
|
|
102
|
+
console.log(" systemctl --user status servermon # check status");
|
|
103
|
+
console.log(" systemctl --user stop servermon # stop daemon");
|
|
104
|
+
console.log(" systemctl --user restart servermon # restart");
|
|
105
|
+
console.log(" journalctl --user -u servermon -f # watch logs");
|
|
106
|
+
}
|
|
107
|
+
|
|
17
108
|
async function interactiveSetup(): Promise<void> {
|
|
18
109
|
console.log("π₯ Server Monitor β First Time Setup");
|
|
19
110
|
console.log(` Config will be saved to: ${configDir()}\n`);
|
|
@@ -65,32 +156,62 @@ async function interactiveSetup(): Promise<void> {
|
|
|
65
156
|
console.log(`\nβ
Config saved!`);
|
|
66
157
|
console.log(` π ${configPath()}`);
|
|
67
158
|
console.log(` β± Interval: ${interval}s (${label})`);
|
|
68
|
-
console.log(`\nπ‘ Next step: DM your bot once on Telegram, then
|
|
159
|
+
console.log(`\nπ‘ Next step: DM your bot once on Telegram, then run \`servermon\`.`);
|
|
69
160
|
}
|
|
70
161
|
|
|
71
162
|
async function main() {
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
const config = await loadConfig();
|
|
163
|
+
const cmd = process.argv[2];
|
|
75
164
|
|
|
76
|
-
|
|
165
|
+
// --- subcommand routing ---
|
|
166
|
+
if (cmd === "setup") {
|
|
167
|
+
banner();
|
|
77
168
|
await interactiveSetup();
|
|
78
|
-
|
|
169
|
+
return;
|
|
79
170
|
}
|
|
80
171
|
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
|
|
172
|
+
if (cmd === "start") {
|
|
173
|
+
banner();
|
|
174
|
+
const config = await loadConfig();
|
|
175
|
+
if (!config) {
|
|
176
|
+
console.error("β No config found. Run `servermon setup` first.");
|
|
177
|
+
process.exit(1);
|
|
178
|
+
}
|
|
179
|
+
process.env["TELEGRAM_BOT_TOKEN"] = config.token;
|
|
180
|
+
process.env["MONITOR_INTERVAL"] = String(config.interval);
|
|
181
|
+
if (config.chatId) process.env["TELEGRAM_CHAT_ID"] = config.chatId;
|
|
182
|
+
|
|
183
|
+
console.log(`π Config: ${configPath()}`);
|
|
184
|
+
console.log(`π‘ Bot: ...${config.token.slice(-8)}`);
|
|
185
|
+
if (config.chatId) console.log(`π¬ Chat: ${config.chatId}`);
|
|
186
|
+
console.log();
|
|
187
|
+
|
|
188
|
+
const { start } = await import("./index.ts");
|
|
189
|
+
await start();
|
|
190
|
+
return;
|
|
191
|
+
}
|
|
85
192
|
|
|
86
|
-
|
|
87
|
-
|
|
88
|
-
|
|
89
|
-
|
|
193
|
+
if (cmd === "install-service" || cmd === "--install-service" || cmd === "--setup-systemd") {
|
|
194
|
+
await setupSystemd();
|
|
195
|
+
return;
|
|
196
|
+
}
|
|
90
197
|
|
|
91
|
-
//
|
|
92
|
-
|
|
93
|
-
|
|
198
|
+
// --- no/invalid subcommand β help ---
|
|
199
|
+
console.log("ββββββββββββββββββββββββββββββββββββββββ");
|
|
200
|
+
console.log("β π₯ SERVER MONITOR DAEMON π₯ β");
|
|
201
|
+
console.log("β Telegram β’ Bun β’ TypeScript β");
|
|
202
|
+
console.log("ββββββββββββββββββββββββββββββββββββββββ");
|
|
203
|
+
console.log();
|
|
204
|
+
console.log("Usage: servermon <command>");
|
|
205
|
+
console.log();
|
|
206
|
+
console.log("Commands:");
|
|
207
|
+
console.log(" setup First-time setup (bot token + interval)");
|
|
208
|
+
console.log(" start Start the monitoring daemon");
|
|
209
|
+
console.log(" install-service Install as systemd user service");
|
|
210
|
+
console.log();
|
|
211
|
+
console.log("Examples:");
|
|
212
|
+
console.log(" servermon setup");
|
|
213
|
+
console.log(" servermon start");
|
|
214
|
+
console.log(" servermon install-service");
|
|
94
215
|
}
|
|
95
216
|
|
|
96
217
|
main().catch((err) => {
|
package/index.ts
CHANGED
|
@@ -51,12 +51,21 @@ export async function start() {
|
|
|
51
51
|
|
|
52
52
|
if (!chatId) {
|
|
53
53
|
console.log("π TELEGRAM_CHAT_ID not set β auto-detecting...");
|
|
54
|
-
|
|
54
|
+
let detected = await autoDetectChatId(botToken);
|
|
55
|
+
|
|
55
56
|
if (!detected) {
|
|
56
|
-
console.
|
|
57
|
-
console.
|
|
58
|
-
|
|
57
|
+
console.log("β³ Waiting for you to DM the bot on Telegram...");
|
|
58
|
+
console.log(" (polling every 10s β no restart needed)");
|
|
59
|
+
console.log();
|
|
60
|
+
|
|
61
|
+
const POLL_INTERVAL_MS = 10_000;
|
|
62
|
+
|
|
63
|
+
while (!detected) {
|
|
64
|
+
await new Promise((r) => setTimeout(r, POLL_INTERVAL_MS));
|
|
65
|
+
detected = await autoDetectChatId(botToken);
|
|
66
|
+
}
|
|
59
67
|
}
|
|
68
|
+
|
|
60
69
|
chatId = detected;
|
|
61
70
|
// Persist to config
|
|
62
71
|
try {
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@irsyadulibad/servermon",
|
|
3
|
-
"version": "1.0
|
|
3
|
+
"version": "1.1.0",
|
|
4
4
|
"description": "Lightweight server monitoring daemon β collects system metrics and sends structured reports to Telegram. Built with Bun + TypeScript.",
|
|
5
5
|
"module": "index.ts",
|
|
6
6
|
"type": "module",
|