forked from BRETT/BRETT
Initial Commit
This commit is contained in:
@@ -0,0 +1,71 @@
|
||||
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 }
|
||||
})
|
||||
Reference in New Issue
Block a user