From 7befe6f083df02c276caf6fa615a6cea1db0660a Mon Sep 17 00:00:00 2001 From: NishantGupt786 Date: Sun, 20 Oct 2024 15:14:25 +0530 Subject: [PATCH 1/5] moving admin --- src/app/api/admin/bulk-upload-pdf/route.ts | 84 ------------- src/app/api/admin/dashboard/route.ts | 5 - src/app/api/admin/imgtopdf/route.ts | 138 --------------------- src/app/api/admin/route.ts | 109 ---------------- src/app/api/admin/watermark/route.ts | 99 --------------- src/app/api/auth/login/route.ts | 34 ----- src/app/api/auth/signup/route.ts | 35 ------ src/app/upload/select_options.ts | 8 +- 8 files changed, 7 insertions(+), 505 deletions(-) delete mode 100644 src/app/api/admin/bulk-upload-pdf/route.ts delete mode 100644 src/app/api/admin/dashboard/route.ts delete mode 100644 src/app/api/admin/imgtopdf/route.ts delete mode 100644 src/app/api/admin/route.ts delete mode 100644 src/app/api/admin/watermark/route.ts delete mode 100644 src/app/api/auth/login/route.ts delete mode 100644 src/app/api/auth/signup/route.ts diff --git a/src/app/api/admin/bulk-upload-pdf/route.ts b/src/app/api/admin/bulk-upload-pdf/route.ts deleted file mode 100644 index b544201..0000000 --- a/src/app/api/admin/bulk-upload-pdf/route.ts +++ /dev/null @@ -1,84 +0,0 @@ -import { NextResponse } from "next/server"; -import { connectToDatabase } from "@/lib/mongoose"; -import cloudinary from "cloudinary"; -import Paper from "@/db/papers"; - -export async function POST(req: Request) { - try { - await connectToDatabase(); - const body = (await req.json()) as Array<{ - url: string; - slot: string; - subject: string; - exam: string; - year: number; - }>; - - const responseArray: { - status: string; - url: string; - thumbnailUrl: string; - subject: string; - slot: string; - year: number; - }[] = []; - - await Promise.all( - body.map(async (paperData) => { - const { url, slot, subject, exam, year } = paperData; - - const existingPaper = await Paper.findOne({ - subject, - slot, - year, - exam, - }); - if (existingPaper) { - responseArray.push({ - status: "Paper already exists", - url: "", - thumbnailUrl: "", - subject, - slot, - year, - }); - return; - } - - const thumbnailResponse = cloudinary.v2.image(url, { format: "jpg" }); - const thumbnailUrl = thumbnailResponse - .replace("pdf", "jpg") - .replace("upload", "upload/w_400,h_400,c_fill") - .replace(//g, ""); - - const paper = new Paper({ - finalUrl: url, - thumbnailUrl, - subject, - slot, - year, - exam, - }); - - await paper.save(); - - responseArray.push({ - status: "success", - url: url, - thumbnailUrl: thumbnailUrl, - subject, - slot, - year, - }); - }), - ); - - return NextResponse.json(responseArray, { status: 201 }); - } catch (error) { - console.error(error); - return NextResponse.json( - { message: "Failed to upload papers", error }, - { status: 500 }, - ); - } -} diff --git a/src/app/api/admin/dashboard/route.ts b/src/app/api/admin/dashboard/route.ts deleted file mode 100644 index b19dc20..0000000 --- a/src/app/api/admin/dashboard/route.ts +++ /dev/null @@ -1,5 +0,0 @@ -import { NextResponse } from "next/server"; - -export async function GET(req: Request) { - return NextResponse.json({ message: "Hello from the admin dashboard!" }, { status: 200 }); -} \ No newline at end of file diff --git a/src/app/api/admin/imgtopdf/route.ts b/src/app/api/admin/imgtopdf/route.ts deleted file mode 100644 index f26b6fa..0000000 --- a/src/app/api/admin/imgtopdf/route.ts +++ /dev/null @@ -1,138 +0,0 @@ -import { type NextApiResponse } from "next"; -import { PDFDocument } from "pdf-lib"; -import multer from "multer"; -import fs from "fs"; -import path from "path"; -import { promisify } from "util"; -import { NextResponse } from "next/server"; -import { writeFile, unlink } from "fs/promises"; -import { v4 as uuidv4 } from "uuid"; -import { cookies } from "next/headers"; - -const upload = multer({ dest: "uploads/" }); -const uploadMiddleware = promisify(upload.array("files")); - -const COOKIE_NAME = 'session_id'; - -function getSessionId(req: Request): string { - const sessionId = cookies().get(COOKIE_NAME)?.value; - - if (!sessionId) { - const newSessionId = uuidv4(); - cookies().set(COOKIE_NAME, newSessionId); - return newSessionId; - } - - return sessionId; -} - -export async function POST(req: Request, res: NextApiResponse) { - await uploadMiddleware(req as any, res as any); - - const formData = await req.formData(); - const files: File[] = formData.getAll("files") as File[]; - - if (!files || files.length === 0) { - return NextResponse.json({ error: "No files received." }, { status: 400 }); - } - - try { - const pdfDoc = await PDFDocument.create(); - const sessionId = getSessionId(req); - - const orderedFiles = Array.from(files).sort((a, b) => { - return a.name.localeCompare(b.name); - }); - - for (const file of orderedFiles) { - const fileBlob = new Blob([file]); - const imgBytes = Buffer.from(await fileBlob.arrayBuffer()); - let img; - if (file instanceof File) { - if (file.type === "image/png") { - img = await pdfDoc.embedPng(imgBytes); - } else if (file.type === "image/jpeg" || file.type === "image/jpg") { - img = await pdfDoc.embedJpg(imgBytes); - } else { - continue; - } - const page = pdfDoc.addPage([img.width, img.height]); - page.drawImage(img, { - x: 0, - y: 0, - width: img.width, - height: img.height, - }); - } - } - - const mergedPdfBytes = await pdfDoc.save(); - const mergedPdfPath = path.join(process.cwd(), "public", `merged_${sessionId}.pdf`); - await writeFile(mergedPdfPath, mergedPdfBytes); - - const watermarkedPdfPath = path.join(process.cwd(), "public", `watermarked_${sessionId}.pdf`); - await applyWatermark(mergedPdfBytes, watermarkedPdfPath); - - await unlink(mergedPdfPath); // Optionally delete the merged file - - return NextResponse.json({ url: `/${path.basename(watermarkedPdfPath)}` }, { status: 200 }); - } catch (error) { - return NextResponse.json({ error: "Failed to process PDF" }, { status: 500 }); - } -} - - -export async function DELETE(req: Request, res: NextApiResponse) { - try { - const sessionId = cookies().get(COOKIE_NAME)?.value; - if (!sessionId) { - return NextResponse.json({ error: "Session not found" }, { status: 400 }); - } - - const filePath = path.resolve(`./public/watermarked_${sessionId}.pdf`); - - if (fs.existsSync(filePath)) { - await unlink(filePath); // Delete the watermarked PDF file - return NextResponse.json( - { message: "Deleted watermarked PDF file successfully" }, - { status: 200 }, - ); - } else { - return NextResponse.json( - { error: "Watermarked PDF file not found" }, - { status: 400 }, - ); - } - } catch (error) { - console.error("Error deleting PDF file:", error); - return NextResponse.json({ - error: "Failed to delete watermarked PDF file", - }, { status: 500 }); - } -} - -async function applyWatermark(pdfBytes: Uint8Array, outputPath: string) { - const pdfDoc = await PDFDocument.load(pdfBytes); - - const watermarkImage = await pdfDoc.embedJpg(fs.readFileSync(path.resolve("./public/watermark.jpg"))); - const pages = pdfDoc.getPages(); - const { width, height } = pages[0]!.getSize(); - - pages.forEach((page) => { - const x = width - 80; - const y = 50; - const watermarkWidth = 50; - const watermarkHeight = 50; - - page.drawImage(watermarkImage, { - x: x, - y: y, - width: watermarkWidth, - height: watermarkHeight, - opacity: 0.3, - }); - }); - - const watermarkedPdfBytes = await pdfDoc.save(); - await writeFile(outputPath, watermarkedPdfBytes); -} diff --git a/src/app/api/admin/route.ts b/src/app/api/admin/route.ts deleted file mode 100644 index 18914a7..0000000 --- a/src/app/api/admin/route.ts +++ /dev/null @@ -1,109 +0,0 @@ -import { NextResponse } from "next/server"; -import { connectToDatabase } from "@/lib/mongoose"; -import cloudinary from "cloudinary"; -import { type IAdminUpload, type ConverttoPDFResponse } from "@/interface"; -import Paper from "@/db/papers"; - -cloudinary.v2.config({ - cloud_name: process.env.NEXT_PUBLIC_CLOUDINARY_CLOUD_NAME, - api_key: process.env.CLOUDINARY_API_KEY, - api_secret: process.env.CLOUDINARY_SECRET, -}); - -export async function POST(req: Request) { - try { - await connectToDatabase(); - const body = (await req.json()) as IAdminUpload; - const { urls, slot, subject, exam, year, isPdf, publicIds } = body; - let finalUrl: string | undefined = ""; - let thumbnailUrl: string | undefined = ""; - const existingPaper = await Paper.findOne({ - subject, - slot, - year, - exam, - }); - if (existingPaper) { - return NextResponse.json({ message: "Paper already exists" }, { status: 409 }); - } - if (!isPdf) { - // @ts-expect-error: cloudinary was dumb this time - const response = (await cloudinary.v2.uploader.multi({ - urls: urls, - format: "pdf", - density: 50, - })) as ConverttoPDFResponse; - finalUrl = response.url; - const paper = new Paper({ - finalUrl, - subject, - slot, - year, - exam, - }); - await paper.save(); - await Promise.all( - publicIds.map(async (public_id) => { - const deletionResult = - await cloudinary.v2.uploader.destroy(public_id); - - }), - ); - } else { - finalUrl = urls[0]; - const thumbnailResponse = cloudinary.v2.image(finalUrl!, {format: "jpg"}); - thumbnailUrl = thumbnailResponse - .replace("pdf", "jpg") - .replace("upload", "upload/w_400,h_400,c_fill") - .replace(//g, ''); - - const paper = new Paper({ - finalUrl, - thumbnailUrl, - subject, - slot, - year, - exam, - }); - await paper.save(); - } - - return NextResponse.json( - { status: "success", url: finalUrl, thumbnailUrl: thumbnailUrl }, - { status: 201 }, - ); - } catch (error) { - console.error(error); - return NextResponse.json( - { message: "Failed to fetch papers", error }, - - { status: 500 }, - ); - } -} - -export async function DELETE(req: Request) { - try { - const url = new URL(req.url); - const public_id = url.searchParams.get("public_id"); - const type = url.searchParams.get("type"); - - if (!public_id || !type) { - return NextResponse.json( - { message: "Missing parameters: public_id or type" }, - { status: 400 }, - ); - } - - const deletionResult = await cloudinary.v2.uploader.destroy(public_id, { - type: type, - }); - - return NextResponse.json({ message: "Asset deleted successfully" }); - } catch (error) { - return NextResponse.json( - { message: "Failed to delete asset", error }, - { status: 500 }, - ); - } -} diff --git a/src/app/api/admin/watermark/route.ts b/src/app/api/admin/watermark/route.ts deleted file mode 100644 index 3b40a27..0000000 --- a/src/app/api/admin/watermark/route.ts +++ /dev/null @@ -1,99 +0,0 @@ -import { NextApiRequest, NextApiResponse } from "next"; -import { PDFDocument } from "pdf-lib"; -import multer from "multer"; -import fs from "fs"; -import path from "path"; -import { promisify } from "util"; -import { NextResponse } from "next/server"; -import { writeFile } from "fs/promises"; -import { v4 as uuidv4 } from "uuid"; -import { cookies } from "next/headers"; - -const upload = multer({ dest: "uploads/" }); -const uploadMiddleware = promisify(upload.single("file")); - -const COOKIE_NAME = "session_id"; - -function getSessionId(req: Request): string { - const sessionId = cookies().get(COOKIE_NAME)?.value; - - if (!sessionId) { - const newSessionId = uuidv4(); - cookies().set(COOKIE_NAME, newSessionId); - return newSessionId; - } - - return sessionId; -} - -export async function POST(req: Request, res: NextApiResponse) { - await uploadMiddleware(req as any, res as any); - - const sessionId = getSessionId(req); - const formData = await req.formData(); - const file = formData.get("file"); - - if (!file) { - return NextResponse.json({ error: "No files received." }, { status: 400 }); - } - - try { - const fileBlob = new Blob([file]); - const buffer = Buffer.from(await fileBlob.arrayBuffer()); - const pdfDoc = await PDFDocument.load(buffer); - - const watermarkImage = await pdfDoc.embedJpg( - fs.readFileSync(path.resolve("./public/watermark.jpg")), - ); - const pages = pdfDoc.getPages(); - const { width, height } = pages[0]!.getSize(); - - pages.forEach((page) => { - const x = width - 80; - const y = 50; - const watermarkWidth = 50; - const watermarkHeight = 50; - - page.drawImage(watermarkImage, { - x: x, - y: y, - width: watermarkWidth, - height: watermarkHeight, - opacity: 0.3, - }); - }); - - const pdfBytes = await pdfDoc.save(); - const pdfPath = path.join(process.cwd(), `public/watermarked-${sessionId}.pdf`); - - await writeFile(pdfPath, pdfBytes); - - return NextResponse.json({ url: `/watermarked-${sessionId}.pdf` }, { status: 200 }); - } catch (error) { - return NextResponse.json({ error: "Failed to process PDF" }, { status: 500 }); - } -} - -export async function DELETE(req: Request, res: NextApiResponse) { - const sessionId = getSessionId(req); - const filePath = path.resolve(`./public/watermarked-${sessionId}.pdf`); - - try { - if (fs.existsSync(filePath)) { - await fs.promises.unlink(filePath); - return NextResponse.json( - { message: "Deleted watermarked PDF file successfully" }, - { status: 200 }, - ); - } else { - return NextResponse.json( - { error: "Watermarked PDF file not found" }, - { status: 400 }, - ); - } - } catch (error) { - return NextResponse.json({ - error: "Failed to delete watermarked PDF file", - }, { status: 500 }); - } -} diff --git a/src/app/api/auth/login/route.ts b/src/app/api/auth/login/route.ts deleted file mode 100644 index 09cc308..0000000 --- a/src/app/api/auth/login/route.ts +++ /dev/null @@ -1,34 +0,0 @@ -import { connectToDatabase } from "@/lib/mongoose"; -import bcrypt from "bcrypt"; -import User, { type IUser } from "@/db/user"; -import { generateToken } from "@/lib/auth"; -import { NextResponse } from "next/server"; -import { type LoginRequest } from "@/interface"; - -export async function POST(req: Request) { - await connectToDatabase(); - - const body = (await req.json()) as LoginRequest; - const { email, password } = body; - - try { - const user: IUser | null = await User.findOne({ email }); - if (!user) { - return NextResponse.json({ message: "User not found" }, { status: 404 }); - } - const passwordMatch = await bcrypt.compare(password, user.password); - if (!passwordMatch) { - return NextResponse.json({ message: "Invalid credentials" }, { status: 401 }); - } - - const token = await generateToken({ userId: user._id }); - - - return NextResponse.json({ - token, - user: { id: user._id, email: user.email }, - }, { status: 200 }); - } catch (error) { - return NextResponse.json({ message: "Failed to login", error }, { status: 500 }); - } -} diff --git a/src/app/api/auth/signup/route.ts b/src/app/api/auth/signup/route.ts deleted file mode 100644 index 7adfa4a..0000000 --- a/src/app/api/auth/signup/route.ts +++ /dev/null @@ -1,35 +0,0 @@ -import { connectToDatabase } from "@/lib/mongoose"; -import bcrypt from "bcrypt"; -import User, { type IUser } from "@/db/user"; -import { generateToken } from "@/lib/auth"; -import { type LoginRequest } from "@/interface"; -import { NextResponse } from "next/server"; - -export async function POST(req: Request) { - await connectToDatabase(); - - const body = (await req.json()) as LoginRequest; - const { email, password } = body; - - try { - const existingUser = await User.findOne({ email }); - if (existingUser) { - return NextResponse.json({ message: "User already exists" }, { status: 409 }); - } - const hashedPassword = await bcrypt.hash(password, 10); - - const newUser: IUser = new User({ - email, - password: hashedPassword, - }); - await newUser.save(); - const token = generateToken({ userId: newUser._id }); - - return NextResponse.json({ - token, - user: { id: newUser._id, email: newUser.email }, - }, { status: 201 }); - } catch (error) { - return NextResponse.json({ message: "Failed to sign up", error }, { status: 500 }); - } -} diff --git a/src/app/upload/select_options.ts b/src/app/upload/select_options.ts index 94d313a..2d1b417 100644 --- a/src/app/upload/select_options.ts +++ b/src/app/upload/select_options.ts @@ -95,6 +95,7 @@ const courses = [ "Economics of Strategy [BHUM208L]", "Game Theory [BHUM209L]", "Behavioral Economics [BHUM211L]", + "Behavioral Economics [HUM1046]", "Econometrics [BHUM210E]", "Fluid Mechanics and Machines [BMEE204L]", "Cloud Computing using Salesforce [BMEE355L]", @@ -196,6 +197,7 @@ const courses = [ "Organizational Behavior [BMGT103L]", "Cognitive Psychology [BHUM110]", "Microprocessors and Microcontrollers Lab [BEEE309P]", + "Microprocessors and Microcontrollers [BECE204L]", "Big Data Analytic Applications to Electrical Systems [BECS403L]", "Big Data Analytic Applications to Electrical Systems Lab [BECS403P]", "Cyber Security [BCSE410L]", @@ -219,8 +221,12 @@ const courses = [ "Electronic Circuits [BEVD204L]", "Digital Signal Processing [BECE301L]", "Signals and Systems [BECE202L]", + "Signals and Systems [BEEE204L]", "Engineering Electromagnetics [BECE205L]", - "Digital Electronics [BEEE206L]" + "Digital Electronics [BEEE206L]", + "Artficial Intelligence [CBS3004]", + "Information Security [CBS3002]", + "Design and Analysis of Algorithms [MCSE502L]", ]; const slots: string[] = [ From b5f144bd400688b71e4ef0b83aab0c1245318662 Mon Sep 17 00:00:00 2001 From: NishantGupt786 Date: Sun, 20 Oct 2024 16:23:40 +0530 Subject: [PATCH 2/5] redis rate limit --- package.json | 2 ++ pnpm-lock.yaml | 36 ++++++++++++++++++++++++++++++++++++ src/middleware.ts | 33 ++++++++++++++++++--------------- 3 files changed, 56 insertions(+), 15 deletions(-) diff --git a/package.json b/package.json index 703dcba..676e2a9 100644 --- a/package.json +++ b/package.json @@ -24,6 +24,8 @@ "@react-pdf-viewer/zoom": "^3.12.0", "@t3-oss/env-nextjs": "^0.10.1", "@types/mongoose": "^5.11.97", + "@upstash/ratelimit": "^2.0.3", + "@vercel/kv": "^3.0.0", "axios": "^1.7.2", "bcrypt": "^5.1.1", "class-variance-authority": "^0.7.0", diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml index ba69aaa..9d04e4b 100644 --- a/pnpm-lock.yaml +++ b/pnpm-lock.yaml @@ -50,6 +50,12 @@ importers: '@types/mongoose': specifier: ^5.11.97 version: 5.11.97 + '@upstash/ratelimit': + specifier: ^2.0.3 + version: 2.0.3 + '@vercel/kv': + specifier: ^3.0.0 + version: 3.0.0 axios: specifier: ^1.7.2 version: 1.7.2 @@ -1560,6 +1566,20 @@ packages: '@ungap/structured-clone@1.2.0': resolution: {integrity: sha512-zuVdFrMJiuCDQUMCzQaD6KL28MjnqqN8XnAqiEq9PNm/hCPTSGfrXCOfwj1ow4LFb/tNymJPwsNbVePc1xFqrQ==} + '@upstash/core-analytics@0.0.10': + resolution: {integrity: sha512-7qJHGxpQgQr9/vmeS1PktEwvNAF7TI4iJDi8Pu2CFZ9YUGHZH4fOP5TfYlZ4aVxfopnELiE4BS4FBjyK7V1/xQ==} + engines: {node: '>=16.0.0'} + + '@upstash/ratelimit@2.0.3': + resolution: {integrity: sha512-BMUpZPZ9IMwrUwohw0HoVAwjBRo5SDb0riAxfCGrLbutuZTPiVagh017Cm3GfhMqwUWLOp0xJQxTCXp812UJVQ==} + + '@upstash/redis@1.34.3': + resolution: {integrity: sha512-VT25TyODGy/8ljl7GADnJoMmtmJ1F8d84UXfGonRRF8fWYJz7+2J6GzW+a6ETGtk4OyuRTt7FRSvFG5GvrfSdQ==} + + '@vercel/kv@3.0.0': + resolution: {integrity: sha512-pKT8fRnfyYk2MgvyB6fn6ipJPCdfZwiKDdw7vB+HL50rjboEBHDVBEcnwfkEpVSp2AjNtoaOUH7zG+bVC/rvSg==} + engines: {node: '>=14.6'} + '@webassemblyjs/ast@1.12.1': resolution: {integrity: sha512-EKfMUOPRRUTy5UII4qJDGPpqfwjOmZ5jeGFwid9mnoqIFK+e0vqoi1qH56JpmZSzEL53jKnNzScdmftJyG5xWg==} @@ -5552,6 +5572,22 @@ snapshots: '@ungap/structured-clone@1.2.0': {} + '@upstash/core-analytics@0.0.10': + dependencies: + '@upstash/redis': 1.34.3 + + '@upstash/ratelimit@2.0.3': + dependencies: + '@upstash/core-analytics': 0.0.10 + + '@upstash/redis@1.34.3': + dependencies: + crypto-js: 4.2.0 + + '@vercel/kv@3.0.0': + dependencies: + '@upstash/redis': 1.34.3 + '@webassemblyjs/ast@1.12.1': dependencies: '@webassemblyjs/helper-numbers': 1.11.6 diff --git a/src/middleware.ts b/src/middleware.ts index b78ceee..f809989 100644 --- a/src/middleware.ts +++ b/src/middleware.ts @@ -1,19 +1,22 @@ -import { NextResponse } from "next/server"; -import { verifyToken } from "./lib/auth"; +import { type NextRequest, NextResponse } from 'next/server'; +import { Ratelimit } from '@upstash/ratelimit'; +import { kv } from '@vercel/kv'; -export async function middleware(request: Request) { - const authtoken = request.headers.get("Authorization"); - if (!authtoken ?? !authtoken?.startsWith("Bearer ")) { - return NextResponse.json({ message: "Unauthorized" }, { status: 401 }); - } - const token = authtoken.split(" ")[1]; - const isValidToken = await verifyToken(token); - if (!isValidToken) { - return NextResponse.json({ message: "Unauthorized" }, { status: 401 }); - } - return NextResponse.next(); -} +const ratelimit = new Ratelimit({ + redis: kv, + limiter: Ratelimit.slidingWindow(5, '900 s'), +}); export const config = { - matcher: ["/api/admin/:path*"], + matcher: '/api/mail', }; + +export default async function middleware(request: NextRequest) { + const ip = request.ip ?? '127.0.0.1'; + const { success } = await ratelimit.limit( + ip + ); + return success + ? NextResponse.next() + : NextResponse.json({ message: "You can upload a maximum of 5 papers every 15 minutes" }, { status: 429 }); +} \ No newline at end of file From 8fedc517e40de1d54642d15ff33cb53c6348e6ff Mon Sep 17 00:00:00 2001 From: afrin-k Date: Sun, 20 Oct 2024 19:16:48 +0530 Subject: [PATCH 3/5] readme.md update --- README.md | 64 +++++++++++++++++++++++++++++++++++++++---------------- 1 file changed, 46 insertions(+), 18 deletions(-) diff --git a/README.md b/README.md index 67943c7..854978b 100644 --- a/README.md +++ b/README.md @@ -1,29 +1,57 @@ -# Create T3 App +

Codechef-VIT +

-This is a [T3 Stack](https://create.t3.gg/) project bootstrapped with `create-t3-app`. +

Papers

+
-## What's next? How do I make an app with this? +>

Prepare to excel in your CATs and FATs with CodeChef-VIT's dedicated repository of past exam papers. Access key resources to review concepts, tackle challenging questions, and familiarize yourself with exam patterns. Boost your confidence, sharpen your strategy, and get ready to ace your exams!

-We try to keep this project as simple as possible, so you can start with just the scaffolding we set up for you, and add additional things later when they become necessary. +## 🌐 Deploy +[https://papers.codechefvit.com/](https://papers.codechefvit.com/) -If you are not familiar with the different technologies used in this project, please refer to the respective docs. If you still are in the wind, please join our [Discord](https://t3.gg/discord) and ask for help. +## ⚙️ Tech Stack: -- [Next.js](https://nextjs.org) -- [NextAuth.js](https://next-auth.js.org) -- [Prisma](https://prisma.io) -- [Drizzle](https://orm.drizzle.team) -- [Tailwind CSS](https://tailwindcss.com) -- [tRPC](https://trpc.io) +- Next.js +- NextAuth.js +- Prisma +- Drizzle +- Tailwind CSS +- tRPC -## Learn More +## 💡 Features: -To learn more about the [T3 Stack](https://create.t3.gg/), take a look at the following resources: +- Access a vast collection of past CAT and FAT papers +- Review papers sorted by subject, year, and difficulty +- Download papers for offline use +- Familiarize yourself with exam patterns and types of questions +- Stay updated with any new additions to the repository -- [Documentation](https://create.t3.gg/) -- [Learn the T3 Stack](https://create.t3.gg/en/faq#what-learning-resources-are-currently-available) — Check out these awesome tutorials +## 🏁 Get Started -You can check out the [create-t3-app GitHub repository](https://github.com/t3-oss/create-t3-app) — your feedback and contributions are welcome! +To get started, fork your own copy and clone the master branch. To clone a branch you can run the following: -## How do I deploy this? +```bash +git clone -b master https://github.com//papers-codechef.git +``` -Follow our deployment guides for [Vercel](https://create.t3.gg/en/deployment/vercel), [Netlify](https://create.t3.gg/en/deployment/netlify) and [Docker](https://create.t3.gg/en/deployment/docker) for more information. +Run these commands on your bash/terminal and open it in a code editor of your choice. + +Run the following to install all the dependencies: + +```bash +pnpm i +``` + +To start your development server run: + +```bash +pnpm dev +``` + +## License + +[![License](http://img.shields.io/:license-mit-blue.svg?style=flat-square)](http://badges.mit-license.org) + +

+ Made with :heart: by CodeChef-VIT +

From 48d7f14da31d004188ebe5a508a603c3a5629bdc Mon Sep 17 00:00:00 2001 From: afrin-k Date: Mon, 21 Oct 2024 22:03:38 +0530 Subject: [PATCH 4/5] readme changes --- README.md | 13 +++++++------ 1 file changed, 7 insertions(+), 6 deletions(-) diff --git a/README.md b/README.md index 854978b..b9b40c8 100644 --- a/README.md +++ b/README.md @@ -11,12 +11,12 @@ ## ⚙️ Tech Stack: -- Next.js -- NextAuth.js -- Prisma -- Drizzle -- Tailwind CSS -- tRPC +- Next.js : Framework for React applications with server-side rendering. +- Tailwind CSS : Utility-first CSS framework for rapid UI development. +- MongoDB & Mongoose : Database and object data modeling (ODM) for Node.js. +- Cloudinary : Media storage and optimization service. +- Nodemailer : Node.js library for sending emails. +- Shadcn : Collection of pre-built components using Radix UI and Tailwind CSS. ## 💡 Features: @@ -47,6 +47,7 @@ To start your development server run: ```bash pnpm dev ``` +Before getting started, please ensure that the .env file is properly configured. The .env.example file has been provided for your reference, with examples of environment variables to be listed. ## License From 8f17a1bbb7c0d356f5dd456ab631558161439c95 Mon Sep 17 00:00:00 2001 From: NishantGupt786 Date: Thu, 24 Oct 2024 01:45:06 +0530 Subject: [PATCH 5/5] SEO and brushups --- .env.example | 19 +- CONTRIBUTING.md | 72 +++ public/watermark.jpg | Bin 29657 -> 0 bytes src/app/adminupload/page.tsx | 699 ------------------------------ src/app/catalogue/page.tsx | 6 +- src/app/layout.tsx | 71 ++- src/app/papersadminlogin/page.tsx | 111 ----- 7 files changed, 151 insertions(+), 827 deletions(-) create mode 100644 CONTRIBUTING.md delete mode 100644 public/watermark.jpg delete mode 100644 src/app/adminupload/page.tsx delete mode 100644 src/app/papersadminlogin/page.tsx diff --git a/.env.example b/.env.example index adfe836..73a6e6b 100644 --- a/.env.example +++ b/.env.example @@ -9,6 +9,19 @@ # When adding additional environment variables, the schema in "/src/env.js" # should be updated accordingly. -# Example: -# SERVERVAR="foo" -# NEXT_PUBLIC_CLIENTVAR="bar" +CLOUDINARY_API_KEY="" +CLOUDINARY_SECRET="" +CRYPTO_SECRET="" +EMAIL_HOST="" +EMAIL_PASSWORD="" +EMAIL_PORT="" +EMAIL_SECURE="" +EMAIL_USER="" +JWT_SECRET="" +MONGODB_URI="" +NEXT_PUBLIC_CLOUDINARY_CLOUD_NAME="" +NEXT_PUBLIC_CRYPTO_SECRET="" +KV_URL="" +KV_REST_API_URL="" +KV_REST_API_TOKEN="" +KV_REST_API_READ_ONLY_TOKEN="" \ No newline at end of file diff --git a/CONTRIBUTING.md b/CONTRIBUTING.md new file mode 100644 index 0000000..ba69e3b --- /dev/null +++ b/CONTRIBUTING.md @@ -0,0 +1,72 @@ +## Contribution Guidelines + +Please note that this project is released with a [Contributor Code of Conduct](CODE_OF_CONDUCT.md). By participating in this project you agree to abide by its terms. + +## How to contribute + +- Decide which repository to contribute +- Decide what to contribute +- Fork the repo then clone it locally +- Commit your work (You should create a new branch when you're doing development work that is somewhat experimental in nature.) +- Create a **Pull Request** +- Congrats 🎉 you have just contributed towards open source! + +## What to contribute + +- Find an open issue to tackle +- Ask if you can help write a new feature +- Add / Improve Unit Testing +- Write tutorials for how a project can be used and add to the readme +- Review code on other people’s submissions and help improving / finding vulnerabilities + +## Making a PR +- Provide all the appropriate details asked in PR template +- A pull request doesn’t have to represent finished work. It’s usually better to open a pull request early on, so others can watch or give feedback on your progress. Just mark it as a “WIP” (Work in Progress) in the subject line. You can always add more commits later. + +## Opening an Issue +- Make use of an appropriate Issue Template +- We welcome Feature request, Bug Report, Documentation fix and others +- Do not open critical security issues here, report them directly at [our email](mailto:contact@codechefvit.com). + +## Communicating effectively +**Give context.** Help others get quickly up to speed. If you’re running into an error, explain what you’re trying to do and how to reproduce it. If you’re suggesting a new idea, explain why you think it’d be useful to the project (not just to you!). + +``` +✔️ “X doesn’t happen when I do Y” +❌ “X is broken! Please fix it.” +``` + +**Do your homework beforehand.** It’s OK not to know things, but show that you tried. Before asking for help, be sure to check a project’s README, documentation, issues (open or closed), mailing list, and search the internet for an answer. People will appreciate when you demonstrate that you’re trying to learn. + +``` +✔️ ““I’m not sure how to implement X. I checked the help docs and didn’t find any mentions.”” +❌ “How do I X?” +``` + +**Keep requests short and direct.** + +``` +✔️ “I’d like to write an API tutorial.” +❌ “I was driving down the highway the other day and stopped for gas, and then I had this amazing idea for something we should be doing, but before I explain that, let me show you…“ +``` + +**It’s okay to ask questions (but be patient!).** + +``` +✔️ “Thanks for looking into this error. I followed your suggestions. Here’s the output.” +❌ “Why can’t you fix my problem? Isn’t this your project?” +``` + +**Respect community decisions.** + +``` +✔️ “I’m disappointed you can’t support my use case, but as you’ve explained it only affects a minor portion of users, I understand why. Thanks for listening.” +❌ “Why won’t you support my use case? This is unacceptable!” +``` + +## Misc +- You are welcome to Propose a new feature by creating an **Issue**. +- You may Discuss a high-level topic or idea (for example, community, vision or policies) by writing to us at our [Email](mailto:contact@codechefvit.com). + +## Attribution +- [Open Source Guide](https://opensource.guide/how-to-contribute/) \ No newline at end of file diff --git a/public/watermark.jpg b/public/watermark.jpg deleted file mode 100644 index 7040006a2f127bc2e2667a4e4ad8cc021905a273..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 29657 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℘eEh{Nu8c9CU+2XLCqFK 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 KJ#g&f^#2CJf4k8D diff --git a/src/app/adminupload/page.tsx b/src/app/adminupload/page.tsx deleted file mode 100644 index 4c92969..0000000 --- a/src/app/adminupload/page.tsx +++ /dev/null @@ -1,699 +0,0 @@ -"use client"; -import React, { useState, useEffect } from "react"; -import axios, { AxiosError } from "axios"; -import { CldUploadWidget } from "next-cloudinary"; -import { - type CloudinaryUploadWidgetProps, - type CloudinaryUploadResult, -} from "@/interface"; -import { useRouter } from "next/navigation"; -import Image from "next/image"; -import { Trash } from "lucide-react"; -import Link from "next/link"; -import toast from "react-hot-toast"; -import { handleAPIError } from "../../util/error"; -import { ApiError } from "next/dist/server/api-utils"; -import { courses, slots } from "../upload/select_options"; - -interface PostPDF { - url: string; -} -interface DeletePDF { - message: string; -} - -interface PostPDFToCloudinary { - status: boolean; -} - -function Upload() { - const router = useRouter(); - const [subject, setSubject] = useState( - "Digital Logic and Microprocessors[BITE202L]", - ); - const [slot, setSlot] = useState("A1"); - const [year, setYear] = useState("2011"); - const [exam, setExam] = useState("CAT-1"); - const [tag, setTag] = useState(); - const [urls, setUrls] = useState(); - const [publicIds, setPublicIds] = useState(); - const [isPdf, setIsPdf] = useState(true); - const [asset, setAsset] = - useState<(CloudinaryUploadResult | string | undefined)[]>(); - const [file, setFile] = useState(); - const [error, setError] = useState(null); - const [files, setFiles] = useState([]); - const [pdfUrl, setPdfUrl] = useState(null); - - useEffect(() => { - async function makeTage() { - const token = localStorage.getItem("token"); - const headers = { - Authorization: `Bearer ${token}`, - }; - if (!token) { - router.push("/papersadminlogin"); - } - try { - const response = await axios.get("/api/admin/dashboard", { headers }); - const timestamp = Date.now(); - setTag(`papers-${timestamp}`); - } catch (error: unknown) { - if (error instanceof AxiosError) { - const status = error.response?.status; - if (status === 401) { - router.push("/papersadminlogin"); - } else { - toast.error("An unknown error occurred"); - } - } else { - toast.error("An unexpected error occurred"); - } - } - } - void makeTage(); - }, []); - - const handleFileChange = (event: React.ChangeEvent) => { - if (event.target.files) { - setFile(event.target.files[0]); - } - }; - //toast - - const handleSubmit = async (event: React.FormEvent) => { - event.preventDefault(); - const token = localStorage.getItem("token"); - if (!token) { - router.push("/papersadminlogin"); - return; - } - const headers = { - Authorization: `Bearer ${token}`, - }; - if (!file) { - setError("Please select a PDF file to upload."); - return; - } - - const formData = new FormData(); - formData.append("file", file); - void toast.promise( - //Won't refresh the page if error 401 - (async () => { - try { - const response = await axios.post( - "/api/admin/watermark", - formData, - { headers }, - ); - setPdfUrl(response.data.url); - } catch (error: unknown) { - handleAPIError(error); - // if (error instanceof AxiosError) { - // const status = error.response?.status; - // if (status === 401) { - // router.push("/papersadminlogin"); - // } else { - // toast.error("Failed to upload papers."); - // setError("Failed to watermark PDF."); - // } - // } else { - // toast.error("An unexpected error occurred"); - // setError("Failed to watermark PDF."); - // } - } - })(), - { - loading: "Uploading papers...", - success: "papers uploaded", - error: (err: ApiError) => { - setError(err.message); - return err.message; - }, - }, - ); - }; - //toast - - const handleDelete = async ( - public_id: string, - type: string, - delUrl: string, - ) => { - const token = localStorage.getItem("token"); - if (!token) { - router.push("/papersadminlogin"); - return; - } - const headers = { - Authorization: `Bearer ${token}`, - }; - try { - const response = await axios.delete(`/api/admin`, { - params: { public_id, type }, - headers, - }); - - const updatedUrls = urls?.filter((url) => url !== delUrl); - setUrls(updatedUrls); - const updatePublicIds = publicIds?.filter((id) => id !== public_id); - setPublicIds(updatePublicIds); - const updatedAssets = asset?.filter((asset) => { - if (typeof asset === "string") { - return true; - } - return asset?.public_id !== public_id; - }); - setAsset(updatedAssets); - } catch (error: unknown) { - if (error instanceof AxiosError) { - const status = error.response?.status; - if (status === 401) { - router.push("/papersadminlogin"); - } else { - toast.error("Failed to upload papers."); - } - } else { - toast.error("An unexpected error occurred"); - } - } - }; - //toast - - const handleDeletePdf = async () => { - const token = localStorage.getItem("token"); - if (!token) { - router.push("/papersadminlogin"); - return; - } - const headers = { - Authorization: `Bearer ${token}`, - }; - //won't refresh if page 401 - void toast.promise( - (async () => { - try { - const response = await axios.delete("/api/admin/watermark", { - headers, - }); - } catch (error: unknown) { - throw handleAPIError(error); - // if (error instanceof AxiosError) { - // const status = error.response?.status; - // if (status === 401) { - // router.push("/papersadminlogin"); - // } else { - // toast.error("Failed to upload papers."); - // setError("Failed to delete PDF."); - // } - // } else { - // toast.error("An unexpected error occurred"); - // setError("An unexpected error occurred."); - // } - } - })(), - { - loading: "Deleting PDF...", - success: "Paper deleted", - error: (err: ApiError) => { - setError(err.message); - return err.message; - }, - }, - ); - }; - - const handleFileChangeMerged = (e: React.ChangeEvent) => { - const selectedFiles = Array.from(e.target.files ?? []); - setFiles(selectedFiles); - }; - //toast - - const handleSubmitMerged = async (e: React.FormEvent) => { - e.preventDefault(); - const token = localStorage.getItem("token"); - const headers = { - Authorization: `Bearer ${token}`, - }; - if (files.length === 0) { - alert("Please upload at least one file"); - return; - } - - const formData = new FormData(); - files.forEach((file) => { - formData.append("files", file); - }); - void toast.promise( - (async () => { - try { - const response = await axios.post( - "/api/admin/imgtopdf", - formData, - { headers }, - ); - setPdfUrl(response.data.url); - } catch (error: unknown) { - throw handleAPIError(error); - } - })(), - { - loading: "Uploading papers...", - success: "papers uploaded", - error: (err: ApiError) => { - setError(err.message); - return err.message; - }, - }, - ); - - // try response template for easy access :D - // toast.promise( - // (async () => { - // try { - // //put your api request here - // } catch (error: unknown) { - // throw handleAPIError(error); - // } - // })(), - // { - // loading: "Uploading papers...", - // success: "papers uploaded", - // error: (err: ApiError) => { - // setError(err.message); - // return err.message; - // }, - // }, - // ); - - // try { - // const response = await axios.post( - // "/api/admin/imgtopdf", - // formData, - // ); - // setPdfUrl(response.data.url); - // } catch (error: unknown) { - // if (error instanceof AxiosError) { - // const status = error.response?.status; - // if (status === 401) { - // router.push("/papersadminlogin"); - // } else { - // toast.error("Failed to upload papers."); - // } - // } else { - // toast.error("An unexpected error occurred"); - // } - // } - }; - //toast - - const handleDeleteMerged = async () => { - if (!pdfUrl) return; - const token = localStorage.getItem("token"); - const headers = { - Authorization: `Bearer ${token}`, - }; - void toast.promise( - (async () => { - try { - const response = await axios.delete("/api/admin/imgtopdf", { - data: { filePath: pdfUrl }, - headers, - }); - setPdfUrl(null); - } catch (error: unknown) { - throw handleAPIError(error); - } - })(), - { - loading: "Deleting Paper...", - success: "Paper deleted", - error: (err: ApiError) => { - setError(err.message); - return err.message; - }, - }, - ); - // try { - // const response = await axios.delete("/api/admin/imgtopdf", { - // data: { filePath: pdfUrl }, - // }); - // alert(response.data.message); - // setPdfUrl(null); - // } catch (error: unknown) { - // if (error instanceof AxiosError) { - // const status = error.response?.status; - // if (status === 401) { - // router.push("/papersadminlogin"); - // } else { - // toast.error("Failed to upload papers."); - // } - // } else { - // toast.error("An unexpected error occurred"); - // } - // } - }; - //toast - - async function completeUpload() { - const token = localStorage.getItem("token"); - if (!token) { - router.push("/papersadminlogin"); - return; - } - const headers = { - Authorization: `Bearer ${token}`, - }; - const body = { - publicIds: publicIds, - urls: urls, - subject: subject, - slot: slot, - year: year, - exam: exam, - isPdf: isPdf, - }; - void toast.promise( - (async () => { - try { - const response = await axios.post( - "/api/admin", - body, - { headers }, - ); - if (response.data.status) { - setUrls([]); - setSubject(""); - setSlot(""); - setYear(""); - setExam("CAT-1"); - setAsset([]); - setPublicIds([]); - setUrls([]); - } - } catch (error: unknown) { - throw handleAPIError(error); - } - })(), - { - loading: "Uploading papers...", - success: "papers uploaded", - error: (err: ApiError) => { - setError(err.message); - return err.message; - }, - }, - ); - } - - if (!tag) { - return
.
; - } - - return ( -
-
-

Upload Papers

- { - setUrls((prevUrls) => [ - ...(prevUrls ?? []), - results.info?.secure_url, - ]); - setPublicIds((prevIds) => [ - ...(prevIds ?? []), - results.info?.public_id, - ]); - setAsset((prevAssets) => [...(prevAssets ?? []), results.info]); - }} - > - {({ open }) => ( - - )} - -
- -
-
- -
-
- -
-
- -
-
- - setIsPdf(e.target.checked)} - /> -
- -
-
-

- Upload and Watermark PDF -

- {error &&

{error}

} -
-
- - -
- -
-
- {pdfUrl && ( - - View Papers - - )} - - -
-
-
-
-
-

- Upload Images to Convert to PDF -

-
-
- - -
- -
- {pdfUrl && ( -
-

PDF Created

- - Download PDF - - -
- )} -
-
-
-
-

Uploaded Assets:

- {asset && asset.length > 0 ? ( -
- {asset.map((asset, index) => ( -
- {typeof asset !== "string" && - typeof asset?.url === "string" && - asset.url.toLowerCase().endsWith(".pdf") ? ( -
-
- handleDelete(asset.public_id, asset.type, asset.url) - } - > - - Delete PDF -
- -