Files
2026-09-13 20:07:03 +01:00

322 lines
13 KiB
HTML
Raw Permalink Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8" />
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
<title>PST Archive · Admin Panel</title>
<link rel="preconnect" href="https://fonts.googleapis.com">
<link rel="preconnect" href="https://fonts.gstatic.com" crossorigin>
<link href="https://fonts.googleapis.com/css2?family=Fraunces:ital,opsz,wght@0,9..144,300;0,9..144,500;0,9..144,700;1,9..144,400&family=JetBrains+Mono:wght@400;500;700&display=swap" rel="stylesheet">
<link rel="stylesheet" href="admin.css" />
<script>
(function() {
try {
var t = localStorage.getItem("pst-theme");
if (t !== "light" && t !== "dark") {
t = (window.matchMedia && window.matchMedia("(prefers-color-scheme: dark)").matches) ? "dark" : "light";
}
document.documentElement.dataset.theme = t;
} catch (e) {
document.documentElement.dataset.theme = "light";
}
})();
</script>
</head>
<body>
<div class="grain"></div>
<!-- Floating theme toggle (visible on login/setup screens) -->
<button id="themeToggleFloat" class="theme-toggle theme-toggle-float" title="Toggle theme" aria-label="Toggle theme">
<svg class="icon-sun" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="1.7" stroke-linecap="round" stroke-linejoin="round"><circle cx="12" cy="12" r="4.5"/><line x1="12" y1="2" x2="12" y2="4.5"/><line x1="12" y1="19.5" x2="12" y2="22"/><line x1="2" y1="12" x2="4.5" y2="12"/><line x1="19.5" y1="12" x2="22" y2="12"/><line x1="4.9" y1="4.9" x2="6.7" y2="6.7"/><line x1="17.3" y1="17.3" x2="19.1" y2="19.1"/><line x1="4.9" y1="19.1" x2="6.7" y2="17.3"/><line x1="17.3" y1="6.7" x2="19.1" y2="4.9"/></svg>
<svg class="icon-moon" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="1.7" stroke-linecap="round" stroke-linejoin="round"><path d="M20 14.5A9 9 0 019.5 4a7.5 7.5 0 1010.5 10.5z"/></svg>
</button>
<!-- FIRST-RUN NOTICE -->
<section id="setupNotice" class="auth-screen hidden">
<div class="auth-card">
<div class="auth-brand">
<span class="admin-badge">ADMIN</span>
<h1>Setup Required</h1>
<p class="auth-tagline">No administrator exists yet</p>
</div>
<div class="auth-form">
<p class="auth-intro">Please complete first-run setup in the main application before signing in here.</p>
<a id="gotoMainBtn" class="auth-btn" href="#">Open main app →</a>
</div>
</div>
</section>
<!-- LOGIN -->
<section id="loginScreen" class="auth-screen hidden">
<div class="auth-card">
<div class="auth-brand">
<span class="admin-badge">ADMIN</span>
<h1>PST Archive</h1>
<p class="auth-tagline">Admin panel · sign in</p>
</div>
<form id="loginForm" class="auth-form" novalidate>
<label class="auth-label">
<span>Username</span>
<input type="text" name="username" autocomplete="username" required />
</label>
<label class="auth-label">
<span>Password</span>
<input type="password" name="password" autocomplete="current-password" required />
</label>
<label id="mfaField" class="auth-label hidden">
<span>Authentication code</span>
<input type="text" name="totp_code" autocomplete="one-time-code" inputmode="numeric" pattern="[0-9]*" maxlength="6" placeholder="6-digit code" />
<small>From your authenticator app</small>
</label>
<button type="submit" class="auth-btn">Sign in</button>
<div id="loginError" class="auth-error hidden"></div>
</form>
</div>
</section>
<!-- ADMIN APP -->
<div id="adminApp" class="hidden">
<header class="masthead">
<div class="masthead-left">
<span class="admin-badge small">ADMIN</span>
<div class="masthead-title">
<h1>User Management</h1>
<p class="tagline">PST Archive · control panel</p>
</div>
</div>
<div class="masthead-right">
<button id="backToAppBtn" class="back-btn" title="Return to the archive">← Archive</button>
<span id="userChip" class="user-chip"></span>
<button id="themeToggle" class="theme-toggle" title="Toggle theme" aria-label="Toggle theme">
<svg class="icon-sun" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="1.7" stroke-linecap="round" stroke-linejoin="round"><circle cx="12" cy="12" r="4.5"/><line x1="12" y1="2" x2="12" y2="4.5"/><line x1="12" y1="19.5" x2="12" y2="22"/><line x1="2" y1="12" x2="4.5" y2="12"/><line x1="19.5" y1="12" x2="22" y2="12"/><line x1="4.9" y1="4.9" x2="6.7" y2="6.7"/><line x1="17.3" y1="17.3" x2="19.1" y2="19.1"/><line x1="4.9" y1="19.1" x2="6.7" y2="17.3"/><line x1="17.3" y1="6.7" x2="19.1" y2="4.9"/></svg>
<svg class="icon-moon" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="1.7" stroke-linecap="round" stroke-linejoin="round"><path d="M20 14.5A9 9 0 019.5 4a7.5 7.5 0 1010.5 10.5z"/></svg>
</button>
<button id="logoutBtn" class="logout-btn">Sign out</button>
</div>
</header>
<main class="admin-main">
<nav class="admin-tabs" role="tablist">
<button class="admin-tab active" data-tab="users" role="tab">Users</button>
<button class="admin-tab" data-tab="folders" role="tab">Folders</button>
<button class="admin-tab" data-tab="files" role="tab">Files</button>
<button class="admin-tab" data-tab="permissions" role="tab">Permissions</button>
</nav>
<!-- USERS TAB -->
<section class="admin-panel" data-panel="users">
<div class="admin-toolbar">
<div>
<h2 class="section-heading">Users</h2>
<p class="toolbar-meta" id="userCount"></p>
</div>
<button id="newUserBtn" class="primary-btn">+ New user</button>
</div>
<div class="user-list-wrap">
<table class="user-table">
<thead>
<tr>
<th>Username</th>
<th>Role</th>
<th>MFA</th>
<th>Created</th>
<th>Last login</th>
<th>Actions</th>
</tr>
</thead>
<tbody id="userTableBody">
<tr><td colspan="6" class="loading-row">Loading users…</td></tr>
</tbody>
</table>
</div>
</section>
<!-- FOLDERS TAB -->
<section class="admin-panel hidden" data-panel="folders">
<div class="admin-toolbar">
<div>
<h2 class="section-heading">PST Folders</h2>
<p class="toolbar-meta" id="folderCount"></p>
</div>
<button id="newFolderBtn" class="primary-btn">+ New folder</button>
</div>
<div class="user-list-wrap">
<table class="user-table">
<thead>
<tr>
<th>Name</th>
<th>Archives inside</th>
<th>Created</th>
<th>Actions</th>
</tr>
</thead>
<tbody id="folderTableBody">
<tr><td colspan="4" class="loading-row">Loading folders…</td></tr>
</tbody>
</table>
</div>
</section>
<!-- FILES TAB -->
<section class="admin-panel hidden" data-panel="files">
<div class="admin-toolbar">
<div>
<h2 class="section-heading">PST Files</h2>
<p class="toolbar-meta" id="fileCount"></p>
</div>
</div>
<div class="user-list-wrap">
<table class="user-table">
<thead>
<tr>
<th>Filename</th>
<th>Folder</th>
<th>Size</th>
<th>Status</th>
<th>Messages</th>
<th>Uploaded</th>
<th>Actions</th>
</tr>
</thead>
<tbody id="fileTableBody">
<tr><td colspan="7" class="loading-row">Loading files…</td></tr>
</tbody>
</table>
</div>
</section>
<!-- PERMISSIONS TAB -->
<section class="admin-panel hidden" data-panel="permissions">
<div class="admin-toolbar">
<div>
<h2 class="section-heading">Folder Access Permissions</h2>
<p class="toolbar-meta" id="permissionsMeta"></p>
</div>
<div class="view-switch" role="tablist">
<button class="view-btn active" data-view="by-user" type="button">By user</button>
<button class="view-btn" data-view="by-folder" type="button">By folder</button>
</div>
</div>
<p class="perm-hint">
Check a box to grant a non-admin user access to a folder. Administrators
always see every folder regardless of these settings. "Uncategorized" archives
are visible to everyone by default.
</p>
<div class="perm-matrix-wrap">
<table class="user-table perm-matrix" id="permMatrix">
<thead id="permMatrixHead"></thead>
<tbody id="permMatrixBody">
<tr><td class="loading-row">Loading…</td></tr>
</tbody>
</table>
</div>
</section>
</main>
<footer class="site-footer">© 2026 Martinhal IT - Joao Vaz - Version 2.2</footer>
</div>
<!-- CREATE USER MODAL -->
<div id="createUserModal" class="modal hidden">
<div class="modal-card">
<button class="modal-close" data-close>×</button>
<h2>Create new user</h2>
<form id="createUserForm" class="auth-form" novalidate>
<label class="auth-label">
<span>Username</span>
<input type="text" name="username" autocomplete="off" required minlength="3" maxlength="64" />
</label>
<label class="auth-label">
<span>Password</span>
<input type="password" name="password" autocomplete="new-password" required minlength="8" />
<small>Minimum 8 characters</small>
</label>
<label class="auth-label">
<span>Role</span>
<select name="role">
<option value="user">User (can view archives)</option>
<option value="admin">Administrator (full access)</option>
</select>
</label>
<button type="submit" class="auth-btn">Create user</button>
<div id="createError" class="auth-error hidden"></div>
</form>
</div>
</div>
<!-- RESET PASSWORD MODAL -->
<div id="resetPasswordModal" class="modal hidden">
<div class="modal-card">
<button class="modal-close" data-close>×</button>
<h2>Reset password</h2>
<p class="modal-subtitle" id="resetPasswordSubject"></p>
<form id="resetPasswordForm" class="auth-form" novalidate>
<label class="auth-label">
<span>New password</span>
<input type="password" name="password" autocomplete="new-password" required minlength="8" />
<small>Minimum 8 characters</small>
</label>
<button type="submit" class="auth-btn">Update password</button>
<div id="resetError" class="auth-error hidden"></div>
</form>
</div>
</div>
<!-- MFA MODAL -->
<div id="mfaModal" class="modal hidden">
<div class="modal-card wide">
<button class="modal-close" data-close>×</button>
<h2>Multi-factor authentication</h2>
<p class="modal-subtitle" id="mfaSubject"></p>
<div id="mfaPanel" class="mfa-panel">
<div class="mfa-status" id="mfaCurrentStatus"></div>
<div class="mfa-actions" id="mfaActions"></div>
</div>
<div id="mfaEnrollPanel" class="mfa-enroll hidden">
<div class="enroll-grid">
<div class="enroll-qr">
<img id="mfaQrImage" src="" alt="TOTP QR code" />
</div>
<div class="enroll-details">
<p class="enroll-step">1. Scan this QR code with your authenticator app (Google Authenticator, Authy, 1Password, etc.).</p>
<p class="enroll-step">2. Or manually enter this secret:</p>
<code id="mfaSecretText" class="secret-box"></code>
<p class="enroll-step">3. Enter the 6-digit code from the app to confirm.</p>
<form id="mfaConfirmForm" class="auth-form confirm-form">
<label class="auth-label">
<span>Verification code</span>
<input type="text" name="totp_code" inputmode="numeric" pattern="[0-9]*" maxlength="6" required />
</label>
<button type="submit" class="auth-btn">Confirm & enable</button>
<div id="mfaConfirmError" class="auth-error hidden"></div>
</form>
</div>
</div>
</div>
</div>
</div>
<!-- FOLDER CREATE/RENAME MODAL -->
<div id="folderModal" class="modal hidden">
<div class="modal-card">
<button class="modal-close" data-close>×</button>
<h2 id="folderModalTitle">New folder</h2>
<form id="folderForm" class="auth-form" novalidate>
<label class="auth-label">
<span>Folder name</span>
<input type="text" name="name" maxlength="80" required autocomplete="off" />
</label>
<button type="submit" class="auth-btn" id="folderSubmitBtn">Create folder</button>
<div id="folderError" class="auth-error hidden"></div>
</form>
</div>
</div>
<div id="toast" class="toast hidden"></div>
<script src="admin.js"></script>
</body>
</html>