Commit Graph

8 Commits

Author SHA1 Message Date
Inna edf1feedf6 El correo sale en MAYÚSCULAS en los correos de token y activación
- Token: el asunto era «Token de seguridad - NightSpire» a secas. El original
  decía «Token de seguridad de la cuenta {username} - {NOMBRE_SERVIDOR}»; la
  reescritura se dejó la cuenta por el camino. Ese correo sale de
  `session.bnetEmail`, que ya va normalizado, así que las MAYÚSCULAS son gratis.
- Activación: asunto y cuerpo pasan a normalizar el correo, para que se vea igual
  que en el del token. El DESTINATARIO va sin normalizar: es la dirección tal
  cual la escribió el usuario.

El correo de activación se manda desde DOS sitios: `registerAccount` y
`recover.ts` (el reenvío del enlace, «no me llegó»). Se cambian los dos o saldría
distinto según por dónde lo pidieras. Lo delató el build: /api/auth/activate
cargaba un chunk y /api/auth/recover otro con la versión vieja.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
2026-07-15 18:40:33 +00:00
Inna 4f8f101ba8 Impedir cuentas Battle.net duplicadas al activar; citar el límite real de 8
`registerAccount` ya rechazaba un correo con cuenta bnet, pero `activateAccount`
insertaba SIN volver a comprobarlo, y entre registrarse y activar el correo puede
dejar de estar libre. Como `battlenet_accounts.email` no tiene índice único, la
BD tampoco lo frenaba: hay 3 filas TEST@TEST.COM de 2024 que lo demuestran.
Ahora se recomprueba antes del INSERT y se borra la activación, que ya no sirve.
Nuevo error `emailExists` (ES/EN) para no soltar un «enlace inválido» que despista.

MAX_GAME_ACCOUNTS sube a lib/bnet.ts y lo usa el correo de activación: el texto
prometía 10 cuentas mientras el código cortaba en 8, justo por estar el número
escrito a mano en los dos sitios. El límite del panel ya funcionaba (API + aviso
«Has alcanzado el máximo de cuentas»); solo se centraliza la constante.

Verificado en producción con una activación pendiente de un correo que ya tenía
bnet: devuelve emailExists, no crea la cuenta y el contador no sube.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
2026-07-15 18:02:54 +00:00
Inna a78417e954 Activación de cuenta: recuperar el diseño del tema y decir QUÉ cuenta se activó
La página mostraba un genérico «¡Cuenta activada!» + enlace a iniciar sesión.
Se recupera la estructura de las plantillas del tema (activation_success.html /
activation_invalid.html): bienvenida, la cuenta en `yellow-info`, y el aviso de
enlace inválido en `red-info2` con la línea de contacto.

El original dejaba un hueco: renderizaba `{{ username }}` pero la vista hacía
`render(...)` SIN contexto, así que salía «La cuenta  ha sido activada». Aquí
`activateAccount` devuelve el email y sí se muestra. Va el email tal cual se
registró, no el normalizado: ese va en MAYÚSCULAS porque lo exige el SRP6 de
Battle.net y en pantalla quedaría como INNA@INNA.CL.

El nombre del servidor sale de `getRealmName()` (acore_auth.realmlist), como en
el resto de páginas, en vez de escribirlo a mano.

Verificado en producción: el caso inválido lo pinta el servidor y sale idéntico
al del tema; y activando una fila de prueba, el API devuelve {success, email} y
reutilizar el enlace o mandar un hash que no existe dan `invalidLink` (filas de
prueba borradas después).

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
2026-07-15 17:43:46 +00:00
Inna 8c85cf2dbe El enlace de activación vuelve a ser alfanumérico; avisar a qué correo se envió
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>
2026-07-15 17:34:53 +00:00
Inna 579396c34a Rebranding a NightSpire en toda la web
Marca, dominios y correos: UltimoWoW/Nova WoW -> NightSpire, cualquier URL de
esos dominios -> https://www.nightspire.gg y los correos -> consultas@nightspire.gg.
98 sustituciones en 16 ficheros de textos de usuario (messages/, app/, components/).

Assets: logo largo de cabecera y vídeo sustituidos por los de NightSpire (el logo
nuevo es 214x28, igual que el viejo, así que no hace falta tocar el CSS).

Redes: apuntan a las cuentas de NightSpire, tanto en el pie de la web como en el
de los correos (que seguían enlazando a las de NovaWoW desde el porte del diseño).

Cookie de sesión novawow_session -> nightspire_session. Cierra la sesión de todos
los conectados una vez; se asume ahora, recién hecho el cutover.

Ficheros del tema renombrados: novawow-style.css -> nightspire-style.css y
novawow-main-logo-transparent.webp -> nightspire-main-logo-transparent.webp, para
que la marca vieja no quede ni en las rutas.

El foro externo (foro.ultimowow.com) pasa a ser el nuestro: <Link> a /forum, que
respeta el idioma y no abre pestaña nueva.

NO se tocan y es a propósito:
- Los comentarios que nombran AzerothCore: describen el comportamiento REAL del
  core (límite de 12 ítems por correo, convención de baneos, SOAP...). Cambiarlos
  a NightSpire los volvería falsos. Además AzerothCore no aparece en la web: las
  14 menciones son todas comentarios de código.
