fix install script5

This commit is contained in:
Edmund Tan 2025-07-22 02:13:36 +08:00
parent 181884b931
commit cb8a83eb34
5 changed files with 525 additions and 8 deletions

311
UI/dashboard.php Normal file
View File

@ -0,0 +1,311 @@
<?php
/**
* Main dashboard for ZitiNexus Router Enrollment UI
* This is a copy for direct access when document root is set to main UI directory
*/
require_once 'includes/auth.php';
require_once 'includes/enrollment.php';
// Require authentication
AuthManager::requireAuth();
// Get current user
$currentUser = AuthManager::getCurrentUser();
// Initialize enrollment manager
$enrollmentManager = new EnrollmentManager();
// Handle AJAX requests
if (isset($_SERVER['HTTP_X_REQUESTED_WITH']) && strtolower($_SERVER['HTTP_X_REQUESTED_WITH']) === 'xmlhttprequest') {
header('Content-Type: application/json');
if (isset($_GET['action']) && $_GET['action'] === 'get_status') {
// Get system status
$status = $enrollmentManager->getSystemStatus();
echo json_encode($status);
exit;
}
if ($_SERVER['REQUEST_METHOD'] === 'POST' && isset($_POST['action']) && $_POST['action'] === 'enroll') {
// Handle enrollment request
AuthManager::requireCSRF();
$hashKey = sanitizeInput($_POST['hashKey'] ?? '');
$apiEndpoint = sanitizeInput($_POST['apiEndpoint'] ?? DEFAULT_API_ENDPOINT);
// Validate inputs
if (!ApiClient::validateHashKey($hashKey)) {
echo json_encode(['success' => false, 'error' => 'Invalid hash key format']);
exit;
}
if (!ApiClient::validateApiEndpoint($apiEndpoint)) {
echo json_encode(['success' => false, 'error' => 'Invalid API endpoint format']);
exit;
}
// Start enrollment
$result = $enrollmentManager->enrollRouter($hashKey, $apiEndpoint);
echo json_encode($result);
exit;
}
}
// Get system status for initial page load
$systemStatus = $enrollmentManager->getSystemStatus();
?>
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title><?php echo APP_NAME; ?> - Dashboard</title>
<link rel="stylesheet" href="assets/css/style.css">
<link rel="icon" type="image/x-icon" href="assets/images/favicon.ico">
</head>
<body>
<div class="dashboard-container">
<!-- Header -->
<header class="header">
<div class="header-content">
<h1 class="header-title"><?php echo APP_NAME; ?></h1>
<div class="header-actions">
<div class="user-info">
Welcome, <strong><?php echo htmlspecialchars($currentUser['username']); ?></strong>
<span class="text-xs">
| Logged in: <?php echo date('M j, Y g:i A', $currentUser['login_time']); ?>
</span>
</div>
<button id="refreshStatus" class="btn btn-secondary" title="Refresh System Status">
🔄 Refresh
</button>
<a href="index.php?action=logout" class="btn btn-secondary">
Logout
</a>
</div>
</div>
</header>
<!-- Main Content -->
<main class="main-content">
<!-- System Status Card -->
<div class="card">
<div class="card-header">
<h2 class="card-title">System Status</h2>
</div>
<div class="card-body">
<div class="status-grid">
<div class="status-item">
<div id="zitiStatusIcon" class="status-icon <?php echo $systemStatus['ziti_status'] === 'installed' ? 'success' : 'error'; ?>">
<?php echo $systemStatus['ziti_status'] === 'installed' ? '✓' : '✗'; ?>
</div>
<div class="status-content">
<h4>Ziti CLI Status</h4>
<p id="zitiStatus">
<?php
if ($systemStatus['ziti_status'] === 'installed') {
echo 'Installed (' . htmlspecialchars($systemStatus['ziti_version']) . ')';
} else {
echo 'Not Installed';
}
?>
</p>
</div>
</div>
<div class="status-item">
<div id="serviceStatusIcon" class="status-icon <?php echo $systemStatus['service_active'] ? 'success' : 'error'; ?>">
<?php echo $systemStatus['service_active'] ? '▶' : '⏹'; ?>
</div>
<div class="status-content">
<h4>Router Service</h4>
<p id="serviceStatus">
<?php echo $systemStatus['service_active'] ? 'Running' : 'Stopped'; ?>
</p>
</div>
</div>
<div class="status-item">
<div id="configStatusIcon" class="status-icon <?php
if ($systemStatus['config_exists'] && $systemStatus['certificates_exist']) {
echo 'success';
} elseif ($systemStatus['config_exists']) {
echo 'warning';
} else {
echo 'error';
}
?>">
<?php
if ($systemStatus['config_exists'] && $systemStatus['certificates_exist']) {
echo '⚙';
} elseif ($systemStatus['config_exists']) {
echo '⚠';
} else {
echo '✗';
}
?>
</div>
<div class="status-content">
<h4>Configuration</h4>
<p id="configStatus">
<?php
if ($systemStatus['config_exists'] && $systemStatus['certificates_exist']) {
echo 'Configured';
} elseif ($systemStatus['config_exists']) {
echo 'Partial';
} else {
echo 'Not Configured';
}
?>
</p>
</div>
</div>
<div class="status-item">
<div class="status-icon success">
🖥
</div>
<div class="status-content">
<h4>Hostname</h4>
<p id="hostname"><?php echo htmlspecialchars($systemStatus['hostname']); ?></p>
</div>
</div>
</div>
</div>
</div>
<!-- Enrollment Form Card -->
<div class="card">
<div class="card-header">
<h2 class="card-title">Router Enrollment</h2>
</div>
<div class="card-body">
<form id="enrollmentForm" class="enrollment-form">
<input type="hidden" name="csrf_token" value="<?php echo generateCSRFToken(); ?>">
<div class="form-row">
<div class="form-group">
<label for="apiEndpoint" class="form-label">API Endpoint</label>
<input
type="url"
id="apiEndpoint"
name="apiEndpoint"
class="form-input"
value="<?php echo DEFAULT_API_ENDPOINT; ?>"
placeholder="https://backend.zitinexus.com"
required
>
<small class="text-sm text-secondary">ZitiNexus Portal API endpoint</small>
</div>
<div class="form-group">
<label for="hashKey" class="form-label">Hash Key</label>
<input
type="text"
id="hashKey"
name="hashKey"
class="form-input"
placeholder="32-character hexadecimal hash key"
maxlength="32"
pattern="[a-fA-F0-9]{32}"
required
>
<small class="text-sm text-secondary">Router enrollment hash key from ZitiNexus Portal</small>
</div>
</div>
<div class="form-group-full">
<button type="submit" id="enrollBtn" class="btn btn-primary">
Start Enrollment
</button>
<button type="button" id="clearLogs" class="btn btn-secondary" style="margin-left: 1rem;">
Clear Logs
</button>
</div>
</form>
<!-- Progress Container -->
<div id="progressContainer" class="progress-container">
<div class="progress-bar">
<div id="progressFill" class="progress-fill"></div>
</div>
<div class="progress-steps">
<div class="progress-step">Initialize</div>
<div class="progress-step">Requirements</div>
<div class="progress-step">Install</div>
<div class="progress-step">Directories</div>
<div class="progress-step">Register</div>
<div class="progress-step">Configure</div>
<div class="progress-step">Enroll</div>
<div class="progress-step">Service</div>
<div class="progress-step">Start</div>
<div class="progress-step">Report</div>
<div class="progress-step">Complete</div>
</div>
<div id="logContainer" class="log-container">
<!-- Log entries will be added here dynamically -->
</div>
</div>
</div>
</div>
<!-- Information Card -->
<div class="card">
<div class="card-header">
<h2 class="card-title">Information</h2>
</div>
<div class="card-body">
<div style="display: grid; grid-template-columns: repeat(auto-fit, minmax(300px, 1fr)); gap: 2rem;">
<div>
<h4 class="font-medium mb-2">How to Use</h4>
<ol class="text-sm text-secondary" style="padding-left: 1.5rem;">
<li>Obtain a hash key from the ZitiNexus Portal by creating a router enrollment</li>
<li>Enter the API endpoint (default is pre-filled)</li>
<li>Paste the 32-character hash key</li>
<li>Click "Start Enrollment" to begin the process</li>
<li>Monitor the progress and logs for status updates</li>
</ol>
</div>
<div>
<h4 class="font-medium mb-2">System Requirements</h4>
<ul class="text-sm text-secondary" style="padding-left: 1.5rem;">
<li>Ubuntu 22.04 or 24.04 LTS</li>
<li>Root/sudo access required</li>
<li>Internet connectivity</li>
<li>systemctl available</li>
<li>curl and jq packages (auto-installed)</li>
</ul>
</div>
<div>
<h4 class="font-medium mb-2">File Locations</h4>
<ul class="text-sm text-secondary" style="padding-left: 1.5rem;">
<li><code>/etc/zitirouter/</code> - Configuration directory</li>
<li><code>/etc/zitirouter/certs/</code> - Certificates</li>
<li><code>/var/log/ziti-router-enrollment.log</code> - Enrollment log</li>
<li><code>/etc/systemd/system/ziti-router.service</code> - Service file</li>
</ul>
</div>
<div>
<h4 class="font-medium mb-2">Service Management</h4>
<ul class="text-sm text-secondary" style="padding-left: 1.5rem;">
<li><code>systemctl status ziti-router</code> - Check status</li>
<li><code>systemctl start ziti-router</code> - Start service</li>
<li><code>systemctl stop ziti-router</code> - Stop service</li>
<li><code>journalctl -u ziti-router -f</code> - View logs</li>
</ul>
</div>
</div>
</div>
</div>
</main>
</div>
<script src="assets/js/app.js"></script>
</body>
</html>

