feat: comments

fix: use select instead of dropdown for filter on /market
This commit is contained in:
Face 2025-05-24 19:28:38 +03:00
parent 800b5d1a09
commit bd05b269fe
22 changed files with 2715 additions and 97 deletions

View file

@ -0,0 +1,125 @@
import { auth } from '$lib/auth';
import { error, json } from '@sveltejs/kit';
import { db } from '$lib/server/db';
import { comment, coin, user, commentLike } from '$lib/server/db/schema';
import { eq, and, desc, sql } from 'drizzle-orm';
export async function GET({ params, request }) {
const session = await auth.api.getSession({
headers: request.headers
});
const { coinSymbol } = params;
const normalizedSymbol = coinSymbol.toUpperCase();
try {
const [coinData] = await db
.select({ id: coin.id })
.from(coin)
.where(eq(coin.symbol, normalizedSymbol))
.limit(1);
if (!coinData) {
return json({ message: 'Coin not found' }, { status: 404 });
}
const commentsQuery = db
.select({
id: comment.id,
content: comment.content,
likesCount: comment.likesCount,
createdAt: comment.createdAt,
updatedAt: comment.updatedAt,
userId: user.id,
userName: user.name,
userUsername: user.username,
userImage: user.image,
userBio: user.bio,
userCreatedAt: user.createdAt,
isLikedByUser: session?.user ?
sql<boolean>`EXISTS(SELECT 1 FROM ${commentLike} WHERE ${commentLike.userId} = ${session.user.id} AND ${commentLike.commentId} = ${comment.id})` :
sql<boolean>`FALSE`
})
.from(comment)
.innerJoin(user, eq(comment.userId, user.id))
.where(and(eq(comment.coinId, coinData.id), eq(comment.isDeleted, false)))
.orderBy(desc(comment.createdAt));
const comments = await commentsQuery;
return json({ comments });
} catch (err) {
console.error('Failed to fetch comments:', err);
return json({ message: 'Internal server error' }, { status: 500 });
}
}
export async function POST({ request, params }) {
const session = await auth.api.getSession({
headers: request.headers
});
if (!session?.user) {
throw error(401, 'Not authenticated');
}
const { coinSymbol } = params;
const { content } = await request.json();
if (!content || content.trim().length === 0) {
throw error(400, 'Comment content is required');
}
if (content.length > 500) {
throw error(400, 'Comment must be 500 characters or less');
}
const normalizedSymbol = coinSymbol.toUpperCase();
const userId = Number(session.user.id);
try {
const [coinData] = await db
.select({ id: coin.id })
.from(coin)
.where(eq(coin.symbol, normalizedSymbol))
.limit(1);
if (!coinData) {
throw error(404, 'Coin not found');
}
const [newComment] = await db
.insert(comment)
.values({
userId,
coinId: coinData.id,
content: content.trim()
})
.returning();
const [commentWithUser] = await db
.select({
id: comment.id,
content: comment.content,
likesCount: comment.likesCount,
createdAt: comment.createdAt,
updatedAt: comment.updatedAt,
userId: comment.userId,
userName: user.name,
userUsername: user.username,
userImage: user.image,
userBio: user.bio,
userCreatedAt: user.createdAt,
isLikedByUser: sql<boolean>`FALSE`
})
.from(comment)
.innerJoin(user, eq(comment.userId, user.id))
.where(eq(comment.id, newComment.id))
.limit(1);
return json({ comment: commentWithUser });
} catch (e) {
console.error('Error creating comment:', e);
throw error(500, 'Failed to create comment');
}
}

View file

@ -0,0 +1,120 @@
import { error, json } from '@sveltejs/kit';
import { db } from '$lib/server/db';
import { comment, commentLike, coin } from '$lib/server/db/schema';
import { eq, and, sql } from 'drizzle-orm';
import type { RequestHandler } from './$types';
import { auth } from '$lib/auth';
export const POST: RequestHandler = async ({ request, params }) => {
const session = await auth.api.getSession({
headers: request.headers
});
if (!session?.user) {
return json({ message: 'Not authenticated' }, { status: 401 });
}
const commentId = parseInt(params.id);
const { coinSymbol } = params;
const userId = Number(session.user.id);
if (isNaN(commentId)) {
return json({ message: 'Invalid comment ID' }, { status: 400 });
}
try {
// Verify the comment exists and belongs to the specified coin
const [commentData] = await db
.select()
.from(comment)
.innerJoin(coin, eq(comment.coinId, coin.id))
.where(and(eq(comment.id, commentId), eq(coin.symbol, coinSymbol)));
if (!commentData) {
return json({ message: 'Comment not found' }, { status: 404 });
}
// Check if user already liked this comment
const [existingLike] = await db
.select()
.from(commentLike)
.where(and(eq(commentLike.userId, userId), eq(commentLike.commentId, commentId)));
if (existingLike) {
return json({ message: 'Comment already liked' }, { status: 400 });
}
// Add like and increment count
await db.transaction(async (tx) => {
await tx.insert(commentLike).values({ userId, commentId });
await tx
.update(comment)
.set({ likesCount: sql`${comment.likesCount} + 1` })
.where(eq(comment.id, commentId));
});
return json({ success: true });
} catch (error) {
console.error('Failed to like comment:', error);
return json({ message: 'Internal server error' }, { status: 500 });
}
};
export const DELETE: RequestHandler = async ({ request, params }) => {
const session = await auth.api.getSession({
headers: request.headers
});
if (!session?.user) {
throw error(401, 'Not authenticated');
}
const commentId = parseInt(params.id);
const { coinSymbol } = params;
const userId = Number(session.user.id);
if (isNaN(commentId)) {
return json({ message: 'Invalid comment ID' }, { status: 400 });
}
try {
// Verify the comment exists and belongs to the specified coin
const [commentData] = await db
.select()
.from(comment)
.innerJoin(coin, eq(comment.coinId, coin.id))
.where(and(eq(comment.id, commentId), eq(coin.symbol, coinSymbol)));
if (!commentData) {
return json({ message: 'Comment not found' }, { status: 404 });
}
// Check if user has liked this comment
const [existingLike] = await db
.select()
.from(commentLike)
.where(and(eq(commentLike.userId, userId), eq(commentLike.commentId, commentId)));
if (!existingLike) {
return json({ message: 'Comment not liked' }, { status: 400 });
}
// Remove like and decrement count
await db.transaction(async (tx) => {
await tx
.delete(commentLike)
.where(and(eq(commentLike.userId, userId), eq(commentLike.commentId, commentId)));
await tx
.update(comment)
.set({ likesCount: sql`GREATEST(0, ${comment.likesCount} - 1)` })
.where(eq(comment.id, commentId));
});
return json({ success: true });
} catch (error) {
console.error('Failed to unlike comment:', error);
return json({ message: 'Internal server error' }, { status: 500 });
}
};