Repaso de la carpeta sql/ heredada. De sus 8 ficheros:
- forum_schema.sql y forum_views.sql: SE BORRAN. Son del foro anterior, que
af707ad reemplazó por el port de FusionCMS (web-next/sql/forum_fusion.sql).
El primero crea `forums`, tabla que ya no existe (el código usa forum_forums);
el segundo añade un contador `views` que ni existe en prod ni usa el código.
Quedan en el historial por si hicieran falta.
- achievement_points.sql, battlepay_sumup.sql, seed_news.sql y
seed_recruit_rewards.sql: siguen siendo válidos y se mueven a web-next/sql/,
que es la carpeta viva. El primero pasa a achievement_points_world.sql para no
chocar con la copia de django_wow que usa la armería; cada uno avisa del otro.
- rename_home_prefix.sql y drop_home_securitytoken_authuser_fk.sql: se quedan en
sql/ como registro de migraciones ya aplicadas.
Se añade battlepay_purchases.sql: la tabla la escribe el core y no estaba en
ningún SQL, así que no había forma de recrearla. Sus 61 filas son de pruebas
locales (2 cuentas, 127.0.0.1), sin datos de jugadores.
Verificado importando todo en BD limpias: seed_news deja 50 noticias,
seed_recruit_rewards 6, battlepay_sumup crea sus 2 tablas y
battlepay_purchases reproduce prod fila a fila con el mismo esquema.
El renombrado de las 41 tablas (cccc703) no llegó a los ficheros SQL ni a los
generadores, así que seguían creando y consultando tablas home_* que ya no
existen: aplicarlos hoy no reproducía el portal.
- 12 ficheros pasan a las tablas reales (store_item, stripelog, noticia, ...).
- Los 6 que llevaban el prefijo en el NOMBRE se renombran también; nada los
referenciaba.
- gen_store_catalog.py leía FROM home_store_item de django_wow (roto) y
db2_to_sql.py generaba home_item_data; corregidos, con sus comentarios.
- add_stripelog_service.sql usaba ADD COLUMN IF NOT EXISTS, que es de MariaDB:
en MySQL 9.7 daba error de sintaxis y no se podía aplicar (la columna se había
puesto a mano en prod). Ahora es un ADD COLUMN normal, como su hermano.
Se dejan intactos sql/rename_home_prefix.sql y
sql/drop_home_securitytoken_authuser_fk.sql: son migraciones ya aplicadas y los
nombres home_* son parte de su historia.
Verificado importando todo en BD limpias: los ALTER dejan stripelog y noticia
con el esquema idéntico a producción, y la consulta de gen_store_catalog.py
devuelve filas.
Las tres tablas propias que consulta la armería (el resto son de AzerothCore):
- achievement_names: no tenía SQL en el repo. Se añade el volcado (1912 filas).
- achievement_points: solo existía sql/achievement_points.sql, que crea la tabla
en acore_world para el top de /<realm>-players. La armería la lee de django_wow,
así que se añade el volcado de esa copia.
- item_data: el fichero (y su generador) seguían creando `home_item_data`, nombre
que dejó de existir al quitar el prefijo home_ en cccc703; se renombra a
`item_data`, que es lo que consulta el código.
Verificado importando los tres en una BD limpia: coinciden fila a fila con
producción, con las tildes y los apóstrofos correctos.
Se sustituye por completo el foro anterior de Next (que estaba vacío: 0 temas, 0
posts) por una adaptación del foro PHP de FusionCMS que tenía el usuario, con su
estructura, sus imágenes y su estética, llevadas a Next.
BASE DE DATOS (acore_web), esquema idéntico al original (sql/forum_fusion.sql):
forum_categories(id, order, name)
forum_forums(id, category, order, name, description, icon, colortitle, type)
forum_topics(id, forum, name, sticky, locked, deleted, created)
forum_posts(id, topic, poster, text, time, deleted)
Se hizo DROP de las tablas antiguas (forums, forum_categories, forum_topics,
forum_posts) y CREATE de estas, con el seed original (News/Reports/General,
foros con icono, clases con color, subforos por idioma con bandera). Única
diferencia con el dump: forum_topics.created, que faltaba y usan las vistas, y
el recorte del salto de línea final en los nombres de clase.
Los temas no guardan autor ni última actividad: se derivan del primer/último post,
como en el original. `poster` es el AccountID de acore_auth; el nombre se resuelve
al render (username sin el sufijo #N de Battle.net). El texto se guarda como HTML
saneado con nh3 (no BBCode) para encajar con el editor.
EDITOR: TinyMCE community self-hosted (licenseKey gpl), servido desde /tinymce
(scripts/copy-tinymce.mjs en postinstall; public/tinymce en .gitignore por ser
artefacto). El HTML se sanea SIEMPRE en el servidor.
PÁGINAS (app/[locale]/forum): índice con los 3 tipos de foro (fila normal, tarjeta
de clase tipo 1, tarjeta de idioma tipo 2), subforo, tema, crear y editar. La
lógica de forum.js (crear/responder/editar/moderar) se portó a fetch + los avisos
del sitio, sin jQuery/SweetAlert, y la moderación pasa por POST (no GET).
CSS: forum.css adaptado al tema (app/forum.css), rutas de imagen a /forum, dorado
alineado al acento del sitio (#d79602), y se aportan .nice_button/.main-wide/
.pagination que no estaban en theme.css.
ADMIN: lib/admin-forum.ts, sus rutas y AdminForumManager reescritos al esquema
nuevo (categoría, icono, color, tipo, orden; sin _en/visibility).
Se eliminan /forum/search y /forum/user (no existen en el foro nuevo) y los
componentes que quedaban huérfanos (ForumSearchBox, PostActions, TopicModBar,
NewTopicForm). i18n ES/EN completado (next-intl).
Verificado en producción: el índice renderiza en es/en con categorías, clases,
banderas e iconos; los assets y TinyMCE sirven 200; crear tema y responder guardan
las filas con el esquema correcto y las páginas las muestran (autor resuelto, HTML
saneado). Datos de prueba borrados: el foro arranca vacío.
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
El directorio del proyecto se renombró, así que las rutas absolutas de
sql/db2/*.py y la cabecera de sql/item_data.sql apuntaban a un sitio que ya no
existe.
Fuera del repo (no versionado, se anota aquí): el directorio pasó de
/root/NovaWoW a /root/NightSpire y las unidades systemd de novawow-next /
novawow-dpoints-reconcile a nightspire-*. De paso, nightspire-next.service ya no
declara After/Wants de novawow.service, que era el Django borrado y ya no
existía.
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Investigadas las 750 categorías de la BD contra el HTML real del original. Los
ítems estaban perfectos (554 hojas, 3068 ítems, mismo contenido en cada una: no
faltaba ninguno). Todo lo roto era de categorías:
- 45 CATEGORÍAS FANTASMA, todas con nombre de código ("11-1", "23-10"). El seed
dedujo el árbol de los códigos de las hojas ("11-1-1" => padre "11-1"), pero
el original NO siempre pinta ese nivel: en 45 hojas el "-1-" del código no
corresponde a nada y cuelgan directas de su raíz. Ahora se lee el anidamiento
real (<ul>/<li>): 705 categorías (20 + 131 + 554) en vez de 750.
- El nombre de una categoría NO es texto plano, es HTML, y lo estábamos
aplanando: 141 nombres perdían su color y 40 cabeceras el suyo. Hay dos
mecanismos y los dos hacen falta: la clase en la CABECERA, que sustituye a
first-brown para colorear la categoría entera ("Brujo" en color de brujo), y
los <span class="no-toggle X"> dentro del nombre (clases y las etiquetas
[Nivel 232 a 245] / (DPS) en otro tono).
- 21 nombres TRUNCADOS en Tokens de Armadura: guardábamos "Guerrero" donde el
original dice "Guerrero, Sacerdote, Druida" (solo se quedó el primer span).
El HTML se trocea en el SERVIDOR (`parseCategoryName`) y al cliente le llegan
solo texto + nombre de clase, así que no hay HTML de la BD entrando en el DOM.
El texto sin color va suelto, NO envuelto en <span>: el tema tiene
`span { color: #fff }` y lo pondría blanco en vez de heredar el de la cabecera.
`name` pasa a VARCHAR(512): con el HTML, el más largo mide 198 y no cabía en 191.
El name_en se genera en el propio seed traduciendo solo el TEXTO y respetando
las etiquetas, así que sql/store_category_en.sql sobra y se borra.
gen_store_catalog.py regenera el seed entero (categorías del HTML, ítems de la
BD, que ya estaban verificados) y es reproducible byte a byte.
Verificado en el navegador contra el original: mismo árbol, 0 categorías con
nombre de código, "[Nivel 232 a 245]" en marrón tenue y los colores de clase
oficiales (Guerrero #C79C6E, Sacerdote blanco, Druida #FF7D0A). En inglés
también, conservando los colores.
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Dos arreglos pendientes que quedaron señalados:
1. "Sacedorte" era una errata de "Sacerdote" en tres categorías (3-10, 4-10 y
19-9; la 20-9 estaba bien). Se veía así en la web en español. Corregido en la
BD y, sobre todo, en el seed `store_catalog.sql`, que la traía 3 veces: sin
eso, reconstruir la BD la resucitaba. Regenerado store_category_en.sql (217
nombres distintos en vez de 218: las dos grafías ya son una).
Un escaneo por parecido contra los términos oficiales de los DB2 confirma que
era la única errata; el resto de casi-coincidencias son plurales legítimos
("Espadas" vs el "Espada" del juego).
2. El cuadro del ítem medía 160px y el del store original 200. Igualado. Solo
afecta a /store: send-gift fija las columnas de su rejilla a 160px y hace
width:auto, y se ha comprobado que sigue midiendo 160.
Con solo cambiar la anchura se quedaba en 200 en vez de 214: el tema se
diseñó para el content-box del navegador y el preflight de Tailwind mete
border-box, así que el padding y el borde dejaban de sumar (el mismo problema
que ya estaba arreglado para los inputs). Se restaura content-box solo en la
tienda, porque en la rejilla de send-gift desbordaría las columnas.
Medido en el navegador: el cuadro pasa a 214px de ancho, que es lo que mide el
del original (213 en la captura). send-gift sigue en 160.
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Cierra el idioma de /en/store: los nombres de ítem ya salían en inglés
(2daf920), pero el árbol de categorías seguía en español y la tienda se veía a
medias.
Los términos DEL JUEGO no se traducen a mano, se sacan del cliente, para que
casen con lo que ve un jugador en inglés:
- ChrClasses -> clases (Brujo = Warlock, Pícaro = Rogue...)
- ItemSet -> sets (Vestiduras de la Némesis = Nemesis Raiment, Las diez
tormentas = The Ten Storms, Atavío viento abisal = Netherwind
Regalia). Casando sin distinguir mayúsculas: las categorías
capitalizan distinto que los ItemSet.
- ItemSubClass-> tipos de arma (Armas de asta = Polearms, Varitas = Wands)
- home_item_data -> lo que no es ItemSet. Los sets de PvP de WotLK no son
ItemSets reales, así que "Gladiador furioso" = "Furious
Gladiator" sale de los nombres bilingües de sus propios ítems.
Solo se traduce a mano lo que la tienda se inventó y no existe en el juego
(Items de leveo = Leveling Items, Moneda de transfiguración = Transmog
Currency), más las fórmulas (Nivel de objeto N = Item Level N, Set de banda
tier N = Tier N Raid Set). Los ~45 nombres que son solo un código ("11-1") y
los que ya estaban en inglés ("Tier 7.1 (PvE)") se dejan igual.
wdc_db2.py aprende a leer strings de tablas no sparse: el valor guardado no es
un índice a la string table sino un offset relativo a la posición del propio
campo. Verificado con ChrClasses (las 10 clases en ambos idiomas).
Revisadas las 218 a mano. Corregida una que me había inventado: "Equipo de
Batalla del Iluminado de la Fuente del Sol" no es "Sunwell Radiance"
(eso es un buff): "Iluminado" es Lightbringer en el ItemSet y "Fuente del Sol"
es Sunwell según sus ítems -> "Sunwell Lightbringer Battlegear".
OJO, bug de datos preexistente: "Sacedorte" (errata de "Sacerdote") en las
categorías 3-10, 4-10 y 19-9; el 20-9 sí está bien. Se ve así en español. El
name_en va a "Priest"; la errata en español NO se toca.
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
En 3.4.3 `item_template` ya no existe: los datos del juego viven en ficheros
DB2 del cliente, y aquí acore_world está vacío. Se extraen a `home_item_data`
(44873 ítems): nombre es/en, calidad, nivel de objeto, tipo de inventario y
displayId.
No hay ninguna librería en PyPI que lea WDC4 (wdbx, dbc-extract3, wow-db2 no
existen), así que el lector va incluido en sql/db2/:
- `wdc_db2.py`: WDC3/WDC4 con registros fijos (bitpacked, common y pallet).
- `itemsparse.py`: ItemSparse es una tabla *sparse*, que es otro mundo: los
locstring van inline y terminados en \0, así que desplazan todo lo que va
detrás y los offsets de field_storage_info no valen. Hay que recorrer los
campos en orden, y el fichero no dice cuáles son strings: eso sale de las
definiciones oficiales de WoWDBDefs (ItemSparse.dbd, incluido), casando el
layout_hash del propio fichero (D532973D = 3.4.3).
- `db2_to_sql.py`: genera sql/item_data.sql. Regenerable y multi-locale: basta
con dejar el ItemSparse.db2 de cada idioma en /root/<locale>/.
Verificado: los 3068 ítems de la tienda están cubiertos; 2949 nombres (96%)
coinciden literalmente con el catálogo antiguo y los 119 restantes son
revisiones de traducción de Blizzard, no fallos del parser ("Bordón de Luz" ->
"Batuta de Luz"). El ítem 45327 sale q4 / ilvl 238 / "Siren's Cry", idéntico a
lo que devuelve wowhead. La distribución de calidad es realista (53
legendarios, 23 artefactos).
Ojo: la tabla está cargada pero TODAVÍA no la usa ningún código de la web.
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Selector de forma de pago en el carrito (Saldo PD/PV · Stripe · SumUp), como el
resto de servicios. Con tarjeta, el carrito se convierte a euros (100 PD = 1 €,
200 PV = 1 €), se guarda como pedido y se entrega tras confirmarse el pago.
- BD: tabla home_store_order (ref, cuenta, personaje, items, importe) para no
depender del límite de metadata de la pasarela (sql/home_store_order.sql).
- lib/store: storeEuroTotal, createStoreOrder, fulfillStoreOrder.
- paid-services: servicio 'store' (fulfill = enviar el pedido por order_ref);
la entrega la dispara service-success/reconciliación/webhook como los demás.
- /api/store/send: rama provider stripe/sumup (crea pedido + checkout, mínimo
1 €/0,50 €) además del pago con saldo.
- StoreBrowser: selector Saldo/Stripe/SumUp; con tarjeta redirige a la pasarela.
- i18n Store (payMethod/payBalance/payStripe/paySumUp/confirmCard/amountTooLow)
y Paid.store (title/success). Verificado: euros 200PD+28PV=2,14 €, pedido y
fulfill por order_ref (SOAP real pendiente, worldserver caído).
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Porta la tienda de ítems/transfiguración del sistema antiguo: elegir personaje →
árbol de categorías (3 niveles) → carrito → enviar por correo. Se paga con el
saldo PD o PV de la cuenta (cada ítem lleva su precio y moneda).
- BD: home_store_category (árbol por code "1-1-1") + home_store_item
(mismos item_id, nombres, iconos, precios y moneda que el original;
2947 en PD, 121 en PV). Seed en sql/store_catalog.sql, extraído del HTML real.
- lib/store.ts: getStoreCatalog (árbol anidado), getStoreBalances, priceStoreCart
(precios REALES de BD), purchaseStoreCart (cobro atómico PD+PV con FOR UPDATE y
reembolso si el envío SOAP falla; `.send items` troceado a 12/correo).
- API: GET /api/store/catalog (tras elegir personaje) y POST /api/store/send.
- components/StoreBrowser.tsx: selector de personaje, árbol colapsable (ítems
montados solo al abrir), carrito con totales PD/PV, enviar, modal de resultado.
- página /store con la info y el título "Tienda - REINO". Enlaces de ítems e
iconos por wowhead/zamimg (con tooltip y locale). El enlace ya existía en
AccountTools. i18n namespace Store (es/en). CSS del árbol/carrito/modal.
Verificado: catálogo E2E (750 cats/3068 ítems), cobro atómico + reembolso y
detección de saldo insuficiente (cuenta 15). SOAP real sin probar (worldserver
caído en este entorno).
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
forum_categories.name_en y forums.name_en/description_en (sql/add_forum_en.sql,
aplicado en prod y traducidos los existentes: Comunidad/Anuncios/Discusión general).
getForumIndex(locale) y getForum(id, locale) usan EN si existe, si no caen al
español. Las páginas del foro pasan el locale. Labels (Temas/Mensajes) ya estaban
traducidos. Verificado: /en/forum en inglés, /es/forum en español.
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
home_noticia gana columnas titulo_en/contenido_en (sql/add_news_en.sql). getNoticias(locale)
usa la versión EN si existe, si no cae al español (título y contenido independientes); la fecha
también se formatea por idioma. El panel de admin permite escribir la traducción al crear y EDITAR
noticias existentes (nuevo PUT /api/admin/news/[id], updateNews). Claves Admin nuevas (título/
contenido EN, editar/guardar). Probado: /en/ muestra EN, /es/ ES; PUT guardado (403 sin sesión).
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
SumUp no permite cobros por debajo de 1 €. Se actualizan las dos pociones
(0.50 -> 1.00) en el seed y en la BD de prod.
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Envía ítems de la tienda por correo al personaje de un amigo, pagado por
SumUp. lib/gift.ts (catálogo por categoría, priceCart con precios reales de
BD, sendGiftByMail vía SOAP .send items troceado a 12/correo + anti-inyección);
servicio send-gift en PAID_SERVICES; ruta dedicada /api/gift/checkout (origen
propio, destino existe, token de seguridad, carrito); SendGiftForm + página con
el diseño; i18n Paid[send-gift]; seed_giftitems.sql (4 cat, 8 ítems).
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
- /restore-character: recupera personajes borrados por BD (como .character deleted
restore). Condiciones: nivel>60, borrado <30 días, DK no si ya hay DK activo,
nombre libre, cooldown 12h/personaje. Gratis. Tabla home_restorehistory.
- /restore-items: recupera ítems borrados vía SOAP del core (.item restore list /
.item restore); el core aplica calidad/equipable/BoP/<7días. Cada ítem 100 PD
(reembolso si SOAP falla), consulta con cooldown 8h/personaje + Turnstile.
Tabla home_itemsearchhistory.
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
- /change-race-character (3€), /change-faction-character (5€, valida condiciones:
sin hermandad/correo/subastas/capitán-arena y oro <= cap por nivel),
/level-up-character (10€), /gold-character (oro por correo SOAP). Cada uno borra
su ruta antigua y actualiza el link del panel.
- SumUp: hook precheck en PaidServiceConfig (condiciones cambio de facción);
columna home_stripelog.metadata para campos extra (gold_amount).
- /quest-character: rastreador de estado de misiones/cadenas (clase, Hijos de
Hodir, misión por ID) en personaje offline; cooldowns por categoría, Turnstile,
IDs de misión verificados en wotlkdb 3.3.5a. Tabla home_questsearchhistory.
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
- /rename-character y /customize-character: renombrar/personalizar pagados por SumUp
(getRenamePrice/getCustomizePrice). SumUp ahora es service-aware: columna
home_stripelog.service; createSumUpCheckout guarda service+character;
fulfillSumUpCheckout despacha a la acción del servicio (.char rename/customi) o,
sin service, acredita PD. [service]/checkout acepta provider:'sumup';
PaidServiceForm acepta provider+confirmText; service-success maneja el return SumUp.
Se eliminan las antiguas /rename y /customize (Stripe).
- /revive-character (comparte ReviveServiceContent); se elimina /revive.
- Renombres de ruta + todos sus enlaces:
/account -> /my-account, /recruit -> /recruit-a-friend, /unstuck -> /unstuck-character.
(/select-account y /api/account/* intactos.)
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
- CharacterSelect: componente reutilizable que colorea las opciones por clase
(class="priest big-font"…) y el <select> con la clase seleccionada. Usado en
unstuck, revive, gold, transfer, trade-points, servicios de pago y recruit.
getGameCharacters ahora devuelve classCss.
- /unstuck (+revive): diseño del original (info + NOTA, prompt, opciones coloreadas,
botón "Desbloqueado"/"Revivido" con refresh).
- /vote-points: seed de los 4 sitios con logos y URLs (sql/seed_votesites.sql);
botón voted-button para sitios en cooldown; etiqueta "Nota:" separada.
- account-info.png (imagen nueva) y CSS de los paneles de cuenta: se añade
background-blend-mode: saturation y background-size en #account-settings y
.account-fieldset para que la imagen salga como el original.
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
- /transfer-d-points: transferir PD a la cuenta dueña de un personaje
(requiere token de seguridad; movimiento atómico con FOR UPDATE).
- /trade-points: comercio PD<->oro mediante códigos de 1 solo uso
(contraseña + token + Turnstile; oro al creador por SOAP; expira 1h;
lock de 5s tras canjear; rollback total si falla la entrega).
- /promo-code: canje de códigos por PD/PV (sensibles a mayúsculas/minúsculas,
usos limitados, 1 canje por cuenta).
- /admin/promo: panel para crear / editar / activar / desactivar / borrar
códigos de promoción.
- Tablas nuevas (sql/): home_tradecode, home_promocode, home_promoredemption.
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>