53
UI/debug-paths.php Normal file
View File

@ -0,0 +1,53 @@
<?php
/**
* Debug script to check asset paths
*/
require_once 'includes/config.php';
echo "<h1>Asset Path Debug</h1>";
echo "<p><strong>Current Script:</strong> " . $_SERVER['SCRIPT_FILENAME'] . "</p>";
echo "<p><strong>Document Root:</strong> " . $_SERVER['DOCUMENT_ROOT'] . "</p>";
echo "<p><strong>Script Name:</strong> " . $_SERVER['SCRIPT_NAME'] . "</p>";
echo "<p><strong>Request URI:</strong> " . $_SERVER['REQUEST_URI'] . "</p>";
echo "<h2>Asset Paths:</h2>";
echo "<p><strong>CSS Path:</strong> " . getAssetPath('css/style.css') . "</p>";
echo "<p><strong>JS Path:</strong> " . getAssetPath('js/app.js') . "</p>";
echo "<h2>File Existence Check:</h2>";
$cssPath = getAssetPath('css/style.css');
$jsPath = getAssetPath('js/app.js');
echo "<p><strong>CSS File Exists:</strong> " . (file_exists($cssPath) ? 'YES' : 'NO') . " ($cssPath)</p>";
echo "<p><strong>JS File Exists:</strong> " . (file_exists($jsPath) ? 'YES' : 'NO') . " ($jsPath)</p>";
echo "<h2>Directory Structure:</h2>";
echo "<pre>";
echo "Current directory: " . getcwd() . "\n";
echo "Assets directory exists: " . (is_dir('assets') ? 'YES' : 'NO') . "\n";
echo "Public directory exists: " . (is_dir('public') ? 'YES' : 'NO') . "\n";
if (is_dir('assets')) {
echo "\nAssets directory contents:\n";
$files = scandir('assets');
foreach ($files as $file) {
if ($file !== '.' && $file !== '..') {
echo " - $file\n";
if (is_dir("assets/$file")) {
$subfiles = scandir("assets/$file");
foreach ($subfiles as $subfile) {
if ($subfile !== '.' && $subfile !== '..') {
echo " - $subfile\n";
}
}
}
}
}
}
echo "</pre>";
echo "<h2>Test Direct Paths:</h2>";
echo "<p><strong>Direct CSS:</strong> assets/css/style.css - " . (file_exists('assets/css/style.css') ? 'EXISTS' : 'NOT FOUND') . "</p>";
echo "<p><strong>Direct JS:</strong> assets/js/app.js - " . (file_exists('assets/js/app.js') ? 'EXISTS' : 'NOT FOUND') . "</p>";
?>

