forked from BRETT/BRETT
72 lines
2.9 KiB
TypeScript
72 lines
2.9 KiB
TypeScript
import { and, desc, eq, gte, inArray } from 'drizzle-orm'
|
|
import { tags, uploads, uploadTags, users } from '../../db/schema'
|
|
import { normalizeTagName } from '../../../shared/utils/tags'
|
|
|
|
type UploadType = typeof uploads.$inferSelect['type']
|
|
|
|
// Leichtgewichtige Liste — Thumbnails und Kommentarzahlen lädt der Client
|
|
// separat über /api/uploads/meta nach.
|
|
// Pagination per Dense-Pack-Simulation: Der Client schickt, wie viele
|
|
// Grid-Zellen auf den Bildschirm passen (cells) und wie breit das Grid ist
|
|
// (cols). Tiles belegen je nach Modus/Kommentarzahl 1x1, 2x2 oder 3x3 Zellen.
|
|
// grid-flow-dense platziert jedes Tile in die erste freie Position — was
|
|
// nicht passt, wandert auf die nächste Seite. So passt jede Seite garantiert
|
|
// ohne Scrollen in den Viewport, auch bei fragmentierten Zeilenenden.
|
|
export default defineEventHandler(async (event) => {
|
|
const db = useDb()
|
|
const q = getQuery(event)
|
|
const tag = normalizeTagName(String(q.tag ?? ''))
|
|
const types = String(q.type ?? '')
|
|
.split(',')
|
|
.map(t => t.trim())
|
|
.filter(t => (uploads.type.enumValues as readonly string[]).includes(t)) as UploadType[]
|
|
const uploaderId = Number(q.uploader)
|
|
const days = Number(q.days)
|
|
const { page, cells, cols, uniform } = getGridPageParams(q)
|
|
|
|
const conditions = []
|
|
if (tag) {
|
|
conditions.push(inArray(
|
|
uploads.id,
|
|
db.select({ id: uploadTags.uploadId }).from(uploadTags)
|
|
.innerJoin(tags, eq(uploadTags.tagId, tags.id))
|
|
.where(eq(tags.name, tag)),
|
|
))
|
|
}
|
|
if (types.length) conditions.push(inArray(uploads.type, types))
|
|
if (Number.isInteger(uploaderId) && uploaderId > 0) conditions.push(eq(uploads.userId, uploaderId))
|
|
if (Number.isFinite(days) && days > 0) {
|
|
conditions.push(gte(uploads.createdAt, new Date(Date.now() - days * 86400_000)))
|
|
}
|
|
|
|
// Alle Treffer holen (kleine Felder) und clientseitig dieselbe
|
|
// Span-Logik wie UploadTile anwenden, um Seiten exakt zu füllen
|
|
const rows = await db
|
|
.select({
|
|
id: uploads.id,
|
|
type: uploads.type,
|
|
fileName: uploads.fileName,
|
|
externalUrl: uploads.externalUrl,
|
|
commentCount: uploads.commentCount,
|
|
createdAt: uploads.createdAt,
|
|
userId: users.id,
|
|
userName: users.name,
|
|
userColor: users.color,
|
|
userAvatar: users.avatar,
|
|
})
|
|
.from(uploads)
|
|
.innerJoin(users, eq(uploads.userId, users.id))
|
|
.where(conditions.length ? and(...conditions) : undefined)
|
|
.orderBy(desc(uploads.createdAt))
|
|
|
|
const pages = paginateGrid(rows, r => gridTileDims(r.commentCount, uniform, cols), cells, cols)
|
|
|
|
const pageRows = pages[page - 1] ?? []
|
|
const items = pageRows.map((row) => {
|
|
const url = row.externalUrl ?? `/uploads/${row.fileName}`
|
|
return { id: row.id, type: row.type, url, commentCount: row.commentCount, createdAt: row.createdAt, userId: row.userId, userName: row.userName, userColor: row.userColor, userAvatarUrl: avatarUrl(row.userAvatar) }
|
|
})
|
|
|
|
return { items, total: rows.length, pageCount: pages.length }
|
|
})
|