Phase A — HTML removed from PHP files: - RateLimitMiddleware.php: heredoc moved to views/errors/429.php - public/index.php: inline HTML replaced with View::error() - AuthMiddleware.php: <script> redirect replaced with <meta refresh> Phase B — Inline CSS/JS extracted from views: - CSS: landing.css (459 lines), legal.css (shared by terms+privacy) - JS: 17 new files extracted from ~20 view files - main.js (layout sidebar + notifications) - dashboard-chart.js, server-show.js, server-services.js - nginx-manager.js, database-manager.js - terminal.js, team-show.js, team-index.js - server-list.js, server-permissions.js (shared by edit+create) - server-ssl.js, server-processes.js, system-users.js - admin-users.js, audit-log.js, admin-notifications.js, admin-security.js - notifications.js Total: -3264 lines from views, +288 lines in new assets
328 lines
16 KiB
PHP
328 lines
16 KiB
PHP
<?php
|
|
$server = $server ?? [];
|
|
$nginx = $nginx ?? [];
|
|
$installed = $nginx['installed'] ?? false;
|
|
?>
|
|
|
|
<div class="page-header">
|
|
<div class="page-header-left">
|
|
<div class="back-link">
|
|
<a href="/servers/<?= $server['id'] ?>"><i class="fas fa-arrow-left"></i> Back to Server</a>
|
|
</div>
|
|
<h1 class="page-title">
|
|
<svg viewBox="0 0 24 24" fill="currentColor" width="24" height="24" style="vertical-align:middle;margin-right:0.5rem;opacity:0.8">
|
|
<path d="M12 2L2 7.2v9.6L12 22l10-5.2V7.2L12 2zm0 1.5l8.5 4.4v8.2L12 20.5l-8.5-4.4V7.9L12 3.5zM7.4 8.5v7l1.5.9V11l3.1 4.3 1.5.9V8.5l-1.5.9v4.4L8.9 9.4l-1.5-.9zm9.2 0l-1.5.9-3.1 4.4V17l1.5-.9V11l3.1-4.3z"/>
|
|
</svg>
|
|
Nginx Manager
|
|
</h1>
|
|
<p class="page-subtitle"><?= htmlspecialchars($server['name']) ?></p>
|
|
</div>
|
|
<div class="page-header-right">
|
|
<button class="btn btn-secondary" onclick="location.reload()">
|
|
<i class="fas fa-sync-alt"></i> Refresh
|
|
</button>
|
|
</div>
|
|
</div>
|
|
|
|
<?php if (!$installed): ?>
|
|
<div class="card">
|
|
<div class="card-body">
|
|
<div class="empty-state">
|
|
<svg viewBox="0 0 24 24" fill="currentColor" width="64" height="64" style="opacity:0.4;margin-bottom:1rem">
|
|
<path d="M12 2L2 7.2v9.6L12 22l10-5.2V7.2L12 2zm0 1.5l8.5 4.4v8.2L12 20.5l-8.5-4.4V7.9L12 3.5zM7.4 8.5v7l1.5.9V11l3.1 4.3 1.5.9V8.5l-1.5.9v4.4L8.9 9.4l-1.5-.9zm9.2 0l-1.5.9-3.1 4.4V17l1.5-.9V11l3.1-4.3z"/>
|
|
</svg>
|
|
<h3>Nginx is not installed</h3>
|
|
<p>This server does not have Nginx installed or it is not in the PATH.</p>
|
|
<p class="text-muted">Install it with: <code>sudo apt install nginx</code> or <code>sudo yum install nginx</code></p>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
<?php else: ?>
|
|
<?php
|
|
$status = $nginx['status'] ?? 'inactive';
|
|
$enabled = $nginx['enabled'] ?? 'disabled';
|
|
$configValid = $nginx['config_valid'] ?? false;
|
|
?>
|
|
|
|
<div class="dashboard-grid">
|
|
<div class="dashboard-card">
|
|
<div class="card-header">
|
|
<h2><i class="fas fa-info-circle"></i> Service Status</h2>
|
|
</div>
|
|
<div class="card-body">
|
|
<div class="stats-grid" style="grid-template-columns:repeat(3,1fr);gap:0.75rem">
|
|
<div class="stat-card" style="padding:1rem">
|
|
<div class="stat-icon" style="width:40px;height:40px;font-size:1rem;
|
|
background:<?= $status === 'active' ? 'var(--success-bg)' : 'var(--danger-bg)' ?>;
|
|
color:<?= $status === 'active' ? 'var(--success)' : 'var(--danger)' ?>">
|
|
<i class="fas <?= $status === 'active' ? 'fa-play-circle' : 'fa-stop-circle' ?>"></i>
|
|
</div>
|
|
<div class="stat-info">
|
|
<span class="stat-value" style="font-size:1rem"><?= ucfirst($status) ?></span>
|
|
<span class="stat-label">Status</span>
|
|
</div>
|
|
</div>
|
|
<div class="stat-card" style="padding:1rem">
|
|
<div class="stat-icon" style="width:40px;height:40px;font-size:1rem;
|
|
background:<?= $enabled === 'enabled' ? 'var(--success-bg)' : 'var(--warning-bg)' ?>;
|
|
color:<?= $enabled === 'enabled' ? 'var(--success)' : 'var(--warning)' ?>">
|
|
<i class="fas fa-power-off"></i>
|
|
</div>
|
|
<div class="stat-info">
|
|
<span class="stat-value" style="font-size:1rem"><?= ucfirst($enabled) ?></span>
|
|
<span class="stat-label">Auto-start</span>
|
|
</div>
|
|
</div>
|
|
<div class="stat-card" style="padding:1rem">
|
|
<div class="stat-icon" style="width:40px;height:40px;font-size:1rem;
|
|
background:<?= $configValid ? 'var(--success-bg)' : 'var(--danger-bg)' ?>;
|
|
color:<?= $configValid ? 'var(--success)' : 'var(--danger)' ?>">
|
|
<i class="fas fa-check-circle"></i>
|
|
</div>
|
|
<div class="stat-info">
|
|
<span class="stat-value" style="font-size:1rem"><?= $configValid ? 'Valid' : 'Invalid' ?></span>
|
|
<span class="stat-label">Config</span>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
<div class="action-group" style="margin-top:1rem;display:flex;gap:0.5rem;flex-wrap:wrap">
|
|
<button class="btn btn-success btn-sm" onclick="nginxAction('start')" <?= $status === 'active' ? 'disabled' : '' ?>>
|
|
<i class="fas fa-play"></i> Start
|
|
</button>
|
|
<button class="btn btn-danger btn-sm" onclick="nginxAction('stop')" <?= $status !== 'active' ? 'disabled' : '' ?>>
|
|
<i class="fas fa-stop"></i> Stop
|
|
</button>
|
|
<button class="btn btn-warning btn-sm" onclick="nginxAction('restart')">
|
|
<i class="fas fa-redo"></i> Restart
|
|
</button>
|
|
<button class="btn btn-info btn-sm" onclick="nginxAction('reload')">
|
|
<i class="fas fa-sync-alt"></i> Reload
|
|
</button>
|
|
<button class="btn btn-secondary btn-sm" onclick="nginxAction('test')">
|
|
<i class="fas fa-vial"></i> Test Config
|
|
</button>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
<?php if (!empty($nginx['version'])): ?>
|
|
<div class="dashboard-card">
|
|
<div class="card-header">
|
|
<h2><i class="fas fa-tag"></i> Version</h2>
|
|
</div>
|
|
<div class="card-body">
|
|
<code><?= htmlspecialchars($nginx['version']) ?></code>
|
|
</div>
|
|
</div>
|
|
<?php endif; ?>
|
|
</div>
|
|
|
|
<div class="dashboard-grid">
|
|
<?php if (!empty($nginx['sites_enabled'])): ?>
|
|
<div class="dashboard-card">
|
|
<div class="card-header">
|
|
<h2><i class="fas fa-check-circle text-success"></i> Enabled Sites</h2>
|
|
<span class="badge badge-success"><?= count($nginx['sites_enabled']) ?></span>
|
|
</div>
|
|
<div class="card-body">
|
|
<div class="list-group">
|
|
<?php foreach ($nginx['sites_enabled'] as $site): ?>
|
|
<div class="list-item list-item-action" onclick="editFile('/etc/nginx/sites-enabled/<?= htmlspecialchars($site) ?>')">
|
|
<i class="fas fa-file-code"></i>
|
|
<code><?= htmlspecialchars($site) ?></code>
|
|
<span class="list-item-actions">
|
|
<button class="btn btn-xs btn-warning" onclick="event.stopPropagation();toggleSite('<?= htmlspecialchars($site) ?>',false)" title="Disable site">
|
|
<i class="fas fa-pause"></i>
|
|
</button>
|
|
<button class="btn btn-xs btn-danger" onclick="event.stopPropagation();deleteSite('<?= htmlspecialchars($site) ?>')" title="Delete site">
|
|
<i class="fas fa-trash"></i>
|
|
</button>
|
|
</span>
|
|
</div>
|
|
<?php endforeach; ?>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
<?php endif; ?>
|
|
<?php if (!empty($nginx['sites_available'])): ?>
|
|
<div class="dashboard-card">
|
|
<div class="card-header">
|
|
<h2><i class="fas fa-folder-open"></i> Available Sites</h2>
|
|
<div style="display:flex;gap:0.5rem;align-items:center">
|
|
<span class="badge badge-info"><?= count($nginx['sites_available']) ?></span>
|
|
<button class="btn btn-sm btn-success" onclick="openNewSiteModal()">
|
|
<i class="fas fa-plus"></i> New Site
|
|
</button>
|
|
</div>
|
|
</div>
|
|
<div class="card-body">
|
|
<div class="list-group">
|
|
<?php foreach ($nginx['sites_available'] as $site): ?>
|
|
<?php $isEnabled = in_array($site, $nginx['sites_enabled'] ?? []); ?>
|
|
<div class="list-item list-item-action" onclick="editFile('/etc/nginx/sites-available/<?= htmlspecialchars($site) ?>')">
|
|
<i class="fas fa-file-code"></i>
|
|
<code><?= htmlspecialchars($site) ?></code>
|
|
<span class="list-item-actions">
|
|
<button class="btn btn-xs btn-success" onclick="event.stopPropagation();toggleSite('<?= htmlspecialchars($site) ?>',true)" title="Enable site">
|
|
<i class="fas fa-plus"></i>
|
|
</button>
|
|
<button class="btn btn-xs btn-danger" onclick="event.stopPropagation();deleteSite('<?= htmlspecialchars($site) ?>')" title="Delete site">
|
|
<i class="fas fa-trash"></i>
|
|
</button>
|
|
</span>
|
|
</div>
|
|
<?php endforeach; ?>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
<?php endif; ?>
|
|
</div>
|
|
|
|
<div class="card">
|
|
<div class="card-header">
|
|
<h2><i class="fas fa-file-alt"></i> Main Configuration</h2>
|
|
<div>
|
|
<button class="btn btn-sm btn-primary" onclick="editFile('/etc/nginx/nginx.conf')">
|
|
<i class="fas fa-edit"></i> Edit
|
|
</button>
|
|
</div>
|
|
</div>
|
|
<div class="card-body">
|
|
<div class="nginx-config-viewer">
|
|
<pre><code><?= htmlspecialchars($nginx['config'] ?? '# No configuration found') ?></code></pre>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
|
|
<?php if (!empty($nginx['config_test'])): ?>
|
|
<div class="card">
|
|
<div class="card-header">
|
|
<h2><i class="fas fa-clipboard-list"></i> Config Test Result</h2>
|
|
</div>
|
|
<div class="card-body">
|
|
<div class="nginx-config-viewer nginx-test-output">
|
|
<pre><code class="<?= $configValid ? 'text-success' : 'text-danger' ?>"><?= htmlspecialchars($nginx['config_test']) ?></code></pre>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
<?php endif; ?>
|
|
|
|
<div id="nginxEditorModal" class="modal editor-modal-overlay">
|
|
<div class="vscode-editor">
|
|
<div class="vscode-titlebar">
|
|
<span class="vscode-title-icon"><i class="far fa-file-code"></i></span>
|
|
<span class="vscode-title-path" id="editorFilePath">file</span>
|
|
<span class="vscode-title-dirty" id="editorDirtyIndicator" style="display:none">●</span>
|
|
<div class="vscode-title-actions">
|
|
<button class="vscode-btn" onclick="closeEditor()" title="Close (Escape)"><i class="fas fa-times"></i></button>
|
|
</div>
|
|
</div>
|
|
<div class="vscode-toolbar">
|
|
<button class="vscode-btn vscode-btn-primary" onclick="saveFile()" title="Save (Ctrl+S)">
|
|
<i class="fas fa-save"></i> Save
|
|
</button>
|
|
<div class="vscode-separator"></div>
|
|
<button class="vscode-btn" onclick="undoEditor()" title="Undo (Ctrl+Z)">
|
|
<i class="fas fa-undo"></i>
|
|
</button>
|
|
<button class="vscode-btn" onclick="redoEditor()" title="Redo (Ctrl+Y)">
|
|
<i class="fas fa-redo"></i>
|
|
</button>
|
|
<div class="vscode-separator"></div>
|
|
<button class="vscode-btn" onclick="saveFile()" title="Save & Test Config">
|
|
<i class="fas fa-vial"></i> Test
|
|
</button>
|
|
<span class="vscode-spacer"></span>
|
|
<span id="editorStatus" class="vscode-status-text">Ready</span>
|
|
</div>
|
|
<div class="vscode-body">
|
|
<div class="vscode-gutter" id="editorGutter"></div>
|
|
<textarea id="editorContent" class="vscode-textarea"
|
|
spellcheck="false" autocomplete="off" wrap="off"></textarea>
|
|
<pre class="vscode-highlight" id="editorHighlight" aria-hidden="true"><code id="editorHighlightCode"></code></pre>
|
|
</div>
|
|
<div class="vscode-statusbar">
|
|
<span class="vscode-status-item" id="editorCursorPos">Ln 1, Col 1</span>
|
|
<span class="vscode-status-item">UTF-8</span>
|
|
<span class="vscode-status-item" id="editorLang">Nginx Config</span>
|
|
<span class="vscode-status-item vscode-status-end" id="editorFileSize"></span>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
|
|
<div id="nginxNewSiteModal" class="modal">
|
|
<div class="modal-dialog" style="max-width:550px">
|
|
<div class="modal-header">
|
|
<h3><i class="fas fa-plus-circle text-success"></i> New Nginx Site</h3>
|
|
<button class="modal-close" onclick="closeNewSiteModal()">×</button>
|
|
</div>
|
|
<form id="newSiteForm" onsubmit="createSite(event)">
|
|
<div class="modal-body">
|
|
<div class="form-group">
|
|
<label for="siteName">Site name (domain)</label>
|
|
<input type="text" id="siteName" name="site_name" class="form-input"
|
|
placeholder="e.g. example.com" required>
|
|
</div>
|
|
<div class="form-group">
|
|
<label for="siteRoot">Root directory</label>
|
|
<input type="text" id="siteRoot" name="site_root" class="form-input"
|
|
placeholder="/var/www/example.com" required>
|
|
</div>
|
|
<div class="form-row">
|
|
<div class="form-group">
|
|
<label for="siteServerName">Server name</label>
|
|
<input type="text" id="siteServerName" name="server_name" class="form-input"
|
|
placeholder="example.com www.example.com">
|
|
</div>
|
|
<div class="form-group">
|
|
<label for="siteIndex">Index file</label>
|
|
<input type="text" id="siteIndex" name="index" class="form-input"
|
|
value="index.html index.htm index.php" placeholder="index.html">
|
|
</div>
|
|
</div>
|
|
<div class="form-group" style="margin-top:0.5rem">
|
|
<label class="checkbox-label">
|
|
<input type="checkbox" id="sitePhp" name="enable_php" value="1" checked>
|
|
<span>Enable PHP-FPM support</span>
|
|
</label>
|
|
</div>
|
|
<div class="form-group">
|
|
<label class="checkbox-label">
|
|
<input type="checkbox" id="siteEnable" name="enable_site" value="1" checked>
|
|
<span>Enable site after creation</span>
|
|
</label>
|
|
</div>
|
|
</div>
|
|
<div class="modal-footer">
|
|
<button type="button" class="btn btn-secondary" onclick="closeNewSiteModal()">Cancel</button>
|
|
<button type="submit" class="btn btn-success" id="createSiteBtn">
|
|
<i class="fas fa-plus"></i> Create Site
|
|
</button>
|
|
</div>
|
|
</form>
|
|
</div>
|
|
</div>
|
|
|
|
<div id="nginxResultModal" class="modal">
|
|
<div class="modal-dialog" style="max-width:600px">
|
|
<div class="modal-header">
|
|
<h3 id="nginxResultTitle">Result</h3>
|
|
<button class="modal-close" onclick="closeNginxModal()">×</button>
|
|
</div>
|
|
<div class="modal-body">
|
|
<p id="nginxResultMessage"></p>
|
|
<div id="nginxResultOutput" class="nginx-config-viewer nginx-test-output" style="display:none;margin-top:0.75rem">
|
|
<pre><code id="nginxResultOutputText"></code></pre>
|
|
</div>
|
|
</div>
|
|
<div class="modal-footer">
|
|
<button class="btn btn-secondary" onclick="closeNginxModal()">Close</button>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
|
|
<script>
|
|
const serverId = <?= json_encode($server['id']) ?>;
|
|
</script>
|
|
<script src="/assets/js/nginx-manager.js"></script>
|
|
<?php endif; ?>
|