<?php
// Force login + disable caching
session_start();
require_once __DIR__ . '/../core/db_config.php';
// Anti-cache headers
header('Cache-Control: no-store, no-cache, must-revalidate, max-age=0');
header('Cache-Control: post-check=0, pre-check=0', false);
header('Pragma: no-cache');
header('Expires: 0');
// Redirect if not logged in
if (empty($_SESSION['username'])) {
$redirect = urlencode($_SERVER['REQUEST_URI'] ?? '/');
header("Location: /core/auth/login.php?redirect={$redirect}");
exit;
}
// Cache-busting helper
function asset($path) {
$isAbsolute = strlen($path) && $path[0] === '/';
$abs = $isAbsolute ? rtrim($_SERVER['DOCUMENT_ROOT'], '/') . $path : __DIR__ . '/' . $path;
$v = is_file($abs) ? filemtime($abs) : time();
return $path . '?v=' . $v;
}
?>
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8" />
<meta name="viewport" content="width=device-width, initial-scale=1" />
<title>SFTP Manager</title>
<!-- Shared overlay CSS -->
<link rel="stylesheet" href="<?= asset('/core/css/overlay.css') ?>">
<style>
html, body { height: 100%; margin: 0; overscroll-behavior-y: contain; }
body {
background: #0b0f14;
color: #e6edf3;
font-family: system-ui, -apple-system, Segoe UI, Roboto, Inter, "Helvetica Neue", Arial;
}
.body-lock { overflow: hidden !important; touch-action: none; }
.topbar {
position: sticky; top: 0; z-index: 5;
background: linear-gradient(180deg, rgba(11,15,20,.95), rgba(11,15,20,.85));
border-bottom: 1px solid #1e2633;
backdrop-filter: blur(6px);
}
.topbar-inner {
max-width: 1000px;
margin: 0 auto;
padding: .75rem;
display: flex;
align-items: center;
gap: .75rem;
}
#buttonRow {
flex: 1 1 auto;
min-width: 0;
display: flex;
gap: .75rem;
align-items: center;
overflow-x: auto;
overflow-y: hidden;
scrollbar-width: thin;
-webkit-overflow-scrolling: touch;
}
#menuContainer { flex: 0 0 auto; margin-left: .25rem; position: relative; }
.chip {
flex: 0 0 auto;
border: 1px solid #2a3648;
background: #1a2332;
color: #e6edf3;
padding: .55rem .9rem;
border-radius: 999px;
font-weight: 600;
cursor: pointer;
transition: background .15s ease;
}
.chip:hover { background: #263244; }
.container { max-width: 1000px; margin: 1.25rem auto; padding: 0 .75rem; }
.lead { color: #9aa4b2; }
.menu-trigger { width: 38px; text-align: center; }
.menu-list {
display: none;
position: absolute;
right: 0; top: calc(100% + 6px);
background: #1a2332;
border: 1px solid #2a3648;
border-radius: 10px;
min-width: 180px;
padding: .25rem 0;
z-index: 9999;
box-shadow: 0 10px 30px rgba(0,0,0,.3);
}
.menu-list.open { display: block; }
.menu-item {
display: block;
width: 100%;
text-align: left;
background: none;
border: none;
color: #e6edf3;
padding: .6rem 1rem;
cursor: pointer;
font: inherit;
}
.menu-item:hover { background: #263244; }
</style>
</head>
<body>
<?php include __DIR__ . '/../core/auth/header.php'; ?>
<header class="topbar" aria-label="Top navigation">
<div class="topbar-inner">
<div id="buttonRow" role="tablist" aria-label="App sections"></div>
<div id="menuContainer" aria-label="More actions"></div>
</div>
</header>
<main class="container">
<h1>SFTP Manager</h1>
<p class="lead">Manage your SFTP connections and files securely.</p>
</main>
<script>
// Global component registry
window.AppItems = [];
window.AppMenu = [];
</script>
<!-- Core system -->
<script src="/core/js/core.js" defer></script>
<script type="module">
import { FileDrivers, SFTPDriver } from '/core/js/drivers_files/sftp.js';
// Make them global so AppStorage can see them
window.FileDrivers = FileDrivers;
await AppStorage.init('sftp');
console.log('[Boot] AppStorage initialized with SFTP');
</script>
<script src="<?= asset('/core/js/overlay.js') ?>" defer></script>
<script src="<?= asset('connectionmanager.js') ?>" defer></script>
<script src="<?= asset('filemanager.js') ?>" defer></script
<script src="<?= asset('menu.js') ?>" defer></script>
<!-- Menu / overlay rendering logic (already in your page) -->
<!-- Render chips + menu after components load -->
<script defer>
document.addEventListener('DOMContentLoaded', () => {
// Render chips from AppItems
const row = document.getElementById('buttonRow');
row.innerHTML = '';
(window.AppItems || []).forEach((item, i) => {
const btn = document.createElement('button');
btn.className = 'chip';
btn.textContent = item.title || `Item ${i+1}`;
btn.onclick = () => window.AppOverlay && AppOverlay.open(window.AppItems, i, btn);
row.appendChild(btn);
});
// Render menu from AppMenu
const menuContainer = document.getElementById('menuContainer');
menuContainer.innerHTML = '';
const menuItems = window.AppMenu || [];
if (menuItems.length > 0) {
const trigger = document.createElement('button');
trigger.className = 'chip menu-trigger';
trigger.textContent = '⋮';
menuContainer.appendChild(trigger);
const dropdown = document.createElement('div');
dropdown.className = 'menu-list';
menuContainer.appendChild(dropdown);
menuItems.forEach((m, idx) => {
const item = document.createElement('button');
item.className = 'menu-item';
item.textContent = m.label || `Action ${idx+1}`;
item.onclick = () => { dropdown.classList.remove('open'); m.action && m.action(); };
dropdown.appendChild(item);
});
trigger.addEventListener('click', (e)=>{
e.stopPropagation();
dropdown.classList.toggle('open');
});
document.addEventListener('click', (e)=>{
if (!menuContainer.contains(e.target)) dropdown.classList.remove('open');
});
}
});
</script>
</body>
</html>