diff --git a/.dockerignore b/.dockerignore
new file mode 100644
index 0000000..b512c09
--- /dev/null
+++ b/.dockerignore
@@ -0,0 +1 @@
+node_modules
\ No newline at end of file
diff --git a/Dockerfile b/Dockerfile
new file mode 100644
index 0000000..c9f697d
--- /dev/null
+++ b/Dockerfile
@@ -0,0 +1,13 @@
+FROM node:20-alpine
+# Set the working directory in the container
+WORKDIR /app
+# Copy package.json and package-lock.json to the working directory
+COPY package*.json ./
+# Install dependencies
+RUN npm install
+# Copy the rest of the application code to the working directory
+COPY . .
+# Build the application
+EXPOSE 5000
+# Set the environment variable for production
+CMD [ "npm","start" ]
\ No newline at end of file
diff --git a/config/accont_verification_templete.js b/config/accont_verification_templete.js
new file mode 100644
index 0000000..d43f967
--- /dev/null
+++ b/config/accont_verification_templete.js
@@ -0,0 +1,49 @@
+export const ACCOUNT_VERIFICATION_HTML_TEMPLETE = `
+
+
+
+
+ Email Verification
+
+
+
+
+
Email Verification
+
Use the verification code below to confirm your email address.
+
{{otp}}
+
If you did not request this, please ignore this email.
+
+
+
+`;
diff --git a/config/aws/awsconfig.js b/config/aws/awsconfig.js
new file mode 100644
index 0000000..95f83d6
--- /dev/null
+++ b/config/aws/awsconfig.js
@@ -0,0 +1,11 @@
+import AWS from "aws-sdk";
+
+//configure aws
+AWS.config.update({
+ accessKeyId: process.env.AWS_ACCESS_KEY_ID,
+ secretAccessKey: process.env.AWS_SECRET_ACCESS_KEY,
+ region: process.env.AWS_REGION,
+});
+const s3 = new AWS.S3();
+
+export default s3;
\ No newline at end of file
diff --git a/db/database.js b/config/db/database.js
similarity index 100%
rename from db/database.js
rename to config/db/database.js
diff --git a/config/nodemailer.js b/config/nodemailer.js
new file mode 100644
index 0000000..89d9120
--- /dev/null
+++ b/config/nodemailer.js
@@ -0,0 +1,16 @@
+import dotenv from "dotenv";
+import nodemailer from "nodemailer";
+
+dotenv.config();
+//smtp email transport model
+const transporter = nodemailer.createTransport({
+ host: "smtp-relay.brevo.com",
+ port: process.env.SMTP_PORT,
+ secure: false,
+ auth: {
+ user: process.env.SMTP_USER,
+ pass: process.env.SNTP_PASSWORD,
+ },
+});
+
+export default transporter;
diff --git a/config/reset_password_templete.js b/config/reset_password_templete.js
new file mode 100644
index 0000000..e28fbb0
--- /dev/null
+++ b/config/reset_password_templete.js
@@ -0,0 +1,52 @@
+export const PASSWORD_RESET_TEMPLATE = `
+
+
+
+
+
+ Password Reset OTP
+
+
+
+
+
Password Reset Request
+
Use the OTP below to reset your password.
+
{{otp}}
+
If you did not request this, please ignore this email.
+
+
+
+
+
+`;
diff --git a/controller/resourcecontroller.js b/controller/resourcecontroller.js
new file mode 100644
index 0000000..499b665
--- /dev/null
+++ b/controller/resourcecontroller.js
@@ -0,0 +1,181 @@
+import dotenv from "dotenv";
+import s3 from "../config/aws/awsconfig.js";
+import video from "../models/video.model.js";
+
+dotenv.config();
+//s3 bucket
+const bucketName = process.env.AWS_BUCKET_NAME;
+
+//cloudfront url
+const cloudFront = process.env.CLOUDFRONT_URL;
+//get pre singed url
+export const getPreSingedUrl = async (req, res) => {
+ const { userId, fileType, filename } = req.body;
+ if (!userId || !fileType || !filename) {
+ return res.status(400).json({ success: false, message: "empty parameter" });
+ }
+ try {
+ const key = `reels/${userId}/${filename}`;
+ const params = {
+ Bucket: `${bucketName}`,
+ Key: key,
+ ContentType: fileType,
+ };
+
+ const uploadUrl = s3.getSignedUrl("putObject", params);
+
+ if (!uploadUrl) {
+ return res
+ .status(400)
+ .json({ success: false, message: "cannot get url" });
+ }
+ return res.status(200).json({
+ success: true,
+ data: {
+ uploadUrl: uploadUrl,
+ videokey: key,
+ },
+ });
+ } catch (err) {
+ return res.status(500).json({ success: false, message: `${err}` });
+ }
+};
+//save metadata
+export const saveMetaData = async (req, res) => {
+ const { userId, videoKey, title } = req.body;
+ if (!userId || !videoKey || !title) {
+ return res.status(400).json({ success: false, message: "empty parameter" });
+ }
+ try {
+ const newVideo = new video({
+ userId,
+ title,
+ url: videoKey,
+ });
+ const createdVideo = await newVideo.save();
+ return res.status(200).json({
+ success: true,
+ data: {
+ id: createdVideo._id,
+ userId: createdVideo.userId,
+ videokey: createdVideo.url,
+ },
+ });
+ } catch (err) {
+ return res.status(500).json({ success: false, message: `${err}` });
+ }
+};
+//fetch all video data using cloudfront url
+export const fetchVideoData = async (req, res) => {
+ const page = parseInt(req.query.page) || 1;
+ const limit = parseInt(req.query.limit) || 10;
+
+ const skip = (page - 1) * limit;
+ try {
+ const videos = await video
+ .find({})
+ .sort({ createdAt: -1 })
+ .limit(limit)
+ .skip(skip);
+
+ if (!videos) {
+ return res
+ .status(404)
+ .json({ success: false, message: "data not found" });
+ }
+ const allVideos = videos.map((video) => ({
+ reelId: video._id,
+ userId: video.userId,
+ title: video.title,
+ url: `${cloudFront}/${video.url}`,
+ joinedDate: video.createdAt,
+ updatedDate: video.updatedAt,
+ processed: video.processed,
+ weblink: video.weblink,
+ likes: video.likes,
+ disLikes: video.disLikes,
+ comments: video.comments,
+ tags: video.tags,
+ }));
+
+ return res.status(200).json({ success: true, data: { videos: allVideos } });
+ } catch (err) {
+ return res.status(500).json({ success: false, message: `${err}` });
+ }
+};
+//like reels
+export const likeReels = async (req, res) => {
+ const { userId, reelId } = req.body;
+
+ if (!userId || !reelId) {
+ return res
+ .status(400)
+ .json({ success: false, message: "invalid credientials" });
+ }
+
+ try {
+ const reel = await video.findById(reelId);
+ if (!reel) {
+ return res
+ .status(404)
+ .json({ success: false, message: "reel not found" });
+ }
+ //check if like or not
+ const isLiked = reel.likes.includes(userId);
+ if (isLiked) {
+ //remove userid if already liked
+ reel.likes = reel.likes.filter((id) => id !== userId);
+ } else {
+ //add new like
+ reel.likes.push(userId);
+ }
+
+ const updatedReel = await reel.save();``
+ return res.status(200).json({
+ success: true,
+ data: {
+ reel: updatedReel,
+ },
+ });
+ } catch (err) {
+ return res.status(500).json({ success: false, message: `${err}` });
+ }
+};
+//dislike reels
+export const dislikeReels = async (req, res) => {
+ const { userId, reelId } = req.body;
+
+ if (!userId || !reelId) {
+ return res
+ .status(400)
+ .json({ success: false, message: "invalid credientials" });
+ }
+
+ try {
+ const reel = await video.findById(reelId);
+ if (!reel) {
+ return res
+ .status(404)
+ .json({ success: false, message: "reel not found" });
+ }
+ //check if like or not
+ const isdisLiked = reel.disLikes.includes(userId);
+ if (isdisLiked) {
+ //remove userid if already liked
+ reel.disLikes = reel.disLikes.filter((id) => id !== userId);
+ } else {
+ //add new like
+ reel.disLikes.push(userId);
+ }
+
+ const updatedReel = await reel.save();
+ return res.status(200).json({
+ success: true,
+ data: {
+ reel: updatedReel,
+ },
+ });
+ } catch (err) {
+ return res.status(500).json({ success: false, message: `${err}` });
+ }
+};
diff --git a/controller/userauthcontroller.js b/controller/userauthcontroller.js
new file mode 100644
index 0000000..129cb15
--- /dev/null
+++ b/controller/userauthcontroller.js
@@ -0,0 +1,281 @@
+import bcrypt from "bcryptjs";
+import dotenv from "dotenv";
+import jwt from "jsonwebtoken";
+import { ACCOUNT_VERIFICATION_HTML_TEMPLETE } from "../config/accont_verification_templete.js";
+import { PASSWORD_RESET_TEMPLATE } from "../config/reset_password_templete.js";
+
+import transporter from "../config/nodemailer.js";
+import User from "../models/user.model.js";
+dotenv.config();
+//register new user
+export const register = async (req, res) => {
+ const { username, email, password } = req.body;
+ const profileUrl = req.file ? req.file.buffer.toString("base64") : "";
+
+ if (!username || !email || !password) {
+ return res.status(400).json({ succss: false, massage: "missing details" });
+ }
+ // const profilePic = req.file?.filename || "";
+ try {
+ const isexistUser = await User.findOne({ email });
+ if (isexistUser) {
+ return res
+ .status(400)
+ .json({ succss: false, massage: "user already exist" });
+ }
+ //hash password
+ const salt = await bcrypt.genSalt(10);
+ const hasedPassword = await bcrypt.hash(password, salt);
+ //verify code
+ const verifyCode = Math.floor(10000 + Math.random() * 90000).toString();
+ const codeExpireTime = Date.now() + 24 * 60 * 60 * 1000;
+
+ //save user
+ const newUser = new User({
+ username,
+ email,
+ password: hasedPassword,
+ profileUrl,
+ verifyCode,
+ codeExpireTime,
+ });
+
+ const createdUser = await newUser.save();
+ //gen session tokens
+
+ const newToken = await jwt.sign(
+ { id: createdUser._id },
+ process.env.JWT_SECRET_KEY,
+ { expiresIn: "7d" }
+ );
+ //send verification code
+ const mailReciver = {
+ from: process.env.APP_EMAIL,
+ to: createdUser.email,
+ subject: "Account Verification OTP",
+ html: ACCOUNT_VERIFICATION_HTML_TEMPLETE.replace("{{otp}}", verifyCode),
+ };
+ //send email
+ await transporter.sendMail(mailReciver);
+ return res.status(200).json({
+ succss: true,
+ newToken,
+ user: {
+ id: createdUser._id,
+ },
+ });
+ } catch (err) {
+ return res
+ .status(500)
+ .json({ succss: false, massage: `Internal server error` });
+ }
+};
+//verify account
+export const verifyAccount = async (req, res) => {
+ const { userId, verifyCode } = req.body;
+
+ if (!userId || !verifyCode) {
+ return res.status(400).json({ success: false, massage: "Missing Details" });
+ }
+
+ try {
+ const user = await User.findById(userId);
+ //when user not exist
+ if (!user) {
+ return res
+ .status(400)
+ .json({ success: false, massage: "User not found" });
+ }
+ //when invalid otp
+ if (user.verifyCode == "" || user.verifyCode != verifyCode) {
+ return res.status(400).json({ success: false, massage: "Invalid OTP" });
+ }
+ //if otp expired
+ if (user.codeExpireTime < Date.now()) {
+ return res
+ .status(408)
+ .json({ success: false, massage: "Request time out" });
+ }
+
+ //when all set
+ user.isVerified = true;
+ user.verifyCode = "";
+ user.codeExpireTime = 0;
+ //save updated user
+
+ await user.save();
+ //welcome email
+ const mailReciver = {
+ from: process.env.APP_EMAIL,
+ to: user.email,
+ subject: "Welcome To DATE NET.",
+ text: `Welcome ${user.username} to Date Net forum.Your account has been verified succsussfuly.stay with us and explor more content`,
+ };
+ //send email
+ await transporter.sendMail(mailReciver);
+ return res.status(200).json({
+ success: true,
+ user: {
+ id: user._id,
+ username: user.username,
+ email: user.email,
+ profileUrl: user.profileUrl,
+ },
+ });
+ } catch (err) {
+ return res
+ .status(500)
+ .json({ success: false, massage: "Internal server error" });
+ }
+};
+//user login
+export const login = async (req, res) => {
+ const { username, password } = req.body;
+
+ if (!username || !password) {
+ return res
+ .status(400)
+ .json({ success: false, massage: "enter all details" });
+ }
+ try {
+ const existUser = await User.findOne({ username });
+ if (!existUser) {
+ return res
+ .status(400)
+ .json({ success: false, massage: " user not found" });
+ }
+ //check password validation
+ const isValidPassword = await bcrypt.compare(password, existUser.password);
+ if (!isValidPassword) {
+ return res
+ .status(400)
+ .json({ success: false, massage: "invalid password" });
+ }
+ //gen new token
+ const newToken = await jwt.sign(
+ { id: existUser._id },
+ process.env.JWT_SECRET_KEY,
+ { expiresIn: "7d" }
+ );
+ //response-succsuss
+ return res.status(200).json({
+ success: true,
+ newToken,
+ user: {
+ id: existUser._id,
+ username: existUser.username,
+ email: existUser.email,
+ profileUrl: existUser.profileUrl,
+ },
+ });
+ } catch (err) {
+ return res
+ .status(500)
+ .json({ success: false, massage: "internal server error" });
+ }
+};
+
+export const resetPasswordVerification = async (req, res) => {
+ const { email } = req.body;
+
+ if (!email) {
+ return res
+ .status(400)
+ .json({ success: false, massage: "enter all details" });
+ }
+
+ try {
+ const existUser = await User.findOne({ email });
+ if (!existUser) {
+ return res
+ .status(404)
+ .json({ success: false, massage: "User not found" });
+ }
+
+ //verify code
+ const verifyCode = Math.floor(10000 + Math.random() * 90000).toString();
+ const codeExpireTime = Date.now() + 24 * 60 * 60 * 1000;
+
+ existUser.verifyCode = verifyCode;
+ existUser.codeExpireTime = codeExpireTime;
+ // existUser.password = password;
+ //save with verificaton data
+ await existUser.save();
+ //send verification code
+ const mailReciver = {
+ from: process.env.APP_EMAIL,
+ to: existUser.email,
+ subject: "password reset OTP",
+ html: PASSWORD_RESET_TEMPLATE.replace("{{otp}}", verifyCode),
+ };
+ //send email
+ await transporter.sendMail(mailReciver);
+ return res.status(200).json({
+ success: true,
+ user: {
+ id: email,
+ },
+ });
+ } catch (err) {
+ return res
+ .status(500)
+ .json({ success: true, massage: "internal server error" });
+ }
+};
+
+export const resetPassword = async (req, res) => {
+ const { email, otp, password } = req.body;
+
+ if (!email || !otp || !password) {
+ return res
+ .status(400)
+ .json({ success: false, massage: "enter all details" });
+ }
+
+ try {
+ const existUser = await User.findOne({ email });
+ if (!existUser) {
+ return res
+ .status(404)
+ .json({ success: false, massage: "User not found" });
+ }
+
+ if (otp == "" || otp != existUser.verifyCode) {
+ return res.status(400).json({ success: false, massage: "Invalid OTP" });
+ }
+
+ if (existUser.codeExpireTime < Date.now()) {
+ return res
+ .status(408)
+ .json({ success: false, massage: "Request Time out" });
+ }
+ //hash password
+ const salt = await bcrypt.genSalt(10);
+ const hasedPassword = await bcrypt.hash(password, salt);
+ //when all set
+ existUser.isVerified = true;
+ existUser.verifyCode = "";
+ existUser.codeExpireTime = 0;
+ existUser.password = hasedPassword;
+ //save updated user
+
+ await existUser.save();
+ //welcome email
+ const mailReciver = {
+ from: process.env.APP_EMAIL,
+ to: existUser.email,
+ subject: "Password Reset Succsussfuly",
+ text: `${existUser.username} Your password is reset succsussfuly.Log in to your account using new creadientials`,
+ };
+ //send email
+ await transporter.sendMail(mailReciver);
+ return res.status(200).json({
+ success: true,
+ massage: "password reset succssfulty",
+ });
+ } catch (err) {
+ return res
+ .status(500)
+ .json({ success: false, massage: "internal server error" });
+ }
+};
diff --git a/controller/usercontroller.js b/controller/usercontroller.js
new file mode 100644
index 0000000..db7c2ca
--- /dev/null
+++ b/controller/usercontroller.js
@@ -0,0 +1,125 @@
+import User from "../models/user.model.js";
+//get current user details
+export const getCurrentUser = async (req, res) => {
+ const id = req.user.id;
+
+ if (!id) {
+ return res
+ .status(401)
+ .json({ success: false, massage: "Invalid infomation" });
+ }
+ try {
+ const existUser = await User.findById(id);
+ if (!existUser) {
+ return res.status(404).json({ succss: false, massage: "user not found" });
+ }
+ return res.status(200).json({
+ success: true,
+ massage: "data fetched succsussfuly",
+ user: existUser,
+ });
+ } catch (err) {
+ return res
+ .status(500)
+ .json({ success: false, massage: "Internal server error" });
+ }
+};
+//get all user info : will update to all workers
+export const getAllUser = async (req, res) => {
+ try {
+ const allUsers = await User.find({});
+
+ return res.status(200).json({
+ success: true,
+ massage: "data fetched succsussfuly",
+ users: allUsers,
+ });
+ } catch (err) {
+ console.log(`error from get all user ${err}`);
+ return res
+ .status(500)
+ .json({ success: false, massage: "Internal server error" });
+ }
+};
+//get user/s by username
+export const getUserByUserName = async (req, res) => {
+ const { username } = req.params;
+ if (!username) {
+ return res.status(400).json({ success: false, massage: "empty parameter" });
+ }
+ try {
+ // Create a case-insensitive regex pattern to find similar usernames
+ const regex = new RegExp(username, "i");
+ //get similer users by alphbatically sort and limited to 10
+ const user = await User.find({ username: { $regex: regex } })
+ .sort({ username: 1 })
+ .limit(10);
+ if (user === 0) {
+ return res
+ .status(404)
+ .json({ success: false, massage: "User not found" });
+ }
+ return res.status(200).json({
+ success: true,
+ massage: "data fetched succsussfuly",
+ users: user,
+ });
+ } catch (err) {
+ return res
+ .status(500)
+ .json({ success: false, massage: "Internal server error" });
+ }
+};
+//follow and unfollow user
+export const followOrUnfollowUser = async (req, res) => {
+ const guestid = req.params.guestid;
+ const userid = req.user.id;
+
+ if (!userid || !guestid) {
+ return res.status(400).json({ success: false, massage: "empty parameter" });
+ }
+ try {
+ const guestUser = await User.findById(guestid);
+ const currentUser = await User.findById(userid);
+ if (!guestUser || !currentUser) {
+ return res
+ .status(404)
+ .json({ success: false, massage: "user not found" });
+ }
+ //check already following
+ if (guestUser.followers.includes(userid)) {
+ //unfollow user
+ //remove followres
+ guestUser.followers = guestUser.followers.filter((id) => id !== userid);
+
+ await guestUser.save();
+ //remove followings
+ currentUser.following = currentUser.following.filter(
+ (id) => id !== guestid
+ );
+
+ await currentUser.save();
+
+ return res.status(200).json({
+ success: true,
+ massage: "user unfollow",
+ users: guestUser,
+ });
+ }
+ //follow user
+ guestUser.followers.push(userid);
+ await guestUser.save();
+
+ currentUser.following.push(guestid);
+ await currentUser.save();
+ return res.status(200).json({
+ success: true,
+ massage: "user follow",
+ users: guestUser,
+ });
+ } catch (err) {
+ return res
+ .status(500)
+ .json({ success: false, massage: "Internal server error" });
+ }
+};
diff --git a/demo.js b/demo.js
new file mode 100644
index 0000000..80ee5f1
--- /dev/null
+++ b/demo.js
@@ -0,0 +1,240 @@
+// // server.js (Express.js with MongoDB and AWS S3)
+// const express = require('express');
+// const mongoose = require('mongoose');
+// const cors = require('cors');
+// const AWS = require('aws-sdk');
+// const { v4: uuidv4 } = require('uuid');
+// const ffmpeg = require('fluent-ffmpeg');
+// const fs = require('fs');
+// const path = require('path');
+
+// const app = express();
+// app.use(express.json());
+// app.use(cors());
+
+// // MongoDB Connection (can be Atlas for even more scalability)
+// const mongoURI = 'mongodb://localhost:27017/videoApp';
+// mongoose.connect(mongoURI);
+
+// // Configure AWS
+// AWS.config.update({
+// accessKeyId: process.env.AWS_ACCESS_KEY_ID,
+// secretAccessKey: process.env.AWS_SECRET_ACCESS_KEY,
+// region: process.env.AWS_REGION || 'us-east-1'
+// });
+
+// const s3 = new AWS.S3();
+// const cloudfront = new AWS.CloudFront();
+
+// // S3 bucket name
+// const BUCKET_NAME = process.env.S3_BUCKET_NAME || 'your-video-bucket';
+// // CloudFront distribution domain
+// const CLOUDFRONT_URL = process.env.CLOUDFRONT_URL || 'https://your-distribution.cloudfront.net';
+
+// // Video Schema
+// const videoSchema = new mongoose.Schema({
+// userId: { type: String, required: true },
+// videoKey: { type: String, required: true }, // S3 key
+// thumbnailKey: { type: String }, // S3 key for thumbnail
+// description: { type: String, required: true },
+// likesCount: { type: Number, default: 0 },
+// commentsCount: { type: Number, default: 0 },
+// sharesCount: { type: Number, default: 0 },
+// processed: { type: Boolean, default: false },
+// createdAt: { type: Date, default: Date.now }
+// });
+
+// const Video = mongoose.model('Video', videoSchema);
+
+// // Routes
+// // Generate pre-signed URL for direct S3 upload
+// app.post('/api/videos/get-upload-url', async (req, res) => {
+// try {
+// const { userId, fileType, fileName } = req.body;
+// const key = `videos/${userId}/${uuidv4()}-${fileName}`;
+
+// const params = {
+// Bucket: BUCKET_NAME,
+// Key: key,
+// ContentType: fileType,
+// Expires: 300 // URL expires in 5 minutes
+// };
+
+// const uploadUrl = s3.getSignedUrl('putObject', params);
+
+// res.json({
+// uploadUrl,
+// key
+// });
+// } catch (error) {
+// res.status(500).json({ message: 'Error generating upload URL', error: error.message });
+// }
+// });
+
+// // Save video metadata after upload
+// app.post('/api/videos/metadata', async (req, res) => {
+// try {
+// const { userId, videoKey, description } = req.body;
+
+// // Generate a thumbnail key
+// const thumbnailKey = videoKey.replace('videos/', 'thumbnails/').replace(/\.[^/.]+$/, '.jpg');
+
+// // Create the video record in MongoDB
+// const newVideo = new Video({
+// userId,
+// videoKey,
+// thumbnailKey,
+// description
+// });
+
+// await newVideo.save();
+
+// // Trigger thumbnail generation and video processing (async)
+// generateThumbnail(videoKey, thumbnailKey, newVideo._id);
+
+// res.status(201).json({
+// message: 'Video metadata saved successfully',
+// videoId: newVideo._id
+// });
+// } catch (error) {
+// res.status(500).json({ message: 'Error saving video metadata', error: error.message });
+// }
+// });
+
+// // Get videos for feed
+// app.get('/api/videos', async (req, res) => {
+// try {
+// const page = parseInt(req.query.page) || 1;
+// const limit = parseInt(req.query.limit) || 10;
+// const skip = (page - 1) * limit;
+
+// // Only return processed videos (with thumbnails)
+// const videos = await Video.find({ processed: true })
+// .sort({ createdAt: -1 })
+// .skip(skip)
+// .limit(limit);
+
+// // Transform data to include CloudFront URLs
+// const videoData = videos.map(video => ({
+// _id: video._id,
+// userId: video.userId,
+// videoUrl: `${CLOUDFRONT_URL}/${video.videoKey}`,
+// thumbnailUrl: `${CLOUDFRONT_URL}/${video.thumbnailKey}`,
+// videoKey: video.videoKey,
+// description: video.description,
+// likesCount: video.likesCount,
+// commentsCount: video.commentsCount,
+// sharesCount: video.sharesCount,
+// createdAt: video.createdAt
+// }));
+
+// res.json({ data: videoData });
+// } catch (error) {
+// res.status(500).json({ message: 'Error fetching videos', error: error.message });
+// }
+// });
+
+// // Like/unlike video
+// app.post('/api/videos/:id/like', async (req, res) => {
+// try {
+// const video = await Video.findById(req.params.id);
+// if (!video) {
+// return res.status(404).json({ message: 'Video not found' });
+// }
+
+// // In a real app, you'd check if user has already liked and toggle
+// video.likesCount += 1;
+// await video.save();
+
+// res.json({ message: 'Like toggled successfully', likesCount: video.likesCount });
+// } catch (error) {
+// res.status(500).json({ message: 'Error toggling like', error: error.message });
+// }
+// });
+
+// // Function to generate a thumbnail from video
+// async function generateThumbnail(videoKey, thumbnailKey, videoId) {
+// try {
+// // Create temp directory if it doesn't exist
+// const tempDir = path.join(__dirname, 'temp');
+// if (!fs.existsSync(tempDir)) {
+// fs.mkdirSync(tempDir);
+// }
+
+// // Download video from S3 to temp location
+// const videoPath = path.join(tempDir, `video-${uuidv4()}.mp4`);
+// const thumbnailPath = path.join(tempDir, `thumbnail-${uuidv4()}.jpg`);
+
+// const videoObject = await s3.getObject({
+// Bucket: BUCKET_NAME,
+// Key: videoKey
+// }).promise();
+
+// fs.writeFileSync(videoPath, videoObject.Body);
+
+// // Use ffmpeg to generate thumbnail
+// await new Promise((resolve, reject) => {
+// ffmpeg(videoPath)
+// .on('end', resolve)
+// .on('error', reject)
+// .screenshots({
+// timestamps: ['00:00:01.000'],
+// filename: path.basename(thumbnailPath),
+// folder: path.dirname(thumbnailPath),
+// size: '320x240'
+// });
+// });
+
+// // Upload thumbnail to S3
+// await s3.putObject({
+// Bucket: BUCKET_NAME,
+// Key: thumbnailKey,
+// Body: fs.readFileSync(thumbnailPath),
+// ContentType: 'image/jpeg'
+// }).promise();
+
+// // Update video record as processed
+// await Video.findByIdAndUpdate(videoId, { processed: true });
+
+// // Clean up temporary files
+// fs.unlinkSync(videoPath);
+// fs.unlinkSync(thumbnailPath);
+// } catch (error) {
+// console.error('Error generating thumbnail:', error);
+// }
+// }
+
+// // Start server
+// const PORT = process.env.PORT || 3000;
+// app.listen(PORT, () => {
+// console.log(`Server running on port ${PORT}`);
+// });
+
+// // ----- AWS EC2 Deployment Guide -----
+// // 1. Launch an EC2 instance (t2.medium or better recommended for video processing)
+// // 2. Set up security groups to allow HTTP/HTTPS traffic
+// // 3. Install Node.js, MongoDB, and FFmpeg on the instance:
+// // $ sudo apt update
+// // $ sudo apt install -y nodejs npm mongodb ffmpeg
+// // $ sudo systemctl enable mongodb
+// // $ sudo systemctl start mongodb
+// // 4. Set up PM2 for process management:
+// // $ sudo npm install -g pm2
+// // 5. Clone your repo and install dependencies:
+// // $ git clone your-repo-url
+// // $ cd your-app
+// // $ npm install
+// // 6. Set environment variables:
+// // $ export AWS_ACCESS_KEY_ID=your-access-key
+// // $ export AWS_SECRET_ACCESS_KEY=your-secret-key
+// // $ export AWS_REGION=your-region
+// // $ export S3_BUCKET_NAME=your-bucket
+// // $ export CLOUDFRONT_URL=your-distribution-url
+// // 7. Start with PM2:
+// // $ pm2 start server.js
+// // $ pm2 startup
+// // $ pm2 save
+// // 8. Set up Nginx as a reverse proxy (optional)
+
+
+// // --- Main App Entry Point ---
diff --git a/jenkinsfile b/jenkinsfile
new file mode 100644
index 0000000..8b07c0b
--- /dev/null
+++ b/jenkinsfile
@@ -0,0 +1,37 @@
+pipeline {
+ agent any
+
+ stages {
+ stage('SCM Checkout') {
+ steps {
+ retry(3) {
+ git branch: 'main', url: 'https://github.com/HGSChandeepa/test-node'
+ }
+ }
+ }
+ stage('Build Docker Image') {
+ steps {
+ bat 'docker build -t adomicarts/nodeapp-cuban:%BUILD_NUMBER% .'
+ }
+ }
+ stage('Login to Docker Hub') {
+ steps {
+ withCredentials([string(credentialsId: 'samin-docker', variable: 'samindocker')]) {
+ script {
+ bat "docker login -u adomicarts -p %samindocker%"
+ }
+ }
+ }
+ }
+ stage('Push Image') {
+ steps {
+ bat 'docker push adomicarts/nodeapp-cuban:%BUILD_NUMBER%'
+ }
+ }
+ }
+ post {
+ always {
+ bat 'docker logout'
+ }
+ }
+}
\ No newline at end of file
diff --git a/middleware/auth.js b/middleware/auth.js
new file mode 100644
index 0000000..93ee6c4
--- /dev/null
+++ b/middleware/auth.js
@@ -0,0 +1,32 @@
+import { response } from "express";
+import jwt from "jsonwebtoken";
+const jwtAuth = (req, res, next) => {
+ const token = req.header("Authorization");
+ // const token = authHeader && authHeader.split(" ")[1];
+ if (!token) {
+ return res
+ .status(401)
+ .json({ succss: false, massage: " authorization denied,Invalid user" });
+ }
+ try {
+ const decodeToken = jwt.verify(token, process.env.JWT_SECRET_KEY);
+ console.log(decodeToken);
+ req.user = decodeToken;
+ next();
+ } catch (err) {
+ console.error("Token verification error:", err.message);
+
+ // Check if the error is due to token expiration
+ if (err.name === "jwt expired") {
+ return response
+ .status(401)
+ .json({ succss: false, massage: "Token has expired" });
+ }
+
+ // For other errors, respond with a generic message
+ return res
+ .status(400)
+ .json({ succss: false, massage: "Token is not valid" });
+ }
+};
+export default jwtAuth;
diff --git a/middleware/uploads.js b/middleware/uploads.js
new file mode 100644
index 0000000..37e780f
--- /dev/null
+++ b/middleware/uploads.js
@@ -0,0 +1,16 @@
+// import multer from "multer";
+// import path from "path";
+
+// const storage = multer.diskStorage({
+// destination: (req, file, cb) => {
+// cb(null, "uploads/"); // Make sure this folder exists
+// },
+// filename: (req, file, cb) => {
+// cb(null, Date.now() + path.extname(file.originalname));
+// },
+// });
+
+// const upload = multer({ storage });
+import multer from "multer";
+const upload = multer({ storage: multer.memoryStorage() });//store in memory
+export default upload;
diff --git a/models/user.model.js b/models/user.model.js
new file mode 100644
index 0000000..fd6232a
--- /dev/null
+++ b/models/user.model.js
@@ -0,0 +1,81 @@
+import mongoose from "mongoose";
+//user model
+const UserSchema = new mongoose.Schema(
+ {
+ profileUrl: {
+ type: String,
+
+ trim: true,
+ },
+ username: {
+ type: String,
+ required: true,
+ trim: true,
+ unique: true,
+ },
+ email: {
+ type: String,
+ required: true,
+ trim: true,
+ unique: true,
+ },
+ password: {
+ type: String,
+ required: true,
+ trim: true,
+ unique: true,
+ },
+ isCreator: {
+ type: Boolean,
+ default: false,
+ },
+ serviceDiscription: {
+ type: String,
+ default: "",
+ trim: true,
+ },
+ bio: {
+ type: String,
+ default: "",
+ trim: true,
+ },
+
+ followers: {
+ type: [String],
+ default: [],
+ },
+ following: {
+ type: [String],
+ default: [],
+ },
+
+ contact: {
+ type: Number,
+ default: 0,
+ trim: true,
+ },
+ referenceUrl: {
+ type: String,
+ default: "",
+ trim: true,
+ },
+ isVerified: {
+ type: Boolean,
+ default: false,
+ },
+ verifyCode: {
+ type: String,
+ },
+ codeExpireTime: {
+ type: Number,
+ default: 0,
+ },
+ },
+
+ {
+ timestamps: true, //cratedAT and updatedAt
+ }
+);
+
+const User = mongoose.model("User", UserSchema);
+export default User;
diff --git a/models/video.model.js b/models/video.model.js
new file mode 100644
index 0000000..035284c
--- /dev/null
+++ b/models/video.model.js
@@ -0,0 +1,53 @@
+import mongoose from "mongoose";
+
+const videoSchema = new mongoose.Schema(
+ {
+ userId: {
+ type: String,
+ required: true,
+ trim: true,
+ },
+ title: {
+ type: String,
+ default: "",
+ trim: true,
+ },
+ tags: {
+ type: [String],
+
+ default: [],
+ },
+ url: {
+ type: String,
+ required: true,
+ trim: true,
+ },
+ likes: {
+ type: [String],
+
+ default: [],
+ },
+ disLikes: {
+ type: [String],
+
+ default: [],
+ },
+ comments: {
+ type: [String],
+
+ default: [],
+ },
+ processed: { type: Boolean, default: false },
+ weblink: {
+ type: String,
+ default: "",
+ trim: true,
+ },
+ },
+ {
+ timestamps: true, //cratedAT and updatedAt
+ }
+);
+
+const video = mongoose.model("video", videoSchema);
+export default video;
diff --git a/package-lock.json b/package-lock.json
index c61cb44..ba30383 100644
--- a/package-lock.json
+++ b/package-lock.json
@@ -9,14 +9,20 @@
"version": "1.0.0",
"license": "ISC",
"dependencies": {
+ "aws-sdk": "^2.1692.0",
"axios": "^1.8.4",
"bcryptjs": "^3.0.2",
"cors": "^2.8.5",
"dotenv": "^16.4.7",
"express": "^5.1.0",
+ "fs": "^0.0.1-security",
"jsonwebtoken": "^9.0.2",
"mongoose": "^8.13.2",
- "nodemon": "^3.1.9"
+ "multer": "^1.4.5-lts.2",
+ "nodemailer": "^6.10.0",
+ "nodemon": "^3.1.9",
+ "path": "^0.12.7",
+ "uuid": "^11.1.0"
}
},
"node_modules/@mongodb-js/saslprep": {
@@ -64,11 +70,59 @@
"node": ">= 8"
}
},
+ "node_modules/append-field": {
+ "version": "1.0.0",
+ "resolved": "https://registry.npmjs.org/append-field/-/append-field-1.0.0.tgz",
+ "integrity": "sha512-klpgFSWLW1ZEs8svjfb7g4qWY0YS5imI82dTg+QahUvJ8YqAY0P10Uk8tTyh9ZGuYEZEMaeJYCF5BFuX552hsw=="
+ },
"node_modules/asynckit": {
"version": "0.4.0",
"resolved": "https://registry.npmjs.org/asynckit/-/asynckit-0.4.0.tgz",
"integrity": "sha512-Oei9OH4tRh0YqU3GxhX79dM/mwVgvbZJaSNaRk+bshkj0S5cfHcgYakreBjrHwatXKbz+IoIdYLxrKim2MjW0Q=="
},
+ "node_modules/available-typed-arrays": {
+ "version": "1.0.7",
+ "resolved": "https://registry.npmjs.org/available-typed-arrays/-/available-typed-arrays-1.0.7.tgz",
+ "integrity": "sha512-wvUjBtSGN7+7SjNpq/9M2Tg350UZD3q62IFZLbRAR1bSMlCo1ZaeW+BJ+D090e4hIIZLBcTDWe4Mh4jvUDajzQ==",
+ "dependencies": {
+ "possible-typed-array-names": "^1.0.0"
+ },
+ "engines": {
+ "node": ">= 0.4"
+ },
+ "funding": {
+ "url": "https://github.com/sponsors/ljharb"
+ }
+ },
+ "node_modules/aws-sdk": {
+ "version": "2.1692.0",
+ "resolved": "https://registry.npmjs.org/aws-sdk/-/aws-sdk-2.1692.0.tgz",
+ "integrity": "sha512-x511uiJ/57FIsbgUe5csJ13k3uzu25uWQE+XqfBis/sB0SFoiElJWXRkgEAUh0U6n40eT3ay5Ue4oPkRMu1LYw==",
+ "hasInstallScript": true,
+ "dependencies": {
+ "buffer": "4.9.2",
+ "events": "1.1.1",
+ "ieee754": "1.1.13",
+ "jmespath": "0.16.0",
+ "querystring": "0.2.0",
+ "sax": "1.2.1",
+ "url": "0.10.3",
+ "util": "^0.12.4",
+ "uuid": "8.0.0",
+ "xml2js": "0.6.2"
+ },
+ "engines": {
+ "node": ">= 10.0.0"
+ }
+ },
+ "node_modules/aws-sdk/node_modules/uuid": {
+ "version": "8.0.0",
+ "resolved": "https://registry.npmjs.org/uuid/-/uuid-8.0.0.tgz",
+ "integrity": "sha512-jOXGuXZAWdsTH7eZLtyXMqUb9EcWMGZNbL9YcGBJl4MH4nrxHmZJhEHvyLFrkxo+28uLb/NYRcStH48fnD0Vzw==",
+ "bin": {
+ "uuid": "dist/bin/uuid"
+ }
+ },
"node_modules/axios": {
"version": "1.8.4",
"resolved": "https://registry.npmjs.org/axios/-/axios-1.8.4.tgz",
@@ -84,6 +138,25 @@
"resolved": "https://registry.npmjs.org/balanced-match/-/balanced-match-1.0.2.tgz",
"integrity": "sha512-3oSeUO0TMV67hN1AmbXsK4yaqU7tjiHlbxRDZOpH0KW9+CeX4bRAaX0Anxt0tx2MrpRpWwQaPwIlISEJhYU5Pw=="
},
+ "node_modules/base64-js": {
+ "version": "1.5.1",
+ "resolved": "https://registry.npmjs.org/base64-js/-/base64-js-1.5.1.tgz",
+ "integrity": "sha512-AKpaYlHn8t4SVbOHCy+b5+KKgvR4vrsD8vbvrbiQJps7fKDTkjkDry6ji0rUJjC0kzbNePLwzxq8iypo41qeWA==",
+ "funding": [
+ {
+ "type": "github",
+ "url": "https://github.com/sponsors/feross"
+ },
+ {
+ "type": "patreon",
+ "url": "https://www.patreon.com/feross"
+ },
+ {
+ "type": "consulting",
+ "url": "https://feross.org/support"
+ }
+ ]
+ },
"node_modules/bcryptjs": {
"version": "3.0.2",
"resolved": "https://registry.npmjs.org/bcryptjs/-/bcryptjs-3.0.2.tgz",
@@ -150,11 +223,37 @@
"node": ">=16.20.1"
}
},
+ "node_modules/buffer": {
+ "version": "4.9.2",
+ "resolved": "https://registry.npmjs.org/buffer/-/buffer-4.9.2.tgz",
+ "integrity": "sha512-xq+q3SRMOxGivLhBNaUdC64hDTQwejJ+H0T/NB1XMtTVEwNTrfFF3gAxiyW0Bu/xWEGhjVKgUcMhCrUy2+uCWg==",
+ "dependencies": {
+ "base64-js": "^1.0.2",
+ "ieee754": "^1.1.4",
+ "isarray": "^1.0.0"
+ }
+ },
"node_modules/buffer-equal-constant-time": {
"version": "1.0.1",
"resolved": "https://registry.npmjs.org/buffer-equal-constant-time/-/buffer-equal-constant-time-1.0.1.tgz",
"integrity": "sha512-zRpUiDwd/xk6ADqPMATG8vc9VPrkck7T07OIx0gnjmJAnHnTVXNQG3vfvWNuiZIkwu9KrKdA1iJKfsfTVxE6NA=="
},
+ "node_modules/buffer-from": {
+ "version": "1.1.2",
+ "resolved": "https://registry.npmjs.org/buffer-from/-/buffer-from-1.1.2.tgz",
+ "integrity": "sha512-E+XQCRwSbaaiChtv6k6Dwgc+bx+Bs6vuKJHHl5kox/BaKbhiXzqQOwK4cO22yElGp2OCmjwVhT3HmxgyPGnJfQ=="
+ },
+ "node_modules/busboy": {
+ "version": "1.6.0",
+ "resolved": "https://registry.npmjs.org/busboy/-/busboy-1.6.0.tgz",
+ "integrity": "sha512-8SFQbg/0hQ9xy3UNTB0YEnsNBbWfhf7RtnzpL7TkBiTBRfrQ9Fxcnz7VJsleJpyp6rVLvXiuORqjlHi5q+PYuA==",
+ "dependencies": {
+ "streamsearch": "^1.1.0"
+ },
+ "engines": {
+ "node": ">=10.16.0"
+ }
+ },
"node_modules/bytes": {
"version": "3.1.2",
"resolved": "https://registry.npmjs.org/bytes/-/bytes-3.1.2.tgz",
@@ -163,6 +262,23 @@
"node": ">= 0.8"
}
},
+ "node_modules/call-bind": {
+ "version": "1.0.8",
+ "resolved": "https://registry.npmjs.org/call-bind/-/call-bind-1.0.8.tgz",
+ "integrity": "sha512-oKlSFMcMwpUg2ednkhQ454wfWiU/ul3CkJe/PEHcTKuiX6RpbehUiFMXu13HalGZxfUwCQzZG747YXBn1im9ww==",
+ "dependencies": {
+ "call-bind-apply-helpers": "^1.0.0",
+ "es-define-property": "^1.0.0",
+ "get-intrinsic": "^1.2.4",
+ "set-function-length": "^1.2.2"
+ },
+ "engines": {
+ "node": ">= 0.4"
+ },
+ "funding": {
+ "url": "https://github.com/sponsors/ljharb"
+ }
+ },
"node_modules/call-bind-apply-helpers": {
"version": "1.0.2",
"resolved": "https://registry.npmjs.org/call-bind-apply-helpers/-/call-bind-apply-helpers-1.0.2.tgz",
@@ -229,6 +345,20 @@
"resolved": "https://registry.npmjs.org/concat-map/-/concat-map-0.0.1.tgz",
"integrity": "sha512-/Srv4dswyQNBfohGpz9o6Yb3Gz3SrUDqBH5rTuhGR7ahtlbYKnVxw2bCFMRljaA7EXHaXZ8wsHdodFvbkhKmqg=="
},
+ "node_modules/concat-stream": {
+ "version": "1.6.2",
+ "resolved": "https://registry.npmjs.org/concat-stream/-/concat-stream-1.6.2.tgz",
+ "integrity": "sha512-27HBghJxjiZtIk3Ycvn/4kbJk/1uZuJFfuPEns6LaEvpvG1f0hTea8lilrouyo9mVc2GWdcEZ8OLoGmSADlrCw==",
+ "engines": [
+ "node >= 0.8"
+ ],
+ "dependencies": {
+ "buffer-from": "^1.0.0",
+ "inherits": "^2.0.3",
+ "readable-stream": "^2.2.2",
+ "typedarray": "^0.0.6"
+ }
+ },
"node_modules/content-disposition": {
"version": "1.0.0",
"resolved": "https://registry.npmjs.org/content-disposition/-/content-disposition-1.0.0.tgz",
@@ -264,6 +394,11 @@
"node": ">=6.6.0"
}
},
+ "node_modules/core-util-is": {
+ "version": "1.0.3",
+ "resolved": "https://registry.npmjs.org/core-util-is/-/core-util-is-1.0.3.tgz",
+ "integrity": "sha512-ZQBvi1DcpJ4GDqanjucZ2Hj3wEO5pZDS89BWbkcrvdxksJorwUDDZamX9ldFkp9aw2lmBDLgkObEA4DWNJ9FYQ=="
+ },
"node_modules/cors": {
"version": "2.8.5",
"resolved": "https://registry.npmjs.org/cors/-/cors-2.8.5.tgz",
@@ -292,6 +427,22 @@
}
}
},
+ "node_modules/define-data-property": {
+ "version": "1.1.4",
+ "resolved": "https://registry.npmjs.org/define-data-property/-/define-data-property-1.1.4.tgz",
+ "integrity": "sha512-rBMvIzlpA8v6E+SJZoo++HAYqsLrkg7MSfIinMPFhmkorw7X+dOXVJQs+QT69zGkzMyfDnIMN2Wid1+NbL3T+A==",
+ "dependencies": {
+ "es-define-property": "^1.0.0",
+ "es-errors": "^1.3.0",
+ "gopd": "^1.0.1"
+ },
+ "engines": {
+ "node": ">= 0.4"
+ },
+ "funding": {
+ "url": "https://github.com/sponsors/ljharb"
+ }
+ },
"node_modules/delayed-stream": {
"version": "1.0.0",
"resolved": "https://registry.npmjs.org/delayed-stream/-/delayed-stream-1.0.0.tgz",
@@ -407,6 +558,14 @@
"node": ">= 0.6"
}
},
+ "node_modules/events": {
+ "version": "1.1.1",
+ "resolved": "https://registry.npmjs.org/events/-/events-1.1.1.tgz",
+ "integrity": "sha512-kEcvvCBByWXGnZy6JUlgAp2gBIUjfCAV6P6TgT1/aaQKcmuAEC4OZTV1I4EWQLz2gxZw76atuVyvHhTxvi0Flw==",
+ "engines": {
+ "node": ">=0.4.x"
+ }
+ },
"node_modules/express": {
"version": "5.1.0",
"resolved": "https://registry.npmjs.org/express/-/express-5.1.0.tgz",
@@ -494,6 +653,20 @@
}
}
},
+ "node_modules/for-each": {
+ "version": "0.3.5",
+ "resolved": "https://registry.npmjs.org/for-each/-/for-each-0.3.5.tgz",
+ "integrity": "sha512-dKx12eRCVIzqCxFGplyFKJMPvLEWgmNtUrpTiJIR5u97zEhRG8ySrtboPHZXx7daLxQVrl643cTzbab2tkQjxg==",
+ "dependencies": {
+ "is-callable": "^1.2.7"
+ },
+ "engines": {
+ "node": ">= 0.4"
+ },
+ "funding": {
+ "url": "https://github.com/sponsors/ljharb"
+ }
+ },
"node_modules/form-data": {
"version": "4.0.2",
"resolved": "https://registry.npmjs.org/form-data/-/form-data-4.0.2.tgz",
@@ -543,6 +716,11 @@
"node": ">= 0.8"
}
},
+ "node_modules/fs": {
+ "version": "0.0.1-security",
+ "resolved": "https://registry.npmjs.org/fs/-/fs-0.0.1-security.tgz",
+ "integrity": "sha512-3XY9e1pP0CVEUCdj5BmfIZxRBTSDycnbqhIOGec9QYtmVH2fbLpj86CFWkrNOkt/Fvty4KZG5lTglL9j/gJ87w=="
+ },
"node_modules/fsevents": {
"version": "2.3.3",
"resolved": "https://registry.npmjs.org/fsevents/-/fsevents-2.3.3.tgz",
@@ -629,6 +807,17 @@
"node": ">=4"
}
},
+ "node_modules/has-property-descriptors": {
+ "version": "1.0.2",
+ "resolved": "https://registry.npmjs.org/has-property-descriptors/-/has-property-descriptors-1.0.2.tgz",
+ "integrity": "sha512-55JNKuIW+vq4Ke1BjOTjM2YctQIvCT7GFzHwmfZPGo5wnrgkid0YQtnAleFSqumZm4az3n2BS+erby5ipJdgrg==",
+ "dependencies": {
+ "es-define-property": "^1.0.0"
+ },
+ "funding": {
+ "url": "https://github.com/sponsors/ljharb"
+ }
+ },
"node_modules/has-symbols": {
"version": "1.1.0",
"resolved": "https://registry.npmjs.org/has-symbols/-/has-symbols-1.1.0.tgz",
@@ -691,6 +880,11 @@
"node": ">=0.10.0"
}
},
+ "node_modules/ieee754": {
+ "version": "1.1.13",
+ "resolved": "https://registry.npmjs.org/ieee754/-/ieee754-1.1.13.tgz",
+ "integrity": "sha512-4vf7I2LYV/HaWerSo3XmlMkp5eZ83i+/CDluXi/IGTs/O1sejBNhTtnxzmRZfvOUqj7lZjqHkeTvpgSFDlWZTg=="
+ },
"node_modules/ignore-by-default": {
"version": "1.0.1",
"resolved": "https://registry.npmjs.org/ignore-by-default/-/ignore-by-default-1.0.1.tgz",
@@ -709,6 +903,21 @@
"node": ">= 0.10"
}
},
+ "node_modules/is-arguments": {
+ "version": "1.2.0",
+ "resolved": "https://registry.npmjs.org/is-arguments/-/is-arguments-1.2.0.tgz",
+ "integrity": "sha512-7bVbi0huj/wrIAOzb8U1aszg9kdi3KN/CyU19CTI7tAoZYEZoL9yCDXpbXN+uPsuWnP02cyug1gleqq+TU+YCA==",
+ "dependencies": {
+ "call-bound": "^1.0.2",
+ "has-tostringtag": "^1.0.2"
+ },
+ "engines": {
+ "node": ">= 0.4"
+ },
+ "funding": {
+ "url": "https://github.com/sponsors/ljharb"
+ }
+ },
"node_modules/is-binary-path": {
"version": "2.1.0",
"resolved": "https://registry.npmjs.org/is-binary-path/-/is-binary-path-2.1.0.tgz",
@@ -720,6 +929,17 @@
"node": ">=8"
}
},
+ "node_modules/is-callable": {
+ "version": "1.2.7",
+ "resolved": "https://registry.npmjs.org/is-callable/-/is-callable-1.2.7.tgz",
+ "integrity": "sha512-1BC0BVFhS/p0qtw6enp8e+8OD0UrK0oFLztSjNzhcKA3WDuJxxAPXzPuPtKkjEY9UUoEWlX/8fgKeu2S8i9JTA==",
+ "engines": {
+ "node": ">= 0.4"
+ },
+ "funding": {
+ "url": "https://github.com/sponsors/ljharb"
+ }
+ },
"node_modules/is-extglob": {
"version": "2.1.1",
"resolved": "https://registry.npmjs.org/is-extglob/-/is-extglob-2.1.1.tgz",
@@ -728,6 +948,23 @@
"node": ">=0.10.0"
}
},
+ "node_modules/is-generator-function": {
+ "version": "1.1.0",
+ "resolved": "https://registry.npmjs.org/is-generator-function/-/is-generator-function-1.1.0.tgz",
+ "integrity": "sha512-nPUB5km40q9e8UfN/Zc24eLlzdSf9OfKByBw9CIdw4H1giPMeA0OIJvbchsCu4npfI2QcMVBsGEBHKZ7wLTWmQ==",
+ "dependencies": {
+ "call-bound": "^1.0.3",
+ "get-proto": "^1.0.0",
+ "has-tostringtag": "^1.0.2",
+ "safe-regex-test": "^1.1.0"
+ },
+ "engines": {
+ "node": ">= 0.4"
+ },
+ "funding": {
+ "url": "https://github.com/sponsors/ljharb"
+ }
+ },
"node_modules/is-glob": {
"version": "4.0.3",
"resolved": "https://registry.npmjs.org/is-glob/-/is-glob-4.0.3.tgz",
@@ -752,6 +989,50 @@
"resolved": "https://registry.npmjs.org/is-promise/-/is-promise-4.0.0.tgz",
"integrity": "sha512-hvpoI6korhJMnej285dSg6nu1+e6uxs7zG3BYAm5byqDsgJNWwxzM6z6iZiAgQR4TJ30JmBTOwqZUw3WlyH3AQ=="
},
+ "node_modules/is-regex": {
+ "version": "1.2.1",
+ "resolved": "https://registry.npmjs.org/is-regex/-/is-regex-1.2.1.tgz",
+ "integrity": "sha512-MjYsKHO5O7mCsmRGxWcLWheFqN9DJ/2TmngvjKXihe6efViPqc274+Fx/4fYj/r03+ESvBdTXK0V6tA3rgez1g==",
+ "dependencies": {
+ "call-bound": "^1.0.2",
+ "gopd": "^1.2.0",
+ "has-tostringtag": "^1.0.2",
+ "hasown": "^2.0.2"
+ },
+ "engines": {
+ "node": ">= 0.4"
+ },
+ "funding": {
+ "url": "https://github.com/sponsors/ljharb"
+ }
+ },
+ "node_modules/is-typed-array": {
+ "version": "1.1.15",
+ "resolved": "https://registry.npmjs.org/is-typed-array/-/is-typed-array-1.1.15.tgz",
+ "integrity": "sha512-p3EcsicXjit7SaskXHs1hA91QxgTw46Fv6EFKKGS5DRFLD8yKnohjF3hxoju94b/OcMZoQukzpPpBE9uLVKzgQ==",
+ "dependencies": {
+ "which-typed-array": "^1.1.16"
+ },
+ "engines": {
+ "node": ">= 0.4"
+ },
+ "funding": {
+ "url": "https://github.com/sponsors/ljharb"
+ }
+ },
+ "node_modules/isarray": {
+ "version": "1.0.0",
+ "resolved": "https://registry.npmjs.org/isarray/-/isarray-1.0.0.tgz",
+ "integrity": "sha512-VLghIWNM6ELQzo7zwmcg0NmTVyWKYjvIeM83yjp0wRDTmUnrM678fQbcKBo6n2CJEF0szoG//ytg+TKla89ALQ=="
+ },
+ "node_modules/jmespath": {
+ "version": "0.16.0",
+ "resolved": "https://registry.npmjs.org/jmespath/-/jmespath-0.16.0.tgz",
+ "integrity": "sha512-9FzQjJ7MATs1tSpnco1K6ayiYE3figslrXA72G2HQ/n76RzvYlofyi5QM+iX4YRs/pu3yzxlVQSST23+dMDknw==",
+ "engines": {
+ "node": ">= 0.6.0"
+ }
+ },
"node_modules/jsonwebtoken": {
"version": "9.0.2",
"resolved": "https://registry.npmjs.org/jsonwebtoken/-/jsonwebtoken-9.0.2.tgz",
@@ -897,6 +1178,25 @@
"node": "*"
}
},
+ "node_modules/minimist": {
+ "version": "1.2.8",
+ "resolved": "https://registry.npmjs.org/minimist/-/minimist-1.2.8.tgz",
+ "integrity": "sha512-2yyAR8qBkN3YuheJanUpWC5U3bb5osDywNB8RzDVlDwDHbocAJveqqj1u8+SVD7jkWT4yvsHCpWqqWqAxb0zCA==",
+ "funding": {
+ "url": "https://github.com/sponsors/ljharb"
+ }
+ },
+ "node_modules/mkdirp": {
+ "version": "0.5.6",
+ "resolved": "https://registry.npmjs.org/mkdirp/-/mkdirp-0.5.6.tgz",
+ "integrity": "sha512-FP+p8RB8OWpF3YZBCrP5gtADmtXApB5AMLn+vdyA+PyxCjrCs00mjyUozssO33cwDeT3wNGdLxJ5M//YqtHAJw==",
+ "dependencies": {
+ "minimist": "^1.2.6"
+ },
+ "bin": {
+ "mkdirp": "bin/cmd.js"
+ }
+ },
"node_modules/mongodb": {
"version": "6.15.0",
"resolved": "https://registry.npmjs.org/mongodb/-/mongodb-6.15.0.tgz",
@@ -996,6 +1296,62 @@
"resolved": "https://registry.npmjs.org/ms/-/ms-2.1.3.tgz",
"integrity": "sha512-6FlzubTLZG3J2a/NVCAleEhjzq5oxgHyaCU9yYXvcLsvoVaHJq/s5xXI6/XXP6tz7R9xAOtHnSO/tXtF3WRTlA=="
},
+ "node_modules/multer": {
+ "version": "1.4.5-lts.2",
+ "resolved": "https://registry.npmjs.org/multer/-/multer-1.4.5-lts.2.tgz",
+ "integrity": "sha512-VzGiVigcG9zUAoCNU+xShztrlr1auZOlurXynNvO9GiWD1/mTBbUljOKY+qMeazBqXgRnjzeEgJI/wyjJUHg9A==",
+ "dependencies": {
+ "append-field": "^1.0.0",
+ "busboy": "^1.0.0",
+ "concat-stream": "^1.5.2",
+ "mkdirp": "^0.5.4",
+ "object-assign": "^4.1.1",
+ "type-is": "^1.6.4",
+ "xtend": "^4.0.0"
+ },
+ "engines": {
+ "node": ">= 6.0.0"
+ }
+ },
+ "node_modules/multer/node_modules/media-typer": {
+ "version": "0.3.0",
+ "resolved": "https://registry.npmjs.org/media-typer/-/media-typer-0.3.0.tgz",
+ "integrity": "sha512-dq+qelQ9akHpcOl/gUVRTxVIOkAJ1wR3QAvb4RsVjS8oVoFjDGTc679wJYmUmknUF5HwMLOgb5O+a3KxfWapPQ==",
+ "engines": {
+ "node": ">= 0.6"
+ }
+ },
+ "node_modules/multer/node_modules/mime-db": {
+ "version": "1.52.0",
+ "resolved": "https://registry.npmjs.org/mime-db/-/mime-db-1.52.0.tgz",
+ "integrity": "sha512-sPU4uV7dYlvtWJxwwxHD0PuihVNiE7TyAbQ5SWxDCB9mUYvOgroQOwYQQOKPJ8CIbE+1ETVlOoK1UC2nU3gYvg==",
+ "engines": {
+ "node": ">= 0.6"
+ }
+ },
+ "node_modules/multer/node_modules/mime-types": {
+ "version": "2.1.35",
+ "resolved": "https://registry.npmjs.org/mime-types/-/mime-types-2.1.35.tgz",
+ "integrity": "sha512-ZDY+bPm5zTTF+YpCrAU9nK0UgICYPT0QtT1NZWFv4s++TNkcgVaT0g6+4R2uI4MjQjzysHB1zxuWL50hzaeXiw==",
+ "dependencies": {
+ "mime-db": "1.52.0"
+ },
+ "engines": {
+ "node": ">= 0.6"
+ }
+ },
+ "node_modules/multer/node_modules/type-is": {
+ "version": "1.6.18",
+ "resolved": "https://registry.npmjs.org/type-is/-/type-is-1.6.18.tgz",
+ "integrity": "sha512-TkRKr9sUTxEH8MdfuCSP7VizJyzRNMjj2J2do2Jr3Kym598JVdEksuzPQCnlFPW4ky9Q+iA+ma9BGm06XQBy8g==",
+ "dependencies": {
+ "media-typer": "0.3.0",
+ "mime-types": "~2.1.24"
+ },
+ "engines": {
+ "node": ">= 0.6"
+ }
+ },
"node_modules/negotiator": {
"version": "1.0.0",
"resolved": "https://registry.npmjs.org/negotiator/-/negotiator-1.0.0.tgz",
@@ -1004,6 +1360,14 @@
"node": ">= 0.6"
}
},
+ "node_modules/nodemailer": {
+ "version": "6.10.0",
+ "resolved": "https://registry.npmjs.org/nodemailer/-/nodemailer-6.10.0.tgz",
+ "integrity": "sha512-SQ3wZCExjeSatLE/HBaXS5vqUOQk6GtBdIIKxiFdmm01mOQZX/POJkO3SUX1wDiYcwUOJwT23scFSC9fY2H8IA==",
+ "engines": {
+ "node": ">=6.0.0"
+ }
+ },
"node_modules/nodemon": {
"version": "3.1.9",
"resolved": "https://registry.npmjs.org/nodemon/-/nodemon-3.1.9.tgz",
@@ -1085,6 +1449,15 @@
"node": ">= 0.8"
}
},
+ "node_modules/path": {
+ "version": "0.12.7",
+ "resolved": "https://registry.npmjs.org/path/-/path-0.12.7.tgz",
+ "integrity": "sha512-aXXC6s+1w7otVF9UletFkFcDsJeO7lSZBPUQhtb5O0xJe8LtYhj/GxldoL09bBj9+ZmE2hNoHqQSFMN5fikh4Q==",
+ "dependencies": {
+ "process": "^0.11.1",
+ "util": "^0.10.3"
+ }
+ },
"node_modules/path-to-regexp": {
"version": "8.2.0",
"resolved": "https://registry.npmjs.org/path-to-regexp/-/path-to-regexp-8.2.0.tgz",
@@ -1093,6 +1466,19 @@
"node": ">=16"
}
},
+ "node_modules/path/node_modules/inherits": {
+ "version": "2.0.3",
+ "resolved": "https://registry.npmjs.org/inherits/-/inherits-2.0.3.tgz",
+ "integrity": "sha512-x00IRNXNy63jwGkJmzPigoySHbaqpNuzKbBOmzK+g2OdZpQ9w+sxCN+VSB3ja7IAge2OP2qpfxTjeNcyjmW1uw=="
+ },
+ "node_modules/path/node_modules/util": {
+ "version": "0.10.4",
+ "resolved": "https://registry.npmjs.org/util/-/util-0.10.4.tgz",
+ "integrity": "sha512-0Pm9hTQ3se5ll1XihRic3FDIku70C+iHUdT/W926rSgHV5QgXsYbKZN8MSC3tJtSkhuROzvsQjAaFENRXr+19A==",
+ "dependencies": {
+ "inherits": "2.0.3"
+ }
+ },
"node_modules/picomatch": {
"version": "2.3.1",
"resolved": "https://registry.npmjs.org/picomatch/-/picomatch-2.3.1.tgz",
@@ -1104,6 +1490,27 @@
"url": "https://github.com/sponsors/jonschlinkert"
}
},
+ "node_modules/possible-typed-array-names": {
+ "version": "1.1.0",
+ "resolved": "https://registry.npmjs.org/possible-typed-array-names/-/possible-typed-array-names-1.1.0.tgz",
+ "integrity": "sha512-/+5VFTchJDoVj3bhoqi6UeymcD00DAwb1nJwamzPvHEszJ4FpF6SNNbUbOS8yI56qHzdV8eK0qEfOSiodkTdxg==",
+ "engines": {
+ "node": ">= 0.4"
+ }
+ },
+ "node_modules/process": {
+ "version": "0.11.10",
+ "resolved": "https://registry.npmjs.org/process/-/process-0.11.10.tgz",
+ "integrity": "sha512-cdGef/drWFoydD1JsMzuFf8100nZl+GT+yacc2bEced5f9Rjk4z+WtFUTBu9PhOi9j/jfmBPu0mMEY4wIdAF8A==",
+ "engines": {
+ "node": ">= 0.6.0"
+ }
+ },
+ "node_modules/process-nextick-args": {
+ "version": "2.0.1",
+ "resolved": "https://registry.npmjs.org/process-nextick-args/-/process-nextick-args-2.0.1.tgz",
+ "integrity": "sha512-3ouUOpQhtgrbOa17J7+uxOTpITYWaGP7/AhoR3+A+/1e9skrzelGi/dXzEYyvbxubEF6Wn2ypscTKiKJFFn1ag=="
+ },
"node_modules/proxy-addr": {
"version": "2.0.7",
"resolved": "https://registry.npmjs.org/proxy-addr/-/proxy-addr-2.0.7.tgz",
@@ -1148,6 +1555,15 @@
"url": "https://github.com/sponsors/ljharb"
}
},
+ "node_modules/querystring": {
+ "version": "0.2.0",
+ "resolved": "https://registry.npmjs.org/querystring/-/querystring-0.2.0.tgz",
+ "integrity": "sha512-X/xY82scca2tau62i9mDyU9K+I+djTMUsvwf7xnUX5GLvVzgJybOJf4Y6o9Zx3oJK/LSXg5tTZBjwzqVPaPO2g==",
+ "deprecated": "The querystring API is considered Legacy. new code should use the URLSearchParams API instead.",
+ "engines": {
+ "node": ">=0.4.x"
+ }
+ },
"node_modules/range-parser": {
"version": "1.2.1",
"resolved": "https://registry.npmjs.org/range-parser/-/range-parser-1.2.1.tgz",
@@ -1170,6 +1586,25 @@
"node": ">= 0.8"
}
},
+ "node_modules/readable-stream": {
+ "version": "2.3.8",
+ "resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-2.3.8.tgz",
+ "integrity": "sha512-8p0AUk4XODgIewSi0l8Epjs+EVnWiK7NoDIEGU0HhE7+ZyY8D1IMY7odu5lRrFXGg71L15KG8QrPmum45RTtdA==",
+ "dependencies": {
+ "core-util-is": "~1.0.0",
+ "inherits": "~2.0.3",
+ "isarray": "~1.0.0",
+ "process-nextick-args": "~2.0.0",
+ "safe-buffer": "~5.1.1",
+ "string_decoder": "~1.1.1",
+ "util-deprecate": "~1.0.1"
+ }
+ },
+ "node_modules/readable-stream/node_modules/safe-buffer": {
+ "version": "5.1.2",
+ "resolved": "https://registry.npmjs.org/safe-buffer/-/safe-buffer-5.1.2.tgz",
+ "integrity": "sha512-Gd2UZBJDkXlY7GbJxfsE8/nvKkUEU1G38c1siN6QP6a9PT9MmHB8GnpscSmMJSoF8LOIrt8ud/wPtojys4G6+g=="
+ },
"node_modules/readdirp": {
"version": "3.6.0",
"resolved": "https://registry.npmjs.org/readdirp/-/readdirp-3.6.0.tgz",
@@ -1215,11 +1650,32 @@
}
]
},
+ "node_modules/safe-regex-test": {
+ "version": "1.1.0",
+ "resolved": "https://registry.npmjs.org/safe-regex-test/-/safe-regex-test-1.1.0.tgz",
+ "integrity": "sha512-x/+Cz4YrimQxQccJf5mKEbIa1NzeCRNI5Ecl/ekmlYaampdNLPalVyIcCZNNH3MvmqBugV5TMYZXv0ljslUlaw==",
+ "dependencies": {
+ "call-bound": "^1.0.2",
+ "es-errors": "^1.3.0",
+ "is-regex": "^1.2.1"
+ },
+ "engines": {
+ "node": ">= 0.4"
+ },
+ "funding": {
+ "url": "https://github.com/sponsors/ljharb"
+ }
+ },
"node_modules/safer-buffer": {
"version": "2.1.2",
"resolved": "https://registry.npmjs.org/safer-buffer/-/safer-buffer-2.1.2.tgz",
"integrity": "sha512-YZo3K82SD7Riyi0E1EQPojLz7kpepnSQI9IyPbHHg1XXXevb5dJI7tpyN2ADxGcQbHG7vcyRHk0cbwqcQriUtg=="
},
+ "node_modules/sax": {
+ "version": "1.2.1",
+ "resolved": "https://registry.npmjs.org/sax/-/sax-1.2.1.tgz",
+ "integrity": "sha512-8I2a3LovHTOpm7NV5yOyO8IHqgVsfK4+UuySrXU8YXkSRX7k6hCV9b3HrkKCr3nMpgj+0bmocaJJWpvp1oc7ZA=="
+ },
"node_modules/semver": {
"version": "7.7.1",
"resolved": "https://registry.npmjs.org/semver/-/semver-7.7.1.tgz",
@@ -1266,6 +1722,22 @@
"node": ">= 18"
}
},
+ "node_modules/set-function-length": {
+ "version": "1.2.2",
+ "resolved": "https://registry.npmjs.org/set-function-length/-/set-function-length-1.2.2.tgz",
+ "integrity": "sha512-pgRc4hJ4/sNjWCSS9AmnS40x3bNMDTknHgL5UaMBTMyJnU90EgWh1Rz+MC9eFu4BuN/UwZjKQuY/1v3rM7HMfg==",
+ "dependencies": {
+ "define-data-property": "^1.1.4",
+ "es-errors": "^1.3.0",
+ "function-bind": "^1.1.2",
+ "get-intrinsic": "^1.2.4",
+ "gopd": "^1.0.1",
+ "has-property-descriptors": "^1.0.2"
+ },
+ "engines": {
+ "node": ">= 0.4"
+ }
+ },
"node_modules/setprototypeof": {
"version": "1.2.0",
"resolved": "https://registry.npmjs.org/setprototypeof/-/setprototypeof-1.2.0.tgz",
@@ -1371,6 +1843,27 @@
"node": ">= 0.8"
}
},
+ "node_modules/streamsearch": {
+ "version": "1.1.0",
+ "resolved": "https://registry.npmjs.org/streamsearch/-/streamsearch-1.1.0.tgz",
+ "integrity": "sha512-Mcc5wHehp9aXz1ax6bZUyY5afg9u2rv5cqQI3mRrYkGC8rW2hM02jWuwjtL++LS5qinSyhj2QfLyNsuc+VsExg==",
+ "engines": {
+ "node": ">=10.0.0"
+ }
+ },
+ "node_modules/string_decoder": {
+ "version": "1.1.1",
+ "resolved": "https://registry.npmjs.org/string_decoder/-/string_decoder-1.1.1.tgz",
+ "integrity": "sha512-n/ShnvDi6FHbbVfviro+WojiFzv+s8MPMHBczVePfUpDJLwoLT0ht1l4YwBCbi8pJAveEEdnkHyPyTP/mzRfwg==",
+ "dependencies": {
+ "safe-buffer": "~5.1.0"
+ }
+ },
+ "node_modules/string_decoder/node_modules/safe-buffer": {
+ "version": "5.1.2",
+ "resolved": "https://registry.npmjs.org/safe-buffer/-/safe-buffer-5.1.2.tgz",
+ "integrity": "sha512-Gd2UZBJDkXlY7GbJxfsE8/nvKkUEU1G38c1siN6QP6a9PT9MmHB8GnpscSmMJSoF8LOIrt8ud/wPtojys4G6+g=="
+ },
"node_modules/supports-color": {
"version": "5.5.0",
"resolved": "https://registry.npmjs.org/supports-color/-/supports-color-5.5.0.tgz",
@@ -1433,6 +1926,11 @@
"node": ">= 0.6"
}
},
+ "node_modules/typedarray": {
+ "version": "0.0.6",
+ "resolved": "https://registry.npmjs.org/typedarray/-/typedarray-0.0.6.tgz",
+ "integrity": "sha512-/aCDEGatGvZ2BIk+HmLf4ifCJFwvKFNb9/JeZPMulfgFracn9QFcAf5GO8B/mweUjSoblS5In0cWhqpfs/5PQA=="
+ },
"node_modules/undefsafe": {
"version": "2.0.5",
"resolved": "https://registry.npmjs.org/undefsafe/-/undefsafe-2.0.5.tgz",
@@ -1446,6 +1944,49 @@
"node": ">= 0.8"
}
},
+ "node_modules/url": {
+ "version": "0.10.3",
+ "resolved": "https://registry.npmjs.org/url/-/url-0.10.3.tgz",
+ "integrity": "sha512-hzSUW2q06EqL1gKM/a+obYHLIO6ct2hwPuviqTTOcfFVc61UbfJ2Q32+uGL/HCPxKqrdGB5QUwIe7UqlDgwsOQ==",
+ "dependencies": {
+ "punycode": "1.3.2",
+ "querystring": "0.2.0"
+ }
+ },
+ "node_modules/url/node_modules/punycode": {
+ "version": "1.3.2",
+ "resolved": "https://registry.npmjs.org/punycode/-/punycode-1.3.2.tgz",
+ "integrity": "sha512-RofWgt/7fL5wP1Y7fxE7/EmTLzQVnB0ycyibJ0OOHIlJqTNzglYFxVwETOcIoJqJmpDXJ9xImDv+Fq34F/d4Dw=="
+ },
+ "node_modules/util": {
+ "version": "0.12.5",
+ "resolved": "https://registry.npmjs.org/util/-/util-0.12.5.tgz",
+ "integrity": "sha512-kZf/K6hEIrWHI6XqOFUiiMa+79wE/D8Q+NCNAWclkyg3b4d2k7s0QGepNjiABc+aR3N1PAyHL7p6UcLY6LmrnA==",
+ "dependencies": {
+ "inherits": "^2.0.3",
+ "is-arguments": "^1.0.4",
+ "is-generator-function": "^1.0.7",
+ "is-typed-array": "^1.1.3",
+ "which-typed-array": "^1.1.2"
+ }
+ },
+ "node_modules/util-deprecate": {
+ "version": "1.0.2",
+ "resolved": "https://registry.npmjs.org/util-deprecate/-/util-deprecate-1.0.2.tgz",
+ "integrity": "sha512-EPD5q1uXyFxJpCrLnCc1nHnq3gOa6DZBocAIiI2TaSCA7VCJ1UJDMagCzIkXNsUYfD1daK//LTEQ8xiIbrHtcw=="
+ },
+ "node_modules/uuid": {
+ "version": "11.1.0",
+ "resolved": "https://registry.npmjs.org/uuid/-/uuid-11.1.0.tgz",
+ "integrity": "sha512-0/A9rDy9P7cJ+8w1c9WD9V//9Wj15Ce2MPz8Ri6032usz+NfePxx5AcN3bN+r6ZL6jEo066/yNYB3tn4pQEx+A==",
+ "funding": [
+ "https://github.com/sponsors/broofa",
+ "https://github.com/sponsors/ctavan"
+ ],
+ "bin": {
+ "uuid": "dist/esm/bin/uuid"
+ }
+ },
"node_modules/vary": {
"version": "1.1.2",
"resolved": "https://registry.npmjs.org/vary/-/vary-1.1.2.tgz",
@@ -1474,10 +2015,58 @@
"node": ">=18"
}
},
+ "node_modules/which-typed-array": {
+ "version": "1.1.19",
+ "resolved": "https://registry.npmjs.org/which-typed-array/-/which-typed-array-1.1.19.tgz",
+ "integrity": "sha512-rEvr90Bck4WZt9HHFC4DJMsjvu7x+r6bImz0/BrbWb7A2djJ8hnZMrWnHo9F8ssv0OMErasDhftrfROTyqSDrw==",
+ "dependencies": {
+ "available-typed-arrays": "^1.0.7",
+ "call-bind": "^1.0.8",
+ "call-bound": "^1.0.4",
+ "for-each": "^0.3.5",
+ "get-proto": "^1.0.1",
+ "gopd": "^1.2.0",
+ "has-tostringtag": "^1.0.2"
+ },
+ "engines": {
+ "node": ">= 0.4"
+ },
+ "funding": {
+ "url": "https://github.com/sponsors/ljharb"
+ }
+ },
"node_modules/wrappy": {
"version": "1.0.2",
"resolved": "https://registry.npmjs.org/wrappy/-/wrappy-1.0.2.tgz",
"integrity": "sha512-l4Sp/DRseor9wL6EvV2+TuQn63dMkPjZ/sp9XkghTEbV9KlPS1xUsZ3u7/IQO4wxtcFB4bgpQPRcR3QCvezPcQ=="
+ },
+ "node_modules/xml2js": {
+ "version": "0.6.2",
+ "resolved": "https://registry.npmjs.org/xml2js/-/xml2js-0.6.2.tgz",
+ "integrity": "sha512-T4rieHaC1EXcES0Kxxj4JWgaUQHDk+qwHcYOCFHfiwKz7tOVPLq7Hjq9dM1WCMhylqMEfP7hMcOIChvotiZegA==",
+ "dependencies": {
+ "sax": ">=0.6.0",
+ "xmlbuilder": "~11.0.0"
+ },
+ "engines": {
+ "node": ">=4.0.0"
+ }
+ },
+ "node_modules/xmlbuilder": {
+ "version": "11.0.1",
+ "resolved": "https://registry.npmjs.org/xmlbuilder/-/xmlbuilder-11.0.1.tgz",
+ "integrity": "sha512-fDlsI/kFEx7gLvbecc0/ohLG50fugQp8ryHzMTuW9vSa1GJ0XYWKnhsUx7oie3G98+r56aTQIUB4kht42R3JvA==",
+ "engines": {
+ "node": ">=4.0"
+ }
+ },
+ "node_modules/xtend": {
+ "version": "4.0.2",
+ "resolved": "https://registry.npmjs.org/xtend/-/xtend-4.0.2.tgz",
+ "integrity": "sha512-LKYU1iAXJXUgAXn9URjiu+MWhyUXHsvfp7mcuYm9dSUKK0/CjtrUwFAxD82/mCWbtLsGjFIad0wIsod4zrTAEQ==",
+ "engines": {
+ "node": ">=0.4"
+ }
}
}
}
diff --git a/package.json b/package.json
index 78f9078..fd162b1 100644
--- a/package.json
+++ b/package.json
@@ -13,13 +13,19 @@
"author": "",
"license": "ISC",
"dependencies": {
+ "aws-sdk": "^2.1692.0",
"axios": "^1.8.4",
"bcryptjs": "^3.0.2",
"cors": "^2.8.5",
"dotenv": "^16.4.7",
"express": "^5.1.0",
+ "fs": "^0.0.1-security",
"jsonwebtoken": "^9.0.2",
"mongoose": "^8.13.2",
- "nodemon": "^3.1.9"
+ "multer": "^1.4.5-lts.2",
+ "nodemailer": "^6.10.0",
+ "nodemon": "^3.1.9",
+ "path": "^0.12.7",
+ "uuid": "^11.1.0"
}
}
diff --git a/readme b/readme
new file mode 100644
index 0000000..c137778
--- /dev/null
+++ b/readme
@@ -0,0 +1,21 @@
+{
+ "Version": "2008-10-17",
+ "Id": "PolicyForCloudFrontPrivateContent",
+ "Statement": [
+ {
+ "Sid": "AllowCloudFrontServicePrincipal",
+ "Effect": "Allow",
+ "Principal": {
+ "Service": "cloudfront.amazonaws.com"
+ },
+ "Action": "s3:GetObject",
+ "Resource": "arn:aws:s3:::datenet-video-reels/*",
+ "Condition": {
+ "StringEquals": {
+ "AWS:SourceArn": "arn:aws:cloudfront::954976291528:distribution/E273U9FNKDYIC4"
+ }
+ }
+ }
+ ]
+ }
+
diff --git a/routes/authroutes.js b/routes/authroutes.js
new file mode 100644
index 0000000..3eead0d
--- /dev/null
+++ b/routes/authroutes.js
@@ -0,0 +1,18 @@
+import express from "express";
+import {
+ login,
+ register,
+ resetPassword,
+ resetPasswordVerification,
+ verifyAccount,
+} from "../controller/userauthcontroller.js";
+import upload from "../middleware/uploads.js";
+
+const authRoute = express.Router();
+authRoute.post("/register", upload.single("profileUrl"), register);
+authRoute.post("/login", login);
+authRoute.post("/verify", verifyAccount);
+authRoute.post("/user-verification", resetPasswordVerification);
+authRoute.post("/reset-password", resetPassword);
+
+export default authRoute;
diff --git a/routes/resourceroute.js b/routes/resourceroute.js
new file mode 100644
index 0000000..bb0e0a5
--- /dev/null
+++ b/routes/resourceroute.js
@@ -0,0 +1,18 @@
+import express from "express";
+import {
+ dislikeReels,
+ fetchVideoData,
+ getPreSingedUrl,
+ likeReels,
+ saveMetaData,
+} from "../controller/resourcecontroller.js";
+const resourceRouter = express.Router();
+//upload file
+resourceRouter.post("/get-pre-signed-url", getPreSingedUrl);
+resourceRouter.post("/save-metadata", saveMetaData);
+resourceRouter.get("/fetch-videos", fetchVideoData);
+resourceRouter.post("/like-videos", likeReels);
+resourceRouter.post("/dislike-videos", dislikeReels);
+
+//render file
+export default resourceRouter;
diff --git a/routes/userroutes.js b/routes/userroutes.js
new file mode 100644
index 0000000..30254d4
--- /dev/null
+++ b/routes/userroutes.js
@@ -0,0 +1,16 @@
+import express from "express";
+import {
+ followOrUnfollowUser,
+ getAllUser,
+ getCurrentUser,
+ getUserByUserName,
+} from "../controller/usercontroller.js";
+import middleware from "../middleware/auth.js";
+const userRouter = express.Router();
+
+userRouter.get("/getalluser", middleware, getAllUser);
+userRouter.get("/getcurrentuser", middleware, getCurrentUser);
+userRouter.get("/getuserbyusername/:username", middleware, getUserByUserName);
+userRouter.post("/followuser/:guestid", middleware, followOrUnfollowUser);
+
+export default userRouter;
diff --git a/server.js b/server.js
index 00170ea..b1911ba 100644
--- a/server.js
+++ b/server.js
@@ -1,17 +1,28 @@
import cors from "cors";
import dotenv from "dotenv";
import express from "express";
-import { connectDB } from "./db/database.js";
+import { connectDB } from "./config/db/database.js";
+import authRoute from "./routes/authroutes.js";
+import userRouter from "./routes/userroutes.js";
+import resourceRouter from "./routes/resourceroute.js";
+
dotenv.config();
//app
const app = express();
const PORT = process.env.PORT || 5000;
+
+
+
+//s3 bucket
+const bucketName = process.env.AWS_BUCKET_NAME;
//midddleware
app.use(express.json());
app.use(cors());
-
+app.use("/api/auth", authRoute);
+app.use("/api/user", userRouter);
+app.use("/api/resources", resourceRouter);
app.get("/", (req, res) => {
res.send("server is ready to use");
});