Files
NightSpire/web-next/lib/soap.ts
T
Inna 96e996e9c2 Cliente SOAP + servicios de personaje SOAP (revive/unstuck) en Next.js
- lib/soap.ts: executeSoapCommand (port de ac_soap.py; POST SOAP + basic auth al
  worldserver, timeout 5s, null si inaccesible). AC_SOAP_* en .env.local.
- lib/characters.ts: getGameCharacters / characterIsOfflineAndOwned (acore_characters).
- lib/character-services.ts: soapCharacterAction genérico (valida propiedad+offline,
  cooldown 12h vía home_revive/unstuckhistory, SOAP, registra historial) ->
  reviveCharacter / unstuckCharacter.
- components/CharacterActionForm.tsx (cliente reutilizable: select + botón).
- Routes /api/character/revive|unstuck (guard de sesión) y páginas
  app/[locale]/revive|unstuck (SSR, guardas, i18n). Namespace Services.

Verificado: API sin sesión 401, páginas redirigen a login, cliente SOAP devuelve null
limpio sin worldserver. Patrón base para el resto de servicios de personaje.

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

35 lines
1.2 KiB
TypeScript

// Cliente SOAP al worldserver de AzerothCore (port de home/ac_soap.py).
function escapeXml(s: string): string {
return s.replace(/&/g, '&amp;').replace(/</g, '&lt;').replace(/>/g, '&gt;')
}
/** Ejecuta un comando en el worldserver por SOAP. Devuelve el texto de respuesta o null. */
export async function executeSoapCommand(command: string): Promise<string | null> {
const url = process.env.AC_SOAP_URL
const user = process.env.AC_SOAP_USER ?? ''
const pass = process.env.AC_SOAP_PASSWORD ?? ''
const urn = process.env.AC_SOAP_URN ?? 'urn:AC'
if (!url) return null
const soap = `<?xml version="1.0" encoding="utf-8"?>
<SOAP-ENV:Envelope xmlns:SOAP-ENV="http://schemas.xmlsoap.org/soap/envelope/" xmlns:ns1="${urn}">
<SOAP-ENV:Body><ns1:executeCommand><command>${escapeXml(command)}</command></ns1:executeCommand></SOAP-ENV:Body>
</SOAP-ENV:Envelope>`
try {
const res = await fetch(url, {
method: 'POST',
headers: {
Authorization: 'Basic ' + Buffer.from(`${user}:${pass}`).toString('base64'),
'Content-Type': 'application/xml',
},
body: soap,
signal: AbortSignal.timeout(5000),
})
if (!res.ok) return null
return await res.text()
} catch {
return null // worldserver inaccesible
}
}