easy-devops 0.2.1 → 0.2.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 +32 -3
- package/cli/menus/dashboard.js +22 -2
- package/cli/menus/update.js +147 -55
- package/core/nginx-conf-generator.js +4 -3
- package/dashboard/lib/nginx-service.js +19 -6
- package/dashboard/routes/domains.js +6 -1
- package/package.json +1 -1
package/README.md
CHANGED
|
@@ -21,6 +21,7 @@ A unified DevOps management tool with interactive CLI and web dashboard for mana
|
|
|
21
21
|
|
|
22
22
|
- **Node.js 18+** (with npm)
|
|
23
23
|
- **Linux** (Debian/Ubuntu) or **Windows**
|
|
24
|
+
- ⚠️ **Windows users: PowerShell must be run as Administrator** (required for installing packages via winget, managing services, and SSL certificates)
|
|
24
25
|
- Optional: Nginx, Certbot, nvm (installed separately or via the tool)
|
|
25
26
|
|
|
26
27
|
## Installation
|
|
@@ -49,6 +50,8 @@ wget -qO- https://raw.githubusercontent.com/omar00050/Easy-DevOps/main/install.s
|
|
|
49
50
|
|
|
50
51
|
#### Windows (PowerShell)
|
|
51
52
|
|
|
53
|
+
> ⚠️ **Important:** Run PowerShell **as Administrator**. Right-click PowerShell → "Run as Administrator".
|
|
54
|
+
|
|
52
55
|
```powershell
|
|
53
56
|
Invoke-WebRequest -Uri "https://raw.githubusercontent.com/omar00050/Easy-DevOps/main/install.ps1" -OutFile "install.ps1"; ./install.ps1
|
|
54
57
|
```
|
|
@@ -175,6 +178,20 @@ Manage Let's Encrypt SSL certificates using Certbot.
|
|
|
175
178
|
|
|
176
179
|
> **Note:** Renewing a certificate temporarily stops Nginx to free port 80, then restarts it automatically.
|
|
177
180
|
|
|
181
|
+
#### Windows Package Manager (winget)
|
|
182
|
+
|
|
183
|
+
Easy DevOps uses **winget** (Windows Package Manager) to install certbot and other packages on Windows. If winget is not installed, Easy DevOps will automatically prompt to install it using the [asheroto/winget-install](https://github.com/asheroto/winget-install) script.
|
|
184
|
+
|
|
185
|
+
**Installing winget manually:**
|
|
186
|
+
|
|
187
|
+
If you prefer to install winget separately, you can:
|
|
188
|
+
|
|
189
|
+
1. **Install from Microsoft Store:** Search for "App Installer" in the Microsoft Store
|
|
190
|
+
2. **Use the winget-install script:** [https://github.com/asheroto/winget-install](https://github.com/asheroto/winget-install)
|
|
191
|
+
3. **Official Microsoft documentation:** [https://learn.microsoft.com/en-us/windows/package-manager/winget/](https://learn.microsoft.com/en-us/windows/package-manager/winget/)
|
|
192
|
+
|
|
193
|
+
> **Note:** The embedded `winget-install.ps1` script in this project is sourced from [asheroto/winget-install](https://github.com/asheroto/winget-install) — a community-maintained, reliable installer for winget on Windows Server and systems without the Microsoft Store.
|
|
194
|
+
|
|
178
195
|
---
|
|
179
196
|
|
|
180
197
|
### Web Dashboard
|
|
@@ -187,7 +204,15 @@ Start the web dashboard:
|
|
|
187
204
|
npm run dashboard
|
|
188
205
|
```
|
|
189
206
|
|
|
190
|
-
Access at `http://localhost:
|
|
207
|
+
Access at `http://localhost:6443` (or configured port).
|
|
208
|
+
|
|
209
|
+
#### First-Time Login
|
|
210
|
+
|
|
211
|
+
Default credentials:
|
|
212
|
+
- **Username:** `admin`
|
|
213
|
+
- **Password:** Set in Settings menu or check your configuration
|
|
214
|
+
|
|
215
|
+
> **Tip:** From the Dashboard menu, select "How to use" for a quick guide on getting started.
|
|
191
216
|
|
|
192
217
|
#### Dashboard Pages
|
|
193
218
|
|
|
@@ -245,7 +270,8 @@ easy-devops/
|
|
|
245
270
|
│ ├── config.js # Configuration loader
|
|
246
271
|
│ ├── db.js # SQLite database (good.db)
|
|
247
272
|
│ ├── detector.js # System environment detection
|
|
248
|
-
│
|
|
273
|
+
│ ├── shell.js # Cross-platform shell executor
|
|
274
|
+
│ └── nginx-conf-generator.js # Nginx config file generator
|
|
249
275
|
├── dashboard/
|
|
250
276
|
│ ├── server.js # Express + Socket.io server
|
|
251
277
|
│ ├── routes/ # API endpoints
|
|
@@ -255,7 +281,10 @@ easy-devops/
|
|
|
255
281
|
├── data/
|
|
256
282
|
│ └── easy-devops.sqlite
|
|
257
283
|
└── lib/
|
|
258
|
-
└── installer/
|
|
284
|
+
└── installer/
|
|
285
|
+
├── install.ps1 # Windows bootstrap installer
|
|
286
|
+
├── install.sh # Linux/macOS bootstrap installer
|
|
287
|
+
└── winget-install.ps1 # Windows Package Manager installer
|
|
259
288
|
```
|
|
260
289
|
|
|
261
290
|
---
|
package/cli/menus/dashboard.js
CHANGED
|
@@ -139,6 +139,26 @@ function openBrowser(url) {
|
|
|
139
139
|
run(cmd).catch(() => {});
|
|
140
140
|
}
|
|
141
141
|
|
|
142
|
+
// ─── Usage Info ──────────────────────────────────────────────────────────────
|
|
143
|
+
|
|
144
|
+
function showUsageInfo() {
|
|
145
|
+
console.log(chalk.cyan('\n How to use the Dashboard:'));
|
|
146
|
+
console.log(chalk.gray(' ─'.repeat(40)));
|
|
147
|
+
console.log(' 1. Start the dashboard from this menu');
|
|
148
|
+
console.log(' 2. Open it in your browser (or visit the URL shown)');
|
|
149
|
+
console.log(' 3. Log in with the admin password from config');
|
|
150
|
+
console.log(' 4. Use the dashboard to:');
|
|
151
|
+
console.log(chalk.gray(' • Manage domains → create nginx configs'));
|
|
152
|
+
console.log(chalk.gray(' • View SSL certificates'));
|
|
153
|
+
console.log(chalk.gray(' • Control nginx (start/stop/reload)'));
|
|
154
|
+
console.log(chalk.gray(' • Edit nginx configuration files'));
|
|
155
|
+
console.log();
|
|
156
|
+
console.log(chalk.yellow(' Default login:'));
|
|
157
|
+
console.log(chalk.gray(' Username: admin'));
|
|
158
|
+
console.log(chalk.gray(' Password: (check your config or set one)'));
|
|
159
|
+
console.log();
|
|
160
|
+
}
|
|
161
|
+
|
|
142
162
|
// ─── Menu ─────────────────────────────────────────────────────────────────────
|
|
143
163
|
|
|
144
164
|
export default async function dashboardMenu() {
|
|
@@ -160,8 +180,8 @@ export default async function dashboardMenu() {
|
|
|
160
180
|
console.log();
|
|
161
181
|
|
|
162
182
|
const choices = status.running
|
|
163
|
-
? ['Open in browser', 'Stop dashboard', new inquirer.Separator(), '← Back']
|
|
164
|
-
: ['Start dashboard', new inquirer.Separator(), '← Back'];
|
|
183
|
+
? ['Open in browser', 'Stop dashboard', 'How to use', new inquirer.Separator(), '← Back']
|
|
184
|
+
: ['Start dashboard', 'How to use', new inquirer.Separator(), '← Back'];
|
|
165
185
|
|
|
166
186
|
let choice;
|
|
167
187
|
try {
|
package/cli/menus/update.js
CHANGED
|
@@ -4,31 +4,37 @@
|
|
|
4
4
|
* Check for updates and upgrade easy-devops in place.
|
|
5
5
|
*
|
|
6
6
|
* Flow:
|
|
7
|
-
*
|
|
8
|
-
*
|
|
9
|
-
*
|
|
10
|
-
*
|
|
11
|
-
*
|
|
12
|
-
*
|
|
13
|
-
*
|
|
14
|
-
*
|
|
7
|
+
* 1. Fetch latest version from npm registry
|
|
8
|
+
* 2. If an update is available, offer to install it
|
|
9
|
+
* 3. Before installing: record dashboard running state in DB
|
|
10
|
+
* (key: 'update-pre-dashboard') so it survives a crash mid-update
|
|
11
|
+
* 4. Stop dashboard if it was running
|
|
12
|
+
* 5. Close database connection
|
|
13
|
+
* 6. Spawn an external update script that runs npm install
|
|
14
|
+
* after this process exits (to avoid EBUSY on Windows)
|
|
15
|
+
* 7. This process exits; update script runs
|
|
16
|
+
* 8. On next launch, recoverIfNeeded restarts dashboard if needed
|
|
15
17
|
*/
|
|
16
18
|
|
|
17
19
|
import chalk from 'chalk';
|
|
18
20
|
import inquirer from 'inquirer';
|
|
19
21
|
import ora from 'ora';
|
|
20
22
|
import path from 'path';
|
|
23
|
+
import fs from 'fs';
|
|
21
24
|
import { fileURLToPath } from 'url';
|
|
22
25
|
import { createRequire } from 'module';
|
|
26
|
+
import { spawn } from 'child_process';
|
|
23
27
|
import { run } from '../../core/shell.js';
|
|
24
|
-
import { dbGet, dbSet, closeDb
|
|
28
|
+
import { dbGet, dbSet, closeDb } from '../../core/db.js';
|
|
25
29
|
import { loadConfig } from '../../core/config.js';
|
|
26
|
-
import { getDashboardStatus,
|
|
30
|
+
import { getDashboardStatus, stopDashboard } from './dashboard.js';
|
|
27
31
|
|
|
28
32
|
const __dirname = path.dirname(fileURLToPath(import.meta.url));
|
|
29
|
-
const require
|
|
33
|
+
const require = createRequire(import.meta.url);
|
|
30
34
|
const { version: currentVersion } = require('../../package.json');
|
|
31
35
|
|
|
36
|
+
const isWindows = process.platform === 'win32';
|
|
37
|
+
|
|
32
38
|
// ─── Version helpers ──────────────────────────────────────────────────────────
|
|
33
39
|
|
|
34
40
|
async function fetchLatestVersion() {
|
|
@@ -54,18 +60,20 @@ async function recoverIfNeeded() {
|
|
|
54
60
|
const saved = dbGet('update-pre-dashboard');
|
|
55
61
|
if (!saved?.wasRunning) return;
|
|
56
62
|
|
|
57
|
-
console.log(chalk.yellow('\n
|
|
63
|
+
console.log(chalk.yellow('\n A previous update left the dashboard stopped.'));
|
|
58
64
|
const { restart } = await inquirer.prompt([{
|
|
59
|
-
type:
|
|
60
|
-
name:
|
|
65
|
+
type: 'confirm',
|
|
66
|
+
name: 'restart',
|
|
61
67
|
message: 'Restart the dashboard now?',
|
|
62
68
|
default: true,
|
|
63
69
|
}]);
|
|
64
70
|
|
|
65
71
|
if (restart) {
|
|
72
|
+
// Dynamic import to avoid circular dependency and ensure fresh state
|
|
73
|
+
const { startDashboard } = await import('./dashboard.js');
|
|
66
74
|
const port = saved.port || loadConfig().dashboardPort;
|
|
67
|
-
const sp
|
|
68
|
-
const res
|
|
75
|
+
const sp = ora(`Starting dashboard on port ${port}...`).start();
|
|
76
|
+
const res = await startDashboard(port);
|
|
69
77
|
res.success
|
|
70
78
|
? sp.succeed(`Dashboard restarted on port ${port}`)
|
|
71
79
|
: sp.fail('Could not restart dashboard — use the Dashboard menu');
|
|
@@ -76,54 +84,141 @@ async function recoverIfNeeded() {
|
|
|
76
84
|
|
|
77
85
|
// ─── Perform update ───────────────────────────────────────────────────────────
|
|
78
86
|
|
|
87
|
+
/**
|
|
88
|
+
* Creates and spawns an external update script that runs after this process exits.
|
|
89
|
+
* This is necessary on Windows because better-sqlite3's native module stays locked
|
|
90
|
+
* as long as any Node.js process has the database open.
|
|
91
|
+
*/
|
|
79
92
|
async function performUpdate(latestVersion) {
|
|
80
93
|
// Step 1 — snapshot dashboard state and persist it
|
|
81
94
|
const status = await getDashboardStatus();
|
|
82
95
|
dbSet('update-pre-dashboard', {
|
|
83
96
|
wasRunning: status.running,
|
|
84
|
-
pid:
|
|
85
|
-
port:
|
|
97
|
+
pid: status.pid,
|
|
98
|
+
port: status.port,
|
|
86
99
|
});
|
|
87
100
|
|
|
88
|
-
// Step 2 — stop dashboard if running
|
|
101
|
+
// Step 2 — stop dashboard if running (it has its own DB connection)
|
|
89
102
|
if (status.running) {
|
|
90
103
|
const sp = ora('Stopping dashboard...').start();
|
|
91
104
|
await stopDashboard(status.pid);
|
|
92
105
|
sp.succeed('Dashboard stopped');
|
|
106
|
+
// Give the dashboard process a moment to fully terminate
|
|
107
|
+
await new Promise(r => setTimeout(r, 2000));
|
|
93
108
|
}
|
|
94
109
|
|
|
95
|
-
// Step 3 — close
|
|
110
|
+
// Step 3 — close this process's database connection
|
|
96
111
|
closeDb();
|
|
97
112
|
|
|
98
|
-
// Step 4 —
|
|
99
|
-
|
|
100
|
-
const
|
|
113
|
+
// Step 4 — create an external update script and spawn it
|
|
114
|
+
// The script will run npm install after this process exits
|
|
115
|
+
const updateScriptPS = `
|
|
116
|
+
$ProgressPreference = 'SilentlyContinue'
|
|
117
|
+
Write-Host ""
|
|
118
|
+
Write-Host "Installing easy-devops@${latestVersion}..." -ForegroundColor Cyan
|
|
119
|
+
Write-Host ""
|
|
120
|
+
npm install -g easy-devops@${latestVersion}
|
|
121
|
+
if ($LASTEXITCODE -eq 0) {
|
|
122
|
+
Write-Host ""
|
|
123
|
+
Write-Host "Successfully updated to v${latestVersion}" -ForegroundColor Green
|
|
124
|
+
Write-Host "Run 'easy-devops' to start the new version." -ForegroundColor Gray
|
|
125
|
+
} else {
|
|
126
|
+
Write-Host ""
|
|
127
|
+
Write-Host "Update failed. Please try again or update manually:" -ForegroundColor Red
|
|
128
|
+
Write-Host " npm install -g easy-devops@${latestVersion}" -ForegroundColor Yellow
|
|
129
|
+
}
|
|
130
|
+
Write-Host ""
|
|
131
|
+
Write-Host "Press any key to close this window..." -ForegroundColor Gray
|
|
132
|
+
$null = $Host.UI.RawUI.ReadKey('NoEcho,IncludeKeyDown')
|
|
133
|
+
`;
|
|
101
134
|
|
|
102
|
-
|
|
103
|
-
|
|
104
|
-
|
|
105
|
-
|
|
106
|
-
|
|
107
|
-
|
|
135
|
+
const updateScriptBash = `#!/bin/bash
|
|
136
|
+
echo ""
|
|
137
|
+
echo -e "\\033[36mInstalling easy-devops@${latestVersion}...\\033[0m"
|
|
138
|
+
echo ""
|
|
139
|
+
npm install -g easy-devops@${latestVersion}
|
|
140
|
+
if [ $? -eq 0 ]; then
|
|
141
|
+
echo ""
|
|
142
|
+
echo -e "\\033[32mSuccessfully updated to v${latestVersion}\\033[0m"
|
|
143
|
+
echo -e "\\033[90mRun 'easy-devops' to start the new version.\\033[0m"
|
|
144
|
+
else
|
|
145
|
+
echo ""
|
|
146
|
+
echo -e "\\033[31mUpdate failed. Please try again or update manually:\\033[0m"
|
|
147
|
+
echo -e "\\033[33m npm install -g easy-devops@${latestVersion}\\033[0m"
|
|
148
|
+
fi
|
|
149
|
+
echo ""
|
|
150
|
+
read -p "Press Enter to close this window..."
|
|
151
|
+
`;
|
|
108
152
|
|
|
109
|
-
|
|
153
|
+
console.log(chalk.cyan('\n Starting external update process...'));
|
|
154
|
+
console.log(chalk.gray(' A new window will open to complete the update.'));
|
|
155
|
+
console.log(chalk.gray(' This window will close after the update starts.\n'));
|
|
110
156
|
|
|
111
|
-
|
|
112
|
-
|
|
157
|
+
if (isWindows) {
|
|
158
|
+
// Write script to temp file and run in new PowerShell window
|
|
159
|
+
const tempDir = process.env.TEMP || 'C:\\Windows\\Temp';
|
|
160
|
+
const tempScript = path.join(tempDir, 'easy-devops-update.ps1');
|
|
161
|
+
fs.writeFileSync(tempScript, updateScriptPS, 'utf8');
|
|
113
162
|
|
|
114
|
-
|
|
115
|
-
|
|
116
|
-
|
|
163
|
+
// Run in a new window, this process will exit
|
|
164
|
+
spawn('powershell.exe', [
|
|
165
|
+
'-NoExit',
|
|
166
|
+
'-ExecutionPolicy', 'Bypass',
|
|
167
|
+
'-File', tempScript
|
|
168
|
+
], {
|
|
169
|
+
detached: true,
|
|
170
|
+
stdio: 'ignore',
|
|
171
|
+
windowsHide: false
|
|
172
|
+
}).unref();
|
|
117
173
|
|
|
118
|
-
|
|
119
|
-
|
|
120
|
-
|
|
121
|
-
|
|
122
|
-
|
|
123
|
-
|
|
124
|
-
|
|
174
|
+
// Clean up hint
|
|
175
|
+
console.log(chalk.gray(` Update script: ${tempScript}`));
|
|
176
|
+
} else {
|
|
177
|
+
// On Linux/Mac, run in a new terminal window if possible
|
|
178
|
+
const tempScript = '/tmp/easy-devops-update.sh';
|
|
179
|
+
fs.writeFileSync(tempScript, updateScriptBash, 'utf8');
|
|
180
|
+
fs.chmodSync(tempScript, '755');
|
|
181
|
+
|
|
182
|
+
// Try common terminal emulators
|
|
183
|
+
const terminals = [
|
|
184
|
+
['gnome-terminal', '--', 'bash', '-c', updateScriptBash],
|
|
185
|
+
['xterm', '-e', 'bash', '-c', updateScriptBash],
|
|
186
|
+
['konsole', '-e', 'bash', '-c', updateScriptBash],
|
|
187
|
+
];
|
|
188
|
+
let launched = false;
|
|
189
|
+
|
|
190
|
+
for (const [cmd, ...args] of terminals) {
|
|
191
|
+
try {
|
|
192
|
+
spawn(cmd, args, {
|
|
193
|
+
detached: true,
|
|
194
|
+
stdio: 'ignore'
|
|
195
|
+
}).unref();
|
|
196
|
+
launched = true;
|
|
197
|
+
break;
|
|
198
|
+
} catch { /* try next */ }
|
|
199
|
+
}
|
|
200
|
+
|
|
201
|
+
if (!launched) {
|
|
202
|
+
// Fallback: just run npm directly in this terminal
|
|
203
|
+
console.log(chalk.yellow('\nRunning update in this window...'));
|
|
204
|
+
const result = await run(`npm install -g easy-devops@${latestVersion}`, { timeout: 120000 });
|
|
205
|
+
if (result.success) {
|
|
206
|
+
console.log(chalk.green(`\n Successfully updated to v${latestVersion}`));
|
|
207
|
+
} else {
|
|
208
|
+
console.log(chalk.red('\n Update failed:'));
|
|
209
|
+
console.log(result.stderr || result.stdout);
|
|
210
|
+
}
|
|
211
|
+
return result.success;
|
|
212
|
+
}
|
|
125
213
|
}
|
|
126
214
|
|
|
215
|
+
// Give a moment for the external process to start
|
|
216
|
+
await new Promise(r => setTimeout(r, 1000));
|
|
217
|
+
|
|
218
|
+
console.log(chalk.green('\n Update process launched.'));
|
|
219
|
+
console.log(chalk.gray(' Complete the update in the new window, then run: easy-devops'));
|
|
220
|
+
console.log(chalk.gray(' If the dashboard was running, it will be restarted on next launch.\n'));
|
|
221
|
+
|
|
127
222
|
return true;
|
|
128
223
|
}
|
|
129
224
|
|
|
@@ -137,12 +232,12 @@ export default async function updateMenu() {
|
|
|
137
232
|
const latestVersion = await fetchLatestVersion();
|
|
138
233
|
spinner.stop();
|
|
139
234
|
|
|
140
|
-
console.log(chalk.bold('\n
|
|
141
|
-
console.log(chalk.gray('
|
|
142
|
-
console.log(`
|
|
235
|
+
console.log(chalk.bold('\n Check for Updates'));
|
|
236
|
+
console.log(chalk.gray(' ' + '─'.repeat(40)));
|
|
237
|
+
console.log(` Current version : ${chalk.cyan('v' + currentVersion)}`);
|
|
143
238
|
|
|
144
239
|
if (!latestVersion) {
|
|
145
|
-
console.log(chalk.yellow('
|
|
240
|
+
console.log(chalk.yellow(' Could not reach npm registry. Check your internet connection.\n'));
|
|
146
241
|
await inquirer.prompt([{ type: 'input', name: '_', message: 'Press Enter to go back...' }]);
|
|
147
242
|
return;
|
|
148
243
|
}
|
|
@@ -150,9 +245,9 @@ export default async function updateMenu() {
|
|
|
150
245
|
const updateAvailable = isNewer(latestVersion, currentVersion);
|
|
151
246
|
|
|
152
247
|
if (updateAvailable) {
|
|
153
|
-
console.log(`
|
|
248
|
+
console.log(` Latest version : ${chalk.green('v' + latestVersion)} ${chalk.yellow('← update available')}\n`);
|
|
154
249
|
} else {
|
|
155
|
-
console.log(`
|
|
250
|
+
console.log(` Latest version : ${chalk.green('v' + latestVersion)} ${chalk.gray('✓ up to date')}\n`);
|
|
156
251
|
}
|
|
157
252
|
|
|
158
253
|
const choices = updateAvailable
|
|
@@ -162,8 +257,8 @@ export default async function updateMenu() {
|
|
|
162
257
|
let choice;
|
|
163
258
|
try {
|
|
164
259
|
({ choice } = await inquirer.prompt([{
|
|
165
|
-
type:
|
|
166
|
-
name:
|
|
260
|
+
type: 'list',
|
|
261
|
+
name: 'choice',
|
|
167
262
|
message: 'Select an option:',
|
|
168
263
|
choices,
|
|
169
264
|
}]));
|
|
@@ -173,12 +268,9 @@ export default async function updateMenu() {
|
|
|
173
268
|
}
|
|
174
269
|
|
|
175
270
|
if (choice === `Update to v${latestVersion}`) {
|
|
176
|
-
|
|
177
|
-
if (success) {
|
|
178
|
-
console.log(chalk.gray('\n Restart easy-devops to use the new version.\n'));
|
|
179
|
-
}
|
|
271
|
+
await performUpdate(latestVersion);
|
|
180
272
|
try {
|
|
181
|
-
await inquirer.prompt([{ type: 'input', name: '_', message: 'Press Enter to
|
|
273
|
+
await inquirer.prompt([{ type: 'input', name: '_', message: 'Press Enter to exit...' }]);
|
|
182
274
|
} catch { /* ExitPromptError */ }
|
|
183
275
|
}
|
|
184
276
|
}
|
|
@@ -257,7 +257,8 @@ export function buildConf(domain, nginxDir, certbotDir) {
|
|
|
257
257
|
|
|
258
258
|
// Logging
|
|
259
259
|
if (advanced?.accessLog) {
|
|
260
|
-
|
|
260
|
+
const logDir = isWindows ? `${nginxDir.replace(/\\/g, '/')}/logs` : '/var/log/nginx';
|
|
261
|
+
mainBlock.push(` access_log ${logDir}/${name}.access.log;`);
|
|
261
262
|
}
|
|
262
263
|
|
|
263
264
|
// ─── Location Block ─────────────────────────────────────────────────────────
|
|
@@ -301,10 +302,10 @@ export function buildConf(domain, nginxDir, certbotDir) {
|
|
|
301
302
|
mainBlock.push(` error_page 500 502 503 504 /50x.html;`);
|
|
302
303
|
}
|
|
303
304
|
if (security.custom404) {
|
|
304
|
-
mainBlock.push(` location = /404.html { root /usr/share/nginx/html; internal; }`);
|
|
305
|
+
mainBlock.push(` location = /404.html { root ${isWindows ? nginxDir.replace(/\\/g, '/') + '/html' : '/usr/share/nginx/html'}; internal; }`);
|
|
305
306
|
}
|
|
306
307
|
if (security.custom50x) {
|
|
307
|
-
mainBlock.push(` location = /50x.html { root /usr/share/nginx/html; internal; }`);
|
|
308
|
+
mainBlock.push(` location = /50x.html { root ${isWindows ? nginxDir.replace(/\\/g, '/') + '/html' : '/usr/share/nginx/html'}; internal; }`);
|
|
308
309
|
}
|
|
309
310
|
}
|
|
310
311
|
|
|
@@ -141,17 +141,26 @@ export async function start() {
|
|
|
141
141
|
throw new NginxNotFoundError('nginx binary not found');
|
|
142
142
|
}
|
|
143
143
|
|
|
144
|
-
//
|
|
144
|
+
// Ensure required directories exist on Windows
|
|
145
|
+
if (process.platform === 'win32') {
|
|
146
|
+
await fs.mkdir(path.join(nginxDir, 'logs'), { recursive: true });
|
|
147
|
+
await fs.mkdir(path.join(nginxDir, 'temp'), { recursive: true });
|
|
148
|
+
}
|
|
149
|
+
|
|
150
|
+
// Test config before starting with explicit config path on Windows
|
|
145
151
|
await ensureNginxInclude(nginxDir);
|
|
146
|
-
const
|
|
152
|
+
const testCmd = process.platform === 'win32'
|
|
153
|
+
? `${nginxExe} -c "${path.join(nginxDir, 'conf', 'nginx.conf')}" -t`
|
|
154
|
+
: `${nginxExe} -t`;
|
|
155
|
+
const testResult = await run(testCmd);
|
|
147
156
|
if (!testResult.success) {
|
|
148
157
|
return { success: false, output: combineOutput(testResult) };
|
|
149
158
|
}
|
|
150
159
|
|
|
151
|
-
// Start nginx
|
|
160
|
+
// Start nginx with explicit config path on Windows
|
|
152
161
|
if (process.platform === 'win32') {
|
|
153
|
-
|
|
154
|
-
const startCmd = `Start-Process -FilePath "${path.join(nginxDir, 'nginx.exe')}" -WorkingDirectory "${nginxDir}" -WindowStyle Hidden`;
|
|
162
|
+
const confPath = path.join(nginxDir, 'conf', 'nginx.conf');
|
|
163
|
+
const startCmd = `Start-Process -FilePath "${path.join(nginxDir, 'nginx.exe')}" -ArgumentList '-c','"${confPath}"' -WorkingDirectory "${nginxDir}" -WindowStyle Hidden`;
|
|
155
164
|
await run(startCmd, { timeout: 10000 });
|
|
156
165
|
} else {
|
|
157
166
|
const result = await run('nginx', { cwd: nginxDir, timeout: 15000 });
|
|
@@ -216,7 +225,11 @@ export async function test() {
|
|
|
216
225
|
}
|
|
217
226
|
|
|
218
227
|
await ensureNginxInclude(nginxDir);
|
|
219
|
-
|
|
228
|
+
// Use explicit -c flag on Windows to avoid path issues
|
|
229
|
+
const testCmd = process.platform === 'win32'
|
|
230
|
+
? `${nginxExe} -c "${path.join(nginxDir, 'conf', 'nginx.conf')}" -t`
|
|
231
|
+
: `${nginxExe} -t`;
|
|
232
|
+
const result = await run(testCmd);
|
|
220
233
|
return { success: result.success, output: combineOutput(result) };
|
|
221
234
|
}
|
|
222
235
|
|
|
@@ -18,7 +18,12 @@ function getNginxExe(nginxDir) {
|
|
|
18
18
|
|
|
19
19
|
function nginxTestCmd(nginxDir) {
|
|
20
20
|
const exe = getNginxExe(nginxDir);
|
|
21
|
-
|
|
21
|
+
// Use explicit -c flag on Windows to avoid path issues
|
|
22
|
+
if (isWindows) {
|
|
23
|
+
const confPath = `${nginxDir}\\conf\\nginx.conf`;
|
|
24
|
+
return `& "${exe}" -c "${confPath}" -t`;
|
|
25
|
+
}
|
|
26
|
+
return 'nginx -t';
|
|
22
27
|
}
|
|
23
28
|
|
|
24
29
|
function nginxReloadCmd(nginxDir) {
|
package/package.json
CHANGED