@drocketxx/pm2me 1.1.0 → 1.1.3
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 +63 -160
- package/backend/routes/api.js +3 -3
- package/backend/services/gitService.js +6 -6
- package/bin/pm2me.js +55 -18
- package/package.json +58 -58
package/README.md
CHANGED
|
@@ -10,6 +10,48 @@ PM2Me lets you **deploy apps directly from a GitHub repository**, manage PM2 pro
|
|
|
10
10
|
|
|
11
11
|
---
|
|
12
12
|
|
|
13
|
+
## 🚀 Recommended Installation
|
|
14
|
+
|
|
15
|
+
The standard way to use PM2Me is to install it globally via npm:
|
|
16
|
+
|
|
17
|
+
```bash
|
|
18
|
+
# Install globally
|
|
19
|
+
npm install -g @drocketxx/pm2me
|
|
20
|
+
```
|
|
21
|
+
|
|
22
|
+
---
|
|
23
|
+
|
|
24
|
+
## 📋 Commands
|
|
25
|
+
|
|
26
|
+
### Standard Execution
|
|
27
|
+
```bash
|
|
28
|
+
# Run the server in foreground
|
|
29
|
+
pm2me
|
|
30
|
+
|
|
31
|
+
# Run on a custom port
|
|
32
|
+
pm2me --port 8080
|
|
33
|
+
```
|
|
34
|
+
|
|
35
|
+
### Background Service (Recommended)
|
|
36
|
+
Manage PM2Me as a system service using these commands:
|
|
37
|
+
|
|
38
|
+
```bash
|
|
39
|
+
# Install & start as a background service (using PM2)
|
|
40
|
+
pm2me service install
|
|
41
|
+
|
|
42
|
+
# Uninstall/Remove the service
|
|
43
|
+
pm2me service uninstall
|
|
44
|
+
|
|
45
|
+
# Control the service
|
|
46
|
+
pm2me service start
|
|
47
|
+
pm2me service stop
|
|
48
|
+
pm2me service restart
|
|
49
|
+
```
|
|
50
|
+
|
|
51
|
+
> **Note:** The `service install` command will register PM2Me as `pm2me-server` in PM2 and attempt to set up system startup.
|
|
52
|
+
|
|
53
|
+
---
|
|
54
|
+
|
|
13
55
|
## ✨ Features
|
|
14
56
|
|
|
15
57
|
| Feature | Details |
|
|
@@ -45,188 +87,49 @@ PM2Me lets you **deploy apps directly from a GitHub repository**, manage PM2 pro
|
|
|
45
87
|
|
|
46
88
|
---
|
|
47
89
|
|
|
48
|
-
##
|
|
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)
|
|
90
|
+
## 🔐 Setup & Security
|
|
56
91
|
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
git clone https://github.com/drocketxx/PM2Me.git
|
|
60
|
-
cd PM2Me
|
|
92
|
+
### First Run
|
|
93
|
+
On your first run, PM2Me will guide you through a **Setup Wizard** to configure your apps storage path and set your admin password.
|
|
61
94
|
|
|
62
|
-
|
|
63
|
-
|
|
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:
|
|
95
|
+
### Data Storage
|
|
96
|
+
- **Database:** Stored in `~/.pm2me/database.json`
|
|
97
|
+
- **Cloned Apps:** Default to `~/.pm2me/apps/` (User configurable)
|
|
119
98
|
|
|
99
|
+
### GitHub Webhooks
|
|
120
100
|
1. Go to your GitHub repository → **Settings** → **Webhooks** → **Add webhook**
|
|
121
101
|
2. Set **Payload URL** to: `http://your-server-ip:12345/api/webhook`
|
|
122
102
|
3. Set **Content type** to: **`application/json`** ⚠️
|
|
123
|
-
4. Set **Secret** from the Settings page in PM2Me
|
|
103
|
+
4. Set **Secret** from the Settings page in PM2Me
|
|
124
104
|
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
105
|
|
|
183
106
|
---
|
|
184
107
|
|
|
185
|
-
##
|
|
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 |
|
|
108
|
+
## 🛠️ Manual Installation (For Developers)
|
|
203
109
|
|
|
204
|
-
|
|
110
|
+
If you want to contribute or run from source:
|
|
205
111
|
|
|
206
|
-
|
|
112
|
+
```bash
|
|
113
|
+
# 1. Clone the repository
|
|
114
|
+
git clone https://github.com/drocketxx/PM2Me.git
|
|
115
|
+
cd PM2Me
|
|
207
116
|
|
|
208
|
-
|
|
117
|
+
# 2. Install all dependencies
|
|
118
|
+
npm run install:all
|
|
209
119
|
|
|
210
|
-
|
|
211
|
-
# Build the frontend
|
|
120
|
+
# 3. Build the frontend
|
|
212
121
|
npm run build
|
|
213
122
|
|
|
214
|
-
# Start
|
|
215
|
-
|
|
216
|
-
pm2 start app.js --name pm2me
|
|
217
|
-
|
|
218
|
-
# Save and set to auto-restart on reboot
|
|
219
|
-
pm2 save
|
|
220
|
-
pm2 startup
|
|
123
|
+
# 4. Start in development mode
|
|
124
|
+
npm run dev
|
|
221
125
|
```
|
|
222
126
|
|
|
223
127
|
---
|
|
224
128
|
|
|
225
129
|
## 🛡 Security Notes
|
|
226
130
|
|
|
227
|
-
-
|
|
228
|
-
-
|
|
229
|
-
- Always set a **Webhook Secret** to prevent unauthorized deploys.
|
|
131
|
+
- Set a strong admin password during the setup wizard.
|
|
132
|
+
- Always use a **Webhook Secret** to prevent unauthorized deployment triggers.
|
|
230
133
|
- Consider putting PM2Me behind a reverse proxy (e.g., Nginx) with HTTPS in production.
|
|
231
134
|
|
|
232
135
|
---
|
package/backend/routes/api.js
CHANGED
|
@@ -45,7 +45,7 @@ router.get('/pm2/version-check', async (req, res) => {
|
|
|
45
45
|
// Check globally installed PM2 version via npm (avoids local node_modules/.bin/pm2)
|
|
46
46
|
let installedVersion = null;
|
|
47
47
|
try {
|
|
48
|
-
const { stdout } = await execAsync('npm list -g pm2 --depth=0 --json');
|
|
48
|
+
const { stdout } = await execAsync('npm list -g pm2 --depth=0 --json', { windowsHide: true });
|
|
49
49
|
const parsed = JSON.parse(stdout);
|
|
50
50
|
installedVersion = parsed?.dependencies?.pm2?.version || null;
|
|
51
51
|
} catch {
|
|
@@ -404,7 +404,7 @@ export const performDeployment = async (appId, io) => {
|
|
|
404
404
|
await setPipelineState('building');
|
|
405
405
|
logProcess('Executing Build Script', true);
|
|
406
406
|
await new Promise((resolve, reject) => {
|
|
407
|
-
const child = exec(normalizedScript, { cwd: targetPath, maxBuffer: 10 * 1024 * 1024 });
|
|
407
|
+
const child = exec(normalizedScript, { cwd: targetPath, maxBuffer: 10 * 1024 * 1024, windowsHide: true });
|
|
408
408
|
child.stdout.on('data', data => { io.emit(`deploy-log-${appId}`, data.toString()); fs.appendFileSync(logFilePath, data); });
|
|
409
409
|
child.stderr.on('data', data => { io.emit(`deploy-log-${appId}`, data.toString()); fs.appendFileSync(logFilePath, data); });
|
|
410
410
|
child.on('close', code => {
|
|
@@ -1023,7 +1023,7 @@ router.post('/system/update', async (req, res) => {
|
|
|
1023
1023
|
cmd = 'git pull origin main && npm run build';
|
|
1024
1024
|
cwd = path.resolve(__dirname, '../..');
|
|
1025
1025
|
}
|
|
1026
|
-
const { stdout, stderr } = await execAsync(cmd, { cwd }).catch(err => ({
|
|
1026
|
+
const { stdout, stderr } = await execAsync(cmd, { cwd, windowsHide: true }).catch(err => ({
|
|
1027
1027
|
stdout: '', stderr: err.message
|
|
1028
1028
|
}));
|
|
1029
1029
|
const output = [stdout, stderr].filter(Boolean).join('\n').trim();
|
|
@@ -28,9 +28,9 @@ export const syncRepo = async (repoUrl, targetPath, branchName, token = null) =>
|
|
|
28
28
|
fs.rmSync(targetPath, { recursive: true, force: true });
|
|
29
29
|
}
|
|
30
30
|
fs.mkdirSync(targetPath, { recursive: true });
|
|
31
|
-
const git = simpleGit().env(envOptions);
|
|
31
|
+
const git = simpleGit({ spawnOptions: { windowsHide: true } }).env(envOptions);
|
|
32
32
|
await git.clone(finalUrl, targetPath, ['--branch', branchName]);
|
|
33
|
-
const gitLocal = simpleGit(targetPath);
|
|
33
|
+
const gitLocal = simpleGit(targetPath, { spawnOptions: { windowsHide: true } });
|
|
34
34
|
const log = await gitLocal.log(['-1']);
|
|
35
35
|
const commitHash = log.latest.hash;
|
|
36
36
|
const commitMessage = log.latest.message;
|
|
@@ -45,7 +45,7 @@ export const syncRepo = async (repoUrl, targetPath, branchName, token = null) =>
|
|
|
45
45
|
}
|
|
46
46
|
} else {
|
|
47
47
|
try {
|
|
48
|
-
const git = simpleGit(targetPath).env(envOptions);
|
|
48
|
+
const git = simpleGit(targetPath, { spawnOptions: { windowsHide: true } }).env(envOptions);
|
|
49
49
|
await git.remote(['set-url', 'origin', finalUrl]);
|
|
50
50
|
await git.fetch('origin', branchName);
|
|
51
51
|
await git.checkout(branchName);
|
|
@@ -68,7 +68,7 @@ export const syncRepo = async (repoUrl, targetPath, branchName, token = null) =>
|
|
|
68
68
|
|
|
69
69
|
export const getBranches = async (repoUrl, token = null) => {
|
|
70
70
|
const finalUrl = getFinalUrl(repoUrl, token);
|
|
71
|
-
const git = simpleGit().env({ ...process.env, GIT_TERMINAL_PROMPT: '0' });
|
|
71
|
+
const git = simpleGit({ spawnOptions: { windowsHide: true } }).env({ ...process.env, GIT_TERMINAL_PROMPT: '0' });
|
|
72
72
|
const branches = await git.listRemote(['--heads', finalUrl]);
|
|
73
73
|
// Parse branch output, simplistic split
|
|
74
74
|
return branches.split('\n').filter(Boolean).map(line => line.split('refs/heads/')[1]);
|
|
@@ -82,7 +82,7 @@ export const getBehindCount = async (repoUrl, targetPath, branchName, token = nu
|
|
|
82
82
|
if (!isGitRepo) return 0;
|
|
83
83
|
|
|
84
84
|
const finalUrl = getFinalUrl(repoUrl, token);
|
|
85
|
-
const git = simpleGit(targetPath).env({ ...process.env, GIT_TERMINAL_PROMPT: '0' });
|
|
85
|
+
const git = simpleGit(targetPath, { spawnOptions: { windowsHide: true } }).env({ ...process.env, GIT_TERMINAL_PROMPT: '0' });
|
|
86
86
|
|
|
87
87
|
try {
|
|
88
88
|
await git.remote(['set-url', 'origin', finalUrl]);
|
|
@@ -96,7 +96,7 @@ export const getBehindCount = async (repoUrl, targetPath, branchName, token = nu
|
|
|
96
96
|
};
|
|
97
97
|
|
|
98
98
|
export const checkout = async (targetPath, ref) => {
|
|
99
|
-
const git = simpleGit(targetPath).env({ ...process.env, GIT_TERMINAL_PROMPT: '0' });
|
|
99
|
+
const git = simpleGit(targetPath, { spawnOptions: { windowsHide: true } }).env({ ...process.env, GIT_TERMINAL_PROMPT: '0' });
|
|
100
100
|
try {
|
|
101
101
|
await git.checkout(ref);
|
|
102
102
|
const log = await git.log(['-1']);
|
package/bin/pm2me.js
CHANGED
|
@@ -5,6 +5,9 @@
|
|
|
5
5
|
* pm2me (Run in foreground)
|
|
6
6
|
* pm2me service install (Run in background via PM2 + set boot startup)
|
|
7
7
|
* pm2me service uninstall (Remove from PM2 background)
|
|
8
|
+
* pm2me service start (Start background service)
|
|
9
|
+
* pm2me service stop (Stop background service)
|
|
10
|
+
* pm2me service restart (Restart background service)
|
|
8
11
|
*/
|
|
9
12
|
import path from 'path';
|
|
10
13
|
import { fileURLToPath } from 'url';
|
|
@@ -22,32 +25,38 @@ const args = process.argv.slice(2);
|
|
|
22
25
|
// ── Handle Service Commands ───────────────────────────────────────────────────
|
|
23
26
|
if (args[0] === 'service') {
|
|
24
27
|
const action = args[1];
|
|
28
|
+
const SERVICE_NAME = 'pm2me-server';
|
|
29
|
+
|
|
25
30
|
if (action === 'install') {
|
|
26
31
|
console.log('[pm2me] Installing background service...');
|
|
27
32
|
try {
|
|
28
|
-
|
|
29
|
-
// We use the full path to the backend app.js
|
|
33
|
+
const isWindows = os.platform() === 'win32';
|
|
30
34
|
const appPath = path.join(backendDir, 'app.js');
|
|
31
35
|
const homeDir = path.join(os.homedir(), '.pm2me');
|
|
32
36
|
const dbPath = path.join(homeDir, 'database.json');
|
|
33
37
|
|
|
34
|
-
console.log(`[pm2me] Starting via PM2 as '
|
|
35
|
-
execSync(`pm2 start "${appPath}" --name
|
|
38
|
+
console.log(`[pm2me] Starting via PM2 as '${SERVICE_NAME}'...`);
|
|
39
|
+
execSync(`pm2 start "${appPath}" --name ${SERVICE_NAME} --env PM2ME_DB_PATH="${dbPath}"`, { stdio: 'inherit', windowsHide: true });
|
|
36
40
|
|
|
37
|
-
// 2. Save PM2 list
|
|
38
41
|
console.log(`[pm2me] Saving PM2 process list...`);
|
|
39
|
-
execSync(`pm2 save`, { stdio: 'inherit' });
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
console.log(`
|
|
46
|
-
}
|
|
47
|
-
console.log(`[pm2me]
|
|
42
|
+
execSync(`pm2 save`, { stdio: 'inherit', windowsHide: true });
|
|
43
|
+
|
|
44
|
+
if (isWindows) {
|
|
45
|
+
console.log(`\n[pm2me] 💡 Windows Info: '${SERVICE_NAME}' is now running in the background.`);
|
|
46
|
+
console.log(`[pm2me] To make it start automatically on Windows restart, we recommend using 'pm2-windows-startup':`);
|
|
47
|
+
console.log(` npm install -g pm2-windows-startup`);
|
|
48
|
+
console.log(` pm2-startup install`);
|
|
49
|
+
} else {
|
|
50
|
+
console.log(`[pm2me] Setting up boot startup...`);
|
|
51
|
+
try {
|
|
52
|
+
execSync(`pm2 startup`, { stdio: 'inherit', windowsHide: true });
|
|
53
|
+
console.log(`[pm2me] Done! If you see a command above, please copy and run it to finalize startup.`);
|
|
54
|
+
} catch (e) {
|
|
55
|
+
console.log(`[pm2me] 'pm2 startup' might need manual execution. Check instructions above.`);
|
|
56
|
+
}
|
|
48
57
|
}
|
|
49
58
|
|
|
50
|
-
console.log(
|
|
59
|
+
console.log(`\n[pm2me] Service installed successfully. Access your UI at http://localhost:12345`);
|
|
51
60
|
process.exit(0);
|
|
52
61
|
} catch (err) {
|
|
53
62
|
console.error(`[pm2me] Failed to install service:`, err.message);
|
|
@@ -56,16 +65,43 @@ if (args[0] === 'service') {
|
|
|
56
65
|
} else if (action === 'uninstall') {
|
|
57
66
|
console.log('[pm2me] Uninstalling background service...');
|
|
58
67
|
try {
|
|
59
|
-
execSync(`pm2 delete
|
|
60
|
-
execSync(`pm2 save`, { stdio: 'inherit' });
|
|
68
|
+
execSync(`pm2 delete ${SERVICE_NAME}`, { stdio: 'inherit', windowsHide: true });
|
|
69
|
+
execSync(`pm2 save`, { stdio: 'inherit', windowsHide: true });
|
|
61
70
|
console.log(`[pm2me] Service uninstalled successfully.`);
|
|
62
71
|
process.exit(0);
|
|
63
72
|
} catch (err) {
|
|
64
73
|
console.error(`[pm2me] Failed to uninstall service:`, err.message);
|
|
65
74
|
process.exit(1);
|
|
66
75
|
}
|
|
76
|
+
} else if (action === 'start') {
|
|
77
|
+
console.log(`[pm2me] Starting service '${SERVICE_NAME}'...`);
|
|
78
|
+
try {
|
|
79
|
+
execSync(`pm2 start ${SERVICE_NAME}`, { stdio: 'inherit', windowsHide: true });
|
|
80
|
+
process.exit(0);
|
|
81
|
+
} catch (err) {
|
|
82
|
+
console.error(`[pm2me] Failed to start service:`, err.message);
|
|
83
|
+
process.exit(1);
|
|
84
|
+
}
|
|
85
|
+
} else if (action === 'stop') {
|
|
86
|
+
console.log(`[pm2me] Stopping service '${SERVICE_NAME}'...`);
|
|
87
|
+
try {
|
|
88
|
+
execSync(`pm2 stop ${SERVICE_NAME}`, { stdio: 'inherit', windowsHide: true });
|
|
89
|
+
process.exit(0);
|
|
90
|
+
} catch (err) {
|
|
91
|
+
console.error(`[pm2me] Failed to stop service:`, err.message);
|
|
92
|
+
process.exit(1);
|
|
93
|
+
}
|
|
94
|
+
} else if (action === 'restart') {
|
|
95
|
+
console.log(`[pm2me] Restarting service '${SERVICE_NAME}'...`);
|
|
96
|
+
try {
|
|
97
|
+
execSync(`pm2 restart ${SERVICE_NAME}`, { stdio: 'inherit', windowsHide: true });
|
|
98
|
+
process.exit(0);
|
|
99
|
+
} catch (err) {
|
|
100
|
+
console.error(`[pm2me] Failed to restart service:`, err.message);
|
|
101
|
+
process.exit(1);
|
|
102
|
+
}
|
|
67
103
|
} else {
|
|
68
|
-
console.log(`Usage: pm2me service [install|uninstall]`);
|
|
104
|
+
console.log(`Usage: pm2me service [install|uninstall|start|stop|restart]`);
|
|
69
105
|
process.exit(1);
|
|
70
106
|
}
|
|
71
107
|
}
|
|
@@ -109,6 +145,7 @@ console.log(`[pm2me] Using database: ${dbPath}`);
|
|
|
109
145
|
const server = spawn('node', ['app.js'], {
|
|
110
146
|
cwd: backendDir,
|
|
111
147
|
stdio: 'inherit',
|
|
148
|
+
windowsHide: true,
|
|
112
149
|
env: {
|
|
113
150
|
...process.env,
|
|
114
151
|
PORT: String(PORT),
|
package/package.json
CHANGED
|
@@ -1,59 +1,59 @@
|
|
|
1
|
-
{
|
|
2
|
-
"name": "@drocketxx/pm2me",
|
|
3
|
-
"version": "1.1.
|
|
4
|
-
"description": "PM2 Deployment and Management System — Web UI for PM2 process management",
|
|
5
|
-
"type": "module",
|
|
6
|
-
"main": "backend/app.js",
|
|
7
|
-
"bin": {
|
|
8
|
-
"pm2me": "./bin/pm2me.js"
|
|
9
|
-
},
|
|
10
|
-
"files": [
|
|
11
|
-
"bin/",
|
|
12
|
-
"backend/",
|
|
13
|
-
"README.md"
|
|
14
|
-
],
|
|
15
|
-
"dependencies": {
|
|
16
|
-
"bcrypt": "^6.0.0",
|
|
17
|
-
"connect-history-api-fallback": "^2.0.0",
|
|
18
|
-
"cors": "^2.8.6",
|
|
19
|
-
"dotenv": "^17.3.1",
|
|
20
|
-
"express": "^5.2.1",
|
|
21
|
-
"jsonwebtoken": "^9.0.3",
|
|
22
|
-
"lowdb": "^7.0.1",
|
|
23
|
-
"morgan": "^1.10.1",
|
|
24
|
-
"pm2": "^6.0.14",
|
|
25
|
-
"simple-git": "^3.32.3",
|
|
26
|
-
"socket.io": "^4.8.3"
|
|
27
|
-
},
|
|
28
|
-
"engines": {
|
|
29
|
-
"node": ">=18"
|
|
30
|
-
},
|
|
31
|
-
"repository": {
|
|
32
|
-
"type": "git",
|
|
33
|
-
"url": "https://github.com/drocketxx/PM2Me.git"
|
|
34
|
-
},
|
|
35
|
-
"homepage": "https://github.com/drocketxx/PM2Me",
|
|
36
|
-
"keywords": [
|
|
37
|
-
"pm2",
|
|
38
|
-
"deploy",
|
|
39
|
-
"management",
|
|
40
|
-
"nginx",
|
|
41
|
-
"web-ui"
|
|
42
|
-
],
|
|
43
|
-
"author": "drocketxx",
|
|
44
|
-
"license": "ISC",
|
|
45
|
-
"scripts": {
|
|
46
|
-
"dev:frontend": "cd frontend && npm run dev",
|
|
47
|
-
"dev:backend": "cd backend && npm run dev",
|
|
48
|
-
"dev:concurrent": "concurrently \"npm run dev:backend\" \"npm run dev:frontend\"",
|
|
49
|
-
"build": "cd frontend && npm run build",
|
|
50
|
-
"dev": "npm run build && cd backend && npm run dev",
|
|
51
|
-
"install:all": "npm install && cd frontend && npm install && cd ../backend && npm install",
|
|
52
|
-
"pw": "cd backend && node scripts/change-password.js",
|
|
53
|
-
"start": "node bin/pm2me.js"
|
|
54
|
-
},
|
|
55
|
-
"devDependencies": {
|
|
56
|
-
"concurrently": "^8.2.2",
|
|
57
|
-
"nodemon": "^3.1.14"
|
|
58
|
-
}
|
|
1
|
+
{
|
|
2
|
+
"name": "@drocketxx/pm2me",
|
|
3
|
+
"version": "1.1.3",
|
|
4
|
+
"description": "PM2 Deployment and Management System — Web UI for PM2 process management",
|
|
5
|
+
"type": "module",
|
|
6
|
+
"main": "backend/app.js",
|
|
7
|
+
"bin": {
|
|
8
|
+
"pm2me": "./bin/pm2me.js"
|
|
9
|
+
},
|
|
10
|
+
"files": [
|
|
11
|
+
"bin/",
|
|
12
|
+
"backend/",
|
|
13
|
+
"README.md"
|
|
14
|
+
],
|
|
15
|
+
"dependencies": {
|
|
16
|
+
"bcrypt": "^6.0.0",
|
|
17
|
+
"connect-history-api-fallback": "^2.0.0",
|
|
18
|
+
"cors": "^2.8.6",
|
|
19
|
+
"dotenv": "^17.3.1",
|
|
20
|
+
"express": "^5.2.1",
|
|
21
|
+
"jsonwebtoken": "^9.0.3",
|
|
22
|
+
"lowdb": "^7.0.1",
|
|
23
|
+
"morgan": "^1.10.1",
|
|
24
|
+
"pm2": "^6.0.14",
|
|
25
|
+
"simple-git": "^3.32.3",
|
|
26
|
+
"socket.io": "^4.8.3"
|
|
27
|
+
},
|
|
28
|
+
"engines": {
|
|
29
|
+
"node": ">=18"
|
|
30
|
+
},
|
|
31
|
+
"repository": {
|
|
32
|
+
"type": "git",
|
|
33
|
+
"url": "https://github.com/drocketxx/PM2Me.git"
|
|
34
|
+
},
|
|
35
|
+
"homepage": "https://github.com/drocketxx/PM2Me",
|
|
36
|
+
"keywords": [
|
|
37
|
+
"pm2",
|
|
38
|
+
"deploy",
|
|
39
|
+
"management",
|
|
40
|
+
"nginx",
|
|
41
|
+
"web-ui"
|
|
42
|
+
],
|
|
43
|
+
"author": "drocketxx",
|
|
44
|
+
"license": "ISC",
|
|
45
|
+
"scripts": {
|
|
46
|
+
"dev:frontend": "cd frontend && npm run dev",
|
|
47
|
+
"dev:backend": "cd backend && npm run dev",
|
|
48
|
+
"dev:concurrent": "concurrently \"npm run dev:backend\" \"npm run dev:frontend\"",
|
|
49
|
+
"build": "cd frontend && npm run build",
|
|
50
|
+
"dev": "npm run build && cd backend && npm run dev",
|
|
51
|
+
"install:all": "npm install && cd frontend && npm install && cd ../backend && npm install",
|
|
52
|
+
"pw": "cd backend && node scripts/change-password.js",
|
|
53
|
+
"start": "node bin/pm2me.js"
|
|
54
|
+
},
|
|
55
|
+
"devDependencies": {
|
|
56
|
+
"concurrently": "^8.2.2",
|
|
57
|
+
"nodemon": "^3.1.14"
|
|
58
|
+
}
|
|
59
59
|
}
|