From a4ad2cd5900ddb1522f6a91deeaec99536b696a4 Mon Sep 17 00:00:00 2001 From: Lutfian Rahdiansyah Date: Mon, 20 May 2024 20:13:28 +0700 Subject: [PATCH] Feature/discustions (#4) * feature:create discustions :sprakles: * feature: vote and update status discustion :sprakles: * feature: comments :sparkles: * feature: bookmark :sprakles: * feat:filter and sort discustion :sparkles: * bug:fix bug filter use :bug: --- .dockerignore | 4 + Dockerfile | 19 ++++ app/api/discustions/[id]/vote/route.ts | 144 ++++++++++++++++++------- app/api/discustions/route.ts | 102 +++++------------- app/api/quiz/generate/route.ts | 7 ++ utils/azure/openAi.ts | 5 + 6 files changed, 168 insertions(+), 113 deletions(-) create mode 100644 .dockerignore create mode 100644 Dockerfile create mode 100644 app/api/quiz/generate/route.ts diff --git a/.dockerignore b/.dockerignore new file mode 100644 index 0000000..70b60bf --- /dev/null +++ b/.dockerignore @@ -0,0 +1,4 @@ +*.lock +*-lock.json +/node_modules +/.next/ diff --git a/Dockerfile b/Dockerfile new file mode 100644 index 0000000..e1fc38f --- /dev/null +++ b/Dockerfile @@ -0,0 +1,19 @@ +FROM node:20 + +# Create app directory +WORKDIR /app + +# Install app dependencies +COPY . . + + +RUN yarn install + +# build +RUN yarn build + +# Bundle app source +COPY . . + +EXPOSE 3000 +CMD [ "yarn", "start" ] diff --git a/app/api/discustions/[id]/vote/route.ts b/app/api/discustions/[id]/vote/route.ts index 4526a3b..9d0785a 100644 --- a/app/api/discustions/[id]/vote/route.ts +++ b/app/api/discustions/[id]/vote/route.ts @@ -1,53 +1,121 @@ +import { upload } from "@/utils/azure/storageBlob"; import getResponse from "@/utils/getResponse"; import getSessionUser from "@/utils/session"; import { PrismaClient } from "@prisma/client"; const prisma = new PrismaClient(); -export async function POST(req: Request, { params }: any) { - const { id } = params; - const { vote } = await req.json(); - if (!vote) return getResponse(null, "vote is required", 400); - if (["UPVOTE", "DOWNVOTE"].includes(vote) === false) { - return getResponse(400, "Invalid vote"); +export async function POST(req: Request) { + const data = await req.formData(); + const content = data.get("content") as string; + const type = data.get("type") as any; + const tags = data.get("tags") as string; + const attachments = data.getAll("attachments") as unknown as File[]; + if (!content) { + return getResponse(400, "Content is required"); } - const discustion = await prisma.discustions.findFirst({ - where: { - id: +id, + if (["DRAFT", "PUBLISHED", "ARCHIVED"].includes(type) === false) { + return getResponse(400, "Invalid type"); + } + + const user = await getSessionUser(); + let attachmentsPaths: string[] = []; + if (attachments) { + if (!Array.isArray(attachments)) { + return getResponse(400, "Attachments should be an array"); + } + if (attachments.length > 5) { + return getResponse(400, "Maximum 5 attachments allowed"); + } + const attacmentUploaded = await Promise.all( + attachments.map(async (attachment: any) => { + const bytes = await attachment.arrayBuffer(); + const buffer = Buffer.from(bytes); + const now = new Date(); + const prefixFile = `${now.toLocaleString()}`; + return upload( + "attachments", + `${prefixFile} -${attachment.name}`, + buffer + ); + }) + ); + attachmentsPaths = attacmentUploaded.map( + (attachment: any) => attachment.request.url + ); + } + const discustion = await prisma.discustions.create({ + data: { + content, + type, + tags: tags ? JSON.parse(tags) : [], + attachments: attachmentsPaths || [], + user: { + connect: { + id: user?.id, + }, + }, }, + }); + return getResponse(discustion, "Discustion created", 200); +} + +export async function GET(req: Request) { + const {searchParams} = new URL(req.url); + const filter_by = searchParams.get('filter_by') as "tags" | "id" | "user" | "text"; + const filter_value = searchParams.get('filter_value'); + const sort_by = searchParams.get("sort_by") as + | "newest" + | "oldest" + | "top_vote"; + + let discustions = await prisma.discustions.findMany({ include: { + user: { + select: { + id: true, + name: true, + username: true, + class: { + select: { + id: true, + name: true, + }, + }, + // email: true, + // image: true, + }, + }, votes: true, + comments: true, + }, + orderBy: { + created_at: "desc", }, }); - if (!discustion) return getResponse(null, "Discustion not found", 404); - const user = await getSessionUser(); - const voteExist = - discustion.votes && - discustion.votes.find( - (v: any) => v.user_id === user?.id && v.discustion_id === +id - ); - if (voteExist && voteExist.type === vote) { - await prisma.vote.delete({ - where: { - id: voteExist.id, - }, - }); - return getResponse(discustion, "Discustion vote removed", 200); + if (filter_by && filter_value) { + let discustionsFiltered: any = []; + if (filter_by === "tags") { + discustionsFiltered = discustions.filter((discustion: any) => discustion.tags.includes(filter_value)); + } else if (filter_by === "id") { + discustionsFiltered = discustions.filter((discustion: any) => discustion.id === +filter_value); + } else if (filter_by === "user") { + discustionsFiltered = discustions.filter((discustion: any) => discustion.user.id === +filter_value); + } else if (filter_by === "text") { + discustionsFiltered = discustions.filter((discustion: any) => discustion.content.includes(filter_value)); + } + discustions = discustionsFiltered; } - - if (voteExist) { - await prisma.vote.delete({ - where: { - id: voteExist.id, - }, + if (sort_by === "newest") { + discustions = discustions.sort((a: any, b: any) => a.created_at - b.created_at); + } + if (sort_by === "oldest") { + discustions = discustions.sort((a: any, b: any) => b.created_at - a.created_at); + } + if (sort_by === "top_vote") { + const discustionsVote = discustions.map((item) => { + return {...item, upvote: item.votes.filter((vote: any) => vote.type === "UPVOTE").length, downvote: item.votes.filter((vote: any) => vote.type === "DOWNVOTE").length}; }); + discustions = discustionsVote.sort((a: any, b: any) => (b.upvote - b.downvote) - (a.upvote - a.downvote)); } - await prisma.vote.create({ - data: { - user_id: user?.id || 1, - discustion_id: +id, - type: vote, - }, - }); - - return getResponse(discustion, "Discustion voted", 200); + return getResponse(discustions, "Discustions fetched", 200); } diff --git a/app/api/discustions/route.ts b/app/api/discustions/route.ts index 2364226..f042749 100644 --- a/app/api/discustions/route.ts +++ b/app/api/discustions/route.ts @@ -1,88 +1,40 @@ -import { upload } from "@/utils/azure/storageBlob"; import getResponse from "@/utils/getResponse"; import getSessionUser from "@/utils/session"; import { PrismaClient } from "@prisma/client"; const prisma = new PrismaClient(); -export async function POST(req: Request) { - const data = await req.formData(); - const content = data.get("content") as string; - const type = data.get("type") as any; - const tags = data.get("tags") as string; - const attachments = data.getAll("attachments") as unknown as File[]; - if (!content) { - return getResponse(400, "Content is required"); +export async function POST(req: Request, { params }: any) { + const { id } = params; + const { vote } = await req.json(); + if (!vote) return getResponse(null, "vote is required", 400); + if (["UPVOTE", "DOWNVOTE"].includes(vote) === false) { + return getResponse(400, "Invalid vote"); } - if (["DRAFT", "PUBLISHED", "ARCHIVED"].includes(type) === false) { - return getResponse(400, "Invalid type"); - } - - const user = await getSessionUser(); - let attachmentsPaths: string[] = []; - if (attachments) { - if (!Array.isArray(attachments)) { - return getResponse(400, "Attachments should be an array"); - } - if (attachments.length > 5) { - return getResponse(400, "Maximum 5 attachments allowed"); - } - const attacmentUploaded = await Promise.all( - attachments.map(async (attachment: any) => { - const bytes = await attachment.arrayBuffer(); - const buffer = Buffer.from(bytes); - const now = new Date(); - const prefixFile = `${now.toLocaleString()}`; - return upload( - "attachments", - `${prefixFile} -${attachment.name}`, - buffer - ); - }) - ); - attachmentsPaths = attacmentUploaded.map( - (attachment: any) => attachment.request.url - ); - } - const discustion = await prisma.discustions.create({ - data: { - content, - type, - tags: tags ? JSON.parse(tags) : [], - attachments: attachmentsPaths || [], - user: { - connect: { - id: user?.id, - }, - }, + const discustion = await prisma.discustions.findFirst({ + where: { + id: +id, }, - }); - return getResponse(discustion, "Discustion created", 200); -} - -export async function GET(req: Request) { - const discustions = await prisma.discustions.findMany({ include: { - user: { - select: { - id: true, - name: true, - username: true, - class: { - select: { - id: true, - name: true, - }, - }, - // email: true, - // image: true, - }, - }, votes: true, - comments: true, }, - orderBy: { - created_at: "desc", + }); + if (!discustion) return getResponse(null, "Discustion not found", 404); + const user = await getSessionUser(); + const voteExist = discustion.votes && discustion.votes.find((v: any) => v.user_id === user?.id && v.type === vote); + if (voteExist) { + await prisma.vote.delete({ + where: { + id: voteExist.id, + }, + }); + } + await prisma.vote.create({ + data: { + user_id: user?.id ||1, + discustion_id: +id, + type: vote, }, }); - return getResponse(discustions, "Discustions fetched", 200); + + return getResponse(discustion, "Discustion voted", 200); } diff --git a/app/api/quiz/generate/route.ts b/app/api/quiz/generate/route.ts new file mode 100644 index 0000000..c9aa04b --- /dev/null +++ b/app/api/quiz/generate/route.ts @@ -0,0 +1,7 @@ +import getResponse from '@/utils/getResponse'; +import { PrismaClient } from '@prisma/client'; + +const prisma = new PrismaClient() +export async function POST(req: Request) { + +} \ No newline at end of file diff --git a/utils/azure/openAi.ts b/utils/azure/openAi.ts index 44d04fc..b62ad02 100644 --- a/utils/azure/openAi.ts +++ b/utils/azure/openAi.ts @@ -26,4 +26,9 @@ export const getChatCompletions : any = async (indexName: string, message: strin } ); +} +export const GetCompletions: any = async (prompt: string) => { + return await openAiClient.getCompletions(modelName, [ + + ]); } \ No newline at end of file