[add] first
This commit is contained in:
commit
6a9fa3321e
2
.gitignore
vendored
Normal file
2
.gitignore
vendored
Normal file
@ -0,0 +1,2 @@
|
|||||||
|
/*.DS_Store
|
||||||
|
/conf/conf.d
|
71
README.md
Normal file
71
README.md
Normal file
@ -0,0 +1,71 @@
|
|||||||
|
# NGINX 動態 Server 管理工具
|
||||||
|
|
||||||
|
此專案提供一個簡易的 PHP 網頁介面,可以動態新增和刪除 NGINX Server 配置,並即時重載 NGINX 配置,方便本地多專案開發環境管理。
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## 主要功能
|
||||||
|
|
||||||
|
- 顯示目前所有 NGINX 動態 Server(以 conf.d/*.conf 設定檔為基礎)
|
||||||
|
- 新增 Server:指定 Port 與根目錄(根目錄為指定的相對路徑,會自動補上預設絕對路徑前綴)
|
||||||
|
- 刪除 Server
|
||||||
|
- 新增、刪除後自動重載 NGINX 配置
|
||||||
|
- 預設排除保留端口(如 80、8080)及已被使用的端口
|
||||||
|
- 前端輸入檢查,避免重複端口與保留端口使用
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## 系統需求
|
||||||
|
|
||||||
|
- PHP 7.0 以上
|
||||||
|
- NGINX 已安裝且可透過命令行重載
|
||||||
|
- PHP 有權限讀寫 NGINX conf.d 目錄
|
||||||
|
- 適用於 macOS 或 Linux 環境,路徑請自行調整
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## 專案結構
|
||||||
|
|
||||||
|
nginx-manager/
|
||||||
|
│
|
||||||
|
├── conf/
|
||||||
|
│ └── conf.d/ # NGINX 動態 Server 設定檔目錄
|
||||||
|
├── index.php # 主頁面,顯示及新增刪除 Server
|
||||||
|
├── servers.php # 處理新增刪除 Server 請求並重載 NGINX
|
||||||
|
└── README.md
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## 使用說明
|
||||||
|
|
||||||
|
1. 修改 `servers.php` 中的 `$confDir` 及 `$nginxExe` 變數,設定對應的 NGINX 配置目錄與 nginx 可執行檔路徑
|
||||||
|
|
||||||
|
2. 修改 `index.php` 中 `$basePath` 變數(預設為 `/Users/catantech/Desktop/`,用於拼接使用者輸入的相對路徑)
|
||||||
|
|
||||||
|
3. 將此專案放置於支援 PHP 的 Web 伺服器中(例如內建 PHP server 或 Apache)
|
||||||
|
|
||||||
|
4. 開啟瀏覽器進入 `index.php`,即可看到目前動態 Server,並可新增或刪除
|
||||||
|
|
||||||
|
5. 新增時,請輸入相對於 `$basePath` 的路徑(例如:`Project/Line_Project_1/Official/out-dev`)
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## 注意事項
|
||||||
|
|
||||||
|
- 確保 PHP 執行者有讀寫 NGINX 動態設定檔目錄的權限
|
||||||
|
|
||||||
|
- 新增 Server 會立即寫入設定檔並重載 NGINX,請確保 NGINX 配置無誤,避免重載失敗
|
||||||
|
|
||||||
|
- 本專案不包含完整的安全檢查,建議用於本地開發環境
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## 授權
|
||||||
|
|
||||||
|
MIT License
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## 聯絡
|
||||||
|
|
||||||
|
如有問題,歡迎聯絡建喵。
|
BIN
favicon.png
Normal file
BIN
favicon.png
Normal file
Binary file not shown.
After Width: | Height: | Size: 11 KiB |
140
index.php
Normal file
140
index.php
Normal file
@ -0,0 +1,140 @@
|
|||||||
|
<?php
|
||||||
|
session_start();
|
||||||
|
|
||||||
|
function listServerFiles() {
|
||||||
|
$files = glob(__DIR__ . '/conf/conf.d/*.conf');
|
||||||
|
$servers = [];
|
||||||
|
foreach ($files as $file) {
|
||||||
|
$port = basename($file, '.conf');
|
||||||
|
$servers[] = [
|
||||||
|
'port' => $port,
|
||||||
|
'file' => $file,
|
||||||
|
'content' => file_get_contents($file)
|
||||||
|
];
|
||||||
|
}
|
||||||
|
return $servers;
|
||||||
|
}
|
||||||
|
|
||||||
|
$servers = listServerFiles();
|
||||||
|
|
||||||
|
$reservedPorts = [80, 8080];
|
||||||
|
|
||||||
|
// 合併所有已使用 port 與保留 port
|
||||||
|
$usedPorts = array_unique(array_merge(
|
||||||
|
$reservedPorts,
|
||||||
|
array_map('intval', array_column($servers, 'port'))
|
||||||
|
));
|
||||||
|
|
||||||
|
function findRandomAvailablePort($start = 3000, $end = 9000, $usedPorts = []) {
|
||||||
|
$candidates = range($start, $end);
|
||||||
|
shuffle($candidates);
|
||||||
|
foreach ($candidates as $port) {
|
||||||
|
if (!in_array($port, $usedPorts)) {
|
||||||
|
return $port;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
$autoPort = findRandomAvailablePort(3000, 9000, $usedPorts);
|
||||||
|
|
||||||
|
$resultMsg = '';
|
||||||
|
if (isset($_SESSION['resultMsg'])) {
|
||||||
|
$resultMsg = $_SESSION['resultMsg'];
|
||||||
|
unset($_SESSION['resultMsg']);
|
||||||
|
}
|
||||||
|
?>
|
||||||
|
<!DOCTYPE html>
|
||||||
|
<html>
|
||||||
|
<head>
|
||||||
|
<meta charset="UTF-8" />
|
||||||
|
<title>NGINX 動態 Server 管理</title>
|
||||||
|
<link rel="icon" href="/favicon.png" data-rh="true">
|
||||||
|
<style>
|
||||||
|
body { font-family: sans-serif; padding: 20px; }
|
||||||
|
pre { background: #f0f0f0; padding: 10px; overflow-x: auto; }
|
||||||
|
.message { padding: 10px; margin-bottom: 20px; border-radius: 4px; }
|
||||||
|
.success { background-color: #d4edda; color: #155724; }
|
||||||
|
.error { background-color: #f8d7da; color: #721c24; }
|
||||||
|
input[type="number"] { width: 80px; }
|
||||||
|
</style>
|
||||||
|
</head>
|
||||||
|
<body>
|
||||||
|
<?php if ($resultMsg): ?>
|
||||||
|
<div class="message <?= strpos($resultMsg, '成功') !== false ? 'success' : 'error' ?>">
|
||||||
|
<?= nl2br(htmlspecialchars($resultMsg)) ?>
|
||||||
|
</div>
|
||||||
|
<?php endif; ?>
|
||||||
|
|
||||||
|
<h2>📡 現有 Servers</h2>
|
||||||
|
<ul>
|
||||||
|
<?php foreach ($servers as $s): ?>
|
||||||
|
<?php
|
||||||
|
// 從 content 透過正規表達式取 root 路徑
|
||||||
|
preg_match('/root\s+([^\s;]+);/', $s['content'], $matches);
|
||||||
|
$rootPath = $matches[1] ?? '未知';
|
||||||
|
|
||||||
|
// 排除顯示 /Users/catantech/Desktop/
|
||||||
|
$displayPath = str_replace('/Users/catantech/Desktop/', '', $rootPath);
|
||||||
|
?>
|
||||||
|
<li>
|
||||||
|
<strong>Port <?= htmlspecialchars($s['port']) ?></strong><br>
|
||||||
|
<strong>Root 路徑: <?= htmlspecialchars($displayPath) ?></strong><br>
|
||||||
|
<form method="post" action="servers.php" style="display:inline; margin-right: 10px;">
|
||||||
|
<input type="hidden" name="action" value="delete" />
|
||||||
|
<input type="hidden" name="port" value="<?= htmlspecialchars($s['port']) ?>" />
|
||||||
|
<button type="submit">刪除</button>
|
||||||
|
</form>
|
||||||
|
<a href="http://192.168.5.45:<?= htmlspecialchars($s['port']) ?>" target="_blank" style="color: blue; text-decoration: underline;">
|
||||||
|
連結到此 Server
|
||||||
|
</a>
|
||||||
|
</li>
|
||||||
|
<br>
|
||||||
|
<?php endforeach; ?>
|
||||||
|
</ul>
|
||||||
|
|
||||||
|
<hr>
|
||||||
|
<h2>➕ 新增 Server</h2>
|
||||||
|
<form method="post" action="servers.php" id="addServerForm">
|
||||||
|
<input type="hidden" name="action" value="add" />
|
||||||
|
<label for="portInput">🎲 Port:</label>
|
||||||
|
<input type="number" id="portInput" name="port" min="3000" max="9000" required value="<?= $autoPort ?>" />
|
||||||
|
<br />
|
||||||
|
<label for="rootInput">Root 目錄:</label>
|
||||||
|
<input type="text" id="rootInput" name="root" required style="width:400px;" value="Project/Line_Project_1/Official/out-dev" />
|
||||||
|
<br />
|
||||||
|
<button type="submit">新增</button>
|
||||||
|
</form>
|
||||||
|
|
||||||
|
<script>
|
||||||
|
(() => {
|
||||||
|
const reservedPorts = [80, 8080];
|
||||||
|
const usedPorts = <?= json_encode($usedPorts) ?>;
|
||||||
|
|
||||||
|
const portInput = document.getElementById('portInput');
|
||||||
|
const form = document.getElementById('addServerForm');
|
||||||
|
|
||||||
|
form.addEventListener('submit', e => {
|
||||||
|
const portVal = Number(portInput.value);
|
||||||
|
|
||||||
|
if (isNaN(portVal) || portVal < 3000 || portVal > 9000) {
|
||||||
|
alert('❌ Port 必須是 3000 到 9000 之間的數字');
|
||||||
|
e.preventDefault();
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
if (reservedPorts.includes(portVal)) {
|
||||||
|
alert(`❌ Port ${portVal} 是保留端口,請選擇其他 Port`);
|
||||||
|
e.preventDefault();
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
if (usedPorts.includes(portVal)) {
|
||||||
|
alert(`❌ Port ${portVal} 已被使用,請選擇其他 Port`);
|
||||||
|
e.preventDefault();
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
});
|
||||||
|
})();
|
||||||
|
</script>
|
||||||
|
|
||||||
|
</body>
|
||||||
|
</html>
|
82
servers.php
Normal file
82
servers.php
Normal file
@ -0,0 +1,82 @@
|
|||||||
|
<?php
|
||||||
|
session_start();
|
||||||
|
|
||||||
|
$confDir = realpath('/Users/catantech/Desktop/Project/php/nginx-manager/conf/conf.d');
|
||||||
|
$nginxExe = '/opt/homebrew/bin/nginx';
|
||||||
|
|
||||||
|
if (!$confDir) {
|
||||||
|
die("❌ 找不到 conf.d 目錄");
|
||||||
|
}
|
||||||
|
|
||||||
|
function reloadNginx() {
|
||||||
|
global $nginxExe;
|
||||||
|
$configPath = '/opt/homebrew/etc/nginx/nginx.conf'; // 請根據實際路徑調整
|
||||||
|
$cmd = "\"$nginxExe\" -s reload -c \"$configPath\" 2>&1";
|
||||||
|
exec($cmd, $output, $code);
|
||||||
|
return ['code' => $code, 'output' => $output];
|
||||||
|
}
|
||||||
|
|
||||||
|
if ($_SERVER['REQUEST_METHOD'] === 'POST') {
|
||||||
|
$port = intval($_POST['port']);
|
||||||
|
$confFile = "$confDir/{$port}.conf";
|
||||||
|
$resultMsg = '';
|
||||||
|
|
||||||
|
if ($_POST['action'] === 'add') {
|
||||||
|
$baseDir = '/Users/catantech/Desktop/';
|
||||||
|
$relativeRoot = trim($_POST['root']);
|
||||||
|
|
||||||
|
// 防止目錄跳脫
|
||||||
|
if (strpos($relativeRoot, '..') !== false) {
|
||||||
|
$resultMsg = "❌ Root 路徑不能包含 '..' ";
|
||||||
|
} else {
|
||||||
|
// 拼接完整路徑
|
||||||
|
$fullRoot = rtrim($baseDir, '/') . '/' . ltrim($relativeRoot, '/');
|
||||||
|
|
||||||
|
$serverBlock = <<<CONF
|
||||||
|
server {
|
||||||
|
listen $port;
|
||||||
|
server_name localhost;
|
||||||
|
|
||||||
|
location / {
|
||||||
|
root $fullRoot;
|
||||||
|
index index.html;
|
||||||
|
try_files \$uri \$uri/ /index.html;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
CONF;
|
||||||
|
|
||||||
|
$writeOk = file_put_contents($confFile, $serverBlock) !== false;
|
||||||
|
if (!$writeOk) {
|
||||||
|
$resultMsg = "❌ 寫入設定檔失敗";
|
||||||
|
} else {
|
||||||
|
$reload = reloadNginx();
|
||||||
|
if ($reload['code'] !== 0) {
|
||||||
|
$resultMsg = "❌ NGINX 重啟失敗:" . implode("\n", $reload['output']);
|
||||||
|
} else {
|
||||||
|
$resultMsg = "✅ 新增 Server 成功,並已重啟 NGINX";
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
} elseif ($_POST['action'] === 'delete') {
|
||||||
|
if (file_exists($confFile)) {
|
||||||
|
$delOk = unlink($confFile);
|
||||||
|
if (!$delOk) {
|
||||||
|
$resultMsg = "❌ 刪除設定檔失敗";
|
||||||
|
} else {
|
||||||
|
$reload = reloadNginx();
|
||||||
|
if ($reload['code'] !== 0) {
|
||||||
|
$resultMsg = "❌ NGINX 重啟失敗:" . implode("\n", $reload['output']);
|
||||||
|
} else {
|
||||||
|
$resultMsg = "✅ 刪除 Server 成功,並已重啟 NGINX";
|
||||||
|
}
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
$resultMsg = "❌ 設定檔不存在";
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
$_SESSION['resultMsg'] = $resultMsg;
|
||||||
|
header("Location: index.php");
|
||||||
|
exit;
|
||||||
|
}
|
Loading…
x
Reference in New Issue
Block a user