Fix user portal blank after login

- initUser() now hides auth-check and shows main-layout on success
- Remove conflicting inline script from user/index.php that referenced
  #app (non-existent) instead of #main-layout, causing null JS error
  that prevented the panel from ever rendering after successful auth
- Wire logout button in boot sequence (was only in the removed inline script)

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
This commit is contained in:
2026-06-08 22:43:17 +00:00
parent 77f88ca5bf
commit eccbfbfeea
2 changed files with 8 additions and 115 deletions
+7
View File
@@ -14,6 +14,8 @@ async function initUser() {
}
_user = res.data;
document.getElementById('user-name').textContent = _user.username || 'User';
document.getElementById('auth-check').style.display = 'none';
document.getElementById('main-layout').style.display = '';
return true;
}
@@ -1008,6 +1010,11 @@ window.uDockerLaunchApp = async (preselect) => {
document.addEventListener('DOMContentLoaded', async () => {
const ok = await initUser();
if (!ok) return;
document.getElementById('logout-btn')?.addEventListener('click', async e => {
e.preventDefault();
await Nova.api('auth', 'logout', { method: 'POST' });
location.href = '/';
});
renderNav();
window.userNav('dashboard');
});
+1 -115
View File
@@ -209,120 +209,6 @@ window.NOVACPX_BRANDING = <?= json_encode([
</script>
<script src="/assets/js/nova.js<?= $_v('/assets/js/nova.js') ?>"></script>
<script src="/assets/js/user.js<?= $_v('/assets/js/user.js') ?>"></script>
<script>
(async () => {
// Legacy inline login form fallback (user.js handles auth-check div)
const loginForm = document.getElementById('login-form');
if (loginForm) {
loginForm.addEventListener('submit', async e => {
e.preventDefault();
const btn = document.getElementById('l-btn');
const err = document.getElementById('login-err');
btn.disabled = true; btn.textContent = 'Signing in…'; err.style.display = 'none';
const res = await Nova.api('auth', 'login', {
method: 'POST',
body: { username: document.getElementById('l-user').value, password: document.getElementById('l-pass').value }
});
if (res?.success) {
location.reload();
} else {
err.textContent = res?.message || 'Invalid credentials';
err.style.display = '';
btn.disabled = false; btn.textContent = 'Sign In';
}
});
}
const me = await Nova.api('auth', 'me');
if (!me?.success) { return; } // Login form already visible
document.getElementById('auth-check').style.display = 'none';
document.getElementById('app').style.display = '';
document.getElementById('user-name').textContent = me.data.username;
document.getElementById('user-avatar').textContent = me.data.username[0].toUpperCase();
document.getElementById('logout-btn').addEventListener('click', async e => {
e.preventDefault();
await Nova.api('auth', 'logout', { method: 'POST' });
location.href = '/';
});
function ring(pct, color, r = 28) {
const circ = 2 * Math.PI * r;
const offset = circ * (1 - pct / 100);
return `<svg class="ring" width="${r*2+8}" height="${r*2+8}" viewBox="0 0 ${r*2+8} ${r*2+8}">
<circle cx="${r+4}" cy="${r+4}" r="${r}" fill="none" stroke="var(--border)" stroke-width="5"/>
<circle cx="${r+4}" cy="${r+4}" r="${r}" fill="none" stroke="${color}" stroke-width="5"
stroke-dasharray="${circ}" stroke-dashoffset="${offset}" stroke-linecap="round"/>
</svg>`;
}
const homePage = () => `
<div class="usage-rings">
<div>
<h2 style="font-size:1.1rem;font-weight:700">My Hosting</h2>
<p class="text-muted text-sm">example.com &nbsp;·&nbsp; Active</p>
</div>
<div style="margin-left:auto;display:flex;gap:2rem">
<div class="ring-item">${ring(45,'#6366f1')}<div class="ring-label">Disk</div><div class="ring-val">2.3 GB / 5 GB</div></div>
<div class="ring-item">${ring(22,'#0ea5e9')}<div class="ring-label">Bandwidth</div><div class="ring-val">2.2 GB / 10 GB</div></div>
<div class="ring-item">${ring(60,'#10b981')}<div class="ring-label">Email</div><div class="ring-val">6 / 10</div></div>
<div class="ring-item">${ring(30,'#f59e0b')}<div class="ring-label">Databases</div><div class="ring-val">3 / 10</div></div>
</div>
</div>
<div class="section-header"><h2>Quick Access</h2></div>
<div class="feature-grid">
${[
{ page:'files', icon:'folder', color:'fi-yellow', name:'File Manager', desc:'Upload, edit, and manage your website files and directories.' },
{ page:'email-accounts',icon:'mail', color:'fi-sky', name:'Email Accounts', desc:'Create and manage mailboxes for your domains.' },
{ page:'mysql', icon:'db', color:'fi-green', name:'MySQL Databases', desc:'Create databases and users for your PHP applications.' },
{ page:'postgres', icon:'db', color:'fi-teal', name:'PostgreSQL', desc:'Manage PostgreSQL databases and connections.' },
{ page:'domains', icon:'globe', color:'fi-purple', name:'Domains', desc:'Add subdomains, addon domains, and domain aliases.' },
{ page:'dns', icon:'dns', color:'fi-orange', name:'DNS Editor', desc:'Manage A, CNAME, MX, TXT, and SRV records.' },
{ page:'ssl', icon:'lock', color:'fi-green', name:'SSL / TLS', desc:'Issue free Let\'s Encrypt certificates for your domains.' },
{ page:'ftp', icon:'ftp', color:'fi-pink', name:'FTP Accounts', desc:'Create FTP users with directory access controls.' },
{ page:'php-config', icon:'code', color:'fi-purple', name:'PHP Config', desc:'Switch PHP version and configure php.ini settings per domain.' },
{ page:'cron', icon:'clock', color:'fi-yellow', name:'Cron Jobs', desc:'Schedule automated tasks on any interval.' },
{ page:'forwarders', icon:'forward', color:'fi-sky', name:'Email Forwarders', desc:'Forward emails from one address to another.' },
{ page:'backups', icon:'backup', color:'fi-red', name:'Backups', desc:'Create full or partial backups and restore files.' },
].map(f => `
<div class="feature-card" onclick="loadUserPage('${f.page}')">
<div class="feature-icon ${f.color}">
<svg viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2">
${svgPath(f.icon)}
</svg>
</div>
<div class="feature-info">
<div class="feature-name">${f.name}</div>
<div class="feature-desc">${f.desc}</div>
</div>
</div>`).join('')}
</div>`;
function svgPath(icon) {
const p = {
folder:'<path d="M22 19a2 2 0 0 1-2 2H4a2 2 0 0 1-2-2V5a2 2 0 0 1 2-2h5l2 3h9a2 2 0 0 1 2 2z"/>',
mail:'<path d="M4 4h16c1.1 0 2 .9 2 2v12c0 1.1-.9 2-2 2H4c-1.1 0-2-.9-2-2V6c0-1.1.9-2 2-2z"/><polyline points="22,6 12,13 2,6"/>',
db:'<ellipse cx="12" cy="5" rx="9" ry="3"/><path d="M21 12c0 1.66-4 3-9 3s-9-1.34-9-3"/><path d="M3 5v14c0 1.66 4 3 9 3s9-1.34 9-3V5"/>',
globe:'<circle cx="12" cy="12" r="10"/><line x1="2" y1="12" x2="22" y2="12"/><path d="M12 2a15.3 15.3 0 0 1 4 10 15.3 15.3 0 0 1-4 10 15.3 15.3 0 0 1-4-10 15.3 15.3 0 0 1 4-10z"/>',
dns:'<line x1="16.5" y1="9.4" x2="7.5" y2="4.21"/><path d="M21 16V8a2 2 0 0 0-1-1.73l-7-4a2 2 0 0 0-2 0l-7 4A2 2 0 0 0 3 8v8a2 2 0 0 0 1 1.73l7 4a2 2 0 0 0 2 0l7-4A2 2 0 0 0 21 16z"/>',
lock:'<path d="M12 22s8-4 8-10V5l-8-3-8 3v7c0 6 8 10 8 10z"/>',
ftp:'<polyline points="4 17 10 11 4 5"/><line x1="12" y1="19" x2="20" y2="19"/>',
code:'<polyline points="16 18 22 12 16 6"/><polyline points="8 6 2 12 8 18"/>',
clock:'<circle cx="12" cy="12" r="10"/><polyline points="12 6 12 12 16 14"/>',
forward:'<polyline points="17 1 21 5 17 9"/><path d="M3 11V9a4 4 0 0 1 4-4h14"/>',
backup:'<path d="M21 15v4a2 2 0 0 1-2 2H5a2 2 0 0 1-2-2v-4"/><polyline points="17 8 12 3 7 8"/><line x1="12" y1="3" x2="12" y2="15"/>',
};
return p[icon] || '';
}
const pages = { home: homePage };
Nova.initNav(pages);
document.getElementById('page-content').innerHTML = homePage();
window.loadUserPage = (page) => Nova.loadPage(page, pages);
})();
</script>
<!-- user.js boots via DOMContentLoaded and handles all auth/init -->
</body>
</html>