-
Notifications
You must be signed in to change notification settings - Fork 0
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
- Loading branch information
0 parents
commit 3733d36
Showing
69 changed files
with
10,116 additions
and
0 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,3 @@ | ||
{ | ||
"extends": "next/core-web-vitals" | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,36 @@ | ||
# See https://help.github.com/articles/ignoring-files/ for more about ignoring files. | ||
|
||
# dependencies | ||
/node_modules | ||
/.pnp | ||
.pnp.js | ||
.yarn/install-state.gz | ||
|
||
# testing | ||
/coverage | ||
|
||
# next.js | ||
/.next/ | ||
/out/ | ||
|
||
# production | ||
/build | ||
|
||
# misc | ||
.DS_Store | ||
*.pem | ||
|
||
# debug | ||
npm-debug.log* | ||
yarn-debug.log* | ||
yarn-error.log* | ||
|
||
# local env files | ||
.env*.local | ||
# vercel | ||
.vercel | ||
|
||
# typescript | ||
*.tsbuildinfo | ||
next-env.d.ts | ||
bun.lockb |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,49 @@ | ||
|
||
# GatePYQ 📚💻 | ||
|
||
[Live Link](https://gate-pyq-web.vercel.app) | ||
|
||
GatePYQ is a dynamic web application designed to help in preparing for competitive exam Graduate Aptitude Test in Engineering (GATE). It provides a centralized platform for accessing and practicing previous year questions. | ||
|
||
## Features 🚀 | ||
|
||
- 📝 Access to extensive collections of previous year questions for GATE exams. | ||
- 📚 Detailed explanations provided for each question and its correct answer, powered by Google's Gemini Pro Generative AI. | ||
- 🔒 Secure authentication using NextAuth with Google and Credentials Providers. | ||
- 🌐 Built with Next.js 14, MongoDB, Prisma, and React components for a seamless user experience. | ||
- 💪 Written in Typescript. | ||
- 〽️ MongoDB data api as an alternative data source. | ||
|
||
## Screenshots 📸 | ||
|
||
<img src="./Screenshots/Homepage.png" style="width: 75%;margin:16px;" /> | ||
<img src="./Screenshots/Questions - Signed In - Gemini Response.png" style="width: 75%;margin:16px;" /> | ||
<img src="./Screenshots/Sign In Page.png" style="width: 75%;margin:16px;" /> | ||
<img src="./Screenshots/Profile Page.png" style="width: 75%;margin:16px;" /> | ||
<img src="./Screenshots/404.png" style="width: 75%;margin:16px;" /> | ||
|
||
## Installation 🛠️ | ||
|
||
To run GatePYQ locally, follow these steps: | ||
|
||
1. Clone the repository: | ||
``` | ||
git clone https://github.com/anujd64/GatePYQ.git | ||
``` | ||
|
||
2. Navigate to the project directory: | ||
``` | ||
cd GatePYQ | ||
``` | ||
|
||
3. Install dependencies: | ||
``` | ||
npm install | ||
``` | ||
|
||
4. Start the development server: | ||
``` | ||
npm run dev | ||
``` | ||
|
||
5. Access GatePYQ in your browser at `http://localhost:3000`. |
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,90 @@ | ||
import GoogleProvider from 'next-auth/providers/google'; | ||
import CredentialsProvider from 'next-auth/providers/credentials' | ||
import { AuthOptions } from "next-auth"; | ||
export const authOptions: AuthOptions = { | ||
// adapter: PrismaAdapter(PrismaClient), | ||
providers: [ | ||
GoogleProvider({ | ||
clientId: process.env.GOOGLE_CLIENT_ID ?? "", | ||
clientSecret: process.env.GOOGLE_CLIENT_SECRET ?? "", | ||
}), | ||
|
||
CredentialsProvider({ | ||
type: "credentials", | ||
credentials: { | ||
email: { | ||
label: "Email", | ||
type: "email", | ||
}, | ||
password: { label: "Password", type: "password" }, | ||
}, | ||
async authorize(credentials) { | ||
|
||
const credentialDetails = { | ||
email: credentials?.email, | ||
password: credentials?.password, | ||
}; | ||
|
||
const resp = await fetch(process.env.NEXTAUTH_URL+"/api/auth/login", { | ||
method: "POST", | ||
headers: { | ||
Accept: "application/json", | ||
"Content-Type": "application/json", | ||
}, | ||
body: JSON.stringify(credentialDetails), | ||
}); | ||
const user = await resp.json(); | ||
console.log("user request ",credentials?.email,credentials?.password,"user response", user) | ||
if (resp.ok) { | ||
console.log("nextauth user: " + user.user); | ||
|
||
return user.user; | ||
} | ||
if(!resp.ok){ | ||
console.log("check your credentials"); | ||
throw new Error(user.message) | ||
} | ||
return null; | ||
}, | ||
}), | ||
], | ||
|
||
|
||
callbacks: { | ||
jwt: async ({ token, user }) => { | ||
|
||
if (user) { | ||
token.email = user.email ; | ||
token.username = user.name; | ||
token.name = user.name; | ||
token.image = user.image; | ||
} | ||
|
||
|
||
// if (user) { | ||
// const { user: userData } = user; | ||
|
||
// console.log("jwt callback",user, userData) | ||
// token.email = userData?.email ?? user.email; | ||
// token.username = userData?.name ?? user.name; | ||
// token.name = userData?.name ?? user.name; | ||
// token.image = userData?.image ?? user.image; | ||
// // token.accessToken = userData?.token; // Uncomment if needed | ||
// } | ||
return token; | ||
}, | ||
session: ({ session, token }) => { | ||
if (token) { | ||
session.user!.email = token.email; | ||
session.user!.name = token.name; | ||
session.user!.image = token.image as string; | ||
// session.user.accessToken = token.accessToken; | ||
} | ||
return session; | ||
}, | ||
}, | ||
|
||
pages: { | ||
signIn: '/profile/login', | ||
}, | ||
}; |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,3 @@ | ||
import { Outfit } from 'next/font/google'; | ||
|
||
export const outfit = Outfit({ subsets: ['latin'] }); |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,40 @@ | ||
import { MongoClient } from 'mongodb'; | ||
|
||
// Replace with your MongoDB connection string | ||
const uri = process.env.MONGODB_URI as string; | ||
const options = {}; | ||
|
||
declare global { | ||
var _mongoClientPromise: Promise<MongoClient>; | ||
} | ||
|
||
class Singleton { | ||
private static _instance: Singleton; | ||
private client: MongoClient; | ||
private clientPromise: Promise<MongoClient>; | ||
|
||
private constructor() { | ||
this.client = new MongoClient(uri, options); | ||
this.clientPromise = this.client.connect(); | ||
|
||
if (process.env.NODE_ENV === 'development') { | ||
// In development mode, use a global variable to preserve the value | ||
// across module reloads caused by HMR (Hot Module Replacement). | ||
global._mongoClientPromise = this.clientPromise; | ||
} | ||
} | ||
|
||
public static get instance() { | ||
if (!this._instance) { | ||
this._instance = new Singleton(); | ||
} | ||
return this._instance.clientPromise; | ||
} | ||
} | ||
|
||
const clientPromise = Singleton.instance; | ||
|
||
// Export a module-scoped MongoClient promise. | ||
// By doing this in a separate module, | ||
// the client can be shared across functions. | ||
export default clientPromise; |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,94 @@ | ||
|
||
import { type ClassValue, clsx } from "clsx" | ||
import { twMerge } from "tailwind-merge" | ||
import { findUser, createUser } from "../_server/actions/UserAction"; | ||
import { IUser } from "../_server/UserService/IUserService"; | ||
import { json } from "stream/consumers"; | ||
|
||
export function cn(...inputs: ClassValue[]) { | ||
return twMerge(clsx(inputs)) | ||
} | ||
|
||
//https://stackoverflow.com/questions/2450954/how-to-randomize-shuffle-a-javascript-array | ||
/* Randomize array in-place using Durstenfeld shuffle algorithm */ | ||
|
||
export function shuffleArray(array: string[]) { | ||
for (var i = array.length - 1; i > 0; i--) { | ||
var j = Math.floor(Math.random() * (i + 1)); | ||
var temp = array[i]; | ||
array[i] = array[j]; | ||
array[j] = temp; | ||
} | ||
return array; | ||
} | ||
|
||
|
||
// export async function getTags() { | ||
// const res = await fetch('http://localhost:2020/tags') | ||
// // The return value is *not* serialized | ||
// // You can return Date, Map, Set, etc. | ||
|
||
// if (!res.ok) { | ||
// throw new Error('Failed to fetch data') | ||
// } | ||
|
||
// return res.json() | ||
// } | ||
|
||
|
||
export function validateRegisterForm(values: {email:string,password:string,passwordC:string, username:string}) { | ||
const errors: any = {}; | ||
|
||
if (!values.email) { | ||
errors.email = "Required"; | ||
} else if ( | ||
!/^[A-Z0-9._%+-]+@[A-Z0-9.-]+\.[A-Z]{2,}$/i.test(values.email) | ||
) { | ||
errors.email = "Invalid email address"; | ||
} | ||
|
||
if (values.password !== values.passwordC) { | ||
const errMsg = "Passwords don't match"; | ||
errors.password = errMsg; | ||
errors.passwordC = errMsg; | ||
} | ||
|
||
return errors; | ||
} | ||
|
||
export async function onRegisterSubmit(values:{email:string,password:string, passwordC:string,username:string}) { | ||
|
||
const userObj = (values: { email: string; password: string; username?: string; })=> { | ||
return { | ||
email:values.email, | ||
password:values.password, | ||
username:values.username | ||
} | ||
} | ||
const data = findUser({email: values.email}) | ||
let user = await data.then(json => json.data); | ||
if(user === null){ | ||
user = await createUser(userObj(values)).then(json => json.data) | ||
} | ||
return user; | ||
} | ||
|
||
|
||
export function validateLoginForm(values: {email:string,password:string}) { | ||
const errors: any = {}; | ||
|
||
if (!values.email) { | ||
errors.email = "Required"; | ||
} else if ( | ||
!/^[A-Z0-9._%+-]+@[A-Z0-9.-]+\.[A-Z]{2,}$/i.test(values.email) | ||
) { | ||
errors.email = "Invalid email address"; | ||
} | ||
|
||
if (values.password == "") { | ||
const errMsg = "Password is too short"; | ||
errors.password = errMsg; | ||
} | ||
|
||
return errors; | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,8 @@ | ||
interface IRepository<T> { | ||
find( | ||
filter: Partial<T>, | ||
page: number, | ||
limit: number, | ||
projection?: Partial<Record<keyof T, 1 | 0>>, | ||
): Promise<{ data: T[], totalCount: number }>; | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,16 @@ | ||
export type IQuestion = { | ||
_id:string, | ||
question: string, | ||
options: string[], | ||
correct_options:string[], | ||
image_links:string[], | ||
explanation_link:string, | ||
tags: string[] | any, | ||
}; | ||
|
||
|
||
export interface IQuestionService { | ||
getQuestions( | ||
filter: Partial<IQuestion> | ||
): Promise<{ data: IQuestion[]; totalCount: number }>; | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,20 @@ | ||
import { Repository } from "@/app/_server/RepositoryService"; | ||
import { IQuestion, IQuestionService } from "./IQuestionService"; | ||
|
||
export class QuestionService implements IQuestionService { | ||
private repository: Repository<IQuestion>; | ||
|
||
constructor() { | ||
this.repository = new Repository<IQuestion>("questions"); | ||
} | ||
|
||
async getQuestions( | ||
filter: Partial<IQuestion>, | ||
page: number = 1, | ||
limit: number = 10 | ||
): Promise<{ data: IQuestion[], totalCount: number }> { | ||
|
||
|
||
return this.repository.find(filter, page, limit); | ||
} | ||
} |
Oops, something went wrong.