Init repo
This commit is contained in:
269
static/js/auth.js
Normal file
269
static/js/auth.js
Normal file
@@ -0,0 +1,269 @@
|
||||
// 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;
|
||||
}
|
||||
Reference in New Issue
Block a user