Add real user auth (register/login) + admin UI login
This commit is contained in:
@@ -34,23 +34,81 @@
|
||||
<h1>Admin Panel</h1>
|
||||
<p>Modular admin UI: cada módulo se registra automáticamente.</p>
|
||||
|
||||
<label>
|
||||
Tenant ID:
|
||||
<input id="tenantId" value="default" />
|
||||
</label>
|
||||
<button id="refresh" style="margin-left: 0.5rem;">Actualizar módulos</button>
|
||||
<span id="status" style="margin-left: 1rem; color: #555;"></span>
|
||||
<div id="auth" class="module">
|
||||
<h2>Acceso</h2>
|
||||
<div id="login-form">
|
||||
<strong>Iniciar sesión</strong><br/>
|
||||
<input id="loginEmail" placeholder="Email" style="margin-bottom:0.5rem;" />
|
||||
<input id="loginPassword" type="password" placeholder="Contraseña" style="margin-bottom:0.5rem;" />
|
||||
<button id="loginBtn">Iniciar sesión</button>
|
||||
<div id="loginStatus" style="margin-top:0.5rem;color:#555;"></div>
|
||||
<hr />
|
||||
<strong>Registrar usuario</strong><br/>
|
||||
<input id="registerName" placeholder="Nombre (opcional)" style="margin-bottom:0.5rem;" />
|
||||
<input id="registerEmail" placeholder="Email" style="margin-bottom:0.5rem;" />
|
||||
<input id="registerPassword" type="password" placeholder="Contraseña" style="margin-bottom:0.5rem;" />
|
||||
<input id="registerTenant" placeholder="Tenant ID" value="default" style="margin-bottom:0.5rem;" />
|
||||
<button id="registerBtn">Registrar</button>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div id="modules"></div>
|
||||
<div id="main" style="display: none;">
|
||||
<label>
|
||||
Tenant ID:
|
||||
<input id="tenantId" value="default" />
|
||||
</label>
|
||||
<button id="refresh" style="margin-left: 0.5rem;">Actualizar módulos</button>
|
||||
<button id="logout" style="margin-left: 0.5rem;">Cerrar sesión</button>
|
||||
<span id="status" style="margin-left: 1rem; color: #555;"></span>
|
||||
|
||||
<div id="module-detail"></div>
|
||||
<div id="modules"></div>
|
||||
|
||||
<div id="module-detail"></div>
|
||||
</div>
|
||||
|
||||
<script>
|
||||
const apiRoot = '/api/admin';
|
||||
const authRoot = '/api/auth';
|
||||
const tokenKey = 'planner_admin_token';
|
||||
|
||||
const authPanel = document.getElementById('auth');
|
||||
const mainPanel = document.getElementById('main');
|
||||
const tenantInput = document.getElementById('tenantId');
|
||||
const modulesContainer = document.getElementById('modules');
|
||||
const detailContainer = document.getElementById('module-detail');
|
||||
const statusEl = document.getElementById('status');
|
||||
|
||||
const loginStatus = document.getElementById('loginStatus');
|
||||
const loginEmail = document.getElementById('loginEmail');
|
||||
const loginPassword = document.getElementById('loginPassword');
|
||||
const loginBtn = document.getElementById('loginBtn');
|
||||
|
||||
const registerName = document.getElementById('registerName');
|
||||
const registerEmail = document.getElementById('registerEmail');
|
||||
const registerPassword = document.getElementById('registerPassword');
|
||||
const registerTenant = document.getElementById('registerTenant');
|
||||
const registerBtn = document.getElementById('registerBtn');
|
||||
|
||||
const logoutBtn = document.getElementById('logout');
|
||||
|
||||
let authToken = localStorage.getItem(tokenKey) || '';
|
||||
|
||||
function setStatus(msg) {
|
||||
if (statusEl) statusEl.textContent = msg;
|
||||
}
|
||||
|
||||
function setLoginStatus(msg) {
|
||||
if (loginStatus) loginStatus.textContent = msg;
|
||||
}
|
||||
|
||||
function setToken(token) {
|
||||
authToken = token;
|
||||
if (token) {
|
||||
localStorage.setItem(tokenKey, token);
|
||||
} else {
|
||||
localStorage.removeItem(tokenKey);
|
||||
}
|
||||
}
|
||||
|
||||
function withTenant(url) {
|
||||
const tenantId = tenantInput.value || 'default';
|
||||
@@ -59,20 +117,91 @@
|
||||
}
|
||||
|
||||
async function api(path, options = {}) {
|
||||
const statusEl = document.getElementById('status');
|
||||
statusEl.textContent = 'Consultando ' + path + '...';
|
||||
setStatus('Consultando ' + path + '...');
|
||||
if (authToken) {
|
||||
options.headers = {
|
||||
...(options.headers || {}),
|
||||
Authorization: `Bearer ${authToken}`,
|
||||
};
|
||||
}
|
||||
|
||||
const res = await fetch(withTenant(`${apiRoot}${path}`), options);
|
||||
const data = await res.json().catch(() => null);
|
||||
if (!res.ok) {
|
||||
const err = data?.message ? data.message : res.statusText;
|
||||
statusEl.textContent = `Error ${res.status}`;
|
||||
setStatus(`Error ${res.status}`);
|
||||
throw new Error(err || `HTTP ${res.status}`);
|
||||
}
|
||||
statusEl.textContent = 'OK';
|
||||
setTimeout(() => (statusEl.textContent = ''), 1500);
|
||||
setStatus('OK');
|
||||
setTimeout(() => setStatus(''), 1500);
|
||||
return data;
|
||||
}
|
||||
|
||||
async function login() {
|
||||
setLoginStatus('Iniciando sesión...');
|
||||
try {
|
||||
const res = await fetch(`${authRoot}/login`, {
|
||||
method: 'POST',
|
||||
headers: { 'Content-Type': 'application/json' },
|
||||
body: JSON.stringify({
|
||||
tenantId: registerTenant.value || 'default',
|
||||
email: loginEmail.value,
|
||||
password: loginPassword.value,
|
||||
}),
|
||||
});
|
||||
const data = await res.json();
|
||||
if (!res.ok || !data.token) {
|
||||
setLoginStatus(data.message || 'Credenciales inválidas');
|
||||
return;
|
||||
}
|
||||
setToken(data.token);
|
||||
setLoginStatus('Sesión iniciada');
|
||||
showMain();
|
||||
} catch (err) {
|
||||
setLoginStatus(err.message || err);
|
||||
}
|
||||
}
|
||||
|
||||
async function register() {
|
||||
setLoginStatus('Registrando...');
|
||||
try {
|
||||
const res = await fetch(`${authRoot}/register`, {
|
||||
method: 'POST',
|
||||
headers: { 'Content-Type': 'application/json' },
|
||||
body: JSON.stringify({
|
||||
tenantId: registerTenant.value || 'default',
|
||||
name: registerName.value,
|
||||
email: registerEmail.value,
|
||||
password: registerPassword.value,
|
||||
}),
|
||||
});
|
||||
const data = await res.json();
|
||||
if (!res.ok) {
|
||||
setLoginStatus(data.message || 'Error al registrar');
|
||||
return;
|
||||
}
|
||||
setLoginStatus('Registrado. Inicia sesión.');
|
||||
} catch (err) {
|
||||
setLoginStatus(err.message || err);
|
||||
}
|
||||
}
|
||||
|
||||
function logout() {
|
||||
setToken('');
|
||||
showAuth();
|
||||
}
|
||||
|
||||
function showAuth() {
|
||||
if (authPanel) authPanel.style.display = 'block';
|
||||
if (mainPanel) mainPanel.style.display = 'none';
|
||||
}
|
||||
|
||||
function showMain() {
|
||||
if (authPanel) authPanel.style.display = 'none';
|
||||
if (mainPanel) mainPanel.style.display = 'block';
|
||||
loadModules();
|
||||
}
|
||||
|
||||
function formatJson(value) {
|
||||
return `<pre style="white-space: pre-wrap; word-break: break-word;">${JSON.stringify(value, null, 2)}</pre>`;
|
||||
}
|
||||
@@ -341,10 +470,17 @@
|
||||
detailContainer.appendChild(panel);
|
||||
}
|
||||
|
||||
loginBtn.addEventListener('click', login);
|
||||
registerBtn.addEventListener('click', register);
|
||||
logoutBtn.addEventListener('click', logout);
|
||||
document.getElementById('refresh').addEventListener('click', loadModules);
|
||||
tenantInput.addEventListener('change', loadModules);
|
||||
|
||||
loadModules();
|
||||
if (authToken) {
|
||||
showMain();
|
||||
} else {
|
||||
showAuth();
|
||||
}
|
||||
</script>
|
||||
</body>
|
||||
</html>
|
||||
|
||||
Reference in New Issue
Block a user