@drocketxx/pm2me 1.1.12 → 1.1.14
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/backend/db/index.js +6 -0
- package/backend/routes/api.js +14 -2
- package/backend/services/systemService.js +50 -0
- package/package.json +1 -1
- package/apps/Delete-Test-App/README.md +0 -1
- package/apps/Delete-Test-App/dist/server.js +0 -57
- package/apps/Delete-Test-App/package.json +0 -19
- package/apps/Delete-Test-App/src/server.ts +0 -33
- package/apps/Delete-Test-App/tsconfig.json +0 -15
- package/apps/Delete-Test-App-2/README.md +0 -1
- package/apps/Delete-Test-App-2/dist/server.js +0 -57
- package/apps/Delete-Test-App-2/package.json +0 -19
- package/apps/Delete-Test-App-2/src/server.ts +0 -33
- package/apps/Delete-Test-App-2/tsconfig.json +0 -15
- package/apps/Delete-Test-App-2_deploy.log +0 -12
- package/apps/Delete-Test-App_deploy.log +0 -12
- package/apps/PM2Me-main/README.md +0 -236
- package/apps/PM2Me-main/backend/app.js +0 -109
- package/apps/PM2Me-main/backend/db/index.js +0 -40
- package/apps/PM2Me-main/backend/nodemon.json +0 -10
- package/apps/PM2Me-main/backend/package.json +0 -31
- package/apps/PM2Me-main/backend/public/assets/index-DZ4rSpP9.css +0 -1
- package/apps/PM2Me-main/backend/public/assets/index-KLmI9qSM.js +0 -13
- package/apps/PM2Me-main/backend/public/icon.png +0 -0
- package/apps/PM2Me-main/backend/public/index.html +0 -14
- package/apps/PM2Me-main/backend/public/vite.svg +0 -1
- package/apps/PM2Me-main/backend/routes/api.js +0 -932
- package/apps/PM2Me-main/backend/routes/auth.js +0 -44
- package/apps/PM2Me-main/backend/services/gitService.js +0 -110
- package/apps/PM2Me-main/backend/services/notificationService.js +0 -48
- package/apps/PM2Me-main/backend/services/pm2Service.js +0 -84
- package/apps/PM2Me-main/backend/services/systemService.js +0 -113
- package/apps/PM2Me-main/c:VsCodemyPM2Metmp_old_dashboard.vue +0 -390
- package/apps/PM2Me-main/package.json +0 -21
- package/apps/PM2Me-main/screenshot/001.png +0 -0
- package/apps/PM2Me-main/test_db.js +0 -8
- package/apps/PM2Me-main/tmp_old_dashboard.vue +0 -390
- package/apps/PM2Me-main_deploy.log +0 -25
- package/apps/PM2Me-test/README.md +0 -1
- package/apps/PM2Me-test/dist/server.js +0 -63
- package/apps/PM2Me-test/package.json +0 -19
- package/apps/PM2Me-test/src/server.ts +0 -33
- package/apps/PM2Me-test/tsconfig.json +0 -15
- package/apps/PM2Me-test-main/README.md +0 -1
- package/apps/PM2Me-test-main/dist/server.js +0 -63
- package/apps/PM2Me-test-main/package.json +0 -19
- package/apps/PM2Me-test-main/src/server.ts +0 -33
- package/apps/PM2Me-test-main/tsconfig.json +0 -15
- package/apps/PM2Me-test-main_deploy.log +0 -24
- package/apps/PM2Me-test-uat/README.md +0 -1
- package/apps/PM2Me-test-uat/dist/server.js +0 -63
- package/apps/PM2Me-test-uat/package.json +0 -19
- package/apps/PM2Me-test-uat/src/server.ts +0 -33
- package/apps/PM2Me-test-uat/tsconfig.json +0 -15
- package/apps/PM2Me-test-uat-updated/README.md +0 -1
- package/apps/PM2Me-test-uat-updated/dist/server.js +0 -63
- package/apps/PM2Me-test-uat-updated/package.json +0 -19
- package/apps/PM2Me-test-uat-updated/src/server.ts +0 -33
- package/apps/PM2Me-test-uat-updated/tsconfig.json +0 -15
- package/apps/PM2Me-test-uat-updated_deploy.log +0 -24
- package/apps/PM2Me-test-uat2/README.md +0 -1
- package/apps/PM2Me-test-uat2/dist/server.js +0 -63
- package/apps/PM2Me-test-uat2/package.json +0 -19
- package/apps/PM2Me-test-uat2/src/server.ts +0 -33
- package/apps/PM2Me-test-uat2/tsconfig.json +0 -15
- package/apps/PM2Me-test-uat2_deploy.log +0 -25
- package/apps/PM2Me-test-uat_deploy.log +0 -24
- package/apps/PM2Me-test_deploy.log +0 -24
- package/apps/PM2Me-test_health.log +0 -4
package/backend/db/index.js
CHANGED
|
@@ -2,11 +2,16 @@ import { JSONFilePreset } from 'lowdb/node';
|
|
|
2
2
|
import path from 'path';
|
|
3
3
|
import { fileURLToPath } from 'url';
|
|
4
4
|
import os from 'os';
|
|
5
|
+
import fs from 'fs';
|
|
5
6
|
|
|
6
7
|
const __dirname = path.dirname(fileURLToPath(import.meta.url));
|
|
7
8
|
const homeDir = path.join(os.homedir(), '.pm2me');
|
|
8
9
|
const dbPath = process.env.PM2ME_DB_PATH || path.join(homeDir, 'database.json');
|
|
9
10
|
|
|
11
|
+
// Ensure directory exists
|
|
12
|
+
fs.mkdirSync(homeDir, { recursive: true });
|
|
13
|
+
console.log("🚀 ~ homeDir:", homeDir)
|
|
14
|
+
|
|
10
15
|
const defaultData = {
|
|
11
16
|
apps: [],
|
|
12
17
|
settings: {
|
|
@@ -25,6 +30,7 @@ const defaultData = {
|
|
|
25
30
|
};
|
|
26
31
|
|
|
27
32
|
// Initialize DB with node preset
|
|
33
|
+
console.log('Database path:', dbPath);
|
|
28
34
|
const db = await JSONFilePreset(dbPath, defaultData);
|
|
29
35
|
|
|
30
36
|
// Cleanup stuck deploying status from previous crash/restart
|
package/backend/routes/api.js
CHANGED
|
@@ -11,6 +11,7 @@ import crypto from 'crypto';
|
|
|
11
11
|
import { exec } from 'child_process';
|
|
12
12
|
import util from 'util';
|
|
13
13
|
import bcrypt from 'bcrypt';
|
|
14
|
+
import os from 'os';
|
|
14
15
|
|
|
15
16
|
const __dirname = path.dirname(fileURLToPath(import.meta.url));
|
|
16
17
|
const getWorkspacesDir = () => db.data.settings?.appsPath || path.resolve(__dirname, '../../apps');
|
|
@@ -704,7 +705,6 @@ router.get('/settings/webhook-logs', (req, res) => {
|
|
|
704
705
|
});
|
|
705
706
|
|
|
706
707
|
// ─── Nginx Management ────────────────────────────────────────────────────────
|
|
707
|
-
import os from 'os';
|
|
708
708
|
|
|
709
709
|
const isWindows = os.platform() === 'win32';
|
|
710
710
|
const NGINX_CONF = isWindows ? 'C:\\nginx\\conf\\nginx.conf' : '/etc/nginx/nginx.conf';
|
|
@@ -936,7 +936,8 @@ router.post('/nginx/action', async (req, res) => {
|
|
|
936
936
|
router.get('/setup/status', async (req, res) => {
|
|
937
937
|
try {
|
|
938
938
|
// Always read fresh from disk — detects database.json deletion or changes without restart
|
|
939
|
-
const
|
|
939
|
+
const homeDir = path.join(os.homedir(), '.pm2me');
|
|
940
|
+
const dbPath = process.env.PM2ME_DB_PATH || path.join(homeDir, 'database.json');
|
|
940
941
|
if (!fs.existsSync(dbPath)) {
|
|
941
942
|
// File deleted or not created yet
|
|
942
943
|
return res.json({ needsSetup: true, missing: ['adminPassword', 'appsPath', 'setupComplete'] });
|
|
@@ -989,14 +990,25 @@ router.post('/setup/complete', async (req, res) => {
|
|
|
989
990
|
}
|
|
990
991
|
// Hash with bcrypt (same as auth.js uses for verification)
|
|
991
992
|
const passwordHash = await bcrypt.hash(adminPassword, 10);
|
|
993
|
+
|
|
994
|
+
// Update db data
|
|
992
995
|
db.data.admin = { passwordHash };
|
|
993
996
|
db.data.settings = { ...db.data.settings, appsPath };
|
|
994
997
|
db.data.setupComplete = true;
|
|
998
|
+
|
|
999
|
+
// Force write to database
|
|
995
1000
|
await db.write();
|
|
1001
|
+
|
|
1002
|
+
// Verify the data was written by reading it back
|
|
1003
|
+
await db.read();
|
|
1004
|
+
|
|
996
1005
|
// Ensure apps dir exists
|
|
997
1006
|
fs.mkdirSync(appsPath, { recursive: true });
|
|
1007
|
+
|
|
1008
|
+
console.log('Setup completed successfully. Database saved to:', db.data);
|
|
998
1009
|
res.json({ success: true });
|
|
999
1010
|
} catch (err) {
|
|
1011
|
+
console.error('Setup error:', err);
|
|
1000
1012
|
res.status(500).json({ error: err.message });
|
|
1001
1013
|
}
|
|
1002
1014
|
});
|
|
@@ -90,6 +90,56 @@ export const getSystemStats = async () => {
|
|
|
90
90
|
} catch (e) {
|
|
91
91
|
console.error('Failed to get system stats via PowerShell:', e);
|
|
92
92
|
}
|
|
93
|
+
} else {
|
|
94
|
+
// Linux/Unix systems
|
|
95
|
+
try {
|
|
96
|
+
const now = Date.now();
|
|
97
|
+
const promises = [];
|
|
98
|
+
|
|
99
|
+
// 1. Network Stats (Every time) - read from /proc/net/dev
|
|
100
|
+
const netPromise = execPromise("cat /proc/net/dev | awk 'NR>2 {rx+=$2; tx+=$10} END {print rx\" \"tx}'")
|
|
101
|
+
.then(({ stdout }) => {
|
|
102
|
+
const [rxStr, txStr] = stdout.trim().split(' ');
|
|
103
|
+
const totalRx = parseInt(rxStr) || 0;
|
|
104
|
+
const totalTx = parseInt(txStr) || 0;
|
|
105
|
+
const timeDiff = (now - lastNetworkStats.time) / 1000;
|
|
106
|
+
if (timeDiff > 0 && lastNetworkStats.rx > 0) {
|
|
107
|
+
networkUsage.down = Math.max(0, (totalRx - lastNetworkStats.rx) / timeDiff);
|
|
108
|
+
networkUsage.up = Math.max(0, (totalTx - lastNetworkStats.tx) / timeDiff);
|
|
109
|
+
}
|
|
110
|
+
lastNetworkStats = { rx: totalRx, tx: totalTx, time: now };
|
|
111
|
+
})
|
|
112
|
+
.catch(e => console.error('Network fetch failed:', e));
|
|
113
|
+
promises.push(netPromise);
|
|
114
|
+
|
|
115
|
+
// 2. Disk Stats (Cached) - use df for root filesystem
|
|
116
|
+
if (now - cachedDiskStats.lastFetch > DISK_CACHE_TTL) {
|
|
117
|
+
const diskPromise = execPromise("df -B1 / | awk 'NR==2 {print $2\" \"$3\" \"$4}'")
|
|
118
|
+
.then(({ stdout }) => {
|
|
119
|
+
const [totalStr, usedStr, freeStr] = stdout.trim().split(' ');
|
|
120
|
+
const total = parseInt(totalStr) || 0;
|
|
121
|
+
const used = parseInt(usedStr) || 0;
|
|
122
|
+
const free = parseInt(freeStr) || 0;
|
|
123
|
+
if (total > 0) {
|
|
124
|
+
cachedDiskStats = {
|
|
125
|
+
total,
|
|
126
|
+
free,
|
|
127
|
+
used,
|
|
128
|
+
percentage: (used / total) * 100,
|
|
129
|
+
lastFetch: now
|
|
130
|
+
};
|
|
131
|
+
}
|
|
132
|
+
})
|
|
133
|
+
.catch(e => console.error('Disk fetch failed:', e));
|
|
134
|
+
promises.push(diskPromise);
|
|
135
|
+
}
|
|
136
|
+
|
|
137
|
+
// Run all in parallel
|
|
138
|
+
await Promise.all(promises);
|
|
139
|
+
diskUsage = cachedDiskStats;
|
|
140
|
+
} catch (e) {
|
|
141
|
+
console.error('Failed to get system stats on Linux:', e);
|
|
142
|
+
}
|
|
93
143
|
}
|
|
94
144
|
|
|
95
145
|
return {
|
package/package.json
CHANGED
|
@@ -1 +0,0 @@
|
|
|
1
|
-
# PM2Me-test
|
|
@@ -1,57 +0,0 @@
|
|
|
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
|
-
const dotenv = __importStar(require("dotenv"));
|
|
37
|
-
const http = __importStar(require("http"));
|
|
38
|
-
dotenv.config();
|
|
39
|
-
const port = parseInt(process.env.PORT || '6060', 10);
|
|
40
|
-
const server = http.createServer((req, res) => {
|
|
41
|
-
if (req.url === '/') {
|
|
42
|
-
console.log('req.url', req.url, new Date().toISOString());
|
|
43
|
-
res.writeHead(200, { 'Content-Type': 'application/json' });
|
|
44
|
-
res.end(JSON.stringify({
|
|
45
|
-
message: 'PM2Me Test App is running!',
|
|
46
|
-
currentTime: new Date().toISOString(),
|
|
47
|
-
env: process.env
|
|
48
|
-
}, null, 2));
|
|
49
|
-
}
|
|
50
|
-
else {
|
|
51
|
-
res.writeHead(404);
|
|
52
|
-
res.end('Not found');
|
|
53
|
-
}
|
|
54
|
-
});
|
|
55
|
-
server.listen(port, () => {
|
|
56
|
-
console.log(`Server listening on port ${port}`);
|
|
57
|
-
});
|
|
@@ -1,19 +0,0 @@
|
|
|
1
|
-
{
|
|
2
|
-
"name": "pm2me-test",
|
|
3
|
-
"version": "1.0.12",
|
|
4
|
-
"description": "A test project for PM2Me deployment",
|
|
5
|
-
"main": "dist/server.js",
|
|
6
|
-
"scripts": {
|
|
7
|
-
"start": "node dist/server.js",
|
|
8
|
-
"build": "tsc",
|
|
9
|
-
"dev": "ts-node src/server.ts"
|
|
10
|
-
},
|
|
11
|
-
"dependencies": {
|
|
12
|
-
"dotenv": "^16.4.5"
|
|
13
|
-
},
|
|
14
|
-
"devDependencies": {
|
|
15
|
-
"@types/node": "^20.11.24",
|
|
16
|
-
"ts-node": "^10.9.2",
|
|
17
|
-
"typescript": "^5.3.3"
|
|
18
|
-
}
|
|
19
|
-
}
|
|
@@ -1,33 +0,0 @@
|
|
|
1
|
-
import * as dotenv from 'dotenv';
|
|
2
|
-
import * as http from 'http';
|
|
3
|
-
|
|
4
|
-
dotenv.config();
|
|
5
|
-
|
|
6
|
-
// throw new Error('SIMULATE RUN ERROR: App failed to start during RUN stage');
|
|
7
|
-
// RUN stage rollback test v4 (No Race Condition)
|
|
8
|
-
// Simulate a runtime error for the 'RUN' step visualization
|
|
9
|
-
// if (process.env.SIMULATE_RUN_ERROR === 'true') {
|
|
10
|
-
// throw new Error('SIMULATE RUN ERROR: App failed to start during RUN stage');
|
|
11
|
-
// }
|
|
12
|
-
|
|
13
|
-
const port: number = parseInt(process.env.PORT || '6060', 10);
|
|
14
|
-
|
|
15
|
-
const server = http.createServer((req: http.IncomingMessage, res: http.ServerResponse) => {
|
|
16
|
-
if (req.url === '/') {
|
|
17
|
-
console.log('req.url', req.url, new Date().toISOString());
|
|
18
|
-
|
|
19
|
-
res.writeHead(200, { 'Content-Type': 'application/json' },);
|
|
20
|
-
res.end(JSON.stringify({
|
|
21
|
-
message: 'PM2Me Test App is running!',
|
|
22
|
-
currentTime: new Date().toISOString(),
|
|
23
|
-
env: process.env
|
|
24
|
-
}, null, 2));
|
|
25
|
-
} else {
|
|
26
|
-
res.writeHead(404);
|
|
27
|
-
res.end('Not found');
|
|
28
|
-
}
|
|
29
|
-
});
|
|
30
|
-
|
|
31
|
-
server.listen(port, () => {
|
|
32
|
-
console.log(`Server listening on port ${port}`);
|
|
33
|
-
});
|
|
@@ -1,15 +0,0 @@
|
|
|
1
|
-
{
|
|
2
|
-
"compilerOptions": {
|
|
3
|
-
"target": "es2022",
|
|
4
|
-
"module": "commonjs",
|
|
5
|
-
"rootDir": "./src",
|
|
6
|
-
"outDir": "./dist",
|
|
7
|
-
"esModuleInterop": true,
|
|
8
|
-
"forceConsistentCasingInFileNames": true,
|
|
9
|
-
"strict": true,
|
|
10
|
-
"skipLibCheck": true
|
|
11
|
-
},
|
|
12
|
-
"include": [
|
|
13
|
-
"src/**/*"
|
|
14
|
-
]
|
|
15
|
-
}
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
# PM2Me-test
|
|
@@ -1,57 +0,0 @@
|
|
|
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
|
-
const dotenv = __importStar(require("dotenv"));
|
|
37
|
-
const http = __importStar(require("http"));
|
|
38
|
-
dotenv.config();
|
|
39
|
-
const port = parseInt(process.env.PORT || '6060', 10);
|
|
40
|
-
const server = http.createServer((req, res) => {
|
|
41
|
-
if (req.url === '/') {
|
|
42
|
-
console.log('req.url', req.url, new Date().toISOString());
|
|
43
|
-
res.writeHead(200, { 'Content-Type': 'application/json' });
|
|
44
|
-
res.end(JSON.stringify({
|
|
45
|
-
message: 'PM2Me Test App is running!',
|
|
46
|
-
currentTime: new Date().toISOString(),
|
|
47
|
-
env: process.env
|
|
48
|
-
}, null, 2));
|
|
49
|
-
}
|
|
50
|
-
else {
|
|
51
|
-
res.writeHead(404);
|
|
52
|
-
res.end('Not found');
|
|
53
|
-
}
|
|
54
|
-
});
|
|
55
|
-
server.listen(port, () => {
|
|
56
|
-
console.log(`Server listening on port ${port}`);
|
|
57
|
-
});
|
|
@@ -1,19 +0,0 @@
|
|
|
1
|
-
{
|
|
2
|
-
"name": "pm2me-test",
|
|
3
|
-
"version": "1.0.12",
|
|
4
|
-
"description": "A test project for PM2Me deployment",
|
|
5
|
-
"main": "dist/server.js",
|
|
6
|
-
"scripts": {
|
|
7
|
-
"start": "node dist/server.js",
|
|
8
|
-
"build": "tsc",
|
|
9
|
-
"dev": "ts-node src/server.ts"
|
|
10
|
-
},
|
|
11
|
-
"dependencies": {
|
|
12
|
-
"dotenv": "^16.4.5"
|
|
13
|
-
},
|
|
14
|
-
"devDependencies": {
|
|
15
|
-
"@types/node": "^20.11.24",
|
|
16
|
-
"ts-node": "^10.9.2",
|
|
17
|
-
"typescript": "^5.3.3"
|
|
18
|
-
}
|
|
19
|
-
}
|
|
@@ -1,33 +0,0 @@
|
|
|
1
|
-
import * as dotenv from 'dotenv';
|
|
2
|
-
import * as http from 'http';
|
|
3
|
-
|
|
4
|
-
dotenv.config();
|
|
5
|
-
|
|
6
|
-
// throw new Error('SIMULATE RUN ERROR: App failed to start during RUN stage');
|
|
7
|
-
// RUN stage rollback test v4 (No Race Condition)
|
|
8
|
-
// Simulate a runtime error for the 'RUN' step visualization
|
|
9
|
-
// if (process.env.SIMULATE_RUN_ERROR === 'true') {
|
|
10
|
-
// throw new Error('SIMULATE RUN ERROR: App failed to start during RUN stage');
|
|
11
|
-
// }
|
|
12
|
-
|
|
13
|
-
const port: number = parseInt(process.env.PORT || '6060', 10);
|
|
14
|
-
|
|
15
|
-
const server = http.createServer((req: http.IncomingMessage, res: http.ServerResponse) => {
|
|
16
|
-
if (req.url === '/') {
|
|
17
|
-
console.log('req.url', req.url, new Date().toISOString());
|
|
18
|
-
|
|
19
|
-
res.writeHead(200, { 'Content-Type': 'application/json' },);
|
|
20
|
-
res.end(JSON.stringify({
|
|
21
|
-
message: 'PM2Me Test App is running!',
|
|
22
|
-
currentTime: new Date().toISOString(),
|
|
23
|
-
env: process.env
|
|
24
|
-
}, null, 2));
|
|
25
|
-
} else {
|
|
26
|
-
res.writeHead(404);
|
|
27
|
-
res.end('Not found');
|
|
28
|
-
}
|
|
29
|
-
});
|
|
30
|
-
|
|
31
|
-
server.listen(port, () => {
|
|
32
|
-
console.log(`Server listening on port ${port}`);
|
|
33
|
-
});
|
|
@@ -1,15 +0,0 @@
|
|
|
1
|
-
{
|
|
2
|
-
"compilerOptions": {
|
|
3
|
-
"target": "es2022",
|
|
4
|
-
"module": "commonjs",
|
|
5
|
-
"rootDir": "./src",
|
|
6
|
-
"outDir": "./dist",
|
|
7
|
-
"esModuleInterop": true,
|
|
8
|
-
"forceConsistentCasingInFileNames": true,
|
|
9
|
-
"strict": true,
|
|
10
|
-
"skipLibCheck": true
|
|
11
|
-
},
|
|
12
|
-
"include": [
|
|
13
|
-
"src/**/*"
|
|
14
|
-
]
|
|
15
|
-
}
|
|
@@ -1,12 +0,0 @@
|
|
|
1
|
-
--- Deployment Started at 2026-03-01T18:37:14.708Z ---
|
|
2
|
-
[pm2me] Sync
|
|
3
|
-
[pm2me] Synchronizing Repository
|
|
4
|
-
Git: Cloned repository cleanly on branch main (Commit: ืno error)
|
|
5
|
-
[pm2me] Setting up env
|
|
6
|
-
[pm2me] Starting PM2
|
|
7
|
-
[pm2me] Updating Application in PM2
|
|
8
|
-
[pm2me] Fresh Starting
|
|
9
|
-
[pm2me] Stabilization started. Verifying health for 10s...
|
|
10
|
-
[pm2me] Deployment Online (Health period active)
|
|
11
|
-
[pm2me] App unhealthy after 10s (Status: errored, Restarts: 15 vs 0). Rolling back...
|
|
12
|
-
[pm2me] No stable version to rollback to or already on last good version.
|
|
@@ -1,12 +0,0 @@
|
|
|
1
|
-
--- Deployment Started at 2026-03-01T18:34:22.724Z ---
|
|
2
|
-
[pm2me] Sync
|
|
3
|
-
[pm2me] Synchronizing Repository
|
|
4
|
-
Git: Cloned repository cleanly on branch main (Commit: ืno error)
|
|
5
|
-
[pm2me] Setting up env
|
|
6
|
-
[pm2me] Starting PM2
|
|
7
|
-
[pm2me] Updating Application in PM2
|
|
8
|
-
[pm2me] Fresh Starting
|
|
9
|
-
[pm2me] Stabilization started. Verifying health for 10s...
|
|
10
|
-
[pm2me] Deployment Online (Health period active)
|
|
11
|
-
[pm2me] App unhealthy after 10s (Status: errored, Restarts: 15 vs 0). Rolling back...
|
|
12
|
-
[pm2me] No stable version to rollback to or already on last good version.
|
|
@@ -1,236 +0,0 @@
|
|
|
1
|
-
# PM2Me 🚀
|
|
2
|
-
|
|
3
|
-
> A self-hosted, premium web dashboard for deploying and managing Node.js applications via PM2.
|
|
4
|
-
|
|
5
|
-
PM2Me lets you **deploy apps directly from a GitHub repository**, manage PM2 processes, monitor your server in real-time, and receive webhook-triggered auto-deployments — all from a beautiful, dark-mode UI.
|
|
6
|
-
|
|
7
|
-
[](https://buymeacoffee.com/rocketx.x)
|
|
8
|
-
|
|
9
|
-

|
|
10
|
-
|
|
11
|
-
---
|
|
12
|
-
|
|
13
|
-
## ✨ Features
|
|
14
|
-
|
|
15
|
-
| Feature | Details |
|
|
16
|
-
|---|---|
|
|
17
|
-
| 🖥 **Dashboard** | View all PM2 apps with real-time status, CPU, memory, uptime |
|
|
18
|
-
| 🚀 **Deploy from GitHub** | Clone & deploy any GitHub repo with PAT authentication |
|
|
19
|
-
| 🔄 **Auto-Sync via Webhook** | Automatically re-deploy on `git push` using GitHub Webhooks |
|
|
20
|
-
| 📊 **Server Monitor** | Real-time CPU, RAM, Disk, Network, and System Uptime via Socket.IO |
|
|
21
|
-
| 📜 **Live Logs** | Stream PM2 app logs in the browser in real-time |
|
|
22
|
-
| 🔔 **Notifications** | Deployment alerts via Discord Webhook or Telegram Bot |
|
|
23
|
-
| 🔐 **Admin Login** | Secure JWT-based authentication with bcrypt hashed passwords |
|
|
24
|
-
| ⚙️ **Settings Page** | Manage GitHub accounts, webhook secrets, and notification integrations |
|
|
25
|
-
| 📋 **Webhook History** | Last 50 webhook events logged and updated in real-time |
|
|
26
|
-
| 🗑 **Delete/Stop/Restart** | Full PM2 lifecycle management with a premium custom UI |
|
|
27
|
-
|
|
28
|
-
---
|
|
29
|
-
|
|
30
|
-
## 🧱 Tech Stack
|
|
31
|
-
|
|
32
|
-
**Backend**
|
|
33
|
-
- [Express.js](https://expressjs.com/) — REST API server
|
|
34
|
-
- [Socket.IO](https://socket.io/) — Real-time log streaming & system stats
|
|
35
|
-
- [PM2](https://pm2.keymetrics.io/) — Process management (`pm2` Node.js API)
|
|
36
|
-
- [LowDB](https://github.com/typicode/lowdb) — Lightweight JSON file database
|
|
37
|
-
- [bcrypt](https://www.npmjs.com/package/bcrypt) — Password hashing
|
|
38
|
-
- [jsonwebtoken](https://www.npmjs.com/package/jsonwebtoken) — JWT Auth
|
|
39
|
-
- [simple-git](https://github.com/steveukx/git-js) — Git operations
|
|
40
|
-
|
|
41
|
-
**Frontend**
|
|
42
|
-
- [Vue 3](https://vuejs.org/) — Reactive UI
|
|
43
|
-
- [Vite](https://vitejs.dev/) — Build tool
|
|
44
|
-
- [Socket.IO Client](https://socket.io/docs/v4/client-api/) — Real-time updates
|
|
45
|
-
|
|
46
|
-
---
|
|
47
|
-
|
|
48
|
-
## 🚀 Getting Started
|
|
49
|
-
|
|
50
|
-
### Prerequisites
|
|
51
|
-
- [Node.js](https://nodejs.org/) >= 18
|
|
52
|
-
- [PM2](https://pm2.keymetrics.io/) installed globally: `npm install -g pm2`
|
|
53
|
-
- [Git](https://git-scm.com/) installed and accessible in `PATH`
|
|
54
|
-
|
|
55
|
-
### Installation & Running (Recommended with PM2)
|
|
56
|
-
|
|
57
|
-
```bash
|
|
58
|
-
# 1. Clone the repository
|
|
59
|
-
git clone https://github.com/drocketxx/PM2Me.git
|
|
60
|
-
cd PM2Me
|
|
61
|
-
|
|
62
|
-
# 2. Install all dependencies (frontend + backend)
|
|
63
|
-
npm run install:all
|
|
64
|
-
|
|
65
|
-
# 3. Build the frontend
|
|
66
|
-
npm run build
|
|
67
|
-
|
|
68
|
-
# 4. Start PM2Me with PM2 ✅ Recommended
|
|
69
|
-
cd backend
|
|
70
|
-
pm2 start app.js --name pm2me
|
|
71
|
-
|
|
72
|
-
# 5. Auto-start on system reboot
|
|
73
|
-
pm2 save
|
|
74
|
-
pm2 startup
|
|
75
|
-
```
|
|
76
|
-
|
|
77
|
-
Open your browser at: **http://localhost:12345**
|
|
78
|
-
|
|
79
|
-
> 💡 **Useful PM2 commands:**
|
|
80
|
-
> ```bash
|
|
81
|
-
> pm2 status # Check PM2Me status
|
|
82
|
-
> pm2 logs pm2me # View PM2Me logs
|
|
83
|
-
> pm2 restart pm2me # Restart PM2Me
|
|
84
|
-
> pm2 stop pm2me # Stop PM2Me
|
|
85
|
-
> ```
|
|
86
|
-
|
|
87
|
-
**Development mode** (nodemon auto-restart):
|
|
88
|
-
```bash
|
|
89
|
-
cd /path/to/PM2Me
|
|
90
|
-
npm run dev
|
|
91
|
-
```
|
|
92
|
-
|
|
93
|
-
---
|
|
94
|
-
|
|
95
|
-
## 🔐 Default Login & Password Management
|
|
96
|
-
|
|
97
|
-
On first run, **no password is set**. Use the CLI to set one:
|
|
98
|
-
|
|
99
|
-
```bash
|
|
100
|
-
# Set or change admin password
|
|
101
|
-
npm run pw -- <your_password>
|
|
102
|
-
|
|
103
|
-
# Example
|
|
104
|
-
npm run pw -- mySecretPass123
|
|
105
|
-
```
|
|
106
|
-
|
|
107
|
-
> You can also run it directly from the `backend/` folder:
|
|
108
|
-
> ```bash
|
|
109
|
-
> cd backend && node scripts/change-password.js mySecretPass123
|
|
110
|
-
> ```
|
|
111
|
-
>
|
|
112
|
-
> The password is stored as a **bcrypt hash** (12 rounds) in `backend/db/database.json`.
|
|
113
|
-
|
|
114
|
-
---
|
|
115
|
-
|
|
116
|
-
## 🔗 GitHub Webhook Setup
|
|
117
|
-
|
|
118
|
-
PM2Me supports automatic deployments triggered by `git push`. Here's how to enable it:
|
|
119
|
-
|
|
120
|
-
1. Go to your GitHub repository → **Settings** → **Webhooks** → **Add webhook**
|
|
121
|
-
2. Set **Payload URL** to: `http://your-server-ip:12345/api/webhook`
|
|
122
|
-
3. Set **Content type** to: **`application/json`** ⚠️
|
|
123
|
-
4. Set **Secret** from the Settings page in PM2Me (generate one if needed)
|
|
124
|
-
5. Select **Just the push event**
|
|
125
|
-
6. Click **Add webhook**
|
|
126
|
-
|
|
127
|
-
PM2Me will automatically re-deploy any matching app (matched by repo URL + branch) on each push.
|
|
128
|
-
|
|
129
|
-
> **Webhook History** (last 50 events) is displayed on the Settings page in real-time.
|
|
130
|
-
|
|
131
|
-
---
|
|
132
|
-
|
|
133
|
-
## 📂 Project Structure
|
|
134
|
-
|
|
135
|
-
```
|
|
136
|
-
PM2Me/
|
|
137
|
-
├── backend/
|
|
138
|
-
│ ├── app.js # Express server + Socket.IO setup
|
|
139
|
-
│ ├── db/
|
|
140
|
-
│ │ ├── index.js # LowDB initialization
|
|
141
|
-
│ │ └── database.json # App data, settings, webhook logs
|
|
142
|
-
│ ├── routes/
|
|
143
|
-
│ │ ├── api.js # Main API routes
|
|
144
|
-
│ │ └── auth.js # Login / JWT auth
|
|
145
|
-
│ ├── services/
|
|
146
|
-
│ │ ├── gitService.js # Git clone/pull operations
|
|
147
|
-
│ │ ├── pm2Service.js # PM2 process management
|
|
148
|
-
│ │ ├── systemService.js# CPU / RAM / Disk / Network stats
|
|
149
|
-
│ │ └── notificationService.js # Discord & Telegram alerts
|
|
150
|
-
│ ├── scripts/
|
|
151
|
-
│ │ └── change-password.js # CLI tool to change admin password
|
|
152
|
-
│ └── public/ # Built Vue frontend (served statically)
|
|
153
|
-
├── frontend/
|
|
154
|
-
│ ├── src/
|
|
155
|
-
│ │ ├── views/
|
|
156
|
-
│ │ │ ├── Dashboard.vue # Main app dashboard
|
|
157
|
-
│ │ │ ├── Settings.vue # Settings & webhook history
|
|
158
|
-
│ │ │ └── Login.vue # Auth page
|
|
159
|
-
│ │ ├── components/
|
|
160
|
-
│ │ │ ├── DeployModal.vue # New/Edit app deployment modal
|
|
161
|
-
│ │ │ ├── LogViewer.vue # Real-time log stream
|
|
162
|
-
│ │ │ └── ServerStats.vue # CPU/RAM/Network widget
|
|
163
|
-
│ │ ├── router/ # Vue Router config
|
|
164
|
-
│ │ └── App.vue # Nav layout
|
|
165
|
-
│ └── vite.config.js
|
|
166
|
-
├── apps/ # Cloned app repos live here
|
|
167
|
-
└── package.json # Root scripts (dev, build, pw)
|
|
168
|
-
```
|
|
169
|
-
|
|
170
|
-
---
|
|
171
|
-
|
|
172
|
-
## 📝 Available Scripts
|
|
173
|
-
|
|
174
|
-
Run these from the **root** `PM2Me/` directory:
|
|
175
|
-
|
|
176
|
-
| Command | Description |
|
|
177
|
-
|---|---|
|
|
178
|
-
| `npm run dev` | Build frontend & start backend dev server |
|
|
179
|
-
| `npm run build` | Build frontend only |
|
|
180
|
-
| `npm run pw -- <password>` | Change admin password |
|
|
181
|
-
| `npm run install:all` | Install all dependencies (root + frontend + backend) |
|
|
182
|
-
|
|
183
|
-
---
|
|
184
|
-
|
|
185
|
-
## 📡 API Overview
|
|
186
|
-
|
|
187
|
-
| Method | Endpoint | Description |
|
|
188
|
-
|---|---|---|
|
|
189
|
-
| `POST` | `/api/auth/login` | Login, returns JWT token |
|
|
190
|
-
| `GET` | `/api/pm2/list` | List all PM2 processes |
|
|
191
|
-
| `POST` | `/api/pm2/:action` | PM2 action: start/stop/restart/delete |
|
|
192
|
-
| `GET` | `/api/apps` | List all deployed apps (DB) |
|
|
193
|
-
| `POST` | `/api/apps` | Register a new app |
|
|
194
|
-
| `PUT` | `/api/apps/:id` | Update app config |
|
|
195
|
-
| `DELETE` | `/api/apps/:id` | Delete app from DB |
|
|
196
|
-
| `POST` | `/api/apps/:id/deploy` | Trigger deployment |
|
|
197
|
-
| `GET` | `/api/apps/:id/sync-status` | Check if branch is behind remote |
|
|
198
|
-
| `GET` | `/api/settings` | Get current settings |
|
|
199
|
-
| `POST` | `/api/settings` | Save settings |
|
|
200
|
-
| `GET` | `/api/settings/webhook-logs` | Get last 50 webhook events |
|
|
201
|
-
| `POST` | `/api/webhook` | GitHub Webhook receiver |
|
|
202
|
-
| `GET` | `/api/system/stats` | Server system stats |
|
|
203
|
-
|
|
204
|
-
---
|
|
205
|
-
|
|
206
|
-
## 📦 Deployment (Production)
|
|
207
|
-
|
|
208
|
-
For production, it's recommended to run the PM2Me backend itself with PM2:
|
|
209
|
-
|
|
210
|
-
```bash
|
|
211
|
-
# Build the frontend
|
|
212
|
-
npm run build
|
|
213
|
-
|
|
214
|
-
# Start with PM2
|
|
215
|
-
cd backend
|
|
216
|
-
pm2 start app.js --name pm2me
|
|
217
|
-
|
|
218
|
-
# Save and set to auto-restart on reboot
|
|
219
|
-
pm2 save
|
|
220
|
-
pm2 startup
|
|
221
|
-
```
|
|
222
|
-
|
|
223
|
-
---
|
|
224
|
-
|
|
225
|
-
## 🛡 Security Notes
|
|
226
|
-
|
|
227
|
-
- Change the default admin password immediately on first run using `npm run pw`.
|
|
228
|
-
- Set a strong `JWT_SECRET` in your `.env` file.
|
|
229
|
-
- Always set a **Webhook Secret** to prevent unauthorized deploys.
|
|
230
|
-
- Consider putting PM2Me behind a reverse proxy (e.g., Nginx) with HTTPS in production.
|
|
231
|
-
|
|
232
|
-
---
|
|
233
|
-
|
|
234
|
-
## 📄 License
|
|
235
|
-
|
|
236
|
-
MIT
|