import type { RowDataPacket } from 'mysql2' import { db, DB } from './db' export interface ForumInfo { id: number category_id: number name: string description: string icon: string | null topic_count: number post_count: number last_topic_id: number | null last_topic_name: string | null last_topic_poster: string | null } export interface Category { id: number name: string forums: ForumInfo[] } export interface Topic { id: number name: string poster: string created: string | null sticky: boolean locked: boolean views: number } export interface Post { id: number poster: string text: string time: string | null } /** Índice del foro: categorías con sus foros visibles (+ contadores y último tema). */ export async function getForumIndex(): Promise { try { const [cats] = await db(DB.web).query( 'SELECT id, name FROM forum_categories ORDER BY `order`, id', ) const [forums] = await db(DB.web).query( `SELECT f.id, f.category_id, f.name, f.description, f.icon, (SELECT COUNT(*) FROM forum_topics t WHERE t.forum_id = f.id AND t.deleted = 0) AS topic_count, (SELECT COUNT(*) FROM forum_posts p JOIN forum_topics t ON p.topic_id = t.id WHERE t.forum_id = f.id AND p.deleted = 0 AND t.deleted = 0) AS post_count, (SELECT t2.id FROM forum_topics t2 WHERE t2.forum_id = f.id AND t2.deleted = 0 ORDER BY t2.updated_at DESC, t2.id DESC LIMIT 1) AS last_topic_id, (SELECT t3.name FROM forum_topics t3 WHERE t3.forum_id = f.id AND t3.deleted = 0 ORDER BY t3.updated_at DESC, t3.id DESC LIMIT 1) AS last_topic_name, (SELECT t4.poster FROM forum_topics t4 WHERE t4.forum_id = f.id AND t4.deleted = 0 ORDER BY t4.updated_at DESC, t4.id DESC LIMIT 1) AS last_topic_poster FROM forums f WHERE f.visibility = 1 AND f.deleted = 0 ORDER BY f.\`order\`, f.id`, ) const byCat = new Map() for (const f of forums) { const info = f as unknown as ForumInfo if (!byCat.has(info.category_id)) byCat.set(info.category_id, []) byCat.get(info.category_id)!.push(info) } return cats .map((c) => ({ id: c.id, name: c.name, forums: byCat.get(c.id) ?? [] })) .filter((c) => c.forums.length > 0) } catch { return [] } } export async function getForum(id: number): Promise<{ name: string; description: string } | null> { try { const [rows] = await db(DB.web).query( 'SELECT name, description FROM forums WHERE id = ? AND visibility = 1 AND deleted = 0', [id], ) return rows[0] ? { name: rows[0].name, description: rows[0].description } : null } catch { return null } } export async function getTopics(forumId: number): Promise { try { const [rows] = await db(DB.web).query( 'SELECT id, name, poster, created, sticky, locked, views FROM forum_topics WHERE forum_id = ? AND deleted = 0 ORDER BY sticky DESC, updated_at DESC, id DESC', [forumId], ) return rows.map((r) => ({ id: r.id, name: r.name, poster: r.poster, created: r.created ? new Date(r.created).toISOString() : null, sticky: r.sticky === 1, locked: r.locked === 1, views: r.views ?? 0, })) } catch { return [] } } export async function getTopic(id: number): Promise<{ name: string; locked: boolean } | null> { try { const [rows] = await db(DB.web).query( 'SELECT name, locked FROM forum_topics WHERE id = ? AND deleted = 0', [id], ) return rows[0] ? { name: rows[0].name, locked: rows[0].locked === 1 } : null } catch { return null } } export async function getPosts(topicId: number): Promise { try { const [rows] = await db(DB.web).query( 'SELECT id, poster, text, time FROM forum_posts WHERE topic_id = ? AND deleted = 0 ORDER BY time ASC, id ASC', [topicId], ) return rows.map((r) => ({ id: r.id, poster: r.poster, text: r.text, time: r.time ? new Date(r.time).toISOString() : null, })) } catch { return [] } }