Add user management system with JWT authentication

- Database schema: users, permissions, client_access tables
- Auth: JWT tokens with Argon2 password hashing
- API: login, user CRUD, permission management
- Dashboard: login required, admin Users tab
- Auto-creates initial admin user on first run
This commit is contained in:
2025-12-29 20:57:30 -07:00
parent 743b73dfe7
commit 3fc4e1f96a
13 changed files with 2354 additions and 70 deletions

View File

@@ -3,7 +3,7 @@
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>GuruConnect - Technician Login</title>
<title>GuruConnect - Login</title>
<style>
:root {
--background: 222.2 84% 4.9%;
@@ -53,7 +53,7 @@
label { font-size: 14px; font-weight: 500; color: hsl(var(--foreground)); }
input[type="email"], input[type="password"] {
input[type="text"], input[type="password"] {
width: 100%;
padding: 12px 16px;
font-size: 14px;
@@ -119,40 +119,24 @@
@keyframes spin { to { transform: rotate(360deg); } }
.loading .spinner { display: inline-block; }
.demo-hint {
margin-top: 16px;
padding: 12px;
background: hsla(var(--primary), 0.1);
border-radius: 8px;
font-size: 13px;
color: hsl(var(--muted-foreground));
text-align: center;
}
.demo-hint a {
color: hsl(var(--primary));
text-decoration: none;
font-weight: 500;
}
</style>
</head>
<body>
<div class="container">
<div class="logo">
<h1>GuruConnect</h1>
<p>Technician Login</p>
<p>Sign in to your account</p>
</div>
<form class="login-form" id="loginForm">
<div class="form-group">
<label for="email">Email</label>
<input type="email" id="email" placeholder="you@company.com" required>
<label for="username">Username</label>
<input type="text" id="username" placeholder="Enter your username" autocomplete="username" required>
</div>
<div class="form-group">
<label for="password">Password</label>
<input type="password" id="password" placeholder="Enter your password" required>
<input type="password" id="password" placeholder="Enter your password" autocomplete="current-password" required>
</div>
<div class="error-message" id="errorMessage"></div>
@@ -163,10 +147,6 @@
</button>
</form>
<div class="demo-hint">
<p>Auth not yet configured. <a href="/dashboard">Skip to Dashboard</a></p>
</div>
<div class="footer">
<p><a href="/">Back to Support Portal</a></p>
</div>
@@ -177,10 +157,29 @@
const loginBtn = document.getElementById("loginBtn");
const errorMessage = document.getElementById("errorMessage");
// Check if already logged in
const token = localStorage.getItem("guruconnect_token");
if (token) {
// Verify token is still valid
fetch('/api/auth/me', {
headers: { 'Authorization': `Bearer ${token}` }
}).then(res => {
if (res.ok) {
window.location.href = '/dashboard';
} else {
localStorage.removeItem('guruconnect_token');
localStorage.removeItem('guruconnect_user');
}
}).catch(() => {
localStorage.removeItem('guruconnect_token');
localStorage.removeItem('guruconnect_user');
});
}
form.addEventListener("submit", async (e) => {
e.preventDefault();
const email = document.getElementById("email").value;
const username = document.getElementById("username").value;
const password = document.getElementById("password").value;
setLoading(true);
@@ -190,7 +189,7 @@
const response = await fetch("/api/auth/login", {
method: "POST",
headers: { "Content-Type": "application/json" },
body: JSON.stringify({ email, password })
body: JSON.stringify({ username, password })
});
const data = await response.json();
@@ -201,12 +200,13 @@
return;
}
localStorage.setItem("token", data.token);
localStorage.setItem("user", JSON.stringify(data.user));
// Store token and user info
localStorage.setItem("guruconnect_token", data.token);
localStorage.setItem("guruconnect_user", JSON.stringify(data.user));
window.location.href = "/dashboard";
} catch (err) {
showError("Auth not configured yet. Use the demo link below.");
showError("Connection error. Please try again.");
setLoading(false);
}
});
@@ -222,9 +222,8 @@
loginBtn.querySelector(".btn-text").textContent = loading ? "Signing in..." : "Sign In";
}
if (localStorage.getItem("token")) {
window.location.href = "/dashboard";
}
// Focus username field
document.getElementById("username").focus();
</script>
</body>
</html>