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>
This commit is contained in:
2026-07-15 15:15:57 +00:00
parent cccc70338d
commit 579396c34a
29 changed files with 112 additions and 106 deletions
+2 -2
View File
@@ -43,7 +43,7 @@ export async function requestEmailChange(
const link = `${site}/confirm-old-email?hash=${oldHash}`
await sendMail(
current,
`Confirma el cambio de correo - Nova WoW`,
`Confirma el cambio de correo - NightSpire`,
confirmOldEmailHtml(session.username ?? current, newEmail, link),
)
return { success: true }
@@ -62,7 +62,7 @@ export async function confirmOldEmail(hash: string): Promise<Result> {
const link = `${site}/confirm-new-email?hash=${act.hash}`
await sendMail(
act.email,
`Confirma tu nuevo correo - Nova WoW`,
`Confirma tu nuevo correo - NightSpire`,
confirmNewEmailHtml(act.username ?? act.email, link),
)
return { success: true }
+5 -5
View File
@@ -21,7 +21,7 @@
* entra con el correo, no con un usuario).
*/
const SERVER_NAME = 'NovaWoW'
const SERVER_NAME = 'NightSpire'
/** Raíz del sitio, sin barra final. De aquí salen todos los enlaces e imágenes. */
function siteUrl(): string {
@@ -184,10 +184,10 @@ function layout(label: string, title: string, rows: string[], locale = 'es'): st
<td align="center" style="padding:0;Margin:0;padding-top:15px;padding-bottom:15px;font-size:0">
<table cellspacing="0" cellpadding="0" role="presentation" style="border-collapse:collapse;border-spacing:0px">
<tbody><tr style="border-collapse:collapse">
<td valign="top" align="center" style="padding:0;Margin:0;padding-right:20px"><a href="https://www.facebook.com/NovaWoW/" style="font-family:-apple-system,BlinkMacSystemFont,'Segoe UI',Roboto,Helvetica,Arial,sans-serif,'Apple Color Emoji','Segoe UI Emoji','Segoe UI Symbol';font-size:14px;text-decoration:underline;color:#333333" target="_blank"><img title="Facebook" src="${site}/nw-themes/nw-ryu/nw-images/nw-mails/facebook-logo-gray.png" alt="Fb" width="32" height="32" style="display:block;border:0;outline:none;text-decoration:none" class="CToWUd" data-bit="iit"></a></td>
<td valign="top" align="center" style="padding:0;Margin:0;padding-right:20px"><a href="https://www.instagram.com/NovaWoW/" style="font-family:-apple-system,BlinkMacSystemFont,'Segoe UI',Roboto,Helvetica,Arial,sans-serif,'Apple Color Emoji','Segoe UI Emoji','Segoe UI Symbol';font-size:14px;text-decoration:underline;color:#333333" target="_blank"><img title="Instagram" src="${site}/nw-themes/nw-ryu/nw-images/nw-mails/instagram-logo-gray.png" alt="Ig" width="32" height="32" style="display:block;border:0;outline:none;text-decoration:none" class="CToWUd" data-bit="iit"></a></td>
<td valign="top" align="center" style="padding:0;Margin:0;padding-right:20px"><a href="https://twitter.com/NovaWoW" style="font-family:-apple-system,BlinkMacSystemFont,'Segoe UI',Roboto,Helvetica,Arial,sans-serif,'Apple Color Emoji','Segoe UI Emoji','Segoe UI Symbol';font-size:14px;text-decoration:underline;color:#333333" target="_blank"><img title="Twitter" src="${site}/nw-themes/nw-ryu/nw-images/nw-mails/twitter-logo-gray.png" alt="Tw" width="32" height="32" style="display:block;border:0;outline:none;text-decoration:none" class="CToWUd" data-bit="iit"></a></td>
<td valign="top" align="center" style="padding:0;Margin:0"><a href="https://www.youtube.com/channel/UCaHL8BZcho8AkeM9wckQbyg" style="font-family:-apple-system,BlinkMacSystemFont,'Segoe UI',Roboto,Helvetica,Arial,sans-serif,'Apple Color Emoji','Segoe UI Emoji','Segoe UI Symbol';font-size:14px;text-decoration:underline;color:#333333" target="_blank"><img title="Youtube" src="${site}/nw-themes/nw-ryu/nw-images/nw-mails/youtube-logo-gray.png" alt="Yt" width="32" height="32" style="display:block;border:0;outline:none;text-decoration:none" class="CToWUd" data-bit="iit"></a></td>
<td valign="top" align="center" style="padding:0;Margin:0;padding-right:20px"><a href="https://www.facebook.com/NightSpire" style="font-family:-apple-system,BlinkMacSystemFont,'Segoe UI',Roboto,Helvetica,Arial,sans-serif,'Apple Color Emoji','Segoe UI Emoji','Segoe UI Symbol';font-size:14px;text-decoration:underline;color:#333333" target="_blank"><img title="Facebook" src="${site}/nw-themes/nw-ryu/nw-images/nw-mails/facebook-logo-gray.png" alt="Fb" width="32" height="32" style="display:block;border:0;outline:none;text-decoration:none" class="CToWUd" data-bit="iit"></a></td>
<td valign="top" align="center" style="padding:0;Margin:0;padding-right:20px"><a href="https://www.instagram.com/NightSpire" style="font-family:-apple-system,BlinkMacSystemFont,'Segoe UI',Roboto,Helvetica,Arial,sans-serif,'Apple Color Emoji','Segoe UI Emoji','Segoe UI Symbol';font-size:14px;text-decoration:underline;color:#333333" target="_blank"><img title="Instagram" src="${site}/nw-themes/nw-ryu/nw-images/nw-mails/instagram-logo-gray.png" alt="Ig" width="32" height="32" style="display:block;border:0;outline:none;text-decoration:none" class="CToWUd" data-bit="iit"></a></td>
<td valign="top" align="center" style="padding:0;Margin:0;padding-right:20px"><a href="https://twitter.com/NightSpire" style="font-family:-apple-system,BlinkMacSystemFont,'Segoe UI',Roboto,Helvetica,Arial,sans-serif,'Apple Color Emoji','Segoe UI Emoji','Segoe UI Symbol';font-size:14px;text-decoration:underline;color:#333333" target="_blank"><img title="Twitter" src="${site}/nw-themes/nw-ryu/nw-images/nw-mails/twitter-logo-gray.png" alt="Tw" width="32" height="32" style="display:block;border:0;outline:none;text-decoration:none" class="CToWUd" data-bit="iit"></a></td>
<td valign="top" align="center" style="padding:0;Margin:0"><a href="https://www.youtube.com/@NightSpire" style="font-family:-apple-system,BlinkMacSystemFont,'Segoe UI',Roboto,Helvetica,Arial,sans-serif,'Apple Color Emoji','Segoe UI Emoji','Segoe UI Symbol';font-size:14px;text-decoration:underline;color:#333333" target="_blank"><img title="Youtube" src="${site}/nw-themes/nw-ryu/nw-images/nw-mails/youtube-logo-gray.png" alt="Yt" width="32" height="32" style="display:block;border:0;outline:none;text-decoration:none" class="CToWUd" data-bit="iit"></a></td>
</tr>
</tbody></table></td>
</tr>
+1 -1
View File
@@ -22,7 +22,7 @@ export interface ServerStatus {
// Puerto del worldserver (AzerothCore) para el estado del reino.
const WORLD_PORT = 8085
/** Etiqueta de expansión por build del cliente (incluye WoW Classic; Nova WoW = 3.4.3 = 54261). */
/** Etiqueta de expansión por build del cliente (incluye WoW Classic; el nuestro = 3.4.3 = 54261). */
export function expansionFromGamebuild(gamebuild: number): string {
if (gamebuild === 12340) return 'WotLK'
if (gamebuild >= 44000 && gamebuild <= 54261) return 'WotLK Classic'
+3 -3
View File
@@ -7,13 +7,13 @@ export async function getRealmName(): Promise<string> {
const [rows] = await db(DB.auth).query<RowDataPacket[]>(
'SELECT name FROM realmlist ORDER BY id LIMIT 1',
)
return rows[0]?.name || 'NovaWoW'
return rows[0]?.name || 'NightSpire'
} catch {
return 'NovaWoW'
return 'NightSpire'
}
}
/** Slug del reino para la URL (p. ej. «Nova WoW» → «novawow», «Trinity» → «trinity»). */
/** Slug del reino para la URL (p. ej. «Night Spire» → «nightspire», «Trinity» → «trinity»). */
export function realmSlug(name: string): string {
return name.toLowerCase().replace(/[^a-z0-9]/g, '')
}
+4 -4
View File
@@ -51,7 +51,7 @@ export async function requestRecovery(type: string, value: string): Promise<Resu
const link = `${site}/reset-password?token=${token}`
await sendMail(
email,
'Restablecer contraseña - Nova WoW',
'Restablecer contraseña - NightSpire',
passwordResetEmailHtml(email, link),
)
}
@@ -88,7 +88,7 @@ export async function requestRecovery(type: string, value: string): Promise<Resu
const nameById = new Map(games.map((g) => [g.id, g.username]))
await sendMail(
email,
'Tu Token de seguridad - Nova WoW',
'Tu Token de seguridad - NightSpire',
securityTokensListEmailHtml(
email,
toks.map((r) => ({ username: String(nameById.get(r.user_id) ?? r.user_id), token: r.token })),
@@ -116,7 +116,7 @@ export async function requestRecovery(type: string, value: string): Promise<Resu
)
await sendMail(
email,
'Tus cuentas de juego - Nova WoW',
'Tus cuentas de juego - NightSpire',
accountNamesEmailHtml(
email,
games.map((g) => String(g.username)),
@@ -139,7 +139,7 @@ export async function requestRecovery(type: string, value: string): Promise<Resu
const link = `${site}/activate-account?act=${rows[0].hash}`
await sendMail(
email,
`Activación de la cuenta ${email} - Nova WoW`,
`Activación de la cuenta ${email} - NightSpire`,
activationEmailHtml(email, link),
)
}
+1 -1
View File
@@ -73,7 +73,7 @@ export async function registerAccount(input: RegisterInput): Promise<Result> {
)
const link = `${process.env.SITE_URL}/activate-account?act=${hash}`
await sendMail(email, `Activación de la cuenta ${email} - Nova WoW`, activationEmailHtml(email, link))
await sendMail(email, `Activación de la cuenta ${email} - NightSpire`, activationEmailHtml(email, link))
return { success: true }
}
+1 -1
View File
@@ -36,7 +36,7 @@ export async function requestSecurityToken(session: SessionData, ip: string): Pr
)
// La cuenta es el correo: con Battle.net se entra con él, no con un usuario.
await sendMail(email, 'Token de seguridad - Nova WoW', securityTokenEmailHtml(email, token, ip || '0.0.0.0'))
await sendMail(email, 'Token de seguridad - NightSpire', securityTokenEmailHtml(email, token, ip || '0.0.0.0'))
const [created] = await db(DB.default).query<RowDataPacket[]>(
'SELECT created_at FROM securitytoken WHERE user_id = ? ORDER BY created_at DESC LIMIT 1',
+1 -1
View File
@@ -15,7 +15,7 @@ export interface SessionData {
export const sessionOptions: SessionOptions = {
password: process.env.SESSION_SECRET as string,
cookieName: 'novawow_session',
cookieName: 'nightspire_session',
cookieOptions: {
httpOnly: true,
secure: process.env.NODE_ENV === 'production',