c36b543184
Pivote a Next.js full-stack (Django saldrá del todo en el cutover). Next accede a las mismas BD MySQL que Django: - lib/db.ts: pools mysql2 (django_wow + acore_*), singleton en globalThis, creds en web-next/.env.local (gitignored, copiadas del .env de Django). - lib/home.ts: getNoticias (home_noticia), getServerStatus (home_serverselection + online desde acore_characters + TCP check + expansión por gamebuild, incl. Classic). - app/page.tsx: home SSR que llama a lib/home (ya NO a la API Django). Se elimina lib/api.ts. Verificado: con el servicio Django PARADO, la home de Next sigue sirviendo 200 con datos reales -> lee MySQL directo, Django fuera del circuito. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
66 lines
2.6 KiB
TypeScript
66 lines
2.6 KiB
TypeScript
import { getNoticias, getServerStatus, type Noticia, type ServerStatus } from '@/lib/home'
|
|
|
|
// SSR por petición: los datos se leen DIRECTAMENTE de MySQL desde el servidor Next
|
|
// (sin Django). force-dynamic para renderizar por petición.
|
|
export const dynamic = 'force-dynamic'
|
|
|
|
async function getData(): Promise<{ noticias: Noticia[]; status: ServerStatus | null }> {
|
|
const [news, status] = await Promise.allSettled([getNoticias(), getServerStatus()])
|
|
return {
|
|
noticias: news.status === 'fulfilled' ? news.value : [],
|
|
status: status.status === 'fulfilled' ? status.value : null,
|
|
}
|
|
}
|
|
|
|
export default async function HomePage() {
|
|
const { noticias, status } = await getData()
|
|
|
|
return (
|
|
<main className="mx-auto max-w-3xl px-4 py-8">
|
|
<h1 className="text-3xl font-bold text-amber-500">Nova WoW</h1>
|
|
|
|
<section className="my-6 rounded-lg border border-amber-900/60 bg-[#241812] p-4">
|
|
<h2 className="mb-3 text-lg font-semibold">Estado del servidor</h2>
|
|
{status ? (
|
|
<ul className="space-y-1 text-sm">
|
|
<li>
|
|
Servidor: <span className="text-amber-400">{status.server_name ?? '—'}</span> ({status.expansion})
|
|
</li>
|
|
<li>Personajes en línea: {status.online_characters}</li>
|
|
<li>
|
|
Login:{' '}
|
|
<span className={status.status === 'Online' ? 'text-green-400' : 'text-red-400'}>{status.status}</span>
|
|
</li>
|
|
<li>Dirección: {status.address ?? '—'}</li>
|
|
</ul>
|
|
) : (
|
|
<p>No disponible.</p>
|
|
)}
|
|
</section>
|
|
|
|
<section>
|
|
<h2 className="mb-3 text-lg font-semibold">Noticias</h2>
|
|
{noticias.length === 0 ? (
|
|
<p className="text-amber-200/70">No hay noticias disponibles en este momento.</p>
|
|
) : (
|
|
noticias.map((n) => (
|
|
<article key={n.id} className="mb-4 border-b border-amber-900/60 pb-4">
|
|
<h3 className="text-xl font-semibold">{n.titulo}</h3>
|
|
{n.fecha && <small className="text-amber-200/60">{new Date(n.fecha).toLocaleString('es-ES')}</small>}
|
|
<div className="prose prose-invert mt-2 max-w-none" dangerouslySetInnerHTML={{ __html: n.contenido }} />
|
|
{n.enlace && (
|
|
<p className="mt-2">
|
|
Enlace:{' '}
|
|
<a className="text-sky-400 underline" href={n.enlace} target="_blank" rel="noopener noreferrer">
|
|
aquí
|
|
</a>
|
|
</p>
|
|
)}
|
|
</article>
|
|
))
|
|
)}
|
|
</section>
|
|
</main>
|
|
)
|
|
}
|