Regresión del commit anterior. /log-out es un GET que destruye la sesión, y yo
lo enlacé con <Link>: el router de Next lo prefetchea al entrar en pantalla, así
que la cookie se borraba sin que nadie pulsara. La página se renderizaba en el
servidor con la sesión («¡Ya estás conectado!») y al recargar ya no había sesión.
Reproducido: una petición de prefetch (RSC: 1, Next-Router-Prefetch: 1) a
/es/log-out responde `set-cookie: nightspire_session=; Max-Age=0`.
Se enlaza con <a> plano, que es justo como lo hace SiteHeader. Queda comentado
en ambos sitios para que no vuelva a colarse.
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Ninguna de las dos miraba la sesión, así que quien ya estaba conectado veía el
formulario de login o el de registro. Ahora, con `bnetId` en sesión (lo mismo que
da por buena la sesión en el resto del sitio), sale «¡Ya estás conectado!» y un
enlace a /log-out, como las plantillas del tema.
En crear cuenta el cuadro informativo se mantiene: sigue explicando cómo son las
cuentas aunque ya tengas sesión. Solo se sustituye el formulario.
Las dos pasan a `force-dynamic`: leen cookies y no se pueden prerenderizar.
Verificado en producción sellando una cookie de sesión real con iron-session:
con sesión salen los dos avisos y desaparece el formulario; sin sesión el
formulario sigue ahí y el aviso no se renderiza.
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Dos cosas que el port de Django cambió sin querer:
- El hash de activación se generaba con `randomBytes(16).toString('hex')`: 32
caracteres, sí, pero solo `0-9a-f`. El original era `get_random_string(32)`,
alfanumérico con mayúsculas y minúsculas (`?act=P4JHQDJey2jJDnEBnqUePI5O2qEFB1`).
- El aviso de cuenta creada era un genérico «Revisa tu correo», mientras que el
original decía qué cuenta se creó y a qué dirección fue el enlace. Se recupera
el formato de dos líneas (con id `create-response`, como el original).
El muestreo por rechazo del token de seguridad se sube a `lib/random-token.ts` y
lo comparten los dos generadores, que solo se diferencian en el alfabeto: letras
para el token que se teclea a mano, alfanumérico para el hash de la URL.
Verificado con 100.000 hashes: todos casan /^[A-Za-z0-9]{32}$/, salen los 62
caracteres y la desviación por carácter se queda en 1,3% (ruido).
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Al fallar (p.ej. contraseña incorrecta) el token de Turnstile ya se consumió en el
servidor; el reintento reenviaba el mismo token y daba "captcha fallida" aunque
estuviera resuelto. Se resetea el widget (setCaptcha('') + captchaKey++ con
<Turnstile key={captchaKey}>, mismo patrón que recover/restore/quest) en login,
create-account y trade-points (venta y canje). El check de código no usa captcha,
no se resetea ahí.
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Mueve la página de login a /log-in y actualiza todas las referencias (enlace
CONECTAR del header y los redirect de auth de ~45 páginas). /login ahora da 404.
La API /api/auth/login no se toca (solo la ruta de página).
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
El navegador mostraba su tooltip nativo "Rellene este campo" en los inputs required
en vez del mensaje de error del sitio. Con noValidate en el form, la validación en
cliente (clientError) muestra el red-form-response también para campos vacíos.
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Antes los errores (contraseñas/correos que no coinciden, campos vacíos) solo se
mostraban en rojo tras el envío al servidor. Ahora se validan en el cliente y se
muestran al instante como red-form-response, sin llamada al servidor. Nueva clave
emailMismatch. El servidor sigue haciendo la validación final (Gmail, existe, etc.).
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Mueve la página de registro a /create-account (los enlaces del header y del login
apuntan ahí; /register ahora da 404 con el 404 del sitio). Rediseño según el
diseño aportado, adaptado a Battle.net (cuenta por email, sin usuario): caja de
info bnet (tipo bnet, reglas de contraseña/Gmail, activación, login por correo),
ojos para mostrar/ocultar contraseña, checkbox de "no accedo desde EEUU" +
términos (con enlaces), ambos requeridos para habilitar el botón, y bloqueo de
pegar en confirmar contraseña/correo. i18n ES/EN. No incluye FingerprintJS
(antiabuso por huella de dispositivo, requiere backend; la protección es Turnstile
+ Gmail + activación).
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>