@woopsy/mcpanel 1.0.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/LICENSE +21 -0
- package/README.md +151 -0
- package/dist/commands/commandRouter.js +525 -0
- package/dist/config/configManager.js +193 -0
- package/dist/index.js +776 -0
- package/dist/managers/backupManager.js +207 -0
- package/dist/managers/playitManager.js +450 -0
- package/dist/managers/serverManager.js +243 -0
- package/dist/services/downloadService.js +176 -0
- package/dist/services/processManager.js +242 -0
- package/dist/utils/colors.js +73 -0
- package/dist/utils/helpers.js +365 -0
- package/dist/utils/logger.js +85 -0
- package/package.json +67 -0
|
@@ -0,0 +1,73 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.failure = exports.info = exports.warning = exports.success = exports.bgCyan = exports.bgBlue = exports.bgGreen = exports.bgRed = exports.gray = exports.white = exports.cyan = exports.magenta = exports.blue = exports.yellow = exports.green = exports.red = exports.underline = exports.italic = exports.dim = exports.bold = exports.styles = exports.RESET = exports.ESC = void 0;
|
|
4
|
+
exports.ESC = '\x1b[';
|
|
5
|
+
exports.RESET = `${exports.ESC}0m`;
|
|
6
|
+
exports.styles = {
|
|
7
|
+
bold: '1',
|
|
8
|
+
dim: '2',
|
|
9
|
+
italic: '3',
|
|
10
|
+
underline: '4',
|
|
11
|
+
// Foreground colors
|
|
12
|
+
black: '30',
|
|
13
|
+
red: '31',
|
|
14
|
+
green: '32',
|
|
15
|
+
yellow: '33',
|
|
16
|
+
blue: '34',
|
|
17
|
+
magenta: '35',
|
|
18
|
+
cyan: '36',
|
|
19
|
+
white: '37',
|
|
20
|
+
gray: '90',
|
|
21
|
+
// Background colors
|
|
22
|
+
bgBlack: '40',
|
|
23
|
+
bgRed: '41',
|
|
24
|
+
bgGreen: '42',
|
|
25
|
+
bgYellow: '43',
|
|
26
|
+
bgBlue: '44',
|
|
27
|
+
bgMagenta: '45',
|
|
28
|
+
bgCyan: '46',
|
|
29
|
+
bgWhite: '47',
|
|
30
|
+
};
|
|
31
|
+
function format(styleCode, text) {
|
|
32
|
+
return `${exports.ESC}${styleCode}m${text}${exports.RESET}`;
|
|
33
|
+
}
|
|
34
|
+
const bold = (txt) => format(exports.styles.bold, txt);
|
|
35
|
+
exports.bold = bold;
|
|
36
|
+
const dim = (txt) => format(exports.styles.dim, txt);
|
|
37
|
+
exports.dim = dim;
|
|
38
|
+
const italic = (txt) => format(exports.styles.italic, txt);
|
|
39
|
+
exports.italic = italic;
|
|
40
|
+
const underline = (txt) => format(exports.styles.underline, txt);
|
|
41
|
+
exports.underline = underline;
|
|
42
|
+
const red = (txt) => format(exports.styles.red, txt);
|
|
43
|
+
exports.red = red;
|
|
44
|
+
const green = (txt) => format(exports.styles.green, txt);
|
|
45
|
+
exports.green = green;
|
|
46
|
+
const yellow = (txt) => format(exports.styles.yellow, txt);
|
|
47
|
+
exports.yellow = yellow;
|
|
48
|
+
const blue = (txt) => format(exports.styles.blue, txt);
|
|
49
|
+
exports.blue = blue;
|
|
50
|
+
const magenta = (txt) => format(exports.styles.magenta, txt);
|
|
51
|
+
exports.magenta = magenta;
|
|
52
|
+
const cyan = (txt) => format(exports.styles.cyan, txt);
|
|
53
|
+
exports.cyan = cyan;
|
|
54
|
+
const white = (txt) => format(exports.styles.white, txt);
|
|
55
|
+
exports.white = white;
|
|
56
|
+
const gray = (txt) => format(exports.styles.gray, txt);
|
|
57
|
+
exports.gray = gray;
|
|
58
|
+
const bgRed = (txt) => format(exports.styles.bgRed, txt);
|
|
59
|
+
exports.bgRed = bgRed;
|
|
60
|
+
const bgGreen = (txt) => format(exports.styles.bgGreen, txt);
|
|
61
|
+
exports.bgGreen = bgGreen;
|
|
62
|
+
const bgBlue = (txt) => format(exports.styles.bgBlue, txt);
|
|
63
|
+
exports.bgBlue = bgBlue;
|
|
64
|
+
const bgCyan = (txt) => format(exports.styles.bgCyan, txt);
|
|
65
|
+
exports.bgCyan = bgCyan;
|
|
66
|
+
const success = (txt) => `${(0, exports.green)('✓')} ${txt}`;
|
|
67
|
+
exports.success = success;
|
|
68
|
+
const warning = (txt) => `${(0, exports.yellow)('⚠')} ${txt}`;
|
|
69
|
+
exports.warning = warning;
|
|
70
|
+
const info = (txt) => `${(0, exports.cyan)('ℹ')} ${txt}`;
|
|
71
|
+
exports.info = info;
|
|
72
|
+
const failure = (txt) => `${(0, exports.red)('❌')} ${txt}`;
|
|
73
|
+
exports.failure = failure;
|
|
@@ -0,0 +1,365 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) {
|
|
3
|
+
if (k2 === undefined) k2 = k;
|
|
4
|
+
var desc = Object.getOwnPropertyDescriptor(m, k);
|
|
5
|
+
if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) {
|
|
6
|
+
desc = { enumerable: true, get: function() { return m[k]; } };
|
|
7
|
+
}
|
|
8
|
+
Object.defineProperty(o, k2, desc);
|
|
9
|
+
}) : (function(o, m, k, k2) {
|
|
10
|
+
if (k2 === undefined) k2 = k;
|
|
11
|
+
o[k2] = m[k];
|
|
12
|
+
}));
|
|
13
|
+
var __setModuleDefault = (this && this.__setModuleDefault) || (Object.create ? (function(o, v) {
|
|
14
|
+
Object.defineProperty(o, "default", { enumerable: true, value: v });
|
|
15
|
+
}) : function(o, v) {
|
|
16
|
+
o["default"] = v;
|
|
17
|
+
});
|
|
18
|
+
var __importStar = (this && this.__importStar) || (function () {
|
|
19
|
+
var ownKeys = function(o) {
|
|
20
|
+
ownKeys = Object.getOwnPropertyNames || function (o) {
|
|
21
|
+
var ar = [];
|
|
22
|
+
for (var k in o) if (Object.prototype.hasOwnProperty.call(o, k)) ar[ar.length] = k;
|
|
23
|
+
return ar;
|
|
24
|
+
};
|
|
25
|
+
return ownKeys(o);
|
|
26
|
+
};
|
|
27
|
+
return function (mod) {
|
|
28
|
+
if (mod && mod.__esModule) return mod;
|
|
29
|
+
var result = {};
|
|
30
|
+
if (mod != null) for (var k = ownKeys(mod), i = 0; i < k.length; i++) if (k[i] !== "default") __createBinding(result, mod, k[i]);
|
|
31
|
+
__setModuleDefault(result, mod);
|
|
32
|
+
return result;
|
|
33
|
+
};
|
|
34
|
+
})();
|
|
35
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
36
|
+
exports.detectOS = detectOS;
|
|
37
|
+
exports.openInBrowser = openInBrowser;
|
|
38
|
+
exports.openInFileExplorer = openInFileExplorer;
|
|
39
|
+
exports.normalizeInputPath = normalizeInputPath;
|
|
40
|
+
exports.openTerminalTail = openTerminalTail;
|
|
41
|
+
exports.getDirSize = getDirSize;
|
|
42
|
+
exports.checkJava = checkJava;
|
|
43
|
+
exports.findInstalledJavas = findInstalledJavas;
|
|
44
|
+
exports.getSystemStats = getSystemStats;
|
|
45
|
+
const fs = __importStar(require("fs"));
|
|
46
|
+
const os = __importStar(require("os"));
|
|
47
|
+
const child_process_1 = require("child_process");
|
|
48
|
+
/**
|
|
49
|
+
* Detects the runtime OS environment: Windows, WSL, or Linux
|
|
50
|
+
*/
|
|
51
|
+
function detectOS() {
|
|
52
|
+
if (process.platform === 'win32') {
|
|
53
|
+
return 'Windows';
|
|
54
|
+
}
|
|
55
|
+
try {
|
|
56
|
+
if (fs.existsSync('/proc/version')) {
|
|
57
|
+
const versionInfo = fs.readFileSync('/proc/version', 'utf-8').toLowerCase();
|
|
58
|
+
if (versionInfo.includes('microsoft') || versionInfo.includes('wsl')) {
|
|
59
|
+
return 'WSL';
|
|
60
|
+
}
|
|
61
|
+
}
|
|
62
|
+
}
|
|
63
|
+
catch {
|
|
64
|
+
// Ignore error and fall through
|
|
65
|
+
}
|
|
66
|
+
return 'Linux';
|
|
67
|
+
}
|
|
68
|
+
/**
|
|
69
|
+
* Opens a URL in the user's default browser (cross-platform, best-effort).
|
|
70
|
+
* Returns true if a launcher process was spawned, false if none was available.
|
|
71
|
+
*/
|
|
72
|
+
function openInBrowser(url) {
|
|
73
|
+
const osType = detectOS();
|
|
74
|
+
const candidates = [];
|
|
75
|
+
if (osType === 'Windows') {
|
|
76
|
+
candidates.push({ cmd: 'cmd', args: ['/c', 'start', '', url] });
|
|
77
|
+
}
|
|
78
|
+
else if (osType === 'WSL') {
|
|
79
|
+
// Use Windows interop so the link opens in the host's browser.
|
|
80
|
+
candidates.push({ cmd: 'wslview', args: [url] });
|
|
81
|
+
candidates.push({ cmd: 'cmd.exe', args: ['/c', 'start', '', url] });
|
|
82
|
+
candidates.push({ cmd: 'xdg-open', args: [url] });
|
|
83
|
+
}
|
|
84
|
+
else if (process.platform === 'darwin') {
|
|
85
|
+
candidates.push({ cmd: 'open', args: [url] });
|
|
86
|
+
}
|
|
87
|
+
else {
|
|
88
|
+
candidates.push({ cmd: 'xdg-open', args: [url] });
|
|
89
|
+
}
|
|
90
|
+
for (const { cmd, args } of candidates) {
|
|
91
|
+
try {
|
|
92
|
+
const child = (0, child_process_1.spawn)(cmd, args, { stdio: 'ignore', detached: true });
|
|
93
|
+
let failed = false;
|
|
94
|
+
child.on('error', () => { failed = true; });
|
|
95
|
+
child.unref();
|
|
96
|
+
if (!failed)
|
|
97
|
+
return true;
|
|
98
|
+
}
|
|
99
|
+
catch {
|
|
100
|
+
// Try the next candidate launcher.
|
|
101
|
+
}
|
|
102
|
+
}
|
|
103
|
+
return false;
|
|
104
|
+
}
|
|
105
|
+
/**
|
|
106
|
+
* Opens a directory in the OS file explorer (cross-platform, best-effort).
|
|
107
|
+
* Returns true if a launcher process was spawned.
|
|
108
|
+
*/
|
|
109
|
+
function openInFileExplorer(dir) {
|
|
110
|
+
const osType = detectOS();
|
|
111
|
+
const candidates = [];
|
|
112
|
+
if (osType === 'Windows') {
|
|
113
|
+
candidates.push({ cmd: 'explorer', args: [dir] });
|
|
114
|
+
}
|
|
115
|
+
else if (osType === 'WSL') {
|
|
116
|
+
// Convert the WSL path to a Windows path and open in Windows Explorer.
|
|
117
|
+
let winPath = '';
|
|
118
|
+
try {
|
|
119
|
+
winPath = (0, child_process_1.execSync)(`wslpath -w "${dir}"`).toString().trim();
|
|
120
|
+
}
|
|
121
|
+
catch { /* ignore */ }
|
|
122
|
+
if (winPath)
|
|
123
|
+
candidates.push({ cmd: 'explorer.exe', args: [winPath] });
|
|
124
|
+
candidates.push({ cmd: 'xdg-open', args: [dir] });
|
|
125
|
+
}
|
|
126
|
+
else if (process.platform === 'darwin') {
|
|
127
|
+
candidates.push({ cmd: 'open', args: [dir] });
|
|
128
|
+
}
|
|
129
|
+
else {
|
|
130
|
+
candidates.push({ cmd: 'xdg-open', args: [dir] });
|
|
131
|
+
}
|
|
132
|
+
for (const { cmd, args } of candidates) {
|
|
133
|
+
try {
|
|
134
|
+
// explorer.exe returns a non-zero exit code even on success, so we don't
|
|
135
|
+
// inspect the exit code — spawning without an immediate throw is enough.
|
|
136
|
+
const child = (0, child_process_1.spawn)(cmd, args, { stdio: 'ignore', detached: true });
|
|
137
|
+
let failed = false;
|
|
138
|
+
child.on('error', () => { failed = true; });
|
|
139
|
+
child.unref();
|
|
140
|
+
if (!failed)
|
|
141
|
+
return true;
|
|
142
|
+
}
|
|
143
|
+
catch {
|
|
144
|
+
// Try the next candidate.
|
|
145
|
+
}
|
|
146
|
+
}
|
|
147
|
+
return false;
|
|
148
|
+
}
|
|
149
|
+
/**
|
|
150
|
+
* Normalizes a user-supplied folder path so a Windows-style path pasted into a
|
|
151
|
+
* WSL/Linux session (e.g. "C:\Users\me\Server") is converted to its mount path
|
|
152
|
+
* ("/mnt/c/Users/me/Server"). Leaves native paths untouched.
|
|
153
|
+
*/
|
|
154
|
+
function normalizeInputPath(input) {
|
|
155
|
+
// Strip surrounding quotes/whitespace.
|
|
156
|
+
const p = input.trim().replace(/^['"]|['"]$/g, '');
|
|
157
|
+
const isWindowsDrivePath = /^[a-zA-Z]:[\\/]/.test(p);
|
|
158
|
+
const isUncPath = p.startsWith('\\\\');
|
|
159
|
+
// Only translate Windows paths when we're NOT actually on Windows.
|
|
160
|
+
if ((isWindowsDrivePath || isUncPath) && detectOS() !== 'Windows') {
|
|
161
|
+
// Prefer the real wslpath tool (handles UNC, spaces, casing correctly).
|
|
162
|
+
try {
|
|
163
|
+
const converted = (0, child_process_1.execSync)(`wslpath -u '${p.replace(/'/g, "'\\''")}'`)
|
|
164
|
+
.toString()
|
|
165
|
+
.trim();
|
|
166
|
+
if (converted)
|
|
167
|
+
return converted;
|
|
168
|
+
}
|
|
169
|
+
catch {
|
|
170
|
+
// wslpath unavailable — fall back to a manual drive-letter conversion.
|
|
171
|
+
}
|
|
172
|
+
const m = p.match(/^([a-zA-Z]):[\\/](.*)$/);
|
|
173
|
+
if (m) {
|
|
174
|
+
const drive = m[1].toLowerCase();
|
|
175
|
+
const rest = m[2].replace(/\\/g, '/');
|
|
176
|
+
return `/mnt/${drive}/${rest}`;
|
|
177
|
+
}
|
|
178
|
+
}
|
|
179
|
+
// Also convert lone backslashes for non-drive inputs on non-Windows hosts.
|
|
180
|
+
if (detectOS() !== 'Windows' && p.includes('\\') && !p.includes('/')) {
|
|
181
|
+
return p.replace(/\\/g, '/');
|
|
182
|
+
}
|
|
183
|
+
return p;
|
|
184
|
+
}
|
|
185
|
+
/**
|
|
186
|
+
* Opens a NEW terminal window that live-tails a log file (like `tail -f`),
|
|
187
|
+
* so the user can watch server logs while keeping the mcpanel prompt usable.
|
|
188
|
+
* Cross-platform, best-effort. Returns true if a window was launched.
|
|
189
|
+
*/
|
|
190
|
+
function openTerminalTail(logPath, title) {
|
|
191
|
+
const osType = detectOS();
|
|
192
|
+
const candidates = [];
|
|
193
|
+
if (osType === 'Windows') {
|
|
194
|
+
// PowerShell can follow a growing file; cmd's `start` opens a new window.
|
|
195
|
+
const ps = `Get-Content -Path '${logPath.replace(/'/g, "''")}' -Wait -Tail 200`;
|
|
196
|
+
candidates.push({ cmd: 'cmd', args: ['/c', 'start', title, 'powershell', '-NoExit', '-Command', ps] });
|
|
197
|
+
}
|
|
198
|
+
else if (osType === 'WSL') {
|
|
199
|
+
// Open a Windows console that runs `wsl tail -f` on the Linux-side path.
|
|
200
|
+
candidates.push({ cmd: 'cmd.exe', args: ['/c', 'start', title, 'wsl.exe', 'tail', '-n', '200', '-f', logPath] });
|
|
201
|
+
// Fall back to a native X terminal if one is available under WSLg.
|
|
202
|
+
candidates.push({ cmd: 'x-terminal-emulator', args: ['-e', `tail -n 200 -f "${logPath}"`] });
|
|
203
|
+
candidates.push({ cmd: 'xterm', args: ['-T', title, '-e', `tail -n 200 -f "${logPath}"`] });
|
|
204
|
+
}
|
|
205
|
+
else if (process.platform === 'darwin') {
|
|
206
|
+
const script = `tell application "Terminal" to do script "tail -n 200 -f '${logPath}'"`;
|
|
207
|
+
candidates.push({ cmd: 'osascript', args: ['-e', script] });
|
|
208
|
+
}
|
|
209
|
+
else {
|
|
210
|
+
candidates.push({ cmd: 'x-terminal-emulator', args: ['-e', `tail -n 200 -f "${logPath}"`] });
|
|
211
|
+
candidates.push({ cmd: 'gnome-terminal', args: ['--title', title, '--', 'bash', '-c', `tail -n 200 -f "${logPath}"; exec bash`] });
|
|
212
|
+
candidates.push({ cmd: 'konsole', args: ['-e', `tail -n 200 -f "${logPath}"`] });
|
|
213
|
+
candidates.push({ cmd: 'xterm', args: ['-T', title, '-e', `tail -n 200 -f "${logPath}"`] });
|
|
214
|
+
}
|
|
215
|
+
for (const { cmd, args } of candidates) {
|
|
216
|
+
try {
|
|
217
|
+
const child = (0, child_process_1.spawn)(cmd, args, { stdio: 'ignore', detached: true });
|
|
218
|
+
let failed = false;
|
|
219
|
+
child.on('error', () => { failed = true; });
|
|
220
|
+
child.unref();
|
|
221
|
+
if (!failed)
|
|
222
|
+
return true;
|
|
223
|
+
}
|
|
224
|
+
catch {
|
|
225
|
+
// Try the next candidate terminal.
|
|
226
|
+
}
|
|
227
|
+
}
|
|
228
|
+
return false;
|
|
229
|
+
}
|
|
230
|
+
/**
|
|
231
|
+
* Recursively sums the byte size of all files under a directory.
|
|
232
|
+
* Symlinks are not followed. Returns 0 on error.
|
|
233
|
+
*/
|
|
234
|
+
function getDirSize(dir) {
|
|
235
|
+
let total = 0;
|
|
236
|
+
let stack = [dir];
|
|
237
|
+
while (stack.length > 0) {
|
|
238
|
+
const current = stack.pop();
|
|
239
|
+
let entries;
|
|
240
|
+
try {
|
|
241
|
+
entries = fs.readdirSync(current, { withFileTypes: true });
|
|
242
|
+
}
|
|
243
|
+
catch {
|
|
244
|
+
continue;
|
|
245
|
+
}
|
|
246
|
+
for (const entry of entries) {
|
|
247
|
+
const full = `${current}/${entry.name}`;
|
|
248
|
+
if (entry.isSymbolicLink())
|
|
249
|
+
continue;
|
|
250
|
+
if (entry.isDirectory()) {
|
|
251
|
+
stack.push(full);
|
|
252
|
+
}
|
|
253
|
+
else if (entry.isFile()) {
|
|
254
|
+
try {
|
|
255
|
+
total += fs.statSync(full).size;
|
|
256
|
+
}
|
|
257
|
+
catch { /* ignore */ }
|
|
258
|
+
}
|
|
259
|
+
}
|
|
260
|
+
}
|
|
261
|
+
return total;
|
|
262
|
+
}
|
|
263
|
+
/**
|
|
264
|
+
* Checks if Java is installed and returns version details
|
|
265
|
+
*/
|
|
266
|
+
function checkJava(javaPath = 'java') {
|
|
267
|
+
try {
|
|
268
|
+
// `java -version` writes to stderr, so merge it into stdout (2>&1) to capture it.
|
|
269
|
+
const result = (0, child_process_1.execSync)(`"${javaPath}" -version 2>&1`, { stdio: ['pipe', 'pipe', 'pipe'] });
|
|
270
|
+
const output = result.toString() || '';
|
|
271
|
+
return { installed: true, version: cleanJavaVersion(output) };
|
|
272
|
+
}
|
|
273
|
+
catch (error) {
|
|
274
|
+
const stderr = error.stderr ? error.stderr.toString() : '';
|
|
275
|
+
const stdout = error.stdout ? error.stdout.toString() : '';
|
|
276
|
+
const combined = (stderr + '\n' + stdout).trim();
|
|
277
|
+
if (combined.includes('version') || combined.includes('openjdk') || combined.includes('Java(TM)')) {
|
|
278
|
+
return { installed: true, version: cleanJavaVersion(combined) };
|
|
279
|
+
}
|
|
280
|
+
return { installed: false, version: 'None' };
|
|
281
|
+
}
|
|
282
|
+
}
|
|
283
|
+
/**
|
|
284
|
+
* Best-effort discovery of installed Java runtimes (Linux/WSL/macOS common
|
|
285
|
+
* locations). Returns each working java binary with its version, newest first.
|
|
286
|
+
*/
|
|
287
|
+
function findInstalledJavas() {
|
|
288
|
+
const found = new Map(); // path -> version
|
|
289
|
+
const searchRoots = [
|
|
290
|
+
'/usr/lib/jvm',
|
|
291
|
+
'/usr/java',
|
|
292
|
+
'/opt/java',
|
|
293
|
+
'/Library/Java/JavaVirtualMachines',
|
|
294
|
+
];
|
|
295
|
+
for (const root of searchRoots) {
|
|
296
|
+
let entries = [];
|
|
297
|
+
try {
|
|
298
|
+
entries = fs.readdirSync(root);
|
|
299
|
+
}
|
|
300
|
+
catch {
|
|
301
|
+
continue;
|
|
302
|
+
}
|
|
303
|
+
for (const entry of entries) {
|
|
304
|
+
// Linux layout: <root>/<jdk>/bin/java ; macOS: <jdk>/Contents/Home/bin/java
|
|
305
|
+
const candidates = [
|
|
306
|
+
`${root}/${entry}/bin/java`,
|
|
307
|
+
`${root}/${entry}/Contents/Home/bin/java`,
|
|
308
|
+
];
|
|
309
|
+
for (const bin of candidates) {
|
|
310
|
+
if (found.has(bin))
|
|
311
|
+
continue;
|
|
312
|
+
try {
|
|
313
|
+
if (fs.statSync(bin).isFile()) {
|
|
314
|
+
const info = checkJava(bin);
|
|
315
|
+
if (info.installed)
|
|
316
|
+
found.set(bin, info.version);
|
|
317
|
+
}
|
|
318
|
+
}
|
|
319
|
+
catch { /* not a file / not accessible */ }
|
|
320
|
+
}
|
|
321
|
+
}
|
|
322
|
+
}
|
|
323
|
+
return Array.from(found.entries())
|
|
324
|
+
.map(([path, version]) => ({ path, version }))
|
|
325
|
+
// Sort by leading version number descending (newest JDK first).
|
|
326
|
+
.sort((a, b) => parseInt(b.version, 10) - parseInt(a.version, 10));
|
|
327
|
+
}
|
|
328
|
+
/**
|
|
329
|
+
* Cleans the Java version output to a short recognizable string
|
|
330
|
+
*/
|
|
331
|
+
function cleanJavaVersion(output) {
|
|
332
|
+
const lines = output.split('\n');
|
|
333
|
+
const versionLine = lines.find(line => line.includes('version') || line.includes('openjdk'));
|
|
334
|
+
if (versionLine) {
|
|
335
|
+
// Extract version (e.g. "17.0.2" or "21.0.1")
|
|
336
|
+
const match = versionLine.match(/"([^"]+)"/);
|
|
337
|
+
if (match && match[1]) {
|
|
338
|
+
return match[1];
|
|
339
|
+
}
|
|
340
|
+
return versionLine.trim();
|
|
341
|
+
}
|
|
342
|
+
return 'Detected';
|
|
343
|
+
}
|
|
344
|
+
function getSystemStats() {
|
|
345
|
+
const totalMem = os.totalmem();
|
|
346
|
+
const freeMem = os.freemem();
|
|
347
|
+
const usedMem = totalMem - freeMem;
|
|
348
|
+
const totalMemGB = parseFloat((totalMem / (1024 * 1024 * 1024)).toFixed(2));
|
|
349
|
+
const freeMemGB = parseFloat((freeMem / (1024 * 1024 * 1024)).toFixed(2));
|
|
350
|
+
const usedMemGB = parseFloat((usedMem / (1024 * 1024 * 1024)).toFixed(2));
|
|
351
|
+
const memUsagePct = parseFloat(((usedMem / totalMem) * 100).toFixed(1));
|
|
352
|
+
// Calculate average CPU load percentage from load average over the last 1 min
|
|
353
|
+
const loadAvg = os.loadavg()[0]; // 1-minute load average
|
|
354
|
+
const cpuCount = os.cpus().length;
|
|
355
|
+
// load avg / cpus * 100 capped at 100 or actual representation
|
|
356
|
+
const cpuUsage = parseFloat(Math.min((loadAvg / cpuCount) * 100, 100).toFixed(1));
|
|
357
|
+
return {
|
|
358
|
+
cpuUsage: isNaN(cpuUsage) ? 0 : cpuUsage,
|
|
359
|
+
totalMemGB,
|
|
360
|
+
freeMemGB,
|
|
361
|
+
usedMemGB,
|
|
362
|
+
memUsagePct,
|
|
363
|
+
uptimeSeconds: Math.floor(os.uptime()),
|
|
364
|
+
};
|
|
365
|
+
}
|
|
@@ -0,0 +1,85 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) {
|
|
3
|
+
if (k2 === undefined) k2 = k;
|
|
4
|
+
var desc = Object.getOwnPropertyDescriptor(m, k);
|
|
5
|
+
if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) {
|
|
6
|
+
desc = { enumerable: true, get: function() { return m[k]; } };
|
|
7
|
+
}
|
|
8
|
+
Object.defineProperty(o, k2, desc);
|
|
9
|
+
}) : (function(o, m, k, k2) {
|
|
10
|
+
if (k2 === undefined) k2 = k;
|
|
11
|
+
o[k2] = m[k];
|
|
12
|
+
}));
|
|
13
|
+
var __setModuleDefault = (this && this.__setModuleDefault) || (Object.create ? (function(o, v) {
|
|
14
|
+
Object.defineProperty(o, "default", { enumerable: true, value: v });
|
|
15
|
+
}) : function(o, v) {
|
|
16
|
+
o["default"] = v;
|
|
17
|
+
});
|
|
18
|
+
var __importStar = (this && this.__importStar) || (function () {
|
|
19
|
+
var ownKeys = function(o) {
|
|
20
|
+
ownKeys = Object.getOwnPropertyNames || function (o) {
|
|
21
|
+
var ar = [];
|
|
22
|
+
for (var k in o) if (Object.prototype.hasOwnProperty.call(o, k)) ar[ar.length] = k;
|
|
23
|
+
return ar;
|
|
24
|
+
};
|
|
25
|
+
return ownKeys(o);
|
|
26
|
+
};
|
|
27
|
+
return function (mod) {
|
|
28
|
+
if (mod && mod.__esModule) return mod;
|
|
29
|
+
var result = {};
|
|
30
|
+
if (mod != null) for (var k = ownKeys(mod), i = 0; i < k.length; i++) if (k[i] !== "default") __createBinding(result, mod, k[i]);
|
|
31
|
+
__setModuleDefault(result, mod);
|
|
32
|
+
return result;
|
|
33
|
+
};
|
|
34
|
+
})();
|
|
35
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
36
|
+
exports.logger = void 0;
|
|
37
|
+
const fs = __importStar(require("fs"));
|
|
38
|
+
const path = __importStar(require("path"));
|
|
39
|
+
const configManager_1 = require("../config/configManager");
|
|
40
|
+
const LOGS_DIR = path.join(configManager_1.APP_ROOT, 'logs');
|
|
41
|
+
function ensureLogsDirExists() {
|
|
42
|
+
if (!fs.existsSync(LOGS_DIR)) {
|
|
43
|
+
fs.mkdirSync(LOGS_DIR, { recursive: true });
|
|
44
|
+
}
|
|
45
|
+
}
|
|
46
|
+
function writeLog(filename, message) {
|
|
47
|
+
ensureLogsDirExists();
|
|
48
|
+
const filePath = path.join(LOGS_DIR, filename);
|
|
49
|
+
const timestamp = new Date().toISOString();
|
|
50
|
+
fs.appendFileSync(filePath, `[${timestamp}] ${message}\n`, 'utf-8');
|
|
51
|
+
}
|
|
52
|
+
exports.logger = {
|
|
53
|
+
info(message) {
|
|
54
|
+
writeLog('mcpanel.log', `INFO: ${message}`);
|
|
55
|
+
},
|
|
56
|
+
error(message, error) {
|
|
57
|
+
const errorDetails = error ? ` - ${error.stack || error.message || error}` : '';
|
|
58
|
+
writeLog('mcpanel.log', `ERROR: ${message}${errorDetails}`);
|
|
59
|
+
},
|
|
60
|
+
warn(message) {
|
|
61
|
+
writeLog('mcpanel.log', `WARN: ${message}`);
|
|
62
|
+
},
|
|
63
|
+
logServerStart(serverName, message) {
|
|
64
|
+
writeLog('server-start.log', `[${serverName}] ${message}`);
|
|
65
|
+
writeLog('mcpanel.log', `SERVER START: [${serverName}] ${message}`);
|
|
66
|
+
},
|
|
67
|
+
logServerStop(serverName, message) {
|
|
68
|
+
writeLog('server-stop.log', `[${serverName}] ${message}`);
|
|
69
|
+
writeLog('mcpanel.log', `SERVER STOP: [${serverName}] ${message}`);
|
|
70
|
+
},
|
|
71
|
+
logTunnel(message) {
|
|
72
|
+
writeLog('tunnel.log', message);
|
|
73
|
+
writeLog('mcpanel.log', `TUNNEL: ${message}`);
|
|
74
|
+
},
|
|
75
|
+
getServerLogPath(serverName) {
|
|
76
|
+
ensureLogsDirExists();
|
|
77
|
+
// Return path to the runtime console log for the server
|
|
78
|
+
return path.join(LOGS_DIR, `server-${serverName.toLowerCase()}.log`);
|
|
79
|
+
},
|
|
80
|
+
writeServerConsoleLog(serverName, data) {
|
|
81
|
+
ensureLogsDirExists();
|
|
82
|
+
const filePath = path.join(LOGS_DIR, `server-${serverName.toLowerCase()}.log`);
|
|
83
|
+
fs.appendFileSync(filePath, data, 'utf-8');
|
|
84
|
+
}
|
|
85
|
+
};
|
package/package.json
ADDED
|
@@ -0,0 +1,67 @@
|
|
|
1
|
+
{
|
|
2
|
+
"name": "@woopsy/mcpanel",
|
|
3
|
+
"version": "1.0.0",
|
|
4
|
+
"description": "MCPANEL — a terminal-based, single-server Minecraft server manager with an Arch/neofetch-style UI, live logs, backups, plugins and Playit.gg tunnels.",
|
|
5
|
+
"main": "dist/index.js",
|
|
6
|
+
"bin": {
|
|
7
|
+
"mcpanel": "dist/index.js"
|
|
8
|
+
},
|
|
9
|
+
"files": [
|
|
10
|
+
"dist",
|
|
11
|
+
"README.md",
|
|
12
|
+
"LICENSE"
|
|
13
|
+
],
|
|
14
|
+
"scripts": {
|
|
15
|
+
"build": "tsc",
|
|
16
|
+
"postbuild": "node -e \"try{require('fs').chmodSync('dist/index.js',0o755)}catch(e){}\"",
|
|
17
|
+
"start": "ts-node src/index.ts",
|
|
18
|
+
"dev": "ts-node src/index.ts",
|
|
19
|
+
"prod": "node dist/index.js",
|
|
20
|
+
"prepublishOnly": "npm run build",
|
|
21
|
+
"install:global": "npm run build && npm install -g ."
|
|
22
|
+
},
|
|
23
|
+
"keywords": [
|
|
24
|
+
"minecraft",
|
|
25
|
+
"minecraft-server",
|
|
26
|
+
"server-manager",
|
|
27
|
+
"cli",
|
|
28
|
+
"terminal",
|
|
29
|
+
"tui",
|
|
30
|
+
"fabric",
|
|
31
|
+
"paper",
|
|
32
|
+
"playit",
|
|
33
|
+
"backups",
|
|
34
|
+
"wsl"
|
|
35
|
+
],
|
|
36
|
+
"author": "Woopsy (https://github.com/Woopsyyy)",
|
|
37
|
+
"license": "MIT",
|
|
38
|
+
"homepage": "https://github.com/Woopsyyy/MCPANEL#readme",
|
|
39
|
+
"repository": {
|
|
40
|
+
"type": "git",
|
|
41
|
+
"url": "git+https://github.com/Woopsyyy/MCPANEL.git"
|
|
42
|
+
},
|
|
43
|
+
"bugs": {
|
|
44
|
+
"url": "https://github.com/Woopsyyy/MCPANEL/issues"
|
|
45
|
+
},
|
|
46
|
+
"dependencies": {
|
|
47
|
+
"adm-zip": "^0.5.12",
|
|
48
|
+
"chalk": "^4.1.2",
|
|
49
|
+
"figlet": "^1.11.0",
|
|
50
|
+
"pidusage": "^4.0.1"
|
|
51
|
+
},
|
|
52
|
+
"devDependencies": {
|
|
53
|
+
"@types/adm-zip": "^0.5.5",
|
|
54
|
+
"@types/figlet": "^1.7.0",
|
|
55
|
+
"@types/node": "^20.12.12",
|
|
56
|
+
"@types/pidusage": "^2.0.5",
|
|
57
|
+
"ts-node": "^10.9.2",
|
|
58
|
+
"typescript": "^5.4.5"
|
|
59
|
+
},
|
|
60
|
+
"engines": {
|
|
61
|
+
"node": ">=22.0.0"
|
|
62
|
+
},
|
|
63
|
+
"publishConfig": {
|
|
64
|
+
"access": "public"
|
|
65
|
+
},
|
|
66
|
+
"private": false
|
|
67
|
+
}
|