Foro: restaurar posts y temas borrados (moderadores)
- lib/forum.ts: getTopic/getPosts/countPosts aceptan includeDeleted (mods ven lo borrado); Post.deleted y TopicFull.deleted. - API: PUT /api/forum/post restaura post (mod-only); moderate action 'restore' restaura tema (usa includeDeleted al leer el tema). - UI: posts borrados atenuados con badge y botón Restaurar (PostActions); TopicModBar muestra banner + Restaurar tema cuando el tema está borrado. - i18n es/en: Forum.restore, restoreTopic, deletedMark. Verificado: build OK, PUT post y moderate restore 401 sin sesión, /forum 200. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
This commit is contained in:
+13
-6
@@ -37,6 +37,7 @@ export interface Post {
|
||||
text: string
|
||||
time: string | null
|
||||
edited: boolean
|
||||
deleted: boolean
|
||||
}
|
||||
|
||||
export interface TopicFull {
|
||||
@@ -46,6 +47,7 @@ export interface TopicFull {
|
||||
poster_id: number | null
|
||||
locked: boolean
|
||||
sticky: boolean
|
||||
deleted: boolean
|
||||
}
|
||||
|
||||
export interface SearchResult {
|
||||
@@ -148,10 +150,11 @@ export async function getTopics(forumId: number, offset = 0, limit = 20): Promis
|
||||
}
|
||||
}
|
||||
|
||||
export async function getTopic(id: number): Promise<TopicFull | null> {
|
||||
export async function getTopic(id: number, includeDeleted = false): Promise<TopicFull | null> {
|
||||
try {
|
||||
const where = includeDeleted ? 'id = ?' : 'id = ? AND deleted = 0'
|
||||
const [rows] = await db(DB.web).query<RowDataPacket[]>(
|
||||
'SELECT id, name, forum_id, poster_id, locked, sticky FROM forum_topics WHERE id = ? AND deleted = 0',
|
||||
`SELECT id, name, forum_id, poster_id, locked, sticky, deleted FROM forum_topics WHERE ${where}`,
|
||||
[id],
|
||||
)
|
||||
const r = rows[0]
|
||||
@@ -163,6 +166,7 @@ export async function getTopic(id: number): Promise<TopicFull | null> {
|
||||
poster_id: r.poster_id ?? null,
|
||||
locked: r.locked === 1,
|
||||
sticky: r.sticky === 1,
|
||||
deleted: r.deleted === 1,
|
||||
}
|
||||
: null
|
||||
} catch {
|
||||
@@ -170,10 +174,11 @@ export async function getTopic(id: number): Promise<TopicFull | null> {
|
||||
}
|
||||
}
|
||||
|
||||
export async function countPosts(topicId: number): Promise<number> {
|
||||
export async function countPosts(topicId: number, includeDeleted = false): Promise<number> {
|
||||
try {
|
||||
const del = includeDeleted ? '' : 'AND deleted = 0'
|
||||
const [rows] = await db(DB.web).query<RowDataPacket[]>(
|
||||
'SELECT COUNT(*) AS n FROM forum_posts WHERE topic_id = ? AND deleted = 0',
|
||||
`SELECT COUNT(*) AS n FROM forum_posts WHERE topic_id = ? ${del}`,
|
||||
[topicId],
|
||||
)
|
||||
return Number(rows[0]?.n ?? 0)
|
||||
@@ -182,10 +187,11 @@ export async function countPosts(topicId: number): Promise<number> {
|
||||
}
|
||||
}
|
||||
|
||||
export async function getPosts(topicId: number, offset = 0, limit = 15): Promise<Post[]> {
|
||||
export async function getPosts(topicId: number, offset = 0, limit = 15, includeDeleted = false): Promise<Post[]> {
|
||||
try {
|
||||
const del = includeDeleted ? '' : 'AND deleted = 0'
|
||||
const [rows] = await db(DB.web).query<RowDataPacket[]>(
|
||||
'SELECT id, poster, poster_id, text, time, created_at, updated_at FROM forum_posts WHERE topic_id = ? AND deleted = 0 ORDER BY time ASC, id ASC LIMIT ? OFFSET ?',
|
||||
`SELECT id, poster, poster_id, text, time, created_at, updated_at, deleted FROM forum_posts WHERE topic_id = ? ${del} ORDER BY time ASC, id ASC LIMIT ? OFFSET ?`,
|
||||
[topicId, limit, offset],
|
||||
)
|
||||
return rows.map((r) => ({
|
||||
@@ -198,6 +204,7 @@ export async function getPosts(topicId: number, offset = 0, limit = 15): Promise
|
||||
edited: Boolean(
|
||||
r.updated_at && r.created_at && new Date(r.updated_at).getTime() - new Date(r.created_at).getTime() > 2000,
|
||||
),
|
||||
deleted: r.deleted === 1,
|
||||
}))
|
||||
} catch {
|
||||
return []
|
||||
|
||||
Reference in New Issue
Block a user