-
Notifications
You must be signed in to change notification settings - Fork 1
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 7cc5b4d
Showing
142 changed files
with
14,860 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,37 @@ | ||
# 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 | ||
.env | ||
|
||
# vercel | ||
.vercel | ||
|
||
# typescript | ||
*.tsbuildinfo | ||
next-env.d.ts |
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 @@ | ||
{ | ||
"singleQuote": true | ||
} |
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,33 @@ | ||
Next Project: Social Media Platform | ||
|
||
# Tech: | ||
|
||
- Mern Stack (React, NodeJS, Express, MongoDB) | ||
- Build with NEXTJS latest Version [Typescript] | ||
|
||
# Tools & Libs: | ||
|
||
- zod | ||
- react hook form | ||
|
||
# Features: | ||
|
||
- Login / Sign Up ✅ | ||
- User Profile ✅ | ||
- Manage User Profile (create and customize profile, include pictures, cover photo, bio section) ✅ | ||
- News Feed (display latest posts from users in home page) ✅ | ||
- Post Creation (enable user to create post with: text, images, links..) ✅ | ||
- Show Suggested Users (show suggested users to follow) ✅ | ||
- Responsive Design ✅ | ||
|
||
- Dark / Light Mode | ||
- Followers System (follow / unfollow / following list on user profile) | ||
- Notifications (notify users about new followers, likes, comments) | ||
- Real-Time Updates (implement real-time updates for notifications, news feed and messages using techs like webSocket) | ||
- Messaging System (create private messaging system for users to send direct messages to their followers) | ||
- Search Functionality (implement a search feature to find users) | ||
|
||
# Styling: | ||
|
||
- TailwindCSS | ||
- Shadcn |
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,21 @@ | ||
'use server'; | ||
|
||
import { revalidatePath } from 'next/cache'; | ||
import { connectToDb } from '@/lib/db'; | ||
import { UserModel } from '@/models/userModel'; | ||
import { currentUser } from '@/actions/auth/current-user'; | ||
|
||
export async function updateMyAccount(data: any) { | ||
connectToDb(); | ||
|
||
const { user } = await currentUser(); | ||
if (!user) return; | ||
|
||
await UserModel.findOneAndUpdate({ _id: user?._id }, data, { | ||
upsert: true, | ||
}); | ||
|
||
revalidatePath('/profile'); | ||
revalidatePath('/profile/info'); | ||
return { success: 'Your info have been updated!' }; | ||
} |
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,46 @@ | ||
'use server'; | ||
|
||
import { cookies } from 'next/headers'; | ||
import jwt from 'jsonwebtoken'; | ||
|
||
import { connectToDb } from '@/lib/db'; | ||
import { IUser, UserModel } from '@/models/userModel'; | ||
|
||
export async function currentUser() { | ||
connectToDb(); | ||
|
||
const token = cookies().get('jwt')?.value; | ||
|
||
if (token) { | ||
try { | ||
// 1) Verify token | ||
const decoded: any = jwt.verify( | ||
token!, | ||
process.env.JWT_SECRET! as string | ||
); | ||
|
||
// 2) Check if user still exists | ||
const currentUser = await UserModel.findById(decoded.id); | ||
if (!currentUser) { | ||
return { | ||
error: 'The user belonging to this token does no longer exist.', | ||
}; | ||
} | ||
|
||
const plainObject: IUser | null = JSON.parse(JSON.stringify(currentUser)); | ||
|
||
// THERE IS A LOGGED IN USER | ||
return { user: plainObject }; | ||
} catch (error: any) { | ||
if (error instanceof jwt.TokenExpiredError) { | ||
return { error: 'Your token has expired. Please log in again.' }; | ||
} | ||
|
||
if (error instanceof jwt.JsonWebTokenError) { | ||
return { error: 'Token verification failed, Please log in again!' }; | ||
} | ||
} | ||
} | ||
|
||
return { error: 'You are not logged in! Please log in to get access' }; | ||
} |
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,45 @@ | ||
'use server'; | ||
|
||
import { z } from 'zod'; | ||
|
||
import { ForgotPasswordSchema } from '@/schemas/auth'; | ||
import { connectToDb } from '@/lib/db'; | ||
import { UserModel } from '@/models/userModel'; | ||
import { sendPasswordResetEmail } from '@/lib/email-sender'; | ||
|
||
export async function forgotPassword({ | ||
email, | ||
}: z.infer<typeof ForgotPasswordSchema>) { | ||
connectToDb(); | ||
|
||
// 1) Get user based on POSTed email | ||
const user = await UserModel.findOne({ email }); | ||
if (!user) { | ||
return { error: 'There is no user with provided email.' }; | ||
} | ||
|
||
// 2) Generate the random reset token | ||
const resetToken = user.createPasswordResetToken(); | ||
await user.save({ validateBeforeSave: false }); | ||
|
||
// 3) Send it to user's email | ||
try { | ||
const resetURL = `${process.env.CLIENT_DOMAIN}/auth/reset-password/${resetToken}`; | ||
|
||
await sendPasswordResetEmail({ | ||
email: user?.email, | ||
name: user?.firstName || user?.username, | ||
resetURL, | ||
}); | ||
|
||
return { success: 'Reset password sent!, Check your email' }; | ||
} catch (error) { | ||
console.log(error); | ||
|
||
user.passwordResetToken = undefined; | ||
user.passwordResetExpires = undefined; | ||
await user.save({ validateBeforeSave: false }); | ||
|
||
return { error: 'There was an error sending the email. Try again later!' }; | ||
} | ||
} |
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,54 @@ | ||
'use server'; | ||
|
||
import { z } from 'zod'; | ||
import { cookies } from 'next/headers'; | ||
import { revalidatePath } from 'next/cache'; | ||
import { ResponseCookie } from 'next/dist/compiled/@edge-runtime/cookies'; | ||
import { redirect } from 'next/navigation'; | ||
|
||
import { connectToDb } from '@/lib/db'; | ||
import { LoginSchema } from '@/schemas'; | ||
import { UserModel } from '@/models/userModel'; | ||
import { signToken } from '@/lib/utils'; | ||
|
||
const JWT_COOKIE_EXPIRES_IN: number = Number( | ||
process.env.JWT_COOKIE_EXPIRES_IN! | ||
); | ||
|
||
export async function login({ email, password }: z.infer<typeof LoginSchema>) { | ||
connectToDb(); | ||
|
||
// 1) Validate input | ||
if (!email || !password) { | ||
return { error: 'Please provide email and password' }; | ||
} | ||
|
||
// 2) Creating a new user in the database | ||
const user = await UserModel.findOne({ email }).select('+password'); | ||
|
||
// 3) Check if the user exists and validate password | ||
if (!user || !(await user.correctPassword(password, user.password))) { | ||
return { error: 'Incorrect email or password' }; | ||
} | ||
|
||
// 4) Send token to client | ||
const token = signToken(user._id); | ||
|
||
let cookieOptions: ResponseCookie = { | ||
name: 'jwt', | ||
value: token, | ||
expires: new Date(Date.now() + JWT_COOKIE_EXPIRES_IN * 24 * 60 * 60 * 1000), | ||
httpOnly: true, | ||
secure: false, | ||
}; | ||
|
||
if (process.env.NODE_ENV === 'production') { | ||
cookieOptions.secure = true; | ||
} | ||
|
||
cookies().set('jwt', token, cookieOptions); | ||
|
||
revalidatePath('/'); | ||
|
||
redirect('/'); | ||
} |
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,9 @@ | ||
'use server'; | ||
|
||
import { cookies } from 'next/headers'; | ||
import { redirect } from 'next/navigation'; | ||
|
||
export async function logout() { | ||
cookies().delete('jwt'); | ||
redirect('/auth/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,57 @@ | ||
'use server'; | ||
|
||
import crypto from 'crypto'; | ||
|
||
import { UserModel } from '@/models/userModel'; | ||
import { signToken } from '@/lib/utils'; | ||
import { ResponseCookie } from 'next/dist/compiled/@edge-runtime/cookies'; | ||
import { cookies } from 'next/headers'; | ||
import { redirect } from 'next/navigation'; | ||
|
||
const JWT_COOKIE_EXPIRES_IN: number = Number( | ||
process.env.JWT_COOKIE_EXPIRES_IN! | ||
); | ||
|
||
interface Props { | ||
token: string; | ||
newPassword: string; | ||
} | ||
|
||
export async function resetPassword({ token, newPassword }: Props) { | ||
// 1) Get user based on the token | ||
const hashedToken = crypto.createHash('sha256').update(token).digest('hex'); | ||
|
||
const user = await UserModel.findOne({ | ||
passwordResetToken: hashedToken, | ||
passwordResetExpires: { $gt: Date.now() }, | ||
}); | ||
|
||
// 2) If token has not expired, and there is user, set the new password | ||
if (!user) { | ||
return { error: 'Token is invalid or has expired' }; | ||
} | ||
|
||
// 3) Update passwordChangedAt property for the user | ||
user.password = newPassword; | ||
user.passwordResetToken = undefined; | ||
user.passwordResetExpires = undefined; | ||
await user.save(); | ||
|
||
// 4) Log the user in, send JWT | ||
const newToken = signToken(user._id); | ||
|
||
let cookieOptions: ResponseCookie = { | ||
name: 'jwt', | ||
value: newToken, | ||
expires: new Date(Date.now() + JWT_COOKIE_EXPIRES_IN * 24 * 60 * 60 * 1000), | ||
httpOnly: true, | ||
secure: false, | ||
}; | ||
|
||
if (process.env.NODE_ENV === 'production') { | ||
cookieOptions.secure = true; | ||
} | ||
|
||
cookies().set('jwt', newToken, cookieOptions); | ||
redirect('/'); | ||
} |
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,65 @@ | ||
'use server'; | ||
|
||
import { z } from 'zod'; | ||
import { cookies } from 'next/headers'; | ||
import { revalidatePath } from 'next/cache'; | ||
import { ResponseCookie } from 'next/dist/compiled/@edge-runtime/cookies'; | ||
import { redirect } from 'next/navigation'; | ||
|
||
import { connectToDb } from '@/lib/db'; | ||
import { SignUpSchema } from '@/schemas'; | ||
import { UserModel } from '@/models/userModel'; | ||
import { signToken } from '@/lib/utils'; | ||
|
||
const JWT_COOKIE_EXPIRES_IN: number = Number( | ||
process.env.JWT_COOKIE_EXPIRES_IN! | ||
); | ||
|
||
export async function signup({ | ||
email, | ||
password, | ||
username, | ||
}: z.infer<typeof SignUpSchema>) { | ||
connectToDb(); | ||
|
||
// 1) Validate input | ||
if (!email || !password || !username) | ||
return { error: 'Please provide email and password' }; | ||
|
||
// 2) Check if the email already exists in the database | ||
const existingEmail = await UserModel.findOne({ email }); | ||
if (existingEmail) return { error: 'Email already exists' }; | ||
|
||
if (username) { | ||
const existingUsername = await UserModel.findOne({ username }); | ||
if (existingUsername) return { error: 'Username already exists' }; | ||
} | ||
|
||
// 3) Creating a new user in the database | ||
const newUser = await UserModel.create({ | ||
email, | ||
password, | ||
username, | ||
}); | ||
|
||
// 4) Send token to client | ||
const token = signToken(newUser._id); | ||
|
||
let cookieOptions: ResponseCookie = { | ||
name: 'jwt', | ||
value: token, | ||
expires: new Date(Date.now() + JWT_COOKIE_EXPIRES_IN * 24 * 60 * 60 * 1000), | ||
httpOnly: true, | ||
secure: false, | ||
}; | ||
|
||
if (process.env.NODE_ENV === 'production') { | ||
cookieOptions.secure = true; | ||
} | ||
|
||
cookies().set('jwt', token, cookieOptions); | ||
|
||
revalidatePath('/'); | ||
|
||
redirect('/'); | ||
} |
Oops, something went wrong.