tspace-nfs
Version:
tspace-nfs is a Network File System (NFS) and provides both server and client capabilities for accessing files over a network.
121 lines (108 loc) • 4.62 kB
HTML
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<script src="https://cdn.tailwindcss.com/3.4.14"></script>
<title>{{title}}</title>
{{logo.fav}}
<style>
#loading {
display: none;
position: fixed;
top: 0;
left: 0;
right: 0;
bottom: 0;
background: rgba(255, 255, 255, 0.8);
opacity: 0.75;
justify-content: center;
align-items: center;
z-index: 9999;
}
</style>
</head>
<body class="bg-gray-100 dark:bg-gray-900">
<div id="loading" class="flex">
<div class="flex flex-col items-center">
<div class="animate-spin rounded-full h-20 w-20 border-t-4 border-b-4 border-blue-600"></div>
<p class="text-gray-800 dark:text-gray-200 text-xl font-semibold mt-4">Loading...</p>
</div>
</div>
<div class="flex items-center justify-center min-h-screen">
<div class="w-full max-w-md p-8 space-y-6 bg-white rounded-lg shadow-md dark:bg-gray-800">
<div class="sm:mx-auto sm:w-full sm:max-w-sm">
{{logo.login}}
<h2 class="text-center text-2xl/9 font-bold tracking-tight text-gray-900 dark:text-gray-100">Sign in to NFS</h2>
</div>
<form id="loginForm" class="space-y-6" novalidate>
<div>
<label for="username" class="block text-sm font-medium text-gray-700 dark:text-gray-300">Username</label>
<input
type="username"
id="username"
name="username"
required
autocomplete="off"
class="w-full p-2 mt-1 text-gray-900 border border-gray-300 rounded-md shadow-sm dark:bg-gray-700 dark:border-gray-600 dark:text-white focus:ring-indigo-500 focus:border-indigo-500"
>
</div>
<div>
<label for="password" class="block text-sm font-medium text-gray-700 dark:text-gray-300">Password</label>
<input
type="password"
id="password"
name="password"
required
autocomplete="off"
class="w-full p-2 mt-1 text-gray-900 border border-gray-300 rounded-md shadow-sm dark:bg-gray-700 dark:border-gray-600 dark:text-white focus:ring-indigo-500 focus:border-indigo-500"
>
</div>
<div>
<button type="submit" class="w-full py-2 text-white bg-indigo-600 rounded-md hover:bg-indigo-700 focus:ring-2 focus:ring-indigo-500">
Sign in
</button>
</div>
<div id="errorMessage" class="text-red-500 text-sm hidden">Invalid username or password. Please try again.</div>
</form>
</div>
</div>
</body>
<script>
const $query = (element) => document.querySelector(element)
const loading = $query('#loading')
const loginForm = $query('#loginForm');
const errorMessage = $query('#errorMessage');
const url = `${window.location.protocol}//${window.location.host}`
loginForm.addEventListener('submit', async (event) => {
event.preventDefault();
const username = $query('#username').value;
const password = $query('#password').value;
try {
loading.style.display = 'flex';
const response = await fetch(`${url}/studio/api/login`, {
method: 'POST',
headers: {
'Content-Type': 'application/json',
},
body: JSON.stringify({ username, password }),
});
if (response.ok) {
const data = await response.json();
errorMessage.classList.add('hidden');
await new Promise(r => setTimeout(r, 1000));
loading.style.display = 'none'
window.location.reload()
return
}
await new Promise(r => setTimeout(r, 500));
loading.style.display = 'none';
errorMessage.classList.remove('hidden');
} catch (error) {
errorMessage.textContent = 'An error occurred. Please try again later.';
errorMessage.classList.remove('hidden');
loading.style.display = 'flex';
}
})
</script>
</html>