Aceptar el correo en MAYÚSCULAS y mostrar las cuentas como WOW1, no 17#1
Dos fallos que se juntaban justo en «recuperar nombre de cuenta»: - La validación de Gmail era sensible a mayúsculas (`/@gmail\.com$/` sin la `i`), así que NOVAWOW86@GMAIL.COM se rechazaba con invalidEmail. Y desde que los correos muestran la cuenta en MAYÚSCULAS, quien la copiara de ahí y la pegara no podía recuperar la cuenta, ni registrarse, ni cambiar el correo: la misma regex estaba repetida en register/change-email/recover. Ahora es una sola, con la `i`, en lib/bnet.ts (junto a normalizeEmail). El dominio de un correo NO distingue mayúsculas. - Los correos enseñaban el username interno («17#1»), que no le dice nada a nadie, en vez del nombre visible («WOW1»). Afectaba a DOS plantillas: la de nombres de cuenta y la de la lista de tokens. La conversión existía, pero como helper local de my-account; sube a lib/bnet.ts como `gameAccountDisplayName` (el inverso de `makeGameAccountUsername`) y la aplican las propias plantillas, para que ningún sitio que las use pueda olvidarse. Verificado: la regex acepta las 3 variantes de caja y sigue rechazando no-Gmail; los dos correos renderizados muestran «Cuenta: WOW1 / WOW2». Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
This commit is contained in:
@@ -2,6 +2,7 @@ import { getTranslations, setRequestLocale } from 'next-intl/server'
|
|||||||
import { redirect, Link } from '@/i18n/navigation'
|
import { redirect, Link } from '@/i18n/navigation'
|
||||||
import { getSession } from '@/lib/session'
|
import { getSession } from '@/lib/session'
|
||||||
import { getRealmName, realmSlug } from '@/lib/realm'
|
import { getRealmName, realmSlug } from '@/lib/realm'
|
||||||
|
import { gameAccountDisplayName } from '@/lib/bnet'
|
||||||
import { getAccountDashboard, getAccountRank } from '@/lib/account'
|
import { getAccountDashboard, getAccountRank } from '@/lib/account'
|
||||||
import { reconcileSumUpCheckouts } from '@/lib/sumup'
|
import { reconcileSumUpCheckouts } from '@/lib/sumup'
|
||||||
import { getGameAccounts } from '@/lib/auth'
|
import { getGameAccounts } from '@/lib/auth'
|
||||||
@@ -22,13 +23,6 @@ function maskEmail(e: string): string {
|
|||||||
return `${visible}***@${domain}`
|
return `${visible}***@${domain}`
|
||||||
}
|
}
|
||||||
|
|
||||||
// Nombre visible de la cuenta de juego: «15#1» → «WOW1» (el nº tras # es la
|
|
||||||
// cuenta de juego; con varias serían WOW1, WOW2, WOW3…).
|
|
||||||
function wowAccountName(username: string): string {
|
|
||||||
const i = username.indexOf('#')
|
|
||||||
return i >= 0 ? `WOW${username.slice(i + 1)}` : username
|
|
||||||
}
|
|
||||||
|
|
||||||
const ICONS = '/ns-themes/ns-ryu/ns-images/ns-icons'
|
const ICONS = '/ns-themes/ns-ryu/ns-images/ns-icons'
|
||||||
|
|
||||||
export default async function AccountPage({ params }: { params: Promise<{ locale: string }> }) {
|
export default async function AccountPage({ params }: { params: Promise<{ locale: string }> }) {
|
||||||
@@ -67,7 +61,7 @@ export default async function AccountPage({ params }: { params: Promise<{ locale
|
|||||||
<GameAccountSwitcher
|
<GameAccountSwitcher
|
||||||
accounts={games}
|
accounts={games}
|
||||||
currentId={session.accountId ?? 0}
|
currentId={session.accountId ?? 0}
|
||||||
currentLabel={wowAccountName(data.gameAccount)}
|
currentLabel={gameAccountDisplayName(data.gameAccount)}
|
||||||
/>
|
/>
|
||||||
</p>
|
</p>
|
||||||
<p><i className="fa-solid fa-at account-ficon third-brown"></i> {t('regMail')}: <span>{maskEmail(data.regMail)}</span></p>
|
<p><i className="fa-solid fa-at account-ficon third-brown"></i> {t('regMail')}: <span>{maskEmail(data.regMail)}</span></p>
|
||||||
|
|||||||
@@ -147,6 +147,24 @@ export function makeGameAccountUsername(bnetId: number, index = 1): string {
|
|||||||
return `${bnetId}#${index}`
|
return `${bnetId}#${index}`
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Inverso de `makeGameAccountUsername` para enseñárselo al usuario: «15#1» → «WOW1».
|
||||||
|
* El número tras # es la cuenta de juego (WOW1…WOW8). El `15#1` es interno y no le
|
||||||
|
* dice nada a nadie.
|
||||||
|
*/
|
||||||
|
export function gameAccountDisplayName(username: string): string {
|
||||||
|
const i = username.indexOf('#')
|
||||||
|
return i >= 0 ? `WOW${username.slice(i + 1)}` : username
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Solo se admiten cuentas de Gmail. Lleva la `i` a propósito: sin ella
|
||||||
|
* `NOVAWOW86@GMAIL.COM` se rechazaba, y es justo como sale el correo en los
|
||||||
|
* mensajes que enviamos, así que quien lo copiara de ahí no podía ni registrarse
|
||||||
|
* ni recuperar la cuenta. El dominio del correo NO distingue mayúsculas.
|
||||||
|
*/
|
||||||
|
export const GMAIL_RE = /^[a-zA-Z0-9._%+-]+@gmail\.com$/i
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Cuentas de juego por cuenta Battle.net (WoW1…WoW8). Vive aquí, y no en la ruta
|
* Cuentas de juego por cuenta Battle.net (WoW1…WoW8). Vive aquí, y no en la ruta
|
||||||
* que la aplica, para que el correo de activación cite el límite REAL: el texto
|
* que la aplica, para que el correo de activación cite el límite REAL: el texto
|
||||||
|
|||||||
@@ -2,13 +2,12 @@ import crypto from 'node:crypto'
|
|||||||
import type { RowDataPacket, ResultSetHeader } from 'mysql2'
|
import type { RowDataPacket, ResultSetHeader } from 'mysql2'
|
||||||
import { db, DB } from './db'
|
import { db, DB } from './db'
|
||||||
import { authenticate } from './auth'
|
import { authenticate } from './auth'
|
||||||
import { bnetMakeRegistration, normalizeEmail } from './bnet'
|
import { bnetMakeRegistration, normalizeEmail, GMAIL_RE } from './bnet'
|
||||||
import { checkSecurityToken } from './security-token'
|
import { checkSecurityToken } from './security-token'
|
||||||
import { sendMail } from './mail'
|
import { sendMail } from './mail'
|
||||||
import { confirmNewEmailHtml, confirmOldEmailHtml } from './emails'
|
import { confirmNewEmailHtml, confirmOldEmailHtml } from './emails'
|
||||||
import type { SessionData } from './session'
|
import type { SessionData } from './session'
|
||||||
|
|
||||||
const GMAIL_RE = /^[a-zA-Z0-9._%+-]+@gmail\.com$/
|
|
||||||
|
|
||||||
export interface Result {
|
export interface Result {
|
||||||
success: boolean
|
success: boolean
|
||||||
|
|||||||
@@ -21,7 +21,7 @@
|
|||||||
* entra con el correo, no con un usuario).
|
* entra con el correo, no con un usuario).
|
||||||
*/
|
*/
|
||||||
|
|
||||||
import { MAX_GAME_ACCOUNTS } from './bnet'
|
import { MAX_GAME_ACCOUNTS, gameAccountDisplayName } from './bnet'
|
||||||
|
|
||||||
const SERVER_NAME = 'NightSpire'
|
const SERVER_NAME = 'NightSpire'
|
||||||
|
|
||||||
@@ -275,9 +275,10 @@ export function passwordResetEmailHtml(email: string, link: string, locale = 'es
|
|||||||
}
|
}
|
||||||
|
|
||||||
/** `account_names.html` — recordatorio de cuentas de juego. */
|
/** `account_names.html` — recordatorio de cuentas de juego. */
|
||||||
|
/** `accounts` son los usernames internos («17#1»); aquí se muestran como «WOW1». */
|
||||||
export function accountNamesEmailHtml(email: string, accounts: string[], locale = 'es'): string {
|
export function accountNamesEmailHtml(email: string, accounts: string[], locale = 'es'): string {
|
||||||
const list = accounts.length
|
const list = accounts.length
|
||||||
? pData(accounts.map((a) => `Cuenta: ${esc(a)}`).join('<br>') + '<br><br>')
|
? pData(accounts.map((a) => `Cuenta: ${esc(gameAccountDisplayName(a))}`).join('<br>') + '<br><br>')
|
||||||
: pData('No hay cuentas de juego asociadas todavía.<br><br>')
|
: pData('No hay cuentas de juego asociadas todavía.<br><br>')
|
||||||
return layout('CUENTAS DE JUEGO', 'Tus Cuentas de Juego', [
|
return layout('CUENTAS DE JUEGO', 'Tus Cuentas de Juego', [
|
||||||
p(
|
p(
|
||||||
@@ -343,7 +344,9 @@ export function securityTokensListEmailHtml(
|
|||||||
[
|
[
|
||||||
p(`Estos son los Token de seguridad de las cuentas de juego asociadas a ${esc(email)}.<br><br>Tus datos de cuenta son:`),
|
p(`Estos son los Token de seguridad de las cuentas de juego asociadas a ${esc(email)}.<br><br>Tus datos de cuenta son:`),
|
||||||
pData(
|
pData(
|
||||||
items.map((i) => `Cuenta: ${esc(i.username)}<br>Token de Seguridad: ${esc(i.token)}`).join('<br><br>') +
|
items
|
||||||
|
.map((i) => `Cuenta: ${esc(gameAccountDisplayName(i.username))}<br>Token de Seguridad: ${esc(i.token)}`)
|
||||||
|
.join('<br><br>') +
|
||||||
'<br><br>',
|
'<br><br>',
|
||||||
),
|
),
|
||||||
p(
|
p(
|
||||||
|
|||||||
@@ -1,7 +1,7 @@
|
|||||||
import crypto from 'node:crypto'
|
import crypto from 'node:crypto'
|
||||||
import type { RowDataPacket } from 'mysql2'
|
import type { RowDataPacket } from 'mysql2'
|
||||||
import { db, DB } from './db'
|
import { db, DB } from './db'
|
||||||
import { normalizeEmail, bnetMakeRegistration } from './bnet'
|
import { normalizeEmail, bnetMakeRegistration, GMAIL_RE } from './bnet'
|
||||||
import { sendMail } from './mail'
|
import { sendMail } from './mail'
|
||||||
import {
|
import {
|
||||||
activationEmailHtml,
|
activationEmailHtml,
|
||||||
@@ -10,7 +10,6 @@ import {
|
|||||||
securityTokensListEmailHtml,
|
securityTokensListEmailHtml,
|
||||||
} from './emails'
|
} from './emails'
|
||||||
|
|
||||||
const GMAIL_RE = /^[a-zA-Z0-9._%+-]+@gmail\.com$/
|
|
||||||
|
|
||||||
export interface Result {
|
export interface Result {
|
||||||
success: boolean
|
success: boolean
|
||||||
|
|||||||
@@ -1,11 +1,10 @@
|
|||||||
import type { ResultSetHeader, RowDataPacket } from 'mysql2'
|
import type { ResultSetHeader, RowDataPacket } from 'mysql2'
|
||||||
import { db, DB } from './db'
|
import { db, DB } from './db'
|
||||||
import { normalizeEmail, bnetMakeRegistration, gameMakeRegistration, makeGameAccountUsername } from './bnet'
|
import { normalizeEmail, bnetMakeRegistration, gameMakeRegistration, makeGameAccountUsername, GMAIL_RE } from './bnet'
|
||||||
import { sendMail } from './mail'
|
import { sendMail } from './mail'
|
||||||
import { activationEmailHtml } from './emails'
|
import { activationEmailHtml } from './emails'
|
||||||
import { randomToken } from './random-token'
|
import { randomToken } from './random-token'
|
||||||
|
|
||||||
const GMAIL_RE = /^[a-zA-Z0-9._%+-]+@gmail\.com$/
|
|
||||||
|
|
||||||
export interface RegisterInput {
|
export interface RegisterInput {
|
||||||
password: string
|
password: string
|
||||||
|
|||||||
Reference in New Issue
Block a user