forked from BRETT/BRETT
Initial Commit
This commit is contained in:
@@ -0,0 +1,183 @@
|
||||
import { and, asc, desc, eq, gt, inArray, lt, or } from 'drizzle-orm'
|
||||
import { commentAttachments, commentReactions, comments, favorites, tags, threadSubscriptions, uploads, uploadReactions, uploadTags, users } from '../../db/schema'
|
||||
import type { CommentAttachment } from '~~/shared/utils/attachments'
|
||||
|
||||
export default defineEventHandler(async (event) => {
|
||||
const id = getRouterParam(event, 'id') ?? ''
|
||||
if (!/^[A-Za-z0-9]{1,16}$/.test(id)) {
|
||||
throw createError({ statusCode: 400, statusMessage: 'Ungültige ID' })
|
||||
}
|
||||
const db = useDb()
|
||||
|
||||
const uploadRows = await db
|
||||
.select({
|
||||
id: uploads.id,
|
||||
type: uploads.type,
|
||||
fileName: uploads.fileName,
|
||||
externalUrl: uploads.externalUrl,
|
||||
originalName: uploads.originalName,
|
||||
mimeType: uploads.mimeType,
|
||||
previewImage: uploads.previewImage,
|
||||
previewTitle: uploads.previewTitle,
|
||||
audioMeta: uploads.audioMeta,
|
||||
createdAt: uploads.createdAt,
|
||||
userName: users.name,
|
||||
userId: users.id,
|
||||
userColor: users.color,
|
||||
userAvatar: users.avatar,
|
||||
userCardEffect: users.cardEffect,
|
||||
userCardEffectColor: users.cardEffectColor,
|
||||
userCardVignette: users.cardVignette,
|
||||
userCardBorderEffect: users.cardBorderEffect,
|
||||
userCardBorderEffectColor: users.cardBorderEffectColor,
|
||||
})
|
||||
.from(uploads)
|
||||
.innerJoin(users, eq(uploads.userId, users.id))
|
||||
.where(eq(uploads.id, id))
|
||||
.limit(1)
|
||||
const upload = uploadRows[0]
|
||||
if (!upload) {
|
||||
throw createError({ statusCode: 404, statusMessage: 'Upload nicht gefunden' })
|
||||
}
|
||||
|
||||
const commentRows = await db
|
||||
.select({
|
||||
id: comments.id,
|
||||
content: comments.content,
|
||||
createdAt: comments.createdAt,
|
||||
userName: users.name,
|
||||
userId: users.id,
|
||||
userColor: users.color,
|
||||
userAvatar: users.avatar,
|
||||
})
|
||||
.from(comments)
|
||||
.innerJoin(users, eq(comments.userId, users.id))
|
||||
.where(eq(comments.uploadId, id))
|
||||
.orderBy(asc(comments.createdAt))
|
||||
|
||||
// Nachbarn in Grid-Reihenfolge (createdAt desc, id desc als Tiebreak)
|
||||
const sessionUser = await getSessionUser(event)
|
||||
const commentIds = commentRows.map(c => c.id)
|
||||
const [reactionRows, uploadReactionRows, favRows, newerRows, olderRows, tagRows, attachmentRows, subscriptionRows] = await Promise.all([
|
||||
commentIds.length
|
||||
? db
|
||||
.select({
|
||||
commentId: commentReactions.commentId,
|
||||
emoji: commentReactions.emoji,
|
||||
userId: commentReactions.userId,
|
||||
userName: users.name,
|
||||
})
|
||||
.from(commentReactions)
|
||||
.innerJoin(users, eq(commentReactions.userId, users.id))
|
||||
.where(inArray(commentReactions.commentId, commentIds))
|
||||
.orderBy(asc(commentReactions.createdAt))
|
||||
: Promise.resolve([]),
|
||||
db
|
||||
.select({
|
||||
emoji: uploadReactions.emoji,
|
||||
userId: uploadReactions.userId,
|
||||
userName: users.name,
|
||||
})
|
||||
.from(uploadReactions)
|
||||
.innerJoin(users, eq(uploadReactions.userId, users.id))
|
||||
.where(eq(uploadReactions.uploadId, id))
|
||||
.orderBy(asc(uploadReactions.createdAt)),
|
||||
db
|
||||
.select({ id: users.id, name: users.name, color: users.color, avatar: users.avatar })
|
||||
.from(favorites)
|
||||
.innerJoin(users, eq(favorites.userId, users.id))
|
||||
.where(eq(favorites.uploadId, id))
|
||||
.orderBy(desc(favorites.createdAt)),
|
||||
db
|
||||
.select({ id: uploads.id })
|
||||
.from(uploads)
|
||||
.where(or(
|
||||
gt(uploads.createdAt, upload.createdAt),
|
||||
and(eq(uploads.createdAt, upload.createdAt), gt(uploads.id, upload.id)),
|
||||
))
|
||||
.orderBy(asc(uploads.createdAt), asc(uploads.id))
|
||||
.limit(1),
|
||||
db
|
||||
.select({ id: uploads.id })
|
||||
.from(uploads)
|
||||
.where(or(
|
||||
lt(uploads.createdAt, upload.createdAt),
|
||||
and(eq(uploads.createdAt, upload.createdAt), lt(uploads.id, upload.id)),
|
||||
))
|
||||
.orderBy(desc(uploads.createdAt), desc(uploads.id))
|
||||
.limit(1),
|
||||
db
|
||||
.select({ name: tags.name })
|
||||
.from(uploadTags)
|
||||
.innerJoin(tags, eq(uploadTags.tagId, tags.id))
|
||||
.where(eq(uploadTags.uploadId, id))
|
||||
.orderBy(asc(tags.name)),
|
||||
commentIds.length
|
||||
? db
|
||||
.select()
|
||||
.from(commentAttachments)
|
||||
.where(inArray(commentAttachments.commentId, commentIds))
|
||||
.orderBy(asc(commentAttachments.id))
|
||||
: Promise.resolve([]),
|
||||
sessionUser
|
||||
? db.select({ enabled: threadSubscriptions.enabled }).from(threadSubscriptions)
|
||||
.where(and(eq(threadSubscriptions.uploadId, id), eq(threadSubscriptions.userId, sessionUser.id))).limit(1)
|
||||
: Promise.resolve([]),
|
||||
])
|
||||
|
||||
const attachmentsByComment = new Map<number, CommentAttachment[]>()
|
||||
for (const a of attachmentRows) {
|
||||
let list = attachmentsByComment.get(a.commentId)
|
||||
if (!list) {
|
||||
list = []
|
||||
attachmentsByComment.set(a.commentId, list)
|
||||
}
|
||||
list.push({ id: a.id, name: a.originalName, url: `/uploads/attachments/${a.fileName}`, mimeType: a.mimeType, size: a.size, cover: a.cover, meta: a.meta as CommentAttachment['meta'] })
|
||||
}
|
||||
|
||||
const reactionsByComment = new Map<number, { emoji: string, count: number, me: boolean, names: string[] }[]>()
|
||||
for (const r of reactionRows) {
|
||||
let list = reactionsByComment.get(r.commentId)
|
||||
if (!list) {
|
||||
list = []
|
||||
reactionsByComment.set(r.commentId, list)
|
||||
}
|
||||
let entry = list.find(e => e.emoji === r.emoji)
|
||||
if (!entry) {
|
||||
entry = { emoji: r.emoji, count: 0, me: false, names: [] }
|
||||
list.push(entry)
|
||||
}
|
||||
entry.count += 1
|
||||
entry.names.push(r.userName)
|
||||
if (r.userId === sessionUser?.id) entry.me = true
|
||||
}
|
||||
|
||||
const uploadReactionList: { emoji: string, count: number, me: boolean, names: string[] }[] = []
|
||||
for (const r of uploadReactionRows) {
|
||||
let entry = uploadReactionList.find(e => e.emoji === r.emoji)
|
||||
if (!entry) {
|
||||
entry = { emoji: r.emoji, count: 0, me: false, names: [] }
|
||||
uploadReactionList.push(entry)
|
||||
}
|
||||
entry.count += 1
|
||||
entry.names.push(r.userName)
|
||||
if (r.userId === sessionUser?.id) entry.me = true
|
||||
}
|
||||
|
||||
const url = upload.externalUrl ?? `/uploads/${upload.fileName}`
|
||||
const embedId = upload.type === 'youtube' && upload.externalUrl
|
||||
? getYoutubeId(upload.externalUrl)
|
||||
: upload.type === 'twitch' && upload.externalUrl
|
||||
? serializeTwitchTarget(getTwitchTarget(upload.externalUrl))
|
||||
: null
|
||||
return {
|
||||
upload: { ...upload, url, embedId, userAvatarUrl: avatarUrl(upload.userAvatar), tags: tagRows.map(t => t.name) },
|
||||
comments: commentRows.map(c => ({ ...c, userAvatarUrl: avatarUrl(c.userAvatar), reactions: reactionsByComment.get(c.id) ?? [], attachments: attachmentsByComment.get(c.id) ?? [] })),
|
||||
uploadReactions: uploadReactionList,
|
||||
favoritedBy: favRows.map(f => ({ id: f.id, name: f.name, color: f.color, avatarUrl: avatarUrl(f.avatar) })),
|
||||
isFavorited: sessionUser ? favRows.some(f => f.id === sessionUser.id) : false,
|
||||
isSubscribed: subscriptionRows[0]?.enabled ?? false,
|
||||
newerId: newerRows[0]?.id ?? null,
|
||||
olderId: olderRows[0]?.id ?? null,
|
||||
}
|
||||
})
|
||||
Reference in New Issue
Block a user