HINTECH / static /auth.js
Factor Studios
Upload 73 files
aaaaa79 verified
// Authentication JavaScript
// API Configuration
const API_BASE_URL = '/api';
// DOM Elements
let currentForm = 'login';
// Initialize page
document.addEventListener('DOMContentLoaded', function() {
initializeAuth();
setupPasswordStrength();
setupFormValidation();
});
function initializeAuth() {
// Set initial form state
showLogin();
}
function showLogin() {
hideAllForms();
document.getElementById('loginForm').style.display = 'block';
setActiveNavButton('loginNavBtn');
currentForm = 'login';
}
function showRegister() {
hideAllForms();
document.getElementById('registerForm').style.display = 'block';
setActiveNavButton('registerNavBtn');
currentForm = 'register';
}
function showForgotPassword() {
hideAllForms();
document.getElementById('forgotPasswordForm').style.display = 'block';
clearActiveNavButtons();
currentForm = 'forgot';
}
function hideAllForms() {
const forms = ['loginForm', 'registerForm', 'forgotPasswordForm'];
forms.forEach(formId => {
document.getElementById(formId).style.display = 'none';
});
}
function setActiveNavButton(activeId) {
clearActiveNavButtons();
document.getElementById(activeId).classList.add('active');
}
function clearActiveNavButtons() {
document.querySelectorAll('.nav-btn').forEach(btn => {
btn.classList.remove('active');
});
}
// Password visibility toggle
function togglePassword(inputId) {
const input = document.getElementById(inputId);
const button = input.parentElement.querySelector('.password-toggle i');
if (input.type === 'password') {
input.type = 'text';
button.classList.remove('fa-eye');
button.classList.add('fa-eye-slash');
} else {
input.type = 'password';
button.classList.remove('fa-eye-slash');
button.classList.add('fa-eye');
}
}
// Password strength checker
function setupPasswordStrength() {
const passwordInput = document.getElementById('registerPassword');
const strengthBar = document.querySelector('.strength-fill');
const strengthText = document.querySelector('.strength-text');
if (passwordInput) {
passwordInput.addEventListener('input', function() {
const password = this.value;
const strength = calculatePasswordStrength(password);
updatePasswordStrength(strength, strengthBar, strengthText);
});
}
}
function calculatePasswordStrength(password) {
let score = 0;
let feedback = [];
// Length check
if (password.length >= 8) score += 20;
else feedback.push('At least 8 characters');
// Uppercase check
if (/[A-Z]/.test(password)) score += 20;
else feedback.push('One uppercase letter');
// Lowercase check
if (/[a-z]/.test(password)) score += 20;
else feedback.push('One lowercase letter');
// Number check
if (/\d/.test(password)) score += 20;
else feedback.push('One number');
// Special character check
if (/[!@#$%^&*(),.?":{}|<>]/.test(password)) score += 20;
else feedback.push('One special character');
return { score, feedback };
}
function updatePasswordStrength(strength, strengthBar, strengthText) {
const { score, feedback } = strength;
strengthBar.style.width = score + '%';
if (score < 40) {
strengthBar.style.background = '#e74c3c';
strengthText.textContent = 'Weak password';
strengthText.style.color = '#e74c3c';
} else if (score < 80) {
strengthBar.style.background = '#f39c12';
strengthText.textContent = 'Medium password';
strengthText.style.color = '#f39c12';
} else {
strengthBar.style.background = '#27ae60';
strengthText.textContent = 'Strong password';
strengthText.style.color = '#27ae60';
}
if (feedback.length > 0 && score < 100) {
strengthText.textContent += ' - Missing: ' + feedback.join(', ');
}
}
// Form validation
function setupFormValidation() {
// Real-time validation for username
const usernameInput = document.getElementById('registerUsername');
if (usernameInput) {
usernameInput.addEventListener('input', validateUsername);
usernameInput.addEventListener('blur', validateUsername);
}
// Real-time validation for email
const emailInputs = document.querySelectorAll('input[type="email"]');
emailInputs.forEach(input => {
input.addEventListener('input', validateEmail);
input.addEventListener('blur', validateEmail);
});
// Password confirmation validation
const confirmPasswordInput = document.getElementById('confirmPassword');
if (confirmPasswordInput) {
confirmPasswordInput.addEventListener('input', validatePasswordConfirmation);
confirmPasswordInput.addEventListener('blur', validatePasswordConfirmation);
}
}
function validateUsername() {
const input = document.getElementById('registerUsername');
const value = input.value.trim();
const inputGroup = input.parentElement;
clearValidationState(inputGroup);
if (value.length === 0) return;
if (value.length < 3 || value.length > 80) {
setValidationState(inputGroup, 'error', 'Username must be 3-80 characters');
return false;
}
if (!/^[a-zA-Z0-9_-]+$/.test(value)) {
setValidationState(inputGroup, 'error', 'Only letters, numbers, hyphens, and underscores allowed');
return false;
}
setValidationState(inputGroup, 'success');
return true;
}
function validateEmail() {
const input = this;
const value = input.value.trim();
const inputGroup = input.parentElement;
clearValidationState(inputGroup);
if (value.length === 0) return;
const emailRegex = /^[a-zA-Z0-9._%+-]+@[a-zA-Z0-9.-]+\.[a-zA-Z]{2,}$/;
if (!emailRegex.test(value)) {
setValidationState(inputGroup, 'error', 'Please enter a valid email address');
return false;
}
setValidationState(inputGroup, 'success');
return true;
}
function validatePasswordConfirmation() {
const passwordInput = document.getElementById('registerPassword');
const confirmInput = document.getElementById('confirmPassword');
const inputGroup = confirmInput.parentElement;
clearValidationState(inputGroup);
if (confirmInput.value.length === 0) return;
if (passwordInput.value !== confirmInput.value) {
setValidationState(inputGroup, 'error', 'Passwords do not match');
return false;
}
setValidationState(inputGroup, 'success');
return true;
}
function setValidationState(inputGroup, state, message = '') {
inputGroup.classList.remove('error', 'success');
inputGroup.classList.add(state);
// Remove existing error message
const existingError = inputGroup.parentElement.querySelector('.error-message');
if (existingError) {
existingError.remove();
}
// Add error message if provided
if (message && state === 'error') {
const errorDiv = document.createElement('div');
errorDiv.className = 'error-message';
errorDiv.innerHTML = `<i class="fas fa-exclamation-circle"></i> ${message}`;
inputGroup.parentElement.appendChild(errorDiv);
}
}
function clearValidationState(inputGroup) {
inputGroup.classList.remove('error', 'success');
const existingError = inputGroup.parentElement.querySelector('.error-message');
if (existingError) {
existingError.remove();
}
}
// Form submission handlers
async function handleLogin(event) {
event.preventDefault();
const form = event.target;
const formData = new FormData(form);
const loginData = {
login: formData.get('login'),
password: formData.get('password')
};
const button = document.getElementById('loginBtn');
setButtonLoading(button, true);
try {
const response = await fetch(`${API_BASE_URL}/login`, {
method: 'POST',
headers: {
'Content-Type': 'application/json',
},
body: JSON.stringify(loginData)
});
const result = await response.json();
if (response.ok) {
// Store tokens
localStorage.setItem('auth_token', result.auth_token);
localStorage.setItem('refresh_token', result.refresh_token);
localStorage.setItem('user_data', JSON.stringify(result.user));
showAlert('success', 'Login successful! Redirecting to dashboard...');
// Redirect to dashboard
setTimeout(() => {
window.location.href = '/dashboard.html';
}, 1500);
} else {
showAlert('error', result.error || 'Login failed');
}
} catch (error) {
console.error('Login error:', error);
showAlert('error', 'Network error. Please try again.');
} finally {
setButtonLoading(button, false);
}
}
async function handleRegister(event) {
event.preventDefault();
const form = event.target;
const formData = new FormData(form);
// Validate form
if (!validateRegistrationForm(form)) {
showAlert('error', 'Please fix the errors in the form');
return;
}
const registerData = {
username: formData.get('username'),
email: formData.get('email'),
password: formData.get('password'),
subscription_type: formData.get('subscriptionType')
};
const button = document.getElementById('registerBtn');
setButtonLoading(button, true);
try {
const response = await fetch(`${API_BASE_URL}/register`, {
method: 'POST',
headers: {
'Content-Type': 'application/json',
},
body: JSON.stringify(registerData)
});
const result = await response.json();
if (response.ok) {
// Store tokens
localStorage.setItem('auth_token', result.auth_token);
localStorage.setItem('refresh_token', result.refresh_token);
localStorage.setItem('user_data', JSON.stringify(result.user));
showAlert('success', 'Account created successfully! Redirecting to dashboard...');
// Redirect to dashboard
setTimeout(() => {
window.location.href = '/dashboard.html';
}, 1500);
} else {
showAlert('error', result.error || 'Registration failed');
}
} catch (error) {
console.error('Registration error:', error);
showAlert('error', 'Network error. Please try again.');
} finally {
setButtonLoading(button, false);
}
}
async function handleForgotPassword(event) {
event.preventDefault();
const form = event.target;
const formData = new FormData(form);
const email = formData.get('email');
const button = document.getElementById('forgotPasswordBtn');
setButtonLoading(button, true);
try {
const response = await fetch(`${API_BASE_URL}/forgot-password`, {
method: 'POST',
headers: {
'Content-Type': 'application/json',
},
body: JSON.stringify({ email })
});
const result = await response.json();
if (response.ok) {
showAlert('success', 'Password reset instructions sent to your email');
// Show reset token for development (remove in production)
if (result.reset_token) {
showAlert('info', `Development: Reset token is ${result.reset_token}`);
}
// Switch back to login form
setTimeout(() => {
showLogin();
}, 3000);
} else {
showAlert('error', result.error || 'Failed to send reset instructions');
}
} catch (error) {
console.error('Forgot password error:', error);
showAlert('error', 'Network error. Please try again.');
} finally {
setButtonLoading(button, false);
}
}
function validateRegistrationForm(form) {
let isValid = true;
// Validate username
const usernameInput = form.querySelector('#registerUsername');
if (!validateUsername.call(usernameInput)) {
isValid = false;
}
// Validate email
const emailInput = form.querySelector('#registerEmail');
if (!validateEmail.call(emailInput)) {
isValid = false;
}
// Validate password strength
const passwordInput = form.querySelector('#registerPassword');
const strength = calculatePasswordStrength(passwordInput.value);
if (strength.score < 80) {
const inputGroup = passwordInput.parentElement;
setValidationState(inputGroup, 'error', 'Password is too weak');
isValid = false;
}
// Validate password confirmation
if (!validatePasswordConfirmation()) {
isValid = false;
}
// Validate terms agreement
const termsCheckbox = form.querySelector('#agreeTerms');
if (!termsCheckbox.checked) {
showAlert('error', 'You must agree to the Terms of Service and Privacy Policy');
isValid = false;
}
return isValid;
}
// Utility functions
function setButtonLoading(button, loading) {
const btnText = button.querySelector('.btn-text');
const btnLoader = button.querySelector('.btn-loader');
if (loading) {
button.classList.add('loading');
button.disabled = true;
btnText.style.opacity = '0';
btnLoader.style.display = 'block';
} else {
button.classList.remove('loading');
button.disabled = false;
btnText.style.opacity = '1';
btnLoader.style.display = 'none';
}
}
function showAlert(type, message, duration = 5000) {
const alertContainer = document.getElementById('alertContainer');
const alert = document.createElement('div');
alert.className = `alert ${type}`;
const icon = getAlertIcon(type);
alert.innerHTML = `
<i class="${icon}"></i>
<span>${message}</span>
<button class="alert-close" onclick="closeAlert(this)">
<i class="fas fa-times"></i>
</button>
`;
alertContainer.appendChild(alert);
// Auto-remove alert after duration
setTimeout(() => {
if (alert.parentElement) {
closeAlert(alert.querySelector('.alert-close'));
}
}, duration);
}
function getAlertIcon(type) {
const icons = {
success: 'fas fa-check-circle',
error: 'fas fa-exclamation-circle',
warning: 'fas fa-exclamation-triangle',
info: 'fas fa-info-circle'
};
return icons[type] || icons.info;
}
function closeAlert(button) {
const alert = button.parentElement;
alert.style.animation = 'slideOutRight 0.3s ease-out forwards';
setTimeout(() => {
if (alert.parentElement) {
alert.parentElement.removeChild(alert);
}
}, 300);
}
async function verifyTokenAndRedirect(token) {
try {
const response = await fetch(`${API_BASE_URL}/profile`, {
headers: {
'Authorization': `Bearer ${token}`
}
});
if (response.ok) {
// Token is valid, redirect to dashboard
window.location.href = '/dashboard.html';
} else {
// Token is invalid, remove from storage
localStorage.removeItem('auth_token');
localStorage.removeItem('refresh_token');
localStorage.removeItem('user_data');
}
} catch (error) {
console.error('Token verification error:', error);
// Remove invalid tokens
localStorage.removeItem('auth_token');
localStorage.removeItem('refresh_token');
localStorage.removeItem('user_data');
}
}
// Add CSS animation for slide out
const style = document.createElement('style');
style.textContent = `
@keyframes slideOutRight {
from {
opacity: 1;
transform: translateX(0);
}
to {
opacity: 0;
transform: translateX(100%);
}
}
`;
document.head.appendChild(style);