Repository: hiteshchoudhary/chai-backend Branch: main Commit: d1d939021155 Files: 41 Total size: 40.6 KB Directory structure: gitextract_8a_1uytj/ ├── .gitignore ├── .prettierignore ├── .prettierrc ├── Readme.md ├── package.json ├── public/ │ └── temp/ │ └── .gitkeep └── src/ ├── app.js ├── constants.js ├── controllers/ │ ├── comment.controller.js │ ├── dashboard.controller.js │ ├── healthcheck.controller.js │ ├── like.controller.js │ ├── playlist.controller.js │ ├── subscription.controller.js │ ├── tweet.controller.js │ ├── user.controller.js │ └── video.controller.js ├── db/ │ └── index.js ├── index.js ├── middlewares/ │ ├── auth.middleware.js │ └── multer.middleware.js ├── models/ │ ├── comment.model.js │ ├── like.model.js │ ├── playlist.model.js │ ├── subscription.model.js │ ├── tweet.model.js │ ├── user.model.js │ └── video.model.js ├── routes/ │ ├── comment.routes.js │ ├── dashboard.routes.js │ ├── healthcheck.routes.js │ ├── like.routes.js │ ├── playlist.routes.js │ ├── subscription.routes.js │ ├── tweet.routes.js │ ├── user.routes.js │ └── video.routes.js └── utils/ ├── ApiError.js ├── ApiResponse.js ├── asyncHandler.js └── cloudinary.js ================================================ FILE CONTENTS ================================================ ================================================ FILE: .gitignore ================================================ # Logs logs *.log npm-debug.log* yarn-debug.log* yarn-error.log* lerna-debug.log* .pnpm-debug.log* # Diagnostic reports (https://nodejs.org/api/report.html) report.[0-9]*.[0-9]*.[0-9]*.[0-9]*.json # Runtime data pids *.pid *.seed *.pid.lock # Directory for instrumented libs generated by jscoverage/JSCover lib-cov # Coverage directory used by tools like istanbul coverage *.lcov # nyc test coverage .nyc_output # Grunt intermediate storage (https://gruntjs.com/creating-plugins#storing-task-files) .grunt # Bower dependency directory (https://bower.io/) bower_components # node-waf configuration .lock-wscript # Compiled binary addons (https://nodejs.org/api/addons.html) build/Release # Dependency directories node_modules/ jspm_packages/ # Snowpack dependency directory (https://snowpack.dev/) web_modules/ # TypeScript cache *.tsbuildinfo # Optional npm cache directory .npm # Optional eslint cache .eslintcache # Microbundle cache .rpt2_cache/ .rts2_cache_cjs/ .rts2_cache_es/ .rts2_cache_umd/ # Optional REPL history .node_repl_history # Output of 'npm pack' *.tgz # Yarn Integrity file .yarn-integrity # dotenv environment variables file .env .env.test .env.production # parcel-bundler cache (https://parceljs.org/) .cache .parcel-cache # Next.js build output .next out # Nuxt.js build / generate output .nuxt dist # Gatsby files .cache/ # Comment in the public line in if your project uses Gatsby and not Next.js # https://nextjs.org/blog/next-9-1#public-directory-support # public # vuepress build output .vuepress/dist # Serverless directories .serverless/ # FuseBox cache .fusebox/ # DynamoDB Local files .dynamodb/ # TernJS port file .tern-port # Stores VSCode versions used for testing VSCode extensions .vscode-test # yarn v2 .yarn/cache .yarn/unplugged .yarn/build-state.yml .yarn/install-state.gz .pnp.* # End of https://mrkandreev.name/snippets/gitignore-generator/#Node .DS_Store ================================================ FILE: .prettierignore ================================================ /.vscode /node_modules ./dist *.env .env .env.* ================================================ FILE: .prettierrc ================================================ { "singleQuote": false, "bracketSpacing": true, "tabWidth": 2, "trailingComma": "es5", "semi": true } ================================================ FILE: Readme.md ================================================ # chai aur backend series This is a video series on backend with javascript - [Model link](https://app.eraser.io/workspace/YtPqZ1VogxGy1jzIDkzj?origin=share) - [Video playlist](https://www.youtube.com/watch?v=EH3vGeqeIAo&list=PLu71SKxNbfoBGh_8p_NS-ZAh6v7HhYqHW) --- # Summary of this project This project is a complex backend project that is built with nodejs, expressjs, mongodb, mongoose, jwt, bcrypt, and many more. This project is a complete backend project that has all the features that a backend project should have. We are building a complete video hosting website similar to youtube with all the features like login, signup, upload video, like, dislike, comment, reply, subscribe, unsubscribe, and many more. Project uses all standard practices like JWT, bcrypt, access tokens, refresh Tokens and many more. We have spent a lot of time in building this project and we are sure that you will learn a lot from this project. --- Top Contributer to complete all TODOs 1. Spiderman (just sample) [Link to Repo](https://www.youtube.com/@chaiaurcode) --- ## How to contribute in this open source Project First, please understand that this is not your regular project to merge your PR. This repo requires you to finish all assignments that are in controller folder. We don't accept half work, please finish all controllers and then reach us out on [Discord](https://hitesh.ai/discord) or [Twitter](https://twitter.com/@hiteshdotcom) and after checking your repo, I will add link to your repo in this readme. ================================================ FILE: package.json ================================================ { "name": "chai-backend", "version": "1.0.0", "description": "a backend at chai aur code channel - youtube", "type": "module", "main": "index.js", "scripts": { "dev": "nodemon -r dotenv/config --experimental-json-modules src/index.js" }, "keywords": [ "javascript", "backend", "chai" ], "author": "Hitesh Choudhary", "license": "ISC", "devDependencies": { "nodemon": "^3.0.1", "prettier": "^3.0.3" }, "dependencies": { "bcrypt": "^5.1.1", "cloudinary": "^1.41.0", "cookie-parser": "^1.4.6", "cors": "^2.8.5", "dotenv": "^16.3.1", "express": "^4.18.2", "jsonwebtoken": "^9.0.2", "mongoose": "^8.0.0", "mongoose-aggregate-paginate-v2": "^1.0.6", "multer": "^1.4.5-lts.1" } } ================================================ FILE: public/temp/.gitkeep ================================================ ================================================ FILE: src/app.js ================================================ import express from "express" import cors from "cors" import cookieParser from "cookie-parser" const app = express() app.use(cors({ origin: process.env.CORS_ORIGIN, credentials: true })) app.use(express.json({limit: "16kb"})) app.use(express.urlencoded({extended: true, limit: "16kb"})) app.use(express.static("public")) app.use(cookieParser()) //routes import import userRouter from './routes/user.routes.js' import healthcheckRouter from "./routes/healthcheck.routes.js" import tweetRouter from "./routes/tweet.routes.js" import subscriptionRouter from "./routes/subscription.routes.js" import videoRouter from "./routes/video.routes.js" import commentRouter from "./routes/comment.routes.js" import likeRouter from "./routes/like.routes.js" import playlistRouter from "./routes/playlist.routes.js" import dashboardRouter from "./routes/dashboard.routes.js" //routes declaration app.use("/api/v1/healthcheck", healthcheckRouter) app.use("/api/v1/users", userRouter) app.use("/api/v1/tweets", tweetRouter) app.use("/api/v1/subscriptions", subscriptionRouter) app.use("/api/v1/videos", videoRouter) app.use("/api/v1/comments", commentRouter) app.use("/api/v1/likes", likeRouter) app.use("/api/v1/playlist", playlistRouter) app.use("/api/v1/dashboard", dashboardRouter) // http://localhost:8000/api/v1/users/register export { app } ================================================ FILE: src/constants.js ================================================ export const DB_NAME = "videotube" ================================================ FILE: src/controllers/comment.controller.js ================================================ import mongoose from "mongoose" import {Comment} from "../models/comment.model.js" import {ApiError} from "../utils/ApiError.js" import {ApiResponse} from "../utils/ApiResponse.js" import {asyncHandler} from "../utils/asyncHandler.js" const getVideoComments = asyncHandler(async (req, res) => { //TODO: get all comments for a video const {videoId} = req.params const {page = 1, limit = 10} = req.query }) const addComment = asyncHandler(async (req, res) => { // TODO: add a comment to a video }) const updateComment = asyncHandler(async (req, res) => { // TODO: update a comment }) const deleteComment = asyncHandler(async (req, res) => { // TODO: delete a comment }) export { getVideoComments, addComment, updateComment, deleteComment } ================================================ FILE: src/controllers/dashboard.controller.js ================================================ import mongoose from "mongoose" import {Video} from "../models/video.model.js" import {Subscription} from "../models/subscription.model.js" import {Like} from "../models/like.model.js" import {ApiError} from "../utils/ApiError.js" import {ApiResponse} from "../utils/ApiResponse.js" import {asyncHandler} from "../utils/asyncHandler.js" const getChannelStats = asyncHandler(async (req, res) => { // TODO: Get the channel stats like total video views, total subscribers, total videos, total likes etc. }) const getChannelVideos = asyncHandler(async (req, res) => { // TODO: Get all the videos uploaded by the channel }) export { getChannelStats, getChannelVideos } ================================================ FILE: src/controllers/healthcheck.controller.js ================================================ import {ApiError} from "../utils/ApiError.js" import {ApiResponse} from "../utils/ApiResponse.js" import {asyncHandler} from "../utils/asyncHandler.js" const healthcheck = asyncHandler(async (req, res) => { //TODO: build a healthcheck response that simply returns the OK status as json with a message }) export { healthcheck } ================================================ FILE: src/controllers/like.controller.js ================================================ import mongoose, {isValidObjectId} from "mongoose" import {Like} from "../models/like.model.js" import {ApiError} from "../utils/ApiError.js" import {ApiResponse} from "../utils/ApiResponse.js" import {asyncHandler} from "../utils/asyncHandler.js" const toggleVideoLike = asyncHandler(async (req, res) => { const {videoId} = req.params //TODO: toggle like on video }) const toggleCommentLike = asyncHandler(async (req, res) => { const {commentId} = req.params //TODO: toggle like on comment }) const toggleTweetLike = asyncHandler(async (req, res) => { const {tweetId} = req.params //TODO: toggle like on tweet } ) const getLikedVideos = asyncHandler(async (req, res) => { //TODO: get all liked videos }) export { toggleCommentLike, toggleTweetLike, toggleVideoLike, getLikedVideos } ================================================ FILE: src/controllers/playlist.controller.js ================================================ import mongoose, {isValidObjectId} from "mongoose" import {Playlist} from "../models/playlist.model.js" import {ApiError} from "../utils/ApiError.js" import {ApiResponse} from "../utils/ApiResponse.js" import {asyncHandler} from "../utils/asyncHandler.js" const createPlaylist = asyncHandler(async (req, res) => { const {name, description} = req.body //TODO: create playlist }) const getUserPlaylists = asyncHandler(async (req, res) => { const {userId} = req.params //TODO: get user playlists }) const getPlaylistById = asyncHandler(async (req, res) => { const {playlistId} = req.params //TODO: get playlist by id }) const addVideoToPlaylist = asyncHandler(async (req, res) => { const {playlistId, videoId} = req.params }) const removeVideoFromPlaylist = asyncHandler(async (req, res) => { const {playlistId, videoId} = req.params // TODO: remove video from playlist }) const deletePlaylist = asyncHandler(async (req, res) => { const {playlistId} = req.params // TODO: delete playlist }) const updatePlaylist = asyncHandler(async (req, res) => { const {playlistId} = req.params const {name, description} = req.body //TODO: update playlist }) export { createPlaylist, getUserPlaylists, getPlaylistById, addVideoToPlaylist, removeVideoFromPlaylist, deletePlaylist, updatePlaylist } ================================================ FILE: src/controllers/subscription.controller.js ================================================ import mongoose, {isValidObjectId} from "mongoose" import {User} from "../models/user.model.js" import { Subscription } from "../models/subscription.model.js" import {ApiError} from "../utils/ApiError.js" import {ApiResponse} from "../utils/ApiResponse.js" import {asyncHandler} from "../utils/asyncHandler.js" const toggleSubscription = asyncHandler(async (req, res) => { const {channelId} = req.params // TODO: toggle subscription }) // controller to return subscriber list of a channel const getUserChannelSubscribers = asyncHandler(async (req, res) => { const {channelId} = req.params }) // controller to return channel list to which user has subscribed const getSubscribedChannels = asyncHandler(async (req, res) => { const { subscriberId } = req.params }) export { toggleSubscription, getUserChannelSubscribers, getSubscribedChannels } ================================================ FILE: src/controllers/tweet.controller.js ================================================ import mongoose, { isValidObjectId } from "mongoose" import {Tweet} from "../models/tweet.model.js" import {User} from "../models/user.model.js" import {ApiError} from "../utils/ApiError.js" import {ApiResponse} from "../utils/ApiResponse.js" import {asyncHandler} from "../utils/asyncHandler.js" const createTweet = asyncHandler(async (req, res) => { //TODO: create tweet }) const getUserTweets = asyncHandler(async (req, res) => { // TODO: get user tweets }) const updateTweet = asyncHandler(async (req, res) => { //TODO: update tweet }) const deleteTweet = asyncHandler(async (req, res) => { //TODO: delete tweet }) export { createTweet, getUserTweets, updateTweet, deleteTweet } ================================================ FILE: src/controllers/user.controller.js ================================================ import { asyncHandler } from "../utils/asyncHandler.js"; import {ApiError} from "../utils/ApiError.js" import { User} from "../models/user.model.js" import {uploadOnCloudinary} from "../utils/cloudinary.js" import { ApiResponse } from "../utils/ApiResponse.js"; import jwt from "jsonwebtoken" import mongoose from "mongoose"; const generateAccessAndRefereshTokens = async(userId) =>{ try { const user = await User.findById(userId) const accessToken = user.generateAccessToken() const refreshToken = user.generateRefreshToken() user.refreshToken = refreshToken await user.save({ validateBeforeSave: false }) return {accessToken, refreshToken} } catch (error) { throw new ApiError(500, "Something went wrong while generating referesh and access token") } } const registerUser = asyncHandler( async (req, res) => { // get user details from frontend // validation - not empty // check if user already exists: username, email // check for images, check for avatar // upload them to cloudinary, avatar // create user object - create entry in db // remove password and refresh token field from response // check for user creation // return res const {fullName, email, username, password } = req.body //console.log("email: ", email); if ( [fullName, email, username, password].some((field) => field?.trim() === "") ) { throw new ApiError(400, "All fields are required") } const existedUser = await User.findOne({ $or: [{ username }, { email }] }) if (existedUser) { throw new ApiError(409, "User with email or username already exists") } //console.log(req.files); const avatarLocalPath = req.files?.avatar[0]?.path; //const coverImageLocalPath = req.files?.coverImage[0]?.path; let coverImageLocalPath; if (req.files && Array.isArray(req.files.coverImage) && req.files.coverImage.length > 0) { coverImageLocalPath = req.files.coverImage[0].path } if (!avatarLocalPath) { throw new ApiError(400, "Avatar file is required") } const avatar = await uploadOnCloudinary(avatarLocalPath) const coverImage = await uploadOnCloudinary(coverImageLocalPath) if (!avatar) { throw new ApiError(400, "Avatar file is required") } const user = await User.create({ fullName, avatar: avatar.url, coverImage: coverImage?.url || "", email, password, username: username.toLowerCase() }) const createdUser = await User.findById(user._id).select( "-password -refreshToken" ) if (!createdUser) { throw new ApiError(500, "Something went wrong while registering the user") } return res.status(201).json( new ApiResponse(200, createdUser, "User registered Successfully") ) } ) const loginUser = asyncHandler(async (req, res) =>{ // req body -> data // username or email //find the user //password check //access and referesh token //send cookie const {email, username, password} = req.body console.log(email); if (!username && !email) { throw new ApiError(400, "username or email is required") } // Here is an alternative of above code based on logic discussed in video: // if (!(username || email)) { // throw new ApiError(400, "username or email is required") // } const user = await User.findOne({ $or: [{username}, {email}] }) if (!user) { throw new ApiError(404, "User does not exist") } const isPasswordValid = await user.isPasswordCorrect(password) if (!isPasswordValid) { throw new ApiError(401, "Invalid user credentials") } const {accessToken, refreshToken} = await generateAccessAndRefereshTokens(user._id) const loggedInUser = await User.findById(user._id).select("-password -refreshToken") const options = { httpOnly: true, secure: true } return res .status(200) .cookie("accessToken", accessToken, options) .cookie("refreshToken", refreshToken, options) .json( new ApiResponse( 200, { user: loggedInUser, accessToken, refreshToken }, "User logged In Successfully" ) ) }) const logoutUser = asyncHandler(async(req, res) => { await User.findByIdAndUpdate( req.user._id, { $unset: { refreshToken: 1 // this removes the field from document } }, { new: true } ) const options = { httpOnly: true, secure: true } return res .status(200) .clearCookie("accessToken", options) .clearCookie("refreshToken", options) .json(new ApiResponse(200, {}, "User logged Out")) }) const refreshAccessToken = asyncHandler(async (req, res) => { const incomingRefreshToken = req.cookies.refreshToken || req.body.refreshToken if (!incomingRefreshToken) { throw new ApiError(401, "unauthorized request") } try { const decodedToken = jwt.verify( incomingRefreshToken, process.env.REFRESH_TOKEN_SECRET ) const user = await User.findById(decodedToken?._id) if (!user) { throw new ApiError(401, "Invalid refresh token") } if (incomingRefreshToken !== user?.refreshToken) { throw new ApiError(401, "Refresh token is expired or used") } const options = { httpOnly: true, secure: true } const {accessToken, newRefreshToken} = await generateAccessAndRefereshTokens(user._id) return res .status(200) .cookie("accessToken", accessToken, options) .cookie("refreshToken", newRefreshToken, options) .json( new ApiResponse( 200, {accessToken, refreshToken: newRefreshToken}, "Access token refreshed" ) ) } catch (error) { throw new ApiError(401, error?.message || "Invalid refresh token") } }) const changeCurrentPassword = asyncHandler(async(req, res) => { const {oldPassword, newPassword} = req.body const user = await User.findById(req.user?._id) const isPasswordCorrect = await user.isPasswordCorrect(oldPassword) if (!isPasswordCorrect) { throw new ApiError(400, "Invalid old password") } user.password = newPassword await user.save({validateBeforeSave: false}) return res .status(200) .json(new ApiResponse(200, {}, "Password changed successfully")) }) const getCurrentUser = asyncHandler(async(req, res) => { return res .status(200) .json(new ApiResponse( 200, req.user, "User fetched successfully" )) }) const updateAccountDetails = asyncHandler(async(req, res) => { const {fullName, email} = req.body if (!fullName || !email) { throw new ApiError(400, "All fields are required") } const user = await User.findByIdAndUpdate( req.user?._id, { $set: { fullName, email: email } }, {new: true} ).select("-password") return res .status(200) .json(new ApiResponse(200, user, "Account details updated successfully")) }); const updateUserAvatar = asyncHandler(async(req, res) => { const avatarLocalPath = req.file?.path if (!avatarLocalPath) { throw new ApiError(400, "Avatar file is missing") } //TODO: delete old image - assignment const avatar = await uploadOnCloudinary(avatarLocalPath) if (!avatar.url) { throw new ApiError(400, "Error while uploading on avatar") } const user = await User.findByIdAndUpdate( req.user?._id, { $set:{ avatar: avatar.url } }, {new: true} ).select("-password") return res .status(200) .json( new ApiResponse(200, user, "Avatar image updated successfully") ) }) const updateUserCoverImage = asyncHandler(async(req, res) => { const coverImageLocalPath = req.file?.path if (!coverImageLocalPath) { throw new ApiError(400, "Cover image file is missing") } //TODO: delete old image - assignment const coverImage = await uploadOnCloudinary(coverImageLocalPath) if (!coverImage.url) { throw new ApiError(400, "Error while uploading on avatar") } const user = await User.findByIdAndUpdate( req.user?._id, { $set:{ coverImage: coverImage.url } }, {new: true} ).select("-password") return res .status(200) .json( new ApiResponse(200, user, "Cover image updated successfully") ) }) const getUserChannelProfile = asyncHandler(async(req, res) => { const {username} = req.params if (!username?.trim()) { throw new ApiError(400, "username is missing") } const channel = await User.aggregate([ { $match: { username: username?.toLowerCase() } }, { $lookup: { from: "subscriptions", localField: "_id", foreignField: "channel", as: "subscribers" } }, { $lookup: { from: "subscriptions", localField: "_id", foreignField: "subscriber", as: "subscribedTo" } }, { $addFields: { subscribersCount: { $size: "$subscribers" }, channelsSubscribedToCount: { $size: "$subscribedTo" }, isSubscribed: { $cond: { if: {$in: [req.user?._id, "$subscribers.subscriber"]}, then: true, else: false } } } }, { $project: { fullName: 1, username: 1, subscribersCount: 1, channelsSubscribedToCount: 1, isSubscribed: 1, avatar: 1, coverImage: 1, email: 1 } } ]) if (!channel?.length) { throw new ApiError(404, "channel does not exists") } return res .status(200) .json( new ApiResponse(200, channel[0], "User channel fetched successfully") ) }) const getWatchHistory = asyncHandler(async(req, res) => { const user = await User.aggregate([ { $match: { _id: new mongoose.Types.ObjectId(req.user._id) } }, { $lookup: { from: "videos", localField: "watchHistory", foreignField: "_id", as: "watchHistory", pipeline: [ { $lookup: { from: "users", localField: "owner", foreignField: "_id", as: "owner", pipeline: [ { $project: { fullName: 1, username: 1, avatar: 1 } } ] } }, { $addFields:{ owner:{ $first: "$owner" } } } ] } } ]) return res .status(200) .json( new ApiResponse( 200, user[0].watchHistory, "Watch history fetched successfully" ) ) }) export { registerUser, loginUser, logoutUser, refreshAccessToken, changeCurrentPassword, getCurrentUser, updateAccountDetails, updateUserAvatar, updateUserCoverImage, getUserChannelProfile, getWatchHistory } ================================================ FILE: src/controllers/video.controller.js ================================================ import mongoose, {isValidObjectId} from "mongoose" import {Video} from "../models/video.model.js" import {User} from "../models/user.model.js" import {ApiError} from "../utils/ApiError.js" import {ApiResponse} from "../utils/ApiResponse.js" import {asyncHandler} from "../utils/asyncHandler.js" import {uploadOnCloudinary} from "../utils/cloudinary.js" const getAllVideos = asyncHandler(async (req, res) => { const { page = 1, limit = 10, query, sortBy, sortType, userId } = req.query //TODO: get all videos based on query, sort, pagination }) const publishAVideo = asyncHandler(async (req, res) => { const { title, description} = req.body // TODO: get video, upload to cloudinary, create video }) const getVideoById = asyncHandler(async (req, res) => { const { videoId } = req.params //TODO: get video by id }) const updateVideo = asyncHandler(async (req, res) => { const { videoId } = req.params //TODO: update video details like title, description, thumbnail }) const deleteVideo = asyncHandler(async (req, res) => { const { videoId } = req.params //TODO: delete video }) const togglePublishStatus = asyncHandler(async (req, res) => { const { videoId } = req.params }) export { getAllVideos, publishAVideo, getVideoById, updateVideo, deleteVideo, togglePublishStatus } ================================================ FILE: src/db/index.js ================================================ import mongoose from "mongoose"; import { DB_NAME } from "../constants.js"; const connectDB = async () => { try { const connectionInstance = await mongoose.connect(`${process.env.MONGODB_URI}/${DB_NAME}`) console.log(`\n MongoDB connected !! DB HOST: ${connectionInstance.connection.host}`); } catch (error) { console.log("MONGODB connection FAILED ", error); process.exit(1) } } export default connectDB ================================================ FILE: src/index.js ================================================ // require('dotenv').config({path: './env'}) import dotenv from "dotenv" import connectDB from "./db/index.js"; import {app} from './app.js' dotenv.config({ path: './.env' }) connectDB() .then(() => { app.listen(process.env.PORT || 8000, () => { console.log(`⚙️ Server is running at port : ${process.env.PORT}`); }) }) .catch((err) => { console.log("MONGO db connection failed !!! ", err); }) /* import express from "express" const app = express() ( async () => { try { await mongoose.connect(`${process.env.MONGODB_URI}/${DB_NAME}`) app.on("errror", (error) => { console.log("ERRR: ", error); throw error }) app.listen(process.env.PORT, () => { console.log(`App is listening on port ${process.env.PORT}`); }) } catch (error) { console.error("ERROR: ", error) throw err } })() */ ================================================ FILE: src/middlewares/auth.middleware.js ================================================ import { ApiError } from "../utils/ApiError.js"; import { asyncHandler } from "../utils/asyncHandler.js"; import jwt from "jsonwebtoken" import { User } from "../models/user.model.js"; export const verifyJWT = asyncHandler(async(req, _, next) => { try { const token = req.cookies?.accessToken || req.header("Authorization")?.replace("Bearer ", "") // console.log(token); if (!token) { throw new ApiError(401, "Unauthorized request") } const decodedToken = jwt.verify(token, process.env.ACCESS_TOKEN_SECRET) const user = await User.findById(decodedToken?._id).select("-password -refreshToken") if (!user) { throw new ApiError(401, "Invalid Access Token") } req.user = user; next() } catch (error) { throw new ApiError(401, error?.message || "Invalid access token") } }) ================================================ FILE: src/middlewares/multer.middleware.js ================================================ import multer from "multer"; const storage = multer.diskStorage({ destination: function (req, file, cb) { cb(null, "./public/temp") }, filename: function (req, file, cb) { cb(null, file.originalname) } }) export const upload = multer({ storage, }) ================================================ FILE: src/models/comment.model.js ================================================ import mongoose, {Schema} from "mongoose"; import mongooseAggregatePaginate from "mongoose-aggregate-paginate-v2"; const commentSchema = new Schema( { content: { type: String, required: true }, video: { type: Schema.Types.ObjectId, ref: "Video" }, owner: { type: Schema.Types.ObjectId, ref: "User" } }, { timestamps: true } ) commentSchema.plugin(mongooseAggregatePaginate) export const Comment = mongoose.model("Comment", commentSchema) ================================================ FILE: src/models/like.model.js ================================================ import mongoose, {Schema} from "mongoose"; const likeSchema = new Schema({ video: { type: Schema.Types.ObjectId, ref: "Video" }, comment: { type: Schema.Types.ObjectId, ref: "Comment" }, tweet: { type: Schema.Types.ObjectId, ref: "Tweet" }, likedBy: { type: Schema.Types.ObjectId, ref: "User" }, }, {timestamps: true}) export const Like = mongoose.model("Like", likeSchema) ================================================ FILE: src/models/playlist.model.js ================================================ import mongoose, {Schema} from "mongoose"; const playlistSchema = new Schema({ name: { type: String, required: true }, description: { type: String, required: true }, videos: [ { type: Schema.Types.ObjectId, ref: "Video" } ], owner: { type: Schema.Types.ObjectId, ref: "User" }, }, {timestamps: true}) export const Playlist = mongoose.model("Playlist", playlistSchema) ================================================ FILE: src/models/subscription.model.js ================================================ import mongoose, {Schema} from "mongoose" const subscriptionSchema = new Schema({ subscriber: { type: Schema.Types.ObjectId, // one who is subscribing ref: "User" }, channel: { type: Schema.Types.ObjectId, // one to whom 'subscriber' is subscribing ref: "User" } }, {timestamps: true}) export const Subscription = mongoose.model("Subscription", subscriptionSchema) ================================================ FILE: src/models/tweet.model.js ================================================ import mongoose, {Schema} from "mongoose"; const tweetSchema = new Schema({ content: { type: String, required: true }, owner: { type: Schema.Types.ObjectId, ref: "User" } }, {timestamps: true}) export const Tweet = mongoose.model("Tweet", tweetSchema) ================================================ FILE: src/models/user.model.js ================================================ import mongoose, {Schema} from "mongoose"; import jwt from "jsonwebtoken" import bcrypt from "bcrypt" const userSchema = new Schema( { username: { type: String, required: true, unique: true, lowercase: true, trim: true, index: true }, email: { type: String, required: true, unique: true, lowecase: true, trim: true, }, fullName: { type: String, required: true, trim: true, index: true }, avatar: { type: String, // cloudinary url required: true, }, coverImage: { type: String, // cloudinary url }, watchHistory: [ { type: Schema.Types.ObjectId, ref: "Video" } ], password: { type: String, required: [true, 'Password is required'] }, refreshToken: { type: String } }, { timestamps: true } ) userSchema.pre("save", async function (next) { if(!this.isModified("password")) return next(); this.password = await bcrypt.hash(this.password, 10) next() }) userSchema.methods.isPasswordCorrect = async function(password){ return await bcrypt.compare(password, this.password) } userSchema.methods.generateAccessToken = function(){ return jwt.sign( { _id: this._id, email: this.email, username: this.username, fullName: this.fullName }, process.env.ACCESS_TOKEN_SECRET, { expiresIn: process.env.ACCESS_TOKEN_EXPIRY } ) } userSchema.methods.generateRefreshToken = function(){ return jwt.sign( { _id: this._id, }, process.env.REFRESH_TOKEN_SECRET, { expiresIn: process.env.REFRESH_TOKEN_EXPIRY } ) } export const User = mongoose.model("User", userSchema) ================================================ FILE: src/models/video.model.js ================================================ import mongoose, {Schema} from "mongoose"; import mongooseAggregatePaginate from "mongoose-aggregate-paginate-v2"; const videoSchema = new Schema( { videoFile: { type: String, //cloudinary url required: true }, thumbnail: { type: String, //cloudinary url required: true }, title: { type: String, required: true }, description: { type: String, required: true }, duration: { type: Number, required: true }, views: { type: Number, default: 0 }, isPublished: { type: Boolean, default: true }, owner: { type: Schema.Types.ObjectId, ref: "User" } }, { timestamps: true } ) videoSchema.plugin(mongooseAggregatePaginate) export const Video = mongoose.model("Video", videoSchema) ================================================ FILE: src/routes/comment.routes.js ================================================ import { Router } from 'express'; import { addComment, deleteComment, getVideoComments, updateComment, } from "../controllers/comment.controller.js" import {verifyJWT} from "../middlewares/auth.middleware.js" const router = Router(); router.use(verifyJWT); // Apply verifyJWT middleware to all routes in this file router.route("/:videoId").get(getVideoComments).post(addComment); router.route("/c/:commentId").delete(deleteComment).patch(updateComment); export default router ================================================ FILE: src/routes/dashboard.routes.js ================================================ import { Router } from 'express'; import { getChannelStats, getChannelVideos, } from "../controllers/dashboard.controller.js" import {verifyJWT} from "../middlewares/auth.middleware.js" const router = Router(); router.use(verifyJWT); // Apply verifyJWT middleware to all routes in this file router.route("/stats").get(getChannelStats); router.route("/videos").get(getChannelVideos); export default router ================================================ FILE: src/routes/healthcheck.routes.js ================================================ import { Router } from 'express'; import { healthcheck } from "../controllers/healthcheck.controller.js" const router = Router(); router.route('/').get(healthcheck); export default router ================================================ FILE: src/routes/like.routes.js ================================================ import { Router } from 'express'; import { getLikedVideos, toggleCommentLike, toggleVideoLike, toggleTweetLike, } from "../controllers/like.controller.js" import {verifyJWT} from "../middlewares/auth.middleware.js" const router = Router(); router.use(verifyJWT); // Apply verifyJWT middleware to all routes in this file router.route("/toggle/v/:videoId").post(toggleVideoLike); router.route("/toggle/c/:commentId").post(toggleCommentLike); router.route("/toggle/t/:tweetId").post(toggleTweetLike); router.route("/videos").get(getLikedVideos); export default router ================================================ FILE: src/routes/playlist.routes.js ================================================ import { Router } from 'express'; import { addVideoToPlaylist, createPlaylist, deletePlaylist, getPlaylistById, getUserPlaylists, removeVideoFromPlaylist, updatePlaylist, } from "../controllers/playlist.controller.js" import {verifyJWT} from "../middlewares/auth.middleware.js" const router = Router(); router.use(verifyJWT); // Apply verifyJWT middleware to all routes in this file router.route("/").post(createPlaylist) router .route("/:playlistId") .get(getPlaylistById) .patch(updatePlaylist) .delete(deletePlaylist); router.route("/add/:videoId/:playlistId").patch(addVideoToPlaylist); router.route("/remove/:videoId/:playlistId").patch(removeVideoFromPlaylist); router.route("/user/:userId").get(getUserPlaylists); export default router ================================================ FILE: src/routes/subscription.routes.js ================================================ import { Router } from 'express'; import { getSubscribedChannels, getUserChannelSubscribers, toggleSubscription, } from "../controllers/subscription.controller.js" import {verifyJWT} from "../middlewares/auth.middleware.js" const router = Router(); router.use(verifyJWT); // Apply verifyJWT middleware to all routes in this file router .route("/c/:channelId") .get(getSubscribedChannels) .post(toggleSubscription); router.route("/u/:subscriberId").get(getUserChannelSubscribers); export default router ================================================ FILE: src/routes/tweet.routes.js ================================================ import { Router } from 'express'; import { createTweet, deleteTweet, getUserTweets, updateTweet, } from "../controllers/tweet.controller.js" import {verifyJWT} from "../middlewares/auth.middleware.js" const router = Router(); router.use(verifyJWT); // Apply verifyJWT middleware to all routes in this file router.route("/").post(createTweet); router.route("/user/:userId").get(getUserTweets); router.route("/:tweetId").patch(updateTweet).delete(deleteTweet); export default router ================================================ FILE: src/routes/user.routes.js ================================================ import { Router } from "express"; import { loginUser, logoutUser, registerUser, refreshAccessToken, changeCurrentPassword, getCurrentUser, updateUserAvatar, updateUserCoverImage, getUserChannelProfile, getWatchHistory, updateAccountDetails } from "../controllers/user.controller.js"; import {upload} from "../middlewares/multer.middleware.js" import { verifyJWT } from "../middlewares/auth.middleware.js"; const router = Router() router.route("/register").post( upload.fields([ { name: "avatar", maxCount: 1 }, { name: "coverImage", maxCount: 1 } ]), registerUser ) router.route("/login").post(loginUser) //secured routes router.route("/logout").post(verifyJWT, logoutUser) router.route("/refresh-token").post(refreshAccessToken) router.route("/change-password").post(verifyJWT, changeCurrentPassword) router.route("/current-user").get(verifyJWT, getCurrentUser) router.route("/update-account").patch(verifyJWT, updateAccountDetails) router.route("/avatar").patch(verifyJWT, upload.single("avatar"), updateUserAvatar) router.route("/cover-image").patch(verifyJWT, upload.single("coverImage"), updateUserCoverImage) router.route("/c/:username").get(verifyJWT, getUserChannelProfile) router.route("/history").get(verifyJWT, getWatchHistory) export default router ================================================ FILE: src/routes/video.routes.js ================================================ import { Router } from 'express'; import { deleteVideo, getAllVideos, getVideoById, publishAVideo, togglePublishStatus, updateVideo, } from "../controllers/video.controller.js" import {verifyJWT} from "../middlewares/auth.middleware.js" import {upload} from "../middlewares/multer.middleware.js" const router = Router(); router.use(verifyJWT); // Apply verifyJWT middleware to all routes in this file router .route("/") .get(getAllVideos) .post( upload.fields([ { name: "videoFile", maxCount: 1, }, { name: "thumbnail", maxCount: 1, }, ]), publishAVideo ); router .route("/:videoId") .get(getVideoById) .delete(deleteVideo) .patch(upload.single("thumbnail"), updateVideo); router.route("/toggle/publish/:videoId").patch(togglePublishStatus); export default router ================================================ FILE: src/utils/ApiError.js ================================================ class ApiError extends Error { constructor( statusCode, message= "Something went wrong", errors = [], stack = "" ){ super(message) this.statusCode = statusCode this.data = null this.message = message this.success = false; this.errors = errors if (stack) { this.stack = stack } else{ Error.captureStackTrace(this, this.constructor) } } } export {ApiError} ================================================ FILE: src/utils/ApiResponse.js ================================================ class ApiResponse { constructor(statusCode, data, message = "Success"){ this.statusCode = statusCode this.data = data this.message = message this.success = statusCode < 400 } } export { ApiResponse } ================================================ FILE: src/utils/asyncHandler.js ================================================ const asyncHandler = (requestHandler) => { return (req, res, next) => { Promise.resolve(requestHandler(req, res, next)).catch((err) => next(err)) } } export { asyncHandler } // const asyncHandler = () => {} // const asyncHandler = (func) => () => {} // const asyncHandler = (func) => async () => {} // const asyncHandler = (fn) => async (req, res, next) => { // try { // await fn(req, res, next) // } catch (error) { // res.status(err.code || 500).json({ // success: false, // message: err.message // }) // } // } ================================================ FILE: src/utils/cloudinary.js ================================================ import {v2 as cloudinary} from "cloudinary" import fs from "fs" cloudinary.config({ cloud_name: process.env.CLOUDINARY_CLOUD_NAME, api_key: process.env.CLOUDINARY_API_KEY, api_secret: process.env.CLOUDINARY_API_SECRET }); const uploadOnCloudinary = async (localFilePath) => { try { if (!localFilePath) return null //upload the file on cloudinary const response = await cloudinary.uploader.upload(localFilePath, { resource_type: "auto" }) // file has been uploaded successfull //console.log("file is uploaded on cloudinary ", response.url); fs.unlinkSync(localFilePath) return response; } catch (error) { fs.unlinkSync(localFilePath) // remove the locally saved temporary file as the upload operation got failed return null; } } export {uploadOnCloudinary}