jaivial's picture
Okay now add nitro as ssr and add an express backend with a simple login and authentication system that creates session id and stores session id as http only cookie and in the ssr middleware for all routes we do have a check for the http only cookie to check if is present or expired to redirect to home page or to login page
d502d70 verified
<!DOCTYPE html>
<html lang="en" class="dark">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Login - Vite React TS</title>
<link rel="stylesheet" href="style.css">
<script src="https://cdn.tailwindcss.com"></script>
<script src="https://cdn.jsdelivr.net/npm/feather-icons/dist/feather.min.js"></script>
<script>
tailwind.config = {
darkMode: 'class',
theme: {
extend: {
colors: {
primary: '#4f46e5',
secondary: '#ec4899',
dark: '#0f172a',
darker: '#020617',
surface: '#1e293b',
}
}
}
}
</script>
</head>
<body class="bg-darker text-slate-200 font-sans antialiased min-h-screen flex items-center justify-center relative overflow-hidden">
<!-- Background decorative blobs -->
<div class="absolute top-0 left-0 w-96 h-96 bg-primary/20 rounded-full blur-3xl -translate-x-1/2 -translate-y-1/2 pointer-events-none"></div>
<div class="absolute bottom-0 right-0 w-96 h-96 bg-secondary/10 rounded-full blur-3xl translate-x-1/2 translate-y-1/2 pointer-events-none"></div>
<div class="relative z-10 w-full max-w-md p-8">
<!-- Logo -->
<div class="text-center mb-8">
<a href="/" class="inline-flex items-center gap-2 text-2xl font-bold text-white">
<svg height="32" viewBox="0 0 32 32" width="32" xmlns="http://www.w3.org/2000/svg"><path d="M29,1H3A2,2,0,0,0,1,3V29a2,2,0,0,0,2,2H29a2,2,0,0,0,2-2V3A2,2,0,0,0,29,1ZM11,27H7V17h4ZM9,15.5A2.5,2.5,0,1,1,11.5,13,2.5,2.5,0,0,1,9,15.5ZM27,27H23V21c0-1.38-.5-2.5-2-2.5s-2,1.12-2,2.5v6H15V17h4v2.08a4,4,0,0,1,3.5-1.75c2.51,0,4.5,1.75,4.5,5.17Z" fill="#6366f1"/></svg>
Vite<span>ReactTS</span>
</a>
<p class="text-slate-400 mt-2">Sign in to your account</p>
</div>
<!-- Login Form -->
<div class="bg-surface border border-slate-700/50 rounded-2xl p-8 shadow-2xl">
<form id="loginForm" class="space-y-6">
<!-- Email Input -->
<div>
<label for="email" class="block text-sm font-medium text-slate-300 mb-2">Email Address</label>
<div class="relative">
<i data-feather="mail" class="absolute left-3 top-1/2 -translate-y-1/2 text-slate-500 w-5 h-5"></i>
<input
type="email"
id="email"
name="email"
required
class="w-full bg-dark border border-slate-700 rounded-lg py-3 pl-10 pr-4 text-white placeholder-slate-500 focus:outline-none focus:border-primary focus:ring-1 focus:ring-primary transition-colors"
placeholder="admin@vite.com"
>
</div>
</div>
<!-- Password Input -->
<div>
<label for="password" class="block text-sm font-medium text-slate-300 mb-2">Password</label>
<div class="relative">
<i data-feather="lock" class="absolute left-3 top-1/2 -translate-y-1/2 text-slate-500 w-5 h-5"></i>
<input
type="password"
id="password"
name="password"
required
class="w-full bg-dark border border-slate-700 rounded-lg py-3 pl-10 pr-4 text-white placeholder-slate-500 focus:outline-none focus:border-primary focus:ring-1 focus:ring-primary transition-colors"
placeholder="••••••••"
>
</div>
</div>
<!-- Error Message -->
<div id="errorMessage" class="hidden bg-red-500/10 border border-red-500/50 text-red-400 px-4 py-3 rounded-lg text-sm"></div>
<!-- Submit Button -->
<button
type="submit"
id="submitBtn"
class="w-full bg-primary hover:bg-indigo-700 text-white font-semibold py-3 px-4 rounded-lg transition-all shadow-lg shadow-primary/20 active:scale-[0.98] flex items-center justify-center gap-2"
>
<i data-feather="log-in"></i>
<span>Sign In</span>
</button>
</form>
<!-- Demo Credentials -->
<div class="mt-6 p-4 bg-dark/50 rounded-lg border border-slate-700/50">
<p class="text-xs text-slate-500 mb-2">Demo Credentials:</p>
<div class="text-xs space-y-1">
<p class="text-slate-400">Admin: <span class="text-primary font-mono">admin@vite.com / admin123</span></p>
<p class="text-slate-400">User: <span class="text-primary font-mono">user@vite.com / user123</span></p>
</div>
</div>
</div>
<!-- Footer -->
<p class="text-center text-sm text-slate-500 mt-8">
Protected by Nitro SSR + Express Sessions
</p>
</div>
<script>
feather.replace();
const loginForm = document.getElementById('loginForm');
const errorMessage = document.getElementById('errorMessage');
const submitBtn = document.getElementById('submitBtn');
loginForm.addEventListener('submit', async (e) => {
e.preventDefault();
const email = document.getElementById('email').value;
const password = document.getElementById('password').value;
// Show loading state
submitBtn.disabled = true;
submitBtn.innerHTML = `
<svg class="animate-spin h-5 w-5 text-white" xmlns="http://www.w3.org/2000/svg" fill="none" viewBox="0 0 24 24">
<circle class="opacity-25" cx="12" cy="12" r="10" stroke="currentColor" stroke-width="4"></circle>
<path class="opacity-75" fill="currentColor" d="M4 12a8 8 0 018-8V0C5.373 0 0 5.373 0 12h4zm2 5.291A7.962 7.962 0 014 12H0c0 3.042 1.135 5.824 3 7.938l3-2.647z"></path>
</svg>
<span>Signing in...</span>
`;
try {
const response = await fetch('/api/login', {
method: 'POST',
headers: {
'Content-Type': 'application/json'
},
credentials: 'include',
body: JSON.stringify({ email, password })
});
const data = await response.json();
if (response.ok) {
// Redirect to dashboard on success
window.location.href = '/';
} else {
// Show error message
errorMessage.textContent = data.message || 'Login failed. Please try again.';
errorMessage.classList.remove('hidden');
}
} catch (error) {
errorMessage.textContent = 'Network error. Please try again.';
errorMessage.classList.remove('hidden');
} finally {
// Reset button state
submitBtn.disabled = false;
submitBtn.innerHTML = `
<i data-feather="log-in"></i>
<span>Sign In</span>
`;
feather.replace();
}
});
// Hide error message when user starts typing
document.getElementById('email').addEventListener('input', () => {
errorMessage.classList.add('hidden');
});
document.getElementById('password').addEventListener('input', () => {
errorMessage.classList.add('hidden');
});
</script>
</body>
</html>