@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.
Files changed (69) hide show
  1. package/backend/db/index.js +6 -0
  2. package/backend/routes/api.js +14 -2
  3. package/backend/services/systemService.js +50 -0
  4. package/package.json +1 -1
  5. package/apps/Delete-Test-App/README.md +0 -1
  6. package/apps/Delete-Test-App/dist/server.js +0 -57
  7. package/apps/Delete-Test-App/package.json +0 -19
  8. package/apps/Delete-Test-App/src/server.ts +0 -33
  9. package/apps/Delete-Test-App/tsconfig.json +0 -15
  10. package/apps/Delete-Test-App-2/README.md +0 -1
  11. package/apps/Delete-Test-App-2/dist/server.js +0 -57
  12. package/apps/Delete-Test-App-2/package.json +0 -19
  13. package/apps/Delete-Test-App-2/src/server.ts +0 -33
  14. package/apps/Delete-Test-App-2/tsconfig.json +0 -15
  15. package/apps/Delete-Test-App-2_deploy.log +0 -12
  16. package/apps/Delete-Test-App_deploy.log +0 -12
  17. package/apps/PM2Me-main/README.md +0 -236
  18. package/apps/PM2Me-main/backend/app.js +0 -109
  19. package/apps/PM2Me-main/backend/db/index.js +0 -40
  20. package/apps/PM2Me-main/backend/nodemon.json +0 -10
  21. package/apps/PM2Me-main/backend/package.json +0 -31
  22. package/apps/PM2Me-main/backend/public/assets/index-DZ4rSpP9.css +0 -1
  23. package/apps/PM2Me-main/backend/public/assets/index-KLmI9qSM.js +0 -13
  24. package/apps/PM2Me-main/backend/public/icon.png +0 -0
  25. package/apps/PM2Me-main/backend/public/index.html +0 -14
  26. package/apps/PM2Me-main/backend/public/vite.svg +0 -1
  27. package/apps/PM2Me-main/backend/routes/api.js +0 -932
  28. package/apps/PM2Me-main/backend/routes/auth.js +0 -44
  29. package/apps/PM2Me-main/backend/services/gitService.js +0 -110
  30. package/apps/PM2Me-main/backend/services/notificationService.js +0 -48
  31. package/apps/PM2Me-main/backend/services/pm2Service.js +0 -84
  32. package/apps/PM2Me-main/backend/services/systemService.js +0 -113
  33. package/apps/PM2Me-main/c:VsCodemyPM2Metmp_old_dashboard.vue +0 -390
  34. package/apps/PM2Me-main/package.json +0 -21
  35. package/apps/PM2Me-main/screenshot/001.png +0 -0
  36. package/apps/PM2Me-main/test_db.js +0 -8
  37. package/apps/PM2Me-main/tmp_old_dashboard.vue +0 -390
  38. package/apps/PM2Me-main_deploy.log +0 -25
  39. package/apps/PM2Me-test/README.md +0 -1
  40. package/apps/PM2Me-test/dist/server.js +0 -63
  41. package/apps/PM2Me-test/package.json +0 -19
  42. package/apps/PM2Me-test/src/server.ts +0 -33
  43. package/apps/PM2Me-test/tsconfig.json +0 -15
  44. package/apps/PM2Me-test-main/README.md +0 -1
  45. package/apps/PM2Me-test-main/dist/server.js +0 -63
  46. package/apps/PM2Me-test-main/package.json +0 -19
  47. package/apps/PM2Me-test-main/src/server.ts +0 -33
  48. package/apps/PM2Me-test-main/tsconfig.json +0 -15
  49. package/apps/PM2Me-test-main_deploy.log +0 -24
  50. package/apps/PM2Me-test-uat/README.md +0 -1
  51. package/apps/PM2Me-test-uat/dist/server.js +0 -63
  52. package/apps/PM2Me-test-uat/package.json +0 -19
  53. package/apps/PM2Me-test-uat/src/server.ts +0 -33
  54. package/apps/PM2Me-test-uat/tsconfig.json +0 -15
  55. package/apps/PM2Me-test-uat-updated/README.md +0 -1
  56. package/apps/PM2Me-test-uat-updated/dist/server.js +0 -63
  57. package/apps/PM2Me-test-uat-updated/package.json +0 -19
  58. package/apps/PM2Me-test-uat-updated/src/server.ts +0 -33
  59. package/apps/PM2Me-test-uat-updated/tsconfig.json +0 -15
  60. package/apps/PM2Me-test-uat-updated_deploy.log +0 -24
  61. package/apps/PM2Me-test-uat2/README.md +0 -1
  62. package/apps/PM2Me-test-uat2/dist/server.js +0 -63
  63. package/apps/PM2Me-test-uat2/package.json +0 -19
  64. package/apps/PM2Me-test-uat2/src/server.ts +0 -33
  65. package/apps/PM2Me-test-uat2/tsconfig.json +0 -15
  66. package/apps/PM2Me-test-uat2_deploy.log +0 -25
  67. package/apps/PM2Me-test-uat_deploy.log +0 -24
  68. package/apps/PM2Me-test_deploy.log +0 -24
  69. package/apps/PM2Me-test_health.log +0 -4
@@ -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
@@ -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 dbPath = path.resolve(__dirname, '../db/database.json');
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,6 +1,6 @@
1
1
  {
2
2
  "name": "@drocketxx/pm2me",
3
- "version": "1.1.12",
3
+ "version": "1.1.14",
4
4
  "description": "PM2 Deployment and Management System — Web UI for PM2 process management",
5
5
  "type": "module",
6
6
  "main": "backend/app.js",
@@ -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
- [![Buy Me a Coffee](https://img.shields.io/badge/Buy%20Me%20a%20Coffee-ffdd00?style=for-the-badge&logo=buy-me-a-coffee&logoColor=black)](https://buymeacoffee.com/rocketx.x)
8
-
9
- ![PM2Me Dashboard](screenshot/001.png)
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