269 lines
8.3 KiB
JavaScript
269 lines
8.3 KiB
JavaScript
// Authentication Module for LLM Proxy Dashboard
|
|
|
|
class AuthManager {
|
|
constructor() {
|
|
this.isAuthenticated = false;
|
|
this.token = null;
|
|
this.user = null;
|
|
this.init();
|
|
}
|
|
|
|
init() {
|
|
// Check for existing session
|
|
const savedToken = localStorage.getItem('dashboard_token');
|
|
const savedUser = localStorage.getItem('dashboard_user');
|
|
|
|
if (savedToken && savedUser) {
|
|
this.token = savedToken;
|
|
this.user = JSON.parse(savedUser);
|
|
this.isAuthenticated = true;
|
|
this.showDashboard();
|
|
} else {
|
|
this.showLogin();
|
|
}
|
|
|
|
// Setup login form
|
|
this.setupLoginForm();
|
|
this.setupLogout();
|
|
}
|
|
|
|
setupLoginForm() {
|
|
const loginForm = document.getElementById('login-form');
|
|
if (!loginForm) return;
|
|
|
|
loginForm.addEventListener('submit', async (e) => {
|
|
e.preventDefault();
|
|
|
|
const username = document.getElementById('username').value;
|
|
const password = document.getElementById('password').value;
|
|
|
|
await this.login(username, password);
|
|
});
|
|
}
|
|
|
|
setupLogout() {
|
|
const logoutBtn = document.getElementById('logout-btn');
|
|
if (!logoutBtn) return;
|
|
|
|
logoutBtn.addEventListener('click', () => {
|
|
this.logout();
|
|
});
|
|
}
|
|
|
|
async login(username, password) {
|
|
const errorElement = document.getElementById('login-error');
|
|
const loginBtn = document.querySelector('.login-btn');
|
|
|
|
try {
|
|
// Show loading state
|
|
loginBtn.innerHTML = '<i class="fas fa-spinner fa-spin"></i> Authenticating...';
|
|
loginBtn.disabled = true;
|
|
|
|
// Simple authentication - in production, this would call an API
|
|
// For now, using mock authentication
|
|
await new Promise(resolve => setTimeout(resolve, 1000));
|
|
|
|
if (username === 'admin' && password === 'admin123') {
|
|
// Successful login
|
|
this.token = this.generateToken();
|
|
this.user = {
|
|
username: 'admin',
|
|
name: 'Administrator',
|
|
role: 'Super Admin',
|
|
avatar: null
|
|
};
|
|
|
|
// Save to localStorage
|
|
localStorage.setItem('dashboard_token', this.token);
|
|
localStorage.setItem('dashboard_user', JSON.stringify(this.user));
|
|
|
|
this.isAuthenticated = true;
|
|
this.showDashboard();
|
|
|
|
// Show success message
|
|
this.showToast('Successfully logged in!', 'success');
|
|
} else {
|
|
throw new Error('Invalid credentials');
|
|
}
|
|
} catch (error) {
|
|
// Show error
|
|
errorElement.style.display = 'flex';
|
|
errorElement.querySelector('span').textContent = error.message;
|
|
|
|
// Reset button
|
|
loginBtn.innerHTML = '<i class="fas fa-sign-in-alt"></i> Sign In';
|
|
loginBtn.disabled = false;
|
|
}
|
|
}
|
|
|
|
logout() {
|
|
// Clear localStorage
|
|
localStorage.removeItem('dashboard_token');
|
|
localStorage.removeItem('dashboard_user');
|
|
|
|
// Reset state
|
|
this.isAuthenticated = false;
|
|
this.token = null;
|
|
this.user = null;
|
|
|
|
// Show login screen
|
|
this.showLogin();
|
|
|
|
// Show logout message
|
|
this.showToast('Successfully logged out', 'info');
|
|
}
|
|
|
|
generateToken() {
|
|
// Generate a simple token for demo purposes
|
|
// In production, this would come from the server
|
|
const timestamp = Date.now();
|
|
const random = Math.random().toString(36).substring(2);
|
|
return btoa(`${timestamp}:${random}`).replace(/=/g, '');
|
|
}
|
|
|
|
showLogin() {
|
|
const loginScreen = document.getElementById('login-screen');
|
|
const dashboard = document.getElementById('dashboard');
|
|
|
|
if (loginScreen) loginScreen.style.display = 'flex';
|
|
if (dashboard) dashboard.style.display = 'none';
|
|
|
|
// Clear form
|
|
const loginForm = document.getElementById('login-form');
|
|
if (loginForm) loginForm.reset();
|
|
|
|
// Hide error
|
|
const errorElement = document.getElementById('login-error');
|
|
if (errorElement) errorElement.style.display = 'none';
|
|
|
|
// Reset button
|
|
const loginBtn = document.querySelector('.login-btn');
|
|
if (loginBtn) {
|
|
loginBtn.innerHTML = '<i class="fas fa-sign-in-alt"></i> Sign In';
|
|
loginBtn.disabled = false;
|
|
}
|
|
}
|
|
|
|
showDashboard() {
|
|
const loginScreen = document.getElementById('login-screen');
|
|
const dashboard = document.getElementById('dashboard');
|
|
|
|
if (loginScreen) loginScreen.style.display = 'none';
|
|
if (dashboard) dashboard.style.display = 'flex';
|
|
|
|
// Update user info in sidebar
|
|
this.updateUserInfo();
|
|
|
|
// Initialize dashboard components
|
|
if (typeof window.initDashboard === 'function') {
|
|
window.initDashboard();
|
|
}
|
|
}
|
|
|
|
updateUserInfo() {
|
|
const userNameElement = document.querySelector('.user-name');
|
|
const userRoleElement = document.querySelector('.user-role');
|
|
|
|
if (userNameElement && this.user) {
|
|
userNameElement.textContent = this.user.name;
|
|
}
|
|
|
|
if (userRoleElement && this.user) {
|
|
userRoleElement.textContent = this.user.role;
|
|
}
|
|
}
|
|
|
|
getAuthHeaders() {
|
|
if (!this.token) return {};
|
|
|
|
return {
|
|
'Authorization': `Bearer ${this.token}`,
|
|
'Content-Type': 'application/json'
|
|
};
|
|
}
|
|
|
|
async fetchWithAuth(url, options = {}) {
|
|
const headers = this.getAuthHeaders();
|
|
|
|
const response = await fetch(url, {
|
|
...options,
|
|
headers: {
|
|
...headers,
|
|
...options.headers
|
|
}
|
|
});
|
|
|
|
if (response.status === 401) {
|
|
// Token expired or invalid
|
|
this.logout();
|
|
throw new Error('Authentication required');
|
|
}
|
|
|
|
return response;
|
|
}
|
|
|
|
showToast(message, type = 'info') {
|
|
// Create toast container if it doesn't exist
|
|
let container = document.querySelector('.toast-container');
|
|
if (!container) {
|
|
container = document.createElement('div');
|
|
container.className = 'toast-container';
|
|
document.body.appendChild(container);
|
|
}
|
|
|
|
// Create toast
|
|
const toast = document.createElement('div');
|
|
toast.className = `toast ${type}`;
|
|
|
|
// Set icon based on type
|
|
let icon = 'info-circle';
|
|
switch (type) {
|
|
case 'success':
|
|
icon = 'check-circle';
|
|
break;
|
|
case 'error':
|
|
icon = 'exclamation-circle';
|
|
break;
|
|
case 'warning':
|
|
icon = 'exclamation-triangle';
|
|
break;
|
|
}
|
|
|
|
toast.innerHTML = `
|
|
<i class="fas fa-${icon} toast-icon"></i>
|
|
<div class="toast-content">
|
|
<div class="toast-title">${type.charAt(0).toUpperCase() + type.slice(1)}</div>
|
|
<div class="toast-message">${message}</div>
|
|
</div>
|
|
<button class="toast-close">
|
|
<i class="fas fa-times"></i>
|
|
</button>
|
|
`;
|
|
|
|
// Add close functionality
|
|
const closeBtn = toast.querySelector('.toast-close');
|
|
closeBtn.addEventListener('click', () => {
|
|
toast.remove();
|
|
});
|
|
|
|
// Add to container
|
|
container.appendChild(toast);
|
|
|
|
// Auto-remove after 5 seconds
|
|
setTimeout(() => {
|
|
if (toast.parentNode) {
|
|
toast.remove();
|
|
}
|
|
}, 5000);
|
|
}
|
|
}
|
|
|
|
// Initialize auth manager when DOM is loaded
|
|
document.addEventListener('DOMContentLoaded', () => {
|
|
window.authManager = new AuthManager();
|
|
});
|
|
|
|
// Export for use in other modules
|
|
if (typeof module !== 'undefined' && module.exports) {
|
|
module.exports = AuthManager;
|
|
} |