- Real email/password login backed by SQLite (better-sqlite3) - HttpOnly cookie sessions with 8h sliding TTL - Admin role: invite users via Mailgun magic-link, manage roles/status - Per-user One2Edit username mapping for job filtering - Self-service forgot-password / reset-password via email - Admin console (admin.html) with user table, invite modal, row actions - New pages: change-password, forgot-password, reset-password, accept-invite - Gated /api proxy: requires valid session, anti-hijack sessionId check - Bootstrap initial admins from INITIAL_ADMINS env var on first boot - Remove Oliver login button, SSO buttons, and legacy api.js/login.js - deploy.sh: add build-essential (for native module), npm install, data dir Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
87 lines
4 KiB
HTML
87 lines
4 KiB
HTML
<!DOCTYPE html>
|
|
<html lang="en">
|
|
<head>
|
|
<meta charset="UTF-8">
|
|
<meta name="viewport" content="width=device-width, initial-scale=1.0">
|
|
<title>3M - Admin Console</title>
|
|
<link rel="icon" type="image/png" href="Images/Favicon_logo.png">
|
|
<link rel="stylesheet" href="styles.css">
|
|
</head>
|
|
<body class="dashboard-page">
|
|
<div class="dashboard-header-bar">
|
|
<div class="dashboard-title-section">
|
|
<img src="Images/login_logo.png" alt="3M Logo" class="dashboard-logo">
|
|
<h1>Admin Console</h1>
|
|
</div>
|
|
<div class="dashboard-actions">
|
|
<button class="refresh-button" onclick="window.location.href='dashboard.html'">← Dashboard</button>
|
|
<button id="inviteBtn" class="admin-button">Invite User</button>
|
|
</div>
|
|
</div>
|
|
|
|
<div class="dashboard-content">
|
|
<div id="adminError" class="error-message" style="display:none;"></div>
|
|
<div id="adminSuccess" class="success-message" style="display:none;"></div>
|
|
|
|
<div class="admin-card">
|
|
<h2 class="admin-section-title">Users</h2>
|
|
<div id="usersLoading" class="loading-message" style="padding:30px 0;">Loading users...</div>
|
|
<div id="usersTableWrap" style="display:none; overflow-x:auto;">
|
|
<table class="admin-table" id="usersTable">
|
|
<thead>
|
|
<tr>
|
|
<th>Email</th>
|
|
<th>One2Edit User</th>
|
|
<th>Role</th>
|
|
<th>Status</th>
|
|
<th>Last Login</th>
|
|
<th>Actions</th>
|
|
</tr>
|
|
</thead>
|
|
<tbody id="usersTableBody"></tbody>
|
|
</table>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
|
|
<!-- Invite Modal -->
|
|
<div id="inviteModal" class="modal-overlay" style="display:none;" role="dialog" aria-modal="true" aria-labelledby="inviteModalTitle">
|
|
<div class="modal-box">
|
|
<div class="modal-header">
|
|
<h3 id="inviteModalTitle">Invite New User</h3>
|
|
<button class="modal-close" id="inviteModalClose" aria-label="Close">×</button>
|
|
</div>
|
|
<div class="modal-body">
|
|
<div id="inviteError" class="error-message" style="display:none;"></div>
|
|
<div id="inviteSuccess" class="success-message" style="display:none;"></div>
|
|
<div id="inviteFormWrap">
|
|
<div class="form-group">
|
|
<label class="form-label" for="inviteEmail">Email address</label>
|
|
<input type="email" id="inviteEmail" class="form-input" placeholder="user@example.com" autocomplete="off">
|
|
</div>
|
|
<div class="form-group">
|
|
<label class="form-label" for="inviteO2EUser">One2Edit username</label>
|
|
<input type="text" id="inviteO2EUser" class="form-input" placeholder="firstname.lastname" autocomplete="off">
|
|
</div>
|
|
<div class="form-group">
|
|
<label class="form-label" for="inviteRole">Role</label>
|
|
<select id="inviteRole" class="form-input">
|
|
<option value="user">User</option>
|
|
<option value="admin">Admin</option>
|
|
</select>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
<div class="modal-footer" id="inviteFooter">
|
|
<button class="btn-secondary" id="inviteModalCancel">Cancel</button>
|
|
<button class="tmm-button login-button" id="inviteSubmitBtn" style="flex:0 0 auto; padding:10px 24px;">
|
|
<span id="inviteSubmitText">Send Invite</span>
|
|
<span id="inviteSubmitSpinner" class="spinner" style="display:none;"></span>
|
|
</button>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
|
|
<script src="admin.js"></script>
|
|
</body>
|
|
</html>
|