96e996e9c2
- 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>
28 lines
896 B
TypeScript
28 lines
896 B
TypeScript
import type { RowDataPacket } from 'mysql2'
|
|
import { db, DB } from './db'
|
|
|
|
export interface GameCharacter {
|
|
name: string
|
|
online: boolean
|
|
}
|
|
|
|
/** Personajes de una cuenta de juego (acore_characters). Vacío si no está poblada. */
|
|
export async function getGameCharacters(accountId: number): Promise<GameCharacter[]> {
|
|
try {
|
|
const [rows] = await db(DB.characters).query<RowDataPacket[]>(
|
|
'SELECT name, online FROM characters WHERE account = ?',
|
|
[accountId],
|
|
)
|
|
return rows.map((r) => ({ name: r.name, online: r.online === 1 }))
|
|
} catch {
|
|
return []
|
|
}
|
|
}
|
|
|
|
/** True si el personaje pertenece a la cuenta y está desconectado. */
|
|
export async function characterIsOfflineAndOwned(accountId: number, name: string): Promise<boolean> {
|
|
const chars = await getGameCharacters(accountId)
|
|
const c = chars.find((ch) => ch.name === name)
|
|
return Boolean(c && !c.online)
|
|
}
|