View File

@ -149,16 +149,24 @@ function executeCommand($command, &$output = null, &$returnCode = null) {
* Get the correct asset path based on current directory structure * Get the correct asset path based on current directory structure
*/ */
function getAssetPath($asset) { function getAssetPath($asset) {
// Determine if we're in the public directory or main directory // Clean the asset path
$currentDir = dirname($_SERVER['SCRIPT_FILENAME']); $asset = ltrim($asset, '/');
$publicDir = realpath(__DIR__ . '/../public');
if ($currentDir === $publicDir) { // Get the current script's directory and the request URI
// We're in the public directory, use relative paths $scriptDir = dirname($_SERVER['SCRIPT_FILENAME']);
return '../assets/' . ltrim($asset, '/'); $requestUri = $_SERVER['REQUEST_URI'];
$scriptName = $_SERVER['SCRIPT_NAME'];
// Check if we're accessing files from the public directory
// but the document root is set to the main UI directory
if (strpos($scriptName, '/public/') !== false ||
strpos($requestUri, '/public/') !== false ||
basename($scriptDir) === 'public') {
// We're in the public directory context, use relative paths
return '../assets/' . $asset;
} else { } else {
// We're in the main directory, use direct paths // We're in the main directory context, use direct paths
return 'assets/' . ltrim($asset, '/'); return 'assets/' . $asset;
} }
} }

136
UI/index.php Normal file
View File

@ -0,0 +1,136 @@
<?php
/**
* Login page for ZitiNexus Router Enrollment UI
* This is a copy for direct access when document root is set to main UI directory
*/
require_once 'includes/auth.php';
// Redirect if already authenticated
if (isAuthenticated() && isSessionValid()) {
header('Location: dashboard.php');
exit;
}
// Handle messages
$message = '';
$messageType = '';
if (isset($_GET['error'])) {
switch ($_GET['error']) {
case 'session_expired':
$message = 'Your session has expired. Please log in again.';
$messageType = 'warning';
break;
default:
$message = 'An error occurred. Please try again.';
$messageType = 'error';
break;
}
}
if (isset($_GET['message'])) {
switch ($_GET['message']) {
case 'logged_out':
$message = 'You have been logged out successfully.';
$messageType = 'info';
break;
}
}
if (isset($loginError)) {
$message = $loginError;
$messageType = 'error';
}
?>
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title><?php echo APP_NAME; ?> - Login</title>
<link rel="stylesheet" href="assets/css/style.css">
<link rel="icon" type="image/x-icon" href="assets/images/favicon.ico">
</head>
<body>
<div class="login-container">
<div class="login-card">
<div class="login-header">
<h1><?php echo APP_NAME; ?></h1>
<p>Router Enrollment Management Interface</p>
<p class="text-sm text-secondary">Version <?php echo APP_VERSION; ?></p>
</div>
<?php if ($message): ?>
<div class="alert alert-<?php echo $messageType; ?>">
<?php echo htmlspecialchars($message); ?>
</div>
<?php endif; ?>
<form method="POST" action="index.php">
<input type="hidden" name="action" value="login">
<div class="form-group">
<label for="username" class="form-label">Username</label>
<input
type="text"
id="username"
name="username"
class="form-input"
required
autocomplete="username"
value="<?php echo isset($_POST['username']) ? htmlspecialchars($_POST['username']) : ''; ?>"
>
</div>
<div class="form-group">
<label for="password" class="form-label">Password</label>
<input
type="password"
id="password"
name="password"
class="form-input"
required
autocomplete="current-password"
>
</div>
<button type="submit" class="btn btn-primary btn-full">
Sign In
</button>
</form>
<div style="margin-top: 2rem; padding-top: 1rem; border-top: 1px solid var(--border-color); text-align: center;">
<p class="text-sm text-secondary">
Default credentials: <strong>admin</strong> / <strong>admin123</strong>
</p>
<p class="text-xs text-secondary" style="margin-top: 0.5rem;">
Please change the default password in production
</p>
</div>
</div>
</div>
<script>
// Auto-focus on username field
document.addEventListener('DOMContentLoaded', function() {
const usernameField = document.getElementById('username');
if (usernameField && !usernameField.value) {
usernameField.focus();
} else {
const passwordField = document.getElementById('password');
if (passwordField) {
passwordField.focus();
}
}
});
// Handle form submission
document.querySelector('form').addEventListener('submit', function(e) {
const submitBtn = this.querySelector('button[type="submit"]');
submitBtn.disabled = true;
submitBtn.innerHTML = '<span class="spinner"></span>Signing In...';
});
</script>
</body>
</html>

View File

@ -184,6 +184,15 @@ deploy_ui() {
# Copy main UI files # Copy main UI files
cp -r public includes assets "$WEB_DIR/" || error_exit "Failed to copy UI files" cp -r public includes assets "$WEB_DIR/" || error_exit "Failed to copy UI files"
# Copy root-level PHP files for direct access (when document root is main directory)
if [[ -f "index.php" ]]; then
cp index.php "$WEB_DIR/" || log "WARNING" "Failed to copy root index.php"
fi
if [[ -f "dashboard.php" ]]; then
cp dashboard.php "$WEB_DIR/" || log "WARNING" "Failed to copy root dashboard.php"
fi
# Copy optional files if they exist # Copy optional files if they exist
if [[ -f "README.md" ]]; then if [[ -f "README.md" ]]; then
cp README.md "$WEB_DIR/" || log "WARNING" "Failed to copy README.md" cp README.md "$WEB_DIR/" || log "WARNING" "Failed to copy README.md"