- El regex de brandify() sigue buscando los nombres VIEJOS: son los que hay
  escritos en las noticias de la BD.
- lib/changelog.ts REPO='Inna/NovaWoW': es la ruta real del repo en Gitea.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
2026-07-15 15:15:57 +00:00
Inna cccc70338d Quitar el prefijo home_ de las 41 tablas del portal
El prefijo venía del nombre de la app Django (`home`), jubilada y borrada hoy,
así que ya no significaba nada: home_stripelog -> stripelog. La BD se sigue
llamando django_wow por historia (renombrarla es otra operación).

Comprobado antes de tocar nada: 0 colisiones con nombres existentes, 0 palabras
reservadas (contrastado contra las 260 de MySQL), sin vistas ni triggers que
dependieran de ellas. El RENAME va en una sola sentencia porque así es atómico,
y MySQL reapunta solo las 4 claves ajenas entre estas tablas.

170 referencias actualizadas en 37 ficheros. Se dejó a propósito
sql/drop_home_securitytoken_authuser_fk.sql sin tocar: es una migración ya
aplicada y reescribirla sería falsear el historial.

De paso salió un fallo previo: prices.ts consultaba `home_restoreitemprice`,
una tabla que NO existe. El try/catch de priceFrom se comía el error y devolvía
siempre el precio por defecto, así que el precio de restaurar objetos nunca fue
configurable. Se deja documentado en el código; crear la tabla es otra decisión.

Verificado tras aplicar: 0 tablas home_, las 51 siguen ahí, y el conteo exacto
de filas cuadra con el volcado previo (store_item 3068, item_data 44873,
stripelog 35, store_order 26, noticia 50). La web responde 200 en todas las
rutas y la portada carga las noticias sin errores.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
2026-07-15 14:49:19 +00:00
Inna 9390eefa93 Correos: portar el diseño del tema y unificarlos en lib/emails.ts
Los correos usaban un shell inline distinto en cada remitente. Ahora salen
todos de una sola maqueta, la de security_token, portada de las plantillas de
Django. De las 7 de Django solo security_token y activation traían el diseño;
las otras 5 eran HTML soso.

Las plantillas de Django eran un volcado del DOM de Gmail. Al portarlas se
quitó lo que no era diseño: los enlaces iban envueltos en el rastreador de
Mailjet (079xk.mjt.lu), que además redirigía a ultimowow.com en vez de a
nosotros; había un píxel de apertura, un "Click on me" oculto y atributos que
mete Gmail al mostrar el correo. Las imágenes apuntaban al proxy de Gmail
(ci3.googleusercontent.com), no a nuestro /static/, así que el diseño se
rompía el día que Google dejara de servirlas.

Ninguna URL va a fuego: todas cuelgan de SITE_URL, así que cambiar de dominio
es tocar el .env. En un correo han de ser absolutas (se abre desde Gmail), así
que se construyen a partir de esa variable.

El logo nw-mail-logo.png daba 404: solo existía el .webp del tema, que es el de
UltimoWoW (león negro + letras "UW"). El auténtico de NovaWoW, un león de acero
sin letras, sobrevivía únicamente en la caché de Gmail y se recuperó de ahí
(600x320 RGBA, íntegro).

Verificado renderizando el original y el nuestro en un navegador y comparando:
cuadran pixel a pixel. Eso destapó dos fallos que se corrigen aquí: faltaba la
etiqueta <h1> de apertura, y el estilo de los párrafos de datos era el de la
letra pequeña del pie (11px/17px) en vez del real (14px/21px).

El activation de Django mandaba la contraseña en texto plano; aquí no se manda.

mail.ts: leía EMAIL_USE_SSL pero ignoraba EMAIL_USE_TLS, así que en el puerto
587 el STARTTLS era oportunista y, si el servidor no lo ofrecía, nodemailer
enviaba en claro. Ahora requireTLS lo hace obligatorio.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
2026-07-15 14:33:32 +00:00
Inna f332fc39ca Registro + activación por email en Next.js (SRP6 + nodemailer)
- lib/mail.ts: transporte SMTP (nodemailer, mismas creds Gmail que Django).
- lib/register.ts: registerAccount (valida, comprueba email existente, crea fila en
  home_accountactivation reutilizando la tabla de Django, envía email de activación);
  activateAccount (crea battlenet_accounts con SRP6 v2 + account con SRP6 Grunt, como
  activate_account_view: expansion=2, battlenet_index=1; borra la activación).
- Route handlers /api/auth/register y /api/auth/activate.
- Páginas app/[locale]/register (RegisterForm cliente) y app/[locale]/activate-account
  (ActivateClient auto-POST al abrir el enlace). Textos en messages (Register, Activate).

Validado: validaciones (missingFields/passwordMismatch/invalidEmail/passwordTooLong);
ciclo completo activación->crea bnet+account->login OK con la cuenta creada
(needsSelection=false), password mala->invalidCredentials. Todo en TS, crypto validada.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
2026-07-12 22:53:09 +00:00