mirror of
https://github.com/ngthanhvu/kr-phim.git
synced 2026-08-10 16:07:47 +00:00
Add database migration snapshots for comment_votes, comments, movies, and users tables
- Created 0009_snapshot.json with initial structure for comment_votes, comments, movies, and users tables. - Created 0010_snapshot.json with updated structure for comments, including new fields: pinned and spoiler. - Created 0011_snapshot.json with further updates to comments, adding anonymous field and refining existing structures.
This commit is contained in:
@@ -0,0 +1,41 @@
|
||||
import { eq } from 'drizzle-orm'
|
||||
import { comments } from '../../database/schema'
|
||||
import { getTokenFromEvent, verifyToken } from '../../utils/auth'
|
||||
|
||||
export default defineEventHandler(async (event) => {
|
||||
const token = getTokenFromEvent(event)
|
||||
if (!token) {
|
||||
throw createError({ statusCode: 401, message: 'Chưa đăng nhập' })
|
||||
}
|
||||
|
||||
const payload = verifyToken(token)
|
||||
if (!payload) {
|
||||
throw createError({ statusCode: 401, message: 'Phiên đăng nhập hết hạn' })
|
||||
}
|
||||
|
||||
const id = Number(getRouterParam(event, 'id'))
|
||||
if (!id) {
|
||||
throw createError({ statusCode: 400, message: 'Thiếu ID bình luận' })
|
||||
}
|
||||
|
||||
const db = useDb()
|
||||
|
||||
const [comment] = await db
|
||||
.select()
|
||||
.from(comments)
|
||||
.where(eq(comments.id, id))
|
||||
|
||||
if (!comment) {
|
||||
throw createError({ statusCode: 404, message: 'Không tìm thấy bình luận' })
|
||||
}
|
||||
|
||||
if (comment.userId !== payload.id && payload.role !== 'admin') {
|
||||
throw createError({ statusCode: 403, message: 'Không có quyền xóa bình luận này' })
|
||||
}
|
||||
|
||||
await db
|
||||
.delete(comments)
|
||||
.where(eq(comments.id, id))
|
||||
|
||||
return { success: true }
|
||||
})
|
||||
@@ -0,0 +1,45 @@
|
||||
import { eq } from 'drizzle-orm'
|
||||
import { comments } from '../../../database/schema'
|
||||
import { getTokenFromEvent, verifyToken } from '../../../utils/auth'
|
||||
|
||||
export default defineEventHandler(async (event) => {
|
||||
const token = getTokenFromEvent(event)
|
||||
if (!token) {
|
||||
throw createError({ statusCode: 401, message: 'Chưa đăng nhập' })
|
||||
}
|
||||
|
||||
const payload = verifyToken(token)
|
||||
if (!payload) {
|
||||
throw createError({ statusCode: 401, message: 'Phiên đăng nhập hết hạn' })
|
||||
}
|
||||
|
||||
// Only admins can pin/unpin
|
||||
if (payload.role !== 'admin') {
|
||||
throw createError({ statusCode: 403, message: 'Chỉ admin mới có quyền ghim bình luận' })
|
||||
}
|
||||
|
||||
const id = Number(getRouterParam(event, 'id'))
|
||||
if (!id) {
|
||||
throw createError({ statusCode: 400, message: 'Thiếu ID bình luận' })
|
||||
}
|
||||
|
||||
const db = useDb()
|
||||
|
||||
const [comment] = await db
|
||||
.select()
|
||||
.from(comments)
|
||||
.where(eq(comments.id, id))
|
||||
|
||||
if (!comment) {
|
||||
throw createError({ statusCode: 404, message: 'Không tìm thấy bình luận' })
|
||||
}
|
||||
|
||||
// Toggle pinned state
|
||||
const newPinned = !comment.pinned
|
||||
await db
|
||||
.update(comments)
|
||||
.set({ pinned: newPinned })
|
||||
.where(eq(comments.id, id))
|
||||
|
||||
return { success: true, pinned: newPinned }
|
||||
})
|
||||
@@ -0,0 +1,135 @@
|
||||
import { eq, and, desc, sql, or, isNull } from 'drizzle-orm'
|
||||
import { comments, users, commentVotes } from '../../database/schema'
|
||||
|
||||
export default defineEventHandler(async (event) => {
|
||||
const query = getQuery(event)
|
||||
const { source = '', slug, userId } = query
|
||||
|
||||
if (!slug) {
|
||||
throw createError({ statusCode: 400, message: 'Thiếu slug phim' })
|
||||
}
|
||||
|
||||
const db = useDb()
|
||||
|
||||
const whereConditions = [
|
||||
eq(comments.slug, String(slug)),
|
||||
isNull(comments.parentId),
|
||||
]
|
||||
|
||||
if (source) {
|
||||
whereConditions.push(eq(comments.source, String(source)))
|
||||
}
|
||||
|
||||
// Fetch pinned comments first, then regular ones
|
||||
const results = await db
|
||||
.select({
|
||||
id: comments.id,
|
||||
userId: comments.userId,
|
||||
userName: users.name,
|
||||
userAvatar: users.avatar,
|
||||
userRole: users.role,
|
||||
source: comments.source,
|
||||
slug: comments.slug,
|
||||
movieName: comments.movieName,
|
||||
content: comments.content,
|
||||
pinned: comments.pinned,
|
||||
spoiler: comments.spoiler,
|
||||
anonymous: comments.anonymous,
|
||||
likeCount: comments.likeCount,
|
||||
dislikeCount: comments.dislikeCount,
|
||||
createdAt: comments.createdAt,
|
||||
})
|
||||
.from(comments)
|
||||
.leftJoin(users, eq(comments.userId, users.id))
|
||||
.where(and(...whereConditions))
|
||||
.orderBy(sql`${comments.pinned} DESC, ${comments.createdAt} DESC`)
|
||||
.limit(100)
|
||||
|
||||
const commentIds = results.map(r => r.id)
|
||||
|
||||
let userVotes: Record<number, number> = {}
|
||||
if (userId && commentIds.length > 0) {
|
||||
const votes = await db
|
||||
.select()
|
||||
.from(commentVotes)
|
||||
.where(and(
|
||||
eq(commentVotes.userId, Number(userId)),
|
||||
or(...commentIds.map(id => eq(commentVotes.commentId, id)))
|
||||
))
|
||||
userVotes = Object.fromEntries(votes.map(v => [v.commentId, v.vote]))
|
||||
}
|
||||
|
||||
let replies: any[] = []
|
||||
if (commentIds.length > 0) {
|
||||
replies = await db
|
||||
.select({
|
||||
id: comments.id,
|
||||
userId: comments.userId,
|
||||
userName: users.name,
|
||||
userAvatar: users.avatar,
|
||||
userRole: users.role,
|
||||
parentId: comments.parentId,
|
||||
content: comments.content,
|
||||
pinned: comments.pinned,
|
||||
spoiler: comments.spoiler,
|
||||
anonymous: comments.anonymous,
|
||||
likeCount: comments.likeCount,
|
||||
dislikeCount: comments.dislikeCount,
|
||||
createdAt: comments.createdAt,
|
||||
})
|
||||
.from(comments)
|
||||
.leftJoin(users, eq(comments.userId, users.id))
|
||||
.where(
|
||||
and(
|
||||
or(...commentIds.map(id => eq(comments.parentId, id)))
|
||||
)
|
||||
)
|
||||
.orderBy(comments.createdAt)
|
||||
}
|
||||
|
||||
const repliesByParent: Record<number, typeof replies> = {}
|
||||
for (const reply of replies) {
|
||||
const parentId = reply.parentId!
|
||||
if (!repliesByParent[parentId]) {
|
||||
repliesByParent[parentId] = []
|
||||
}
|
||||
repliesByParent[parentId].push(reply)
|
||||
}
|
||||
|
||||
return {
|
||||
items: results.map(r => ({
|
||||
id: r.id,
|
||||
userId: r.userId,
|
||||
userName: r.anonymous ? 'Ẩn danh' : (r.userName || 'Ẩn danh'),
|
||||
userAvatar: r.anonymous ? null : r.userAvatar,
|
||||
userRole: r.userRole,
|
||||
source: r.source,
|
||||
slug: r.slug,
|
||||
movieName: r.movieName,
|
||||
content: r.content,
|
||||
pinned: r.pinned || false,
|
||||
spoiler: r.spoiler || false,
|
||||
anonymous: r.anonymous || false,
|
||||
likeCount: r.likeCount,
|
||||
dislikeCount: r.dislikeCount,
|
||||
createdAt: r.createdAt,
|
||||
userVote: userVotes[r.id] || 0,
|
||||
replies: (repliesByParent[r.id] || []).map(rep => ({
|
||||
id: rep.id,
|
||||
userId: rep.userId,
|
||||
userName: rep.anonymous ? 'Ẩn danh' : (rep.userName || 'Ẩn danh'),
|
||||
userAvatar: rep.anonymous ? null : rep.userAvatar,
|
||||
userRole: rep.userRole,
|
||||
parentId: rep.parentId,
|
||||
content: rep.content,
|
||||
pinned: rep.pinned || false,
|
||||
spoiler: rep.spoiler || false,
|
||||
anonymous: rep.anonymous || false,
|
||||
likeCount: rep.likeCount,
|
||||
dislikeCount: rep.dislikeCount,
|
||||
createdAt: rep.createdAt,
|
||||
userVote: userVotes[rep.id] || 0,
|
||||
})),
|
||||
})),
|
||||
}
|
||||
})
|
||||
@@ -0,0 +1,57 @@
|
||||
import { eq } from 'drizzle-orm'
|
||||
import { comments } from '../../database/schema'
|
||||
import { getTokenFromEvent, verifyToken } from '../../utils/auth'
|
||||
|
||||
export default defineEventHandler(async (event) => {
|
||||
const token = getTokenFromEvent(event)
|
||||
if (!token) {
|
||||
throw createError({ statusCode: 401, message: 'Chưa đăng nhập' })
|
||||
}
|
||||
|
||||
const payload = verifyToken(token)
|
||||
if (!payload) {
|
||||
throw createError({ statusCode: 401, message: 'Phiên đăng nhập hết hạn' })
|
||||
}
|
||||
|
||||
const body = await readBody(event)
|
||||
const { source, slug, content, movieName, parentId, spoiler, anonymous } = body
|
||||
|
||||
if (!slug || !content?.trim()) {
|
||||
throw createError({ statusCode: 400, message: 'Thiếu thông tin bình luận' })
|
||||
}
|
||||
|
||||
if (parentId) {
|
||||
const db = useDb()
|
||||
const [parent] = await db.select().from(comments).where(eq(comments.id, Number(parentId)))
|
||||
if (!parent) {
|
||||
throw createError({ statusCode: 404, message: 'Không tìm thấy bình luận gốc' })
|
||||
}
|
||||
}
|
||||
|
||||
const db = useDb()
|
||||
|
||||
const result = await db.insert(comments).values({
|
||||
userId: payload.id,
|
||||
source: source || '',
|
||||
slug,
|
||||
movieName: movieName || null,
|
||||
content: content.trim(),
|
||||
parentId: parentId ? Number(parentId) : null,
|
||||
spoiler: spoiler ? true : false,
|
||||
anonymous: anonymous ? true : false,
|
||||
})
|
||||
|
||||
return {
|
||||
id: result.insertId,
|
||||
userId: payload.id,
|
||||
source: source || '',
|
||||
slug,
|
||||
movieName: movieName || null,
|
||||
content: content.trim(),
|
||||
spoiler: spoiler ? true : false,
|
||||
anonymous: anonymous ? true : false,
|
||||
parentId: parentId ? Number(parentId) : null,
|
||||
likeCount: 0,
|
||||
dislikeCount: 0,
|
||||
}
|
||||
})
|
||||
@@ -0,0 +1,81 @@
|
||||
import { eq, and, sql } from 'drizzle-orm'
|
||||
import { comments, commentVotes } from '../../database/schema'
|
||||
import { getTokenFromEvent, verifyToken } from '../../utils/auth'
|
||||
|
||||
export default defineEventHandler(async (event) => {
|
||||
const token = getTokenFromEvent(event)
|
||||
if (!token) {
|
||||
throw createError({ statusCode: 401, message: 'Chưa đăng nhập' })
|
||||
}
|
||||
|
||||
const payload = verifyToken(token)
|
||||
if (!payload) {
|
||||
throw createError({ statusCode: 401, message: 'Phiên đăng nhập hết hạn' })
|
||||
}
|
||||
|
||||
const body = await readBody(event)
|
||||
const { commentId, vote } = body
|
||||
|
||||
if (!commentId || ![-1, 0, 1].includes(vote)) {
|
||||
throw createError({ statusCode: 400, message: 'Thiếu thông tin vote' })
|
||||
}
|
||||
|
||||
const db = useDb()
|
||||
|
||||
const [comment] = await db.select().from(comments).where(eq(comments.id, Number(commentId)))
|
||||
if (!comment) {
|
||||
throw createError({ statusCode: 404, message: 'Không tìm thấy bình luận' })
|
||||
}
|
||||
|
||||
const [existingVote] = await db
|
||||
.select()
|
||||
.from(commentVotes)
|
||||
.where(and(
|
||||
eq(commentVotes.userId, payload.id),
|
||||
eq(commentVotes.commentId, Number(commentId))
|
||||
))
|
||||
|
||||
if (existingVote) {
|
||||
if (existingVote.vote === vote) {
|
||||
await db.delete(commentVotes).where(eq(commentVotes.id, existingVote.id))
|
||||
if (vote === 1) {
|
||||
await db.update(comments).set({ likeCount: sql`${comments.likeCount} - 1` }).where(eq(comments.id, Number(commentId)))
|
||||
} else if (vote === -1) {
|
||||
await db.update(comments).set({ dislikeCount: sql`${comments.dislikeCount} - 1` }).where(eq(comments.id, Number(commentId)))
|
||||
}
|
||||
return { vote: 0, likeCount: comment.likeCount + (vote === 1 ? -1 : 0), dislikeCount: comment.dislikeCount + (vote === -1 ? -1 : 0) }
|
||||
} else {
|
||||
if (existingVote.vote === 1) {
|
||||
await db.update(comments).set({ likeCount: sql`${comments.likeCount} - 1` }).where(eq(comments.id, Number(commentId)))
|
||||
} else if (existingVote.vote === -1) {
|
||||
await db.update(comments).set({ dislikeCount: sql`${comments.dislikeCount} - 1` }).where(eq(comments.id, Number(commentId)))
|
||||
}
|
||||
|
||||
await db.update(commentVotes).set({ vote }).where(eq(commentVotes.id, existingVote.id))
|
||||
|
||||
if (vote === 1) {
|
||||
await db.update(comments).set({ likeCount: sql`${comments.likeCount} + 1` }).where(eq(comments.id, Number(commentId)))
|
||||
} else if (vote === -1) {
|
||||
await db.update(comments).set({ dislikeCount: sql`${comments.dislikeCount} + 1` }).where(eq(comments.id, Number(commentId)))
|
||||
}
|
||||
|
||||
return { vote, likeCount: comment.likeCount + (vote === 1 ? 1 : -1), dislikeCount: comment.dislikeCount + (vote === -1 ? 1 : -1) }
|
||||
}
|
||||
} else {
|
||||
if (vote !== 0) {
|
||||
await db.insert(commentVotes).values({
|
||||
userId: payload.id,
|
||||
commentId: Number(commentId),
|
||||
vote,
|
||||
})
|
||||
|
||||
if (vote === 1) {
|
||||
await db.update(comments).set({ likeCount: sql`${comments.likeCount} + 1` }).where(eq(comments.id, Number(commentId)))
|
||||
} else if (vote === -1) {
|
||||
await db.update(comments).set({ dislikeCount: sql`${comments.dislikeCount} + 1` }).where(eq(comments.id, Number(commentId)))
|
||||
}
|
||||
}
|
||||
|
||||
return { vote, likeCount: comment.likeCount + (vote === 1 ? 1 : 0), dislikeCount: comment.dislikeCount + (vote === -1 ? 1 : 0) }
|
||||
}
|
||||
})
|
||||
Reference in New Issue
Block a user