function makeCouchURLDisplay(host, user, pass, dbname) { if (!host) return ''; var display = user + ':' + pass + '@' + host.replace(/^https?:\/\//, '') + '/' + dbname; return display; } PAGES.login = { Esconder: true, Title: 'Login', onboarding: function (step) { // Multi-step onboarding flow step = step || 'config'; if (step === 'config') { // Step 1: "Configuraci贸n de datos" var field_couch = safeuuid(); var field_secret = safeuuid(); var btn_existing_server = safeuuid(); var btn_new_server = safeuuid(); var btn_skip = safeuuid(); var div_server_config = safeuuid(); container.innerHTML = html`

隆Bienvenido a TeleSec! 馃帀

Paso 1: Configuraci贸n de datos

Para comenzar, elige c贸mo quieres configurar tu base de datos:

`; document.getElementById(btn_existing_server).onclick = () => { document.getElementById(div_server_config).style.display = 'block'; }; document.getElementById(btn_new_server).onclick = () => { window.open('https://tech.eus/telesec-signup.php', '_blank'); toastr.info( 'Una vez creado el servidor, vuelve aqu铆 y con茅ctate usando el bot贸n "Conectar a un servidor existente"' ); }; document.getElementById(btn_skip).onclick = () => { // Continue to persona creation without server config // Check if personas already exist (shouldn't happen but safety check) var hasPersonas = Object.keys(SC_Personas).length > 0; if (hasPersonas) { toastr.info('Ya existen personas. Saltando creaci贸n de cuenta.'); localStorage.setItem('TELESEC_ONBOARDING_COMPLETE', 'true'); open_page('login'); setUrlHash('login'); } else { open_page('login,onboarding-persona'); setUrlHash('login,onboarding-persona'); } }; document.getElementById(btn_skip + '-save').onclick = () => { var url = document.getElementById(field_couch).value.trim(); var secret = document.getElementById(field_secret).value.trim(); if (!url) { toastr.error('Por favor ingresa un servidor CouchDB'); return; } if (!secret) { toastr.error('La clave de encriptaci贸n es obligatoria'); return; } // Normalize URL: add https:// if no protocol specified var normalizedUrl = url; if (!/^https?:\/\//i.test(url)) { normalizedUrl = 'https://' + url; } var URL_PARSED = parseURL(normalizedUrl); var user = URL_PARSED.username || ''; var pass = URL_PARSED.password || ''; var dbname = URL_PARSED.pathname ? URL_PARSED.pathname.replace(/^\//, '') : ''; var host = URL_PARSED.hostname || normalizedUrl; localStorage.setItem('TELESEC_COUCH_URL', 'https://' + host); localStorage.setItem('TELESEC_COUCH_DBNAME', dbname); localStorage.setItem('TELESEC_COUCH_USER', user); localStorage.setItem('TELESEC_COUCH_PASS', pass); localStorage.setItem('TELESEC_SECRET', secret.toUpperCase()); SECRET = secret.toUpperCase(); try { DB.init({ secret: SECRET, remoteServer: 'https://' + host, username: user, password: pass, dbname: dbname || undefined, }); toastr.success('Servidor configurado correctamente'); document.getElementById('loading').style.display = 'block'; function waitForReplicationIdle(maxWaitMs, idleMs) { var startTime = Date.now(); var lastSeenSync = window.TELESEC_LAST_SYNC || 0; return new Promise((resolve) => { var interval = setInterval(() => { var now = Date.now(); var currentSync = window.TELESEC_LAST_SYNC || 0; if (currentSync > lastSeenSync) { lastSeenSync = currentSync; } var lastActivity = Math.max(lastSeenSync, startTime); var idleLongEnough = now - lastActivity >= idleMs; var timedOut = now - startTime >= maxWaitMs; if (idleLongEnough || timedOut) { clearInterval(interval); resolve(); } }, 250); }); } // Wait until replication goes idle or timeout waitForReplicationIdle(10000, 2500).then(() => { // Check if personas were replicated from server var hasPersonas = Object.keys(SC_Personas).length > 0; document.getElementById('loading').style.display = 'none'; if (hasPersonas) { // Personas found from server, skip persona creation step toastr.info('Se encontraron personas en el servidor. Saltando creaci贸n de cuenta.'); localStorage.setItem('TELESEC_ONBOARDING_COMPLETE', 'true'); open_page('login'); setUrlHash('login'); } else { // No personas found, continue to persona creation open_page('login,onboarding-persona'); setUrlHash('login,onboarding-persona'); } }); } catch (e) { document.getElementById('loading').style.display = 'none'; toastr.error('Error al configurar el servidor: ' + (e.message || e)); } }; } else if (step === 'persona') { // Step 2: "Crea una persona" var field_nombre = safeuuid(); var btn_crear = safeuuid(); // Check if personas already exist var hasPersonas = Object.keys(SC_Personas).length > 0; if (hasPersonas) { toastr.info('Se detectaron personas existentes. Redirigiendo al login.'); localStorage.setItem('TELESEC_ONBOARDING_COMPLETE', 'true'); open_page('login'); setUrlHash('login'); return; } container.innerHTML = html`

隆Bienvenido a TeleSec! 馃帀

Paso 2: Crea tu cuenta de administrador

Para continuar, necesitas crear una cuenta personal con permisos de administrador.

鈩癸笍 Esta cuenta tendr谩 todos los permisos de administrador y podr谩s gestionar la aplicaci贸n completamente.

`; document.getElementById(btn_crear).onclick = () => { var nombre = document.getElementById(field_nombre).value.trim(); if (!nombre) { toastr.error('Por favor ingresa tu nombre'); return; } // Disable button to prevent duplicate creation var btnElement = document.getElementById(btn_crear); btnElement.disabled = true; btnElement.style.opacity = '0.5'; btnElement.innerText = 'Creando...'; // Create persona with all admin permissions from PERMS object var allPerms = Object.keys(PERMS).join(',') + ','; var personaId = safeuuid('admin-'); var persona = { Nombre: nombre, Roles: allPerms, Region: '', Monedero_Balance: 0, markdown: 'Cuenta de administrador creada durante el onboarding', }; DB.put('personas', personaId, persona) .then(() => { toastr.success('隆Cuenta creada exitosamente! 馃帀'); localStorage.setItem('TELESEC_ONBOARDING_COMPLETE', 'true'); localStorage.setItem('TELESEC_ADMIN_ID', personaId); // Auto-login SUB_LOGGED_IN_ID = personaId; SUB_LOGGED_IN_DETAILS = persona; SUB_LOGGED_IN = true; SetPages(); setTimeout(() => { open_page('index'); setUrlHash('index'); }, 500); }) .catch((e) => { toastr.error('Error creando cuenta: ' + (e.message || e)); // Re-enable button on error btnElement.disabled = false; btnElement.style.opacity = '1'; btnElement.innerText = 'Crear cuenta y empezar'; }); }; } }, edit: function (mid) { // Handle onboarding routes if (mid === 'onboarding-config') { PAGES.login.onboarding('config'); return; } if (mid === 'onboarding-persona') { PAGES.login.onboarding('persona'); return; } // Setup form to configure CouchDB remote and initial group/secret var field_couch = safeuuid(); var field_secret = safeuuid(); var btn_save = safeuuid(); container.innerHTML = html`

Configuraci贸n del servidor CouchDB

Aviso: Despu茅s de guardar, la aplicaci贸n intentar谩 sincronizar con el servidor CouchDB en segundo plano. Puede que falten registros hasta que se termine. Tenga paciencia.

Despu茅s de guardar, el navegador intentar谩 sincronizar en segundo plano con el servidor.

`; // Helper: normalize and apply config object function applyConfig(cfg) { try { if (!cfg) throw new Error('JSON vac铆o'); var url = cfg.server || cfg.couch || cfg.url || cfg.host || cfg.hostname || cfg.server_url; var dbname = cfg.dbname || cfg.database || cfg.db || cfg.name; var user = cfg.username || cfg.user || cfg.u; var pass = cfg.password || cfg.pass || cfg.p; var secret = (cfg.secret || cfg.key || cfg.secretKey || cfg.SECRET || '').toString(); if (!url) throw new Error('Falta campo "server" en JSON'); var URL_PARSED = parseURL(url); var host = URL_PARSED.hostname || url; localStorage.setItem('TELESEC_COUCH_URL', 'https://' + host); if (dbname) localStorage.setItem('TELESEC_COUCH_DBNAME', dbname); if (user) localStorage.setItem('TELESEC_COUCH_USER', user); if (pass) localStorage.setItem('TELESEC_COUCH_PASS', pass); if (secret) { localStorage.setItem('TELESEC_SECRET', secret.toUpperCase()); SECRET = secret.toUpperCase(); } DB.init({ secret: SECRET, remoteServer: 'https://' + url.replace(/^https?:\/\//, ''), username: user, password: pass, dbname: dbname || undefined, }); toastr.success('Configuraci贸n aplicada e iniciando sincronizaci贸n'); location.hash = '#login'; setTimeout(function () { location.reload(); }, 400); } catch (e) { toastr.error('Error aplicando configuraci贸n: ' + (e && e.message ? e.message : e)); } } document.getElementById(btn_save).onclick = () => { var url = document.getElementById(field_couch).value.trim(); var secret = document.getElementById(field_secret).value.trim(); var URL_PARSED = parseURL(url); var host = URL_PARSED.hostname || url; var user = URL_PARSED.username || ''; var pass = URL_PARSED.password || ''; var dbname = URL_PARSED.pathname ? URL_PARSED.pathname.replace(/^\//, '') : ''; localStorage.setItem('TELESEC_COUCH_URL', 'https://' + host); localStorage.setItem('TELESEC_COUCH_DBNAME', dbname); localStorage.setItem('TELESEC_COUCH_USER', user); localStorage.setItem('TELESEC_COUCH_PASS', pass); localStorage.setItem('TELESEC_SECRET', secret.toUpperCase()); SECRET = secret.toUpperCase(); try { DB.init({ secret: SECRET, remoteServer: 'https://' + host, username: user, password: pass, dbname: dbname || undefined, }); toastr.success('Iniciando sincronizaci贸n con CouchDB'); location.hash = '#login'; //location.reload(); } catch (e) { toastr.error('Error al iniciar sincronizaci贸n: ' + e.message); } }; }, index: function (mid) { // Check if onboarding is needed var onboardingComplete = localStorage.getItem('TELESEC_ONBOARDING_COMPLETE'); var hasPersonas = Object.keys(SC_Personas).length > 0; // If no personas exist and onboarding not complete, redirect to onboarding if (!hasPersonas && !onboardingComplete && !AC_BYPASS) { open_page('login,onboarding-config'); setUrlHash('login,onboarding-config'); return; } var field_persona = safeuuid(); var btn_guardar = safeuuid(); var btn_reload = safeuuid(); var div_actions = safeuuid(); container.innerHTML = html`

Iniciar sesi贸n

Valores
Configurar base de datos
`; var divact = document.getElementById(div_actions); addCategory_Personas( divact, SC_Personas, '', (value) => { document.getElementById(field_persona).value = value; }, '驴Qui茅n eres?', true, "- Pulsa recargar o rellena los credenciales abajo, si quieres crear un nuevo grupo, pulsa el boton 'Desde cero' -" ); document.getElementById(btn_guardar).onclick = () => { if (document.getElementById(field_persona).value == '') { alert('Tienes que elegir tu cuenta!'); return; } SUB_LOGGED_IN_ID = document.getElementById(field_persona).value; SUB_LOGGED_IN_DETAILS = SC_Personas[SUB_LOGGED_IN_ID]; SUB_LOGGED_IN = true; SetPages(); if (location.hash.replace('#', '').split("?")[0].startsWith('login')) { open_page('index'); setUrlHash('index'); } else { open_page(location.hash.replace('#', '').split("?")[0]); } }; document.getElementById(btn_reload).onclick = () => { open_page('login'); }; // AC_BYPASS: allow creating a local persona from the login screen if (AC_BYPASS) { var btn_bypass_create = safeuuid(); divact.innerHTML += ``; document.getElementById(btn_bypass_create).onclick = () => { var name = prompt('Nombre de la persona (ej: Admin):'); if (!name) return; var id = 'bypass-' + Date.now(); var persona = { Nombre: name, Roles: 'ADMIN,' }; DB.put('personas', id, persona) .then(() => { toastr.success('Persona creada: ' + id); localStorage.setItem('TELESEC_BYPASS_ID', id); SUB_LOGGED_IN_ID = id; SUB_LOGGED_IN_DETAILS = persona; SUB_LOGGED_IN = true; SetPages(); open_page('index'); }) .catch((e) => { toastr.error('Error creando persona: ' + (e && e.message ? e.message : e)); }); }; } }, };