comment added
This commit is contained in:
parent
07cc9df7a2
commit
777de35c65
6
sequelize-cli.js
Normal file
6
sequelize-cli.js
Normal file
|
@ -0,0 +1,6 @@
|
||||||
|
// sequelize-cli.js
|
||||||
|
require('module-alias/register');
|
||||||
|
// const { config } = require('./config/config.json'); // Import the configuration file
|
||||||
|
|
||||||
|
import { sequelize } from './server/loaders';
|
||||||
|
module.exports = sequelize;
|
39
server/app.js
Normal file
39
server/app.js
Normal file
|
@ -0,0 +1,39 @@
|
||||||
|
import express from 'express';
|
||||||
|
import { createServer } from 'http';
|
||||||
|
import cookieParser from 'cookie-parser';
|
||||||
|
import logger from 'morgan';
|
||||||
|
import indexRouter from './routes/index.js';
|
||||||
|
import { renderFile } from 'ejs';
|
||||||
|
import dotenv from "dotenv";
|
||||||
|
import cors from "cors";
|
||||||
|
dotenv.config();
|
||||||
|
import './loaders/index.js';
|
||||||
|
import { makeResponse } from './helper/index.js';
|
||||||
|
|
||||||
|
const app = express();
|
||||||
|
const server = createServer(app);
|
||||||
|
|
||||||
|
app.use(logger('dev'));
|
||||||
|
app.use(express.json());
|
||||||
|
// enable cors
|
||||||
|
app.use(cors());
|
||||||
|
app.options('*', cors());
|
||||||
|
app.use('/public/', express.static('public'));
|
||||||
|
app.use(express.urlencoded({ extended: false }));
|
||||||
|
app.use(cookieParser());
|
||||||
|
app.engine('html', renderFile);
|
||||||
|
app.set('view engine', 'html');
|
||||||
|
app.use('/', indexRouter);
|
||||||
|
// cron job
|
||||||
|
// Custom error-handling middleware
|
||||||
|
app.use((err, req, res, next) => {
|
||||||
|
if (err) {
|
||||||
|
const statusCode = err.customCode || 500;
|
||||||
|
const message = err.message || 'Internal Server Error';
|
||||||
|
return makeResponse(res, statusCode, false, message)
|
||||||
|
} else {
|
||||||
|
next();
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
export default app;
|
77
server/bin/index.js
Normal file
77
server/bin/index.js
Normal file
|
@ -0,0 +1,77 @@
|
||||||
|
import app from "../app.js";
|
||||||
|
import debugLib from "debug";
|
||||||
|
import http from "http";
|
||||||
|
const debug = debugLib("your-project-name:server");
|
||||||
|
import dotenv from "dotenv";
|
||||||
|
import { Server } from "socket.io";
|
||||||
|
dotenv.config();
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Get port from environment and store in Express.
|
||||||
|
*/
|
||||||
|
|
||||||
|
const port = process.env.PORT || "3001";
|
||||||
|
console.log("App is running on port", port);
|
||||||
|
app.set("port", port);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Create HTTP server.
|
||||||
|
*/
|
||||||
|
|
||||||
|
const server = http.createServer(app);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Listen on provided port, on all network interfaces.
|
||||||
|
*/
|
||||||
|
|
||||||
|
server.listen(port);
|
||||||
|
server.on("error", onError);
|
||||||
|
server.on("listening", onListening);
|
||||||
|
|
||||||
|
const io = new Server(server, {
|
||||||
|
maxHttpBufferSize: 1e9,
|
||||||
|
//transports: ['websocket'], upgrade: false,
|
||||||
|
pingInterval: 1000 * 60 * 5,
|
||||||
|
pingTimeout: 1000 * 60 * 3,
|
||||||
|
cors: {
|
||||||
|
origin: "*",
|
||||||
|
},
|
||||||
|
});
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Event listener for HTTP server "error" event.
|
||||||
|
* @param {Object} error - The error object containing details about the error.
|
||||||
|
* @throws {Error} - Rethrows the error if it's not a listening error.
|
||||||
|
*/
|
||||||
|
|
||||||
|
function onError(error) {
|
||||||
|
if (error.syscall !== "listen") {
|
||||||
|
throw error;
|
||||||
|
}
|
||||||
|
|
||||||
|
let bind = typeof port === "string" ? "Pipe " + port : "Port " + port;
|
||||||
|
|
||||||
|
// handle specific listen errors with friendly messages
|
||||||
|
switch (error.code) {
|
||||||
|
case "EACCES":
|
||||||
|
console.error(bind + " requires elevated privileges");
|
||||||
|
process.exit(1);
|
||||||
|
break;
|
||||||
|
case "EADDRINUSE":
|
||||||
|
console.error(bind + " is already in use");
|
||||||
|
process.exit(1);
|
||||||
|
break;
|
||||||
|
default:
|
||||||
|
throw error;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Event listener for HTTP server "listening" event.
|
||||||
|
*/
|
||||||
|
|
||||||
|
function onListening() {
|
||||||
|
const addr = server.address();
|
||||||
|
const bind = typeof addr === "string" ? "pipe " + addr : "port " + addr.port;
|
||||||
|
debug("Listening on " + bind);
|
||||||
|
}
|
9
server/config/config.json
Normal file
9
server/config/config.json
Normal file
|
@ -0,0 +1,9 @@
|
||||||
|
{
|
||||||
|
"development": {
|
||||||
|
"username": "tennish",
|
||||||
|
"password": "Tensskdb@124",
|
||||||
|
"database": "Ai-Tennis-coach",
|
||||||
|
"host": "14.97.60.131",
|
||||||
|
"dialect": "postgres"
|
||||||
|
}
|
||||||
|
}
|
22
server/config/privateKeys.js
Executable file
22
server/config/privateKeys.js
Executable file
|
@ -0,0 +1,22 @@
|
||||||
|
import dotenv from "dotenv";
|
||||||
|
dotenv.config();
|
||||||
|
|
||||||
|
let {
|
||||||
|
NODE_ENV,
|
||||||
|
PORT,
|
||||||
|
DB_STRING_DEV,
|
||||||
|
TOKEN_SECRET,
|
||||||
|
REFRESH_TOKEN_SECRET,
|
||||||
|
NODE_SERVER_URL,
|
||||||
|
AI_SERVER_URL
|
||||||
|
} = process.env;
|
||||||
|
|
||||||
|
export const privateKey = {
|
||||||
|
'NODE_ENV':NODE_ENV,
|
||||||
|
'PORT':PORT,
|
||||||
|
'DB_STRING_DEV':DB_STRING_DEV,
|
||||||
|
'TOKEN_SECRET':TOKEN_SECRET,
|
||||||
|
'REFRESH_TOKEN_SECRET':REFRESH_TOKEN_SECRET,
|
||||||
|
'NODE_SERVER_URL':NODE_SERVER_URL,
|
||||||
|
'AI_SERVER_URL':AI_SERVER_URL
|
||||||
|
}
|
348
server/controller/assessment/index.js
Normal file
348
server/controller/assessment/index.js
Normal file
|
@ -0,0 +1,348 @@
|
||||||
|
import { catchAsyncAction, generateToken, makeResponse } from "../../helper/index.js";
|
||||||
|
import { responseMessages, statusCodes } from '../../helper/makeResponse/index.js';
|
||||||
|
import { addUserAnswersByUserId, addUserDrillsByUserId, createQuestions, getAllQuestions, getQuestionById, getUserAnswersByUserId, getUserTrainingByUserId, updateQuestionById } from "../../services/assessment.js";
|
||||||
|
import { getUserById } from "../../services/users.js";
|
||||||
|
import { privateKey } from "../../config/privateKeys.js";
|
||||||
|
import axios from "axios";
|
||||||
|
/**
|
||||||
|
* API handler to add assessment questions.
|
||||||
|
* Validates input and creates new questions in the database.
|
||||||
|
*
|
||||||
|
* @param {Object} req - The request object containing assessment questions.
|
||||||
|
* @param {Object} res - The response object used to send responses back to the client.
|
||||||
|
* @returns {Object} - A response object indicating the success or failure of the operation.
|
||||||
|
*/
|
||||||
|
export const addAssessmentQuestions = catchAsyncAction(async (req, res) => {
|
||||||
|
const questions = req.body.assessment; // Extracting assessment questions from the request body
|
||||||
|
|
||||||
|
// Input validation: ensure the array is not empty
|
||||||
|
if (!Array.isArray(questions) || questions.length === 0) {
|
||||||
|
return makeResponse(res, statusCodes.BAD_REQUEST, false, 'Request body must be an array of questions.', {});
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
for (const questionData of questions) {
|
||||||
|
const { question } = questionData; // Destructuring question from questionData
|
||||||
|
|
||||||
|
// Validate that all necessary fields are present
|
||||||
|
if (!question) {
|
||||||
|
return makeResponse(res, statusCodes.BAD_REQUEST, false, 'All fields are required.', {});
|
||||||
|
}
|
||||||
|
questionData.options = JSON.stringify(questionData.options || []); // Stringify options if they exist
|
||||||
|
}
|
||||||
|
|
||||||
|
try {
|
||||||
|
const questionData = await createQuestions(questions); // Create questions in the database
|
||||||
|
|
||||||
|
return makeResponse(res, statusCodes.SUCCESS, true, responseMessages.QUESTIONS_CREATED, questionData);
|
||||||
|
} catch (error) {
|
||||||
|
const code = error?.code || statusCodes.SERVER_ERROR;
|
||||||
|
const message = error?.message || responseMessages.SOMETHING_WRONG;
|
||||||
|
|
||||||
|
return makeResponse(res, code, false, message, {});
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
/**
|
||||||
|
* API handler to update an assessment question by its ID.
|
||||||
|
* Validates input and updates the question in the database.
|
||||||
|
*
|
||||||
|
* @param {Object} req - The request object containing the question ID and updated data.
|
||||||
|
* @param {Object} res - The response object used to send responses back to the client.
|
||||||
|
* @returns {Object} - A response object indicating the success or failure of the operation.
|
||||||
|
*/
|
||||||
|
export const updateAssessmentQuestionById = catchAsyncAction(async (req, res) => {
|
||||||
|
const { id } = req.params; // Get the question id from the URL
|
||||||
|
const { question, options, orderId } = req.body; // Get the updated data from the request body
|
||||||
|
|
||||||
|
try {
|
||||||
|
// Find the question by ID
|
||||||
|
const foundQuestion = await getQuestionById(id);
|
||||||
|
|
||||||
|
if (!foundQuestion) {
|
||||||
|
return makeResponse(res, statusCodes.NOT_FOUND, false, responseMessages.QUESTIONS_NOT_FOUND, {});
|
||||||
|
}
|
||||||
|
|
||||||
|
// Update the question with new data
|
||||||
|
foundQuestion.question = question || foundQuestion.question; // Update question if provided
|
||||||
|
foundQuestion.options = JSON.stringify(options) || foundQuestion.options; // Update options if provided
|
||||||
|
foundQuestion.orderId = orderId || foundQuestion.orderId; // Update orderId if provided
|
||||||
|
|
||||||
|
await updateQuestionById(foundQuestion); // Save the updated question
|
||||||
|
|
||||||
|
return makeResponse(res, statusCodes.SUCCESS, true, responseMessages.QUESTIONS_UPDATED, foundQuestion); // Return success response
|
||||||
|
|
||||||
|
} catch (error) {
|
||||||
|
const code = error?.code || statusCodes.SERVER_ERROR;
|
||||||
|
const message = error?.message || responseMessages.SOMETHING_WRONG;
|
||||||
|
|
||||||
|
return makeResponse(res, code, false, message, {});
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
/**
|
||||||
|
* API handler to get all assessment questions.
|
||||||
|
* Fetches all questions from the database and returns them.
|
||||||
|
*
|
||||||
|
* @param {Object} req - The request object.
|
||||||
|
* @param {Object} res - The response object used to send responses back to the client.
|
||||||
|
* @returns {Object} - A response object containing the fetched questions or an error message.
|
||||||
|
*/
|
||||||
|
export const getAssessmentQuestions = catchAsyncAction(async (req, res) => {
|
||||||
|
try {
|
||||||
|
const questions = await getAllQuestions(); // Get all questions from DB
|
||||||
|
|
||||||
|
if (!questions || questions.length === 0) {
|
||||||
|
return makeResponse(res, statusCodes.NOT_FOUND, false, responseMessages.QUESTIONS_NOT_FOUND, {});
|
||||||
|
}
|
||||||
|
|
||||||
|
// Return all questions in the response
|
||||||
|
return makeResponse(res, statusCodes.SUCCESS, true, responseMessages.QUESTIONS_FETCHED, questions);
|
||||||
|
|
||||||
|
} catch (error) {
|
||||||
|
const code = error?.code || statusCodes.SERVER_ERROR;
|
||||||
|
const message = error?.message || responseMessages.SOMETHING_WRONG;
|
||||||
|
|
||||||
|
return makeResponse(res, code, false, message, {});
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
/**
|
||||||
|
* API handler to add user assessment responses.
|
||||||
|
* Validates user input and saves user responses to the database.
|
||||||
|
*
|
||||||
|
* @param {Object} req - The request object containing user responses.
|
||||||
|
* @param {Object} res - The response object used to send responses back to the client.
|
||||||
|
* @returns {Object} - A response object indicating the success or failure of the operation.
|
||||||
|
*/
|
||||||
|
export const addUserAssessmentResponse = catchAsyncAction(async (req, res) => {
|
||||||
|
const { userId } = req.user; // Get userId from decoded JWT
|
||||||
|
|
||||||
|
try {
|
||||||
|
// Fetch current user details to check for changes
|
||||||
|
const currentUser = await getUserById(userId);
|
||||||
|
|
||||||
|
if (!currentUser) {
|
||||||
|
return makeResponse(res, statusCodes.NOT_FOUND, false, responseMessages.USER_NOT_FOUND, {});
|
||||||
|
}
|
||||||
|
|
||||||
|
// Initialize reqBody with default empty values
|
||||||
|
let reqBody = {
|
||||||
|
experience: "",
|
||||||
|
frequency: "",
|
||||||
|
struggle_area: "",
|
||||||
|
playing_style: "",
|
||||||
|
improvement_focus: "",
|
||||||
|
session_duration: ""
|
||||||
|
};
|
||||||
|
|
||||||
|
|
||||||
|
const answers = req.body.answers || []; // Extract answers from request body
|
||||||
|
answers.forEach((answer) => {
|
||||||
|
switch (answer.questionId) {
|
||||||
|
case 1:
|
||||||
|
reqBody.experience = answer.selectedOption; // Set experience based on selectedOption
|
||||||
|
break;
|
||||||
|
case 2:
|
||||||
|
reqBody.frequency = answer.selectedOption; // Set frequency based on selectedOption
|
||||||
|
break;
|
||||||
|
case 3:
|
||||||
|
reqBody.struggle_area = answer.selectedOption; // Set struggle_area based on selectedOption
|
||||||
|
break;
|
||||||
|
case 4:
|
||||||
|
reqBody.playing_style = answer.selectedOption; // Set playing_style based on selectedOption
|
||||||
|
break;
|
||||||
|
case 6:
|
||||||
|
reqBody.improvement_focus = answer.selectedOption; // Set improvement_focus based on selectedOption
|
||||||
|
break;
|
||||||
|
case 7:
|
||||||
|
reqBody.session_duration = answer.selectedOption; // Set session_duration based on selectedOption
|
||||||
|
break;
|
||||||
|
default:
|
||||||
|
break; // Ignore unrecognized question IDs
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
// Check if all keys in reqBody are non-empty
|
||||||
|
const missingFields = [];
|
||||||
|
for (const [key, value] of Object.entries(reqBody)) {
|
||||||
|
if (value === "" || value === null || value === undefined) {
|
||||||
|
missingFields.push(key);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (missingFields.length > 0) {
|
||||||
|
// If there are missing fields, return an error response
|
||||||
|
return makeResponse(res, statusCodes.BAD_REQUEST, false, "Please submit all answers", {});
|
||||||
|
}
|
||||||
|
|
||||||
|
const analysisData = await getTrainingAnalysis(reqBody); // Get training analysis based on user responses
|
||||||
|
if (!analysisData) {
|
||||||
|
return makeResponse(res, statusCodes.BAD_REQUEST, false, "Something went wrong", {});
|
||||||
|
}
|
||||||
|
|
||||||
|
const trainingData = {
|
||||||
|
drills: analysisData.data.drills,
|
||||||
|
recommendation: analysisData.data.recommendation,
|
||||||
|
videos: analysisData.data.videos,
|
||||||
|
yoga_exercises: analysisData.data.yoga_exercises,
|
||||||
|
weekly_plan: analysisData.data.weekly_plan,
|
||||||
|
videos_watched: analysisData.data.videos_watched ?? [],
|
||||||
|
}
|
||||||
|
trainingData.user_id = userId;
|
||||||
|
|
||||||
|
|
||||||
|
const addFields = {
|
||||||
|
user_id: userId,
|
||||||
|
answers: JSON.stringify(answers) || ""
|
||||||
|
};
|
||||||
|
|
||||||
|
const data = await addUserAnswersByUserId(addFields); // Save user answers
|
||||||
|
const trainingDrillsData = await addUserDrillsByUserId(trainingData); // Save training data
|
||||||
|
console.log(trainingDrillsData); // Log training drills data for debugging
|
||||||
|
|
||||||
|
|
||||||
|
return makeResponse(res, statusCodes.SUCCESS, true, responseMessages.DATA_ADDED, { answersRes: data, trainingDrillsData });
|
||||||
|
|
||||||
|
} catch (error) {
|
||||||
|
const code = error?.code || statusCodes.SERVER_ERROR;
|
||||||
|
const message = error?.message || responseMessages.SOMETHING_WRONG;
|
||||||
|
|
||||||
|
return makeResponse(res, code, false, message, {});
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
/**
|
||||||
|
* API handler to get user answers.
|
||||||
|
* Fetches user answers from the database and returns them.
|
||||||
|
*
|
||||||
|
* @param {Object} req - The request object.
|
||||||
|
* @param {Object} res - The response object used to send responses back to the client.
|
||||||
|
* @returns {Object} - A response object containing the fetched answers or an error message.
|
||||||
|
*/
|
||||||
|
export const getUserAnswers = catchAsyncAction(async (req, res) => {
|
||||||
|
const { userId } = req.user; // Get userId from decoded JWT
|
||||||
|
try {
|
||||||
|
// Fetch current user details to check for changes
|
||||||
|
const currentUser = await getUserById(userId);
|
||||||
|
|
||||||
|
if (!currentUser) {
|
||||||
|
return makeResponse(res, statusCodes.NOT_FOUND, false, responseMessages.USER_NOT_FOUND, {});
|
||||||
|
}
|
||||||
|
|
||||||
|
const data = await getUserAnswersByUserId(userId); // Get user answers
|
||||||
|
|
||||||
|
// Return the updated user data (excluding password)
|
||||||
|
return makeResponse(res, statusCodes.SUCCESS, true, responseMessages.DATA_FETCHED, data);
|
||||||
|
} catch (error) {
|
||||||
|
const code = error?.code || statusCodes.SERVER_ERROR;
|
||||||
|
const message = error?.message || responseMessages.SOMETHING_WRONG;
|
||||||
|
|
||||||
|
return makeResponse(res, code, false, message, {});
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Helper function to handle AI analysis.
|
||||||
|
* Sends user data to the AI server for analysis and returns the response.
|
||||||
|
*
|
||||||
|
* @param {Object} body - The data to be sent to the AI server.
|
||||||
|
* @returns {Object|null} - The response from the AI server or null if an error occurs.
|
||||||
|
*/
|
||||||
|
const getTrainingAnalysis = async (body) => {
|
||||||
|
const postApiUrl = `${privateKey.AI_SERVER_URL}recommend`; // Construct the API URL
|
||||||
|
console.log(postApiUrl); // Log the API URL for debugging
|
||||||
|
try {
|
||||||
|
const aiResponse = await axios.post(postApiUrl, body); // Send POST request to AI server
|
||||||
|
return aiResponse;
|
||||||
|
} catch (err) {
|
||||||
|
console.error('AI analysis error:', err);
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
/**
|
||||||
|
* API handler to get user training details for a specific day.
|
||||||
|
* Fetches training data from the database and filters it by day.
|
||||||
|
*
|
||||||
|
* @param {Object} req - The request object containing the day query parameter.
|
||||||
|
* @param {Object} res - The response object used to send responses back to the client.
|
||||||
|
* @returns {Object} - A response object containing the training data for the specified day or an error message.
|
||||||
|
*/
|
||||||
|
export const getUserDayTraining = catchAsyncAction(async (req, res) => {
|
||||||
|
const { userId } = req.user; // Get userId from decoded JWT
|
||||||
|
try {
|
||||||
|
const { day } = req.query; // Get day from query parameters
|
||||||
|
|
||||||
|
const currentUser = await getUserById(userId);
|
||||||
|
|
||||||
|
if (!currentUser) {
|
||||||
|
return makeResponse(res, statusCodes.NOT_FOUND, false, responseMessages.USER_NOT_FOUND, {});
|
||||||
|
}
|
||||||
|
|
||||||
|
const data = await getUserTrainingByUserId(userId);
|
||||||
|
if (!data) {
|
||||||
|
return makeResponse(res, statusCodes.NOT_FOUND, false, 'Training Details Not Found', {});
|
||||||
|
}
|
||||||
|
let dayObject = data;
|
||||||
|
if (day) {
|
||||||
|
dayObject = data?.dataValues?.weekly_plan.filter(item => item.day === `Day ${day}`)[0]; // Filter training data by day
|
||||||
|
dayObject.videos_watched = data?.dataValues?.videos_watched;
|
||||||
|
}
|
||||||
|
return makeResponse(res, statusCodes.SUCCESS, true, responseMessages.DATA_FETCHED, dayObject ?? 'No training for the day'); // Return training data or message
|
||||||
|
} catch (error) {
|
||||||
|
const code = error?.code || statusCodes.SERVER_ERROR;
|
||||||
|
const message = error?.message || responseMessages.SOMETHING_WRONG;
|
||||||
|
return makeResponse(res, code, false, message, {});
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
/**
|
||||||
|
* API handler to update the list of watched videos by the user.
|
||||||
|
* Validates input and updates the training record in the database.
|
||||||
|
*
|
||||||
|
* @param {Object} req - The request object containing the day query parameter.
|
||||||
|
* @param {Object} res - The response object used to send responses back to the client.
|
||||||
|
* @returns {Object} - A response object indicating the success or failure of the operation.
|
||||||
|
*/
|
||||||
|
export const updateVideoWatchByUser = catchAsyncAction(async (req, res) => {
|
||||||
|
const { userId } = req.user; // Get userId from decoded JWT
|
||||||
|
try {
|
||||||
|
const { day } = req.query; // Get day from query parameters
|
||||||
|
// Fetch current user details to check for changes
|
||||||
|
const currentUser = await getUserById(userId);
|
||||||
|
|
||||||
|
if (!currentUser) {
|
||||||
|
return makeResponse(res, statusCodes.NOT_FOUND, false, responseMessages.USER_NOT_FOUND, {});
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
const training = await getUserTrainingByUserId(userId);
|
||||||
|
|
||||||
|
if (!training) {
|
||||||
|
return makeResponse(res, statusCodes.NOT_FOUND, false, 'Training Details Not Found', {});
|
||||||
|
}
|
||||||
|
|
||||||
|
// Ensure videos_watched is initialized as an empty array if it's null or undefined
|
||||||
|
if (!Array.isArray(training.videos_watched)) {
|
||||||
|
training.videos_watched = [];
|
||||||
|
}
|
||||||
|
|
||||||
|
const newVideo = day; // Set new video to be added
|
||||||
|
|
||||||
|
|
||||||
|
if (!training.videos_watched.includes(newVideo)) {
|
||||||
|
// Add the new value to the array if it doesn't exist
|
||||||
|
training.videos_watched = [...training.videos_watched, newVideo]; // Update the watched videos array
|
||||||
|
}
|
||||||
|
|
||||||
|
// Save the updated training record
|
||||||
|
const data = await training.save(); // Persist changes to the database
|
||||||
|
|
||||||
|
return makeResponse(res, statusCodes.SUCCESS, true, responseMessages.DATA_FETCHED, data);
|
||||||
|
} catch (error) {
|
||||||
|
const code = error?.code || statusCodes.SERVER_ERROR;
|
||||||
|
const message = error?.message || responseMessages.SOMETHING_WRONG;
|
||||||
|
return makeResponse(res, code, false, message, {});
|
||||||
|
}
|
||||||
|
});
|
1389
server/controller/users/index.js
Normal file
1389
server/controller/users/index.js
Normal file
File diff suppressed because it is too large
Load diff
8
server/helper/catchAsyncAction/index.js
Executable file
8
server/helper/catchAsyncAction/index.js
Executable file
|
@ -0,0 +1,8 @@
|
||||||
|
import { makeResponse, statusCodes } from "../index.js";
|
||||||
|
|
||||||
|
// Wrapper for catch block
|
||||||
|
export const catchAsyncAction = fn => {
|
||||||
|
return (req, res, next) => {
|
||||||
|
fn(req, res, next).catch((err) => makeResponse(res, statusCodes.BAD_REQUEST, false, err.message));
|
||||||
|
};
|
||||||
|
};
|
5
server/helper/index.js
Executable file
5
server/helper/index.js
Executable file
|
@ -0,0 +1,5 @@
|
||||||
|
export * from './makeResponse/index.js';
|
||||||
|
export * from './catchAsyncAction/index.js';
|
||||||
|
export * from './qrcode_generator/index.js';
|
||||||
|
export * from './s3/index.js';
|
||||||
|
export * from './jwt/index.js';
|
59
server/helper/jwt/index.js
Normal file
59
server/helper/jwt/index.js
Normal file
|
@ -0,0 +1,59 @@
|
||||||
|
import jwt from 'jsonwebtoken';
|
||||||
|
import { privateKey } from '../../config/privateKeys.js';
|
||||||
|
import { makeResponse } from '../makeResponse/index.js';
|
||||||
|
const { sign, verify } = jwt;
|
||||||
|
|
||||||
|
|
||||||
|
// This function creates tokens based on user data and the rememberMe flag
|
||||||
|
export const generateToken = async (data, rememberMe) => {
|
||||||
|
// Set expiration times
|
||||||
|
const accessTokenExpiresIn = rememberMe ? '365d' : '1d'; // Access token expires in 365 days or 1 day
|
||||||
|
const refreshTokenExpiresIn = '7d'; // Refresh token expires in 7 days
|
||||||
|
|
||||||
|
try {
|
||||||
|
const accessToken = sign({ data }, privateKey.TOKEN_SECRET, { expiresIn: accessTokenExpiresIn });
|
||||||
|
const refreshToken = sign({ data }, privateKey.REFRESH_TOKEN_SECRET, { expiresIn: refreshTokenExpiresIn });
|
||||||
|
|
||||||
|
return { accessToken, refreshToken };
|
||||||
|
} catch (error) {
|
||||||
|
throw new Error("Error generating tokens: " + error.message);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
|
||||||
|
// This function checks the validity of a given token using the appropriate secret
|
||||||
|
export const verifyToken = async (token, type = 'access') => {
|
||||||
|
try {
|
||||||
|
// Use different secrets for access token and refresh token
|
||||||
|
const secret = type === 'access' ? privateKey.TOKEN_SECRET : privateKey.REFRESH_TOKEN_SECRET;
|
||||||
|
|
||||||
|
return verify(token, secret);
|
||||||
|
} catch (error) {
|
||||||
|
throw new Error("Token verification failed: " + error.message);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
// This function handles the logic for refreshing access tokens using a valid refresh token
|
||||||
|
export const refreshAccessToken = async (req, res) => {
|
||||||
|
const { refreshToken } = req.body;
|
||||||
|
|
||||||
|
if (!refreshToken) {
|
||||||
|
return makeResponse(res, 403, false, 'Refresh Token is required'); // Respond if no refresh token is provided
|
||||||
|
}
|
||||||
|
|
||||||
|
try {
|
||||||
|
// Verify the refresh token
|
||||||
|
const decoded = await verifyToken(refreshToken, 'refresh');
|
||||||
|
|
||||||
|
// Generate new access token (and optionally a new refresh token)
|
||||||
|
const { accessToken, refreshToken: newRefreshToken } = await generateToken(decoded.data, true); // Example with rememberMe = true
|
||||||
|
|
||||||
|
return makeResponse(res, 200, true, 'New access and refresh token', {
|
||||||
|
token: accessToken,
|
||||||
|
refreshToken: newRefreshToken // Optionally send a new refresh token
|
||||||
|
});
|
||||||
|
} catch (error) {
|
||||||
|
console.log(error);
|
||||||
|
return makeResponse(res, 403, false, 'Invalid or expired refresh token'); // Handle invalid refresh token
|
||||||
|
}
|
||||||
|
};
|
51
server/helper/makeResponse/index.js
Executable file
51
server/helper/makeResponse/index.js
Executable file
|
@ -0,0 +1,51 @@
|
||||||
|
export const responseMessages = {
|
||||||
|
USER_CREATED: "Users created successfully!",
|
||||||
|
USER_ALREADY_EXISTS: "Email is already in use",
|
||||||
|
USERNAME_ALREADY_EXISTS: "Username is already in use",
|
||||||
|
INVALID_CREDENTIALS: "Invalid Password",
|
||||||
|
USER_NOT_FOUND: "User doesn't exists",
|
||||||
|
UNAUTHORIZED: "Unauthorized",
|
||||||
|
SOMETHING_WRONG: "Something went wrong",
|
||||||
|
LOGIN_SUCCESS: "Login successfully",
|
||||||
|
USER_UPDATED: "User profile updated successfully",
|
||||||
|
|
||||||
|
QUESTIONS_CREATED: "Questions created successfully!",
|
||||||
|
QUESTIONS_UPDATED: "Question updated successfully",
|
||||||
|
QUESTIONS_NOT_FOUND: 'Question not found',
|
||||||
|
QUESTIONS_FETCHED: "Questions Fetched Successfully",
|
||||||
|
|
||||||
|
DATA_ADDED: "Data added successfully",
|
||||||
|
DATA_FETCHED: "Data fetched successfully",
|
||||||
|
INVALID_PARAMETERS: "Parameters are missing",
|
||||||
|
SHOT_FAILURE: "You haven't played this shot"
|
||||||
|
}
|
||||||
|
|
||||||
|
export const notificationPayload = {}
|
||||||
|
|
||||||
|
export const statusCodes = {
|
||||||
|
'SUCCESS': 200,
|
||||||
|
'RECORD_CREATED': 201,
|
||||||
|
'BAD_REQUEST': 400,
|
||||||
|
'AUTH_ERROR': 401,
|
||||||
|
'FORBIDDEN': 403,
|
||||||
|
'NOT_FOUND': 404,
|
||||||
|
'INVALID_REQUEST': 405,
|
||||||
|
'RECORD_ALREADY_EXISTS': 409,
|
||||||
|
'SERVER_ERROR': 500,
|
||||||
|
'UNAUTHORIZED': 400
|
||||||
|
}
|
||||||
|
|
||||||
|
const makeResponse = async (res, statusCode, success, message, payload = null, meta = {}) =>
|
||||||
|
new Promise(resolve => {
|
||||||
|
res.status(statusCode)
|
||||||
|
.send({
|
||||||
|
success,
|
||||||
|
code: statusCode,
|
||||||
|
message,
|
||||||
|
data: payload,
|
||||||
|
meta
|
||||||
|
});
|
||||||
|
resolve(statusCode);
|
||||||
|
});
|
||||||
|
|
||||||
|
export { makeResponse };
|
12
server/helper/qrcode_generator/index.js
Executable file
12
server/helper/qrcode_generator/index.js
Executable file
|
@ -0,0 +1,12 @@
|
||||||
|
import QRCode from 'qrcode'
|
||||||
|
|
||||||
|
//Genrate QR code
|
||||||
|
const generateQR = async text => {
|
||||||
|
try {
|
||||||
|
return QRCode.toDataURL(text);
|
||||||
|
} catch (err) {
|
||||||
|
console.error(err)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
export default generateQR;
|
35
server/helper/s3/index.js
Executable file
35
server/helper/s3/index.js
Executable file
|
@ -0,0 +1,35 @@
|
||||||
|
import { getSignedUrl } from "@aws-sdk/s3-request-presigner";
|
||||||
|
import { GetObjectCommand, S3Client } from "@aws-sdk/client-s3";
|
||||||
|
import { privateKey } from '../../config/privateKeys.js';
|
||||||
|
|
||||||
|
const useGetSignedUrl = (key,bucketName) => {
|
||||||
|
const client = new S3Client({
|
||||||
|
credentials: {
|
||||||
|
accessKeyId: privateKey.S3_ACCESS_KEY,
|
||||||
|
secretAccessKey: privateKey.S3_SECRET_KEY
|
||||||
|
},
|
||||||
|
region: privateKey.S3_REGION
|
||||||
|
})
|
||||||
|
|
||||||
|
const getUrl = async (key) => {
|
||||||
|
try {
|
||||||
|
const url = await getSignedUrl(
|
||||||
|
client,
|
||||||
|
new GetObjectCommand({
|
||||||
|
Bucket: bucketName,
|
||||||
|
Key: key,
|
||||||
|
}),
|
||||||
|
{ expiresIn: 3600 }
|
||||||
|
);
|
||||||
|
return url;
|
||||||
|
} catch (error) {
|
||||||
|
return error;
|
||||||
|
}
|
||||||
|
};
|
||||||
|
return getUrl(key);
|
||||||
|
|
||||||
|
};
|
||||||
|
|
||||||
|
export {
|
||||||
|
useGetSignedUrl
|
||||||
|
}
|
23
server/loaders/db/index.js
Executable file
23
server/loaders/db/index.js
Executable file
|
@ -0,0 +1,23 @@
|
||||||
|
import { Sequelize, DataTypes } from 'sequelize';
|
||||||
|
import { privateKey } from '../../config/privateKeys.js'; // Make sure your DB string is stored here
|
||||||
|
|
||||||
|
// Create a new Sequelize instance with the PostgreSQL connection string
|
||||||
|
const sequelize = new Sequelize(privateKey.DB_STRING_DEV, {
|
||||||
|
dialect: 'postgres', // The database dialect
|
||||||
|
logging: false, // Disable logging (optional)
|
||||||
|
});
|
||||||
|
|
||||||
|
// Test the connection to ensure it's working
|
||||||
|
const connectDb = async () => {
|
||||||
|
try {
|
||||||
|
await sequelize.authenticate();
|
||||||
|
console.log('Database connection established');
|
||||||
|
} catch (err) {
|
||||||
|
console.error('Error connecting to the database:', err);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
connectDb();
|
||||||
|
|
||||||
|
// Export the Sequelize instance to use in other parts of the application
|
||||||
|
export { sequelize };
|
1
server/loaders/index.js
Executable file
1
server/loaders/index.js
Executable file
|
@ -0,0 +1 @@
|
||||||
|
export * from './db/index.js';
|
39
server/middlewares/auth.js
Normal file
39
server/middlewares/auth.js
Normal file
|
@ -0,0 +1,39 @@
|
||||||
|
import { statusCodes, makeResponse, responseMessages } from '../helper/index.js';
|
||||||
|
import { verifyToken } from '../helper/index.js';
|
||||||
|
|
||||||
|
// Middleware function for authentication
|
||||||
|
export default async function auth(req, res, next) {
|
||||||
|
try {
|
||||||
|
// Retrieve the token from the request headers
|
||||||
|
const token = req.headers["Authorization"] || req.headers["authorization"];
|
||||||
|
|
||||||
|
// Check if token is present or not
|
||||||
|
if (!token) {
|
||||||
|
|
||||||
|
return makeResponse(res, statusCodes.AUTH_ERROR, false, responseMessages.UNAUTHORIZED);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Verify the token using the verifyToken function
|
||||||
|
const decode = await verifyToken(token, 'access');
|
||||||
|
|
||||||
|
// Verify if token is valid or not
|
||||||
|
if (!decode) {
|
||||||
|
return makeResponse(res, statusCodes.AUTH_ERROR, false, responseMessages.UNAUTHORIZED);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Check if the decoded token contains an email
|
||||||
|
if (decode.data?.email == null || decode.data?.email == undefined) {
|
||||||
|
|
||||||
|
return makeResponse(res, statusCodes.AUTH_ERROR, false, responseMessages.UNAUTHORIZED);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Attach the user data to the request object
|
||||||
|
req.user = decode.data;
|
||||||
|
|
||||||
|
|
||||||
|
next();
|
||||||
|
} catch (err) {
|
||||||
|
|
||||||
|
return makeResponse(res, statusCodes.AUTH_ERROR, false, err.message);
|
||||||
|
}
|
||||||
|
};
|
70
server/middlewares/upload.js
Normal file
70
server/middlewares/upload.js
Normal file
|
@ -0,0 +1,70 @@
|
||||||
|
import multer from "multer";
|
||||||
|
import { diskStorage } from "multer";
|
||||||
|
import path from 'path';
|
||||||
|
|
||||||
|
// Allowed video file extensions
|
||||||
|
const allowedVideoTypes = ['.mp4', '.mov', '.avi', '.temp'];
|
||||||
|
|
||||||
|
// Filter to check if the file is an image or a video (with specific extensions)
|
||||||
|
const multerFilter = (req, file, cb) => {
|
||||||
|
// Check if the file is an image
|
||||||
|
if (file.mimetype.startsWith("image")) {
|
||||||
|
cb(null, true);
|
||||||
|
} else if (file.mimetype.startsWith("video")) {
|
||||||
|
// Check if the video has a valid extension (.mp4, .mov, .avi)
|
||||||
|
const extname = path.extname(file.originalname).toLowerCase();
|
||||||
|
if (allowedVideoTypes.includes(extname)) {
|
||||||
|
cb(null, true);
|
||||||
|
} else {
|
||||||
|
cb(new Error("Only .temp, .mp4, .mov, or .avi video files are allowed."), false); // Reject invalid video types
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
cb(new Error("Please upload only images or videos."), false); // Reject if neither image nor video
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
// Storage settings
|
||||||
|
const storage = diskStorage({
|
||||||
|
destination: (req, file, cb) => {
|
||||||
|
// Dynamically set the destination folder based on MIME type
|
||||||
|
if (file.mimetype.startsWith("image")) {
|
||||||
|
cb(null, './public/images');
|
||||||
|
} else if (file.mimetype.startsWith("video")) {
|
||||||
|
cb(null, './public/videos');
|
||||||
|
}
|
||||||
|
},
|
||||||
|
filename: (req, file, cb) => {
|
||||||
|
|
||||||
|
const originalName = path.basename(file.originalname, path.extname(file.originalname));
|
||||||
|
|
||||||
|
|
||||||
|
const customFileName = `${originalName}_${Date.now()}${path.extname(file.originalname)}`;
|
||||||
|
|
||||||
|
|
||||||
|
cb(null, customFileName); // E.g., "myImage_1634567890123.jpg" or "videoClip_1634567890123.mp4"
|
||||||
|
}
|
||||||
|
|
||||||
|
});
|
||||||
|
|
||||||
|
|
||||||
|
const upload = multer({
|
||||||
|
storage: storage,
|
||||||
|
fileFilter: multerFilter,
|
||||||
|
limits: {
|
||||||
|
fileSize: 1024 * 1024 * 50 // 20MB file size limit
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
// Error handling middleware for multer
|
||||||
|
export const multerErrorHandler = (err, req, res, next) => {
|
||||||
|
if (err instanceof multer.MulterError) {
|
||||||
|
// If the error is related to file size or other Multer error
|
||||||
|
return res.status(400).json({ message: err.message });
|
||||||
|
} else if (err) {
|
||||||
|
|
||||||
|
return res.status(400).json({ message: err.message });
|
||||||
|
}
|
||||||
|
next();
|
||||||
|
}
|
||||||
|
|
||||||
|
export default upload;
|
123
server/migrations/20241212070757-add-metrics-to-user-uploads.cjs
Normal file
123
server/migrations/20241212070757-add-metrics-to-user-uploads.cjs
Normal file
|
@ -0,0 +1,123 @@
|
||||||
|
'use strict';
|
||||||
|
|
||||||
|
module.exports = {
|
||||||
|
up: async (queryInterface, Sequelize) => {
|
||||||
|
const { NODE_ENV } = process.env; // Get the current environment from NODE_ENV
|
||||||
|
|
||||||
|
// Conditional logic based on environment
|
||||||
|
if (NODE_ENV === 'development' || NODE_ENV === 'staging') {
|
||||||
|
console.log(`Applying migrations for ${NODE_ENV} environment...`);
|
||||||
|
|
||||||
|
await queryInterface.changeColumn('user_uploads', 'file_name', {
|
||||||
|
type: Sequelize.TEXT,
|
||||||
|
allowNull: false, // You can keep it as required, adjust as needed
|
||||||
|
});
|
||||||
|
|
||||||
|
// Change the 'file_name' column to TEXT type
|
||||||
|
await queryInterface.changeColumn('user_uploads', 'file_path', {
|
||||||
|
type: Sequelize.TEXT,
|
||||||
|
allowNull: false, // You can keep it as required, adjust as needed
|
||||||
|
});
|
||||||
|
// Change the 'file_name' column to TEXT type
|
||||||
|
await queryInterface.changeColumn('user_uploads', 'file_type', {
|
||||||
|
type: Sequelize.TEXT,
|
||||||
|
allowNull: false, // You can keep it as required, adjust as needed
|
||||||
|
});
|
||||||
|
// Change the 'file_name' column to TEXT type
|
||||||
|
await queryInterface.changeColumn('user_uploads', 'overall_metrics', {
|
||||||
|
type: Sequelize.TEXT,
|
||||||
|
allowNull: false, // You can keep it as required, adjust as needed
|
||||||
|
});
|
||||||
|
// Change the 'file_name' column to TEXT type
|
||||||
|
await queryInterface.changeColumn('user_uploads', 'backhand', {
|
||||||
|
type: Sequelize.TEXT,
|
||||||
|
allowNull: false, // You can keep it as required, adjust as needed
|
||||||
|
});
|
||||||
|
await queryInterface.changeColumn('user_uploads', 'service', {
|
||||||
|
type: Sequelize.TEXT,
|
||||||
|
allowNull: false, // You can keep it as required, adjust as needed
|
||||||
|
});
|
||||||
|
await queryInterface.changeColumn('user_uploads', 'smash', {
|
||||||
|
type: Sequelize.TEXT,
|
||||||
|
allowNull: false, // You can keep it as required, adjust as needed
|
||||||
|
});
|
||||||
|
await queryInterface.changeColumn('user_uploads', 'forehand', {
|
||||||
|
type: Sequelize.TEXT,
|
||||||
|
allowNull: false, // You can keep it as required, adjust as needed
|
||||||
|
});
|
||||||
|
|
||||||
|
// Add columns only for development or staging environments
|
||||||
|
await queryInterface.addColumn('user_uploads', 'overall_metrics', {
|
||||||
|
type: Sequelize.STRING,
|
||||||
|
allowNull: true,
|
||||||
|
});
|
||||||
|
await queryInterface.addColumn('user_uploads', 'backhand', {
|
||||||
|
type: Sequelize.STRING,
|
||||||
|
allowNull: true,
|
||||||
|
});
|
||||||
|
await queryInterface.addColumn('user_uploads', 'forehand', {
|
||||||
|
type: Sequelize.STRING,
|
||||||
|
allowNull: true,
|
||||||
|
});
|
||||||
|
await queryInterface.addColumn('user_uploads', 'service', {
|
||||||
|
type: Sequelize.STRING,
|
||||||
|
allowNull: true,
|
||||||
|
});
|
||||||
|
await queryInterface.addColumn('user_uploads', 'smash', {
|
||||||
|
type: Sequelize.STRING,
|
||||||
|
allowNull: true,
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
// Optionally, you could apply the columns to the production environment as well:
|
||||||
|
if (NODE_ENV === 'production') {
|
||||||
|
console.log('Applying migrations for production environment...');
|
||||||
|
// You can add production-specific migration logic if needed here.
|
||||||
|
// For now, it's the same as dev and staging
|
||||||
|
await queryInterface.addColumn('user_uploads', 'overall_metrics', {
|
||||||
|
type: Sequelize.STRING,
|
||||||
|
allowNull: true,
|
||||||
|
});
|
||||||
|
await queryInterface.addColumn('user_uploads', 'backhand', {
|
||||||
|
type: Sequelize.STRING,
|
||||||
|
allowNull: true,
|
||||||
|
});
|
||||||
|
await queryInterface.addColumn('user_uploads', 'forehand', {
|
||||||
|
type: Sequelize.STRING,
|
||||||
|
allowNull: true,
|
||||||
|
});
|
||||||
|
await queryInterface.addColumn('user_uploads', 'service', {
|
||||||
|
type: Sequelize.STRING,
|
||||||
|
allowNull: true,
|
||||||
|
});
|
||||||
|
await queryInterface.addColumn('user_uploads', 'smash', {
|
||||||
|
type: Sequelize.STRING,
|
||||||
|
allowNull: true,
|
||||||
|
});
|
||||||
|
}
|
||||||
|
},
|
||||||
|
|
||||||
|
down: async (queryInterface, Sequelize) => {
|
||||||
|
const { NODE_ENV } = process.env; // Get the current environment from NODE_ENV
|
||||||
|
|
||||||
|
// Remove columns based on the environment
|
||||||
|
if (NODE_ENV === 'development' || NODE_ENV === 'staging') {
|
||||||
|
console.log(`Rolling back migrations for ${NODE_ENV} environment...`);
|
||||||
|
await queryInterface.removeColumn('user_uploads', 'overall_metrics');
|
||||||
|
await queryInterface.removeColumn('user_uploads', 'backhand');
|
||||||
|
await queryInterface.removeColumn('user_uploads', 'forehand');
|
||||||
|
await queryInterface.removeColumn('user_uploads', 'service');
|
||||||
|
await queryInterface.removeColumn('user_uploads', 'smash');
|
||||||
|
}
|
||||||
|
|
||||||
|
// Optionally, you could apply the rollback for production as well:
|
||||||
|
if (NODE_ENV === 'production') {
|
||||||
|
console.log('Rolling back migrations for production environment...');
|
||||||
|
await queryInterface.removeColumn('user_uploads', 'overall_metrics');
|
||||||
|
await queryInterface.removeColumn('user_uploads', 'backhand');
|
||||||
|
await queryInterface.removeColumn('user_uploads', 'forehand');
|
||||||
|
await queryInterface.removeColumn('user_uploads', 'service');
|
||||||
|
await queryInterface.removeColumn('user_uploads', 'smash');
|
||||||
|
}
|
||||||
|
}
|
||||||
|
};
|
40
server/model/assessment.js
Normal file
40
server/model/assessment.js
Normal file
|
@ -0,0 +1,40 @@
|
||||||
|
import { sequelize } from './../loaders/index.js';
|
||||||
|
import { DataTypes } from 'sequelize';
|
||||||
|
|
||||||
|
const assessmentQuestion = sequelize.define('assessmentQuestion', {
|
||||||
|
id: {
|
||||||
|
type: DataTypes.INTEGER,
|
||||||
|
primaryKey: true,
|
||||||
|
autoIncrement: true,
|
||||||
|
},
|
||||||
|
orderId: {
|
||||||
|
type: DataTypes.INTEGER,
|
||||||
|
allowNull: false,
|
||||||
|
|
||||||
|
},
|
||||||
|
question: {
|
||||||
|
type: DataTypes.STRING,
|
||||||
|
allowNull: false,
|
||||||
|
},
|
||||||
|
options: {
|
||||||
|
type: DataTypes.TEXT,
|
||||||
|
allowNull: true,
|
||||||
|
}
|
||||||
|
}, {
|
||||||
|
tableName: 'assessment_questions', // Define the name of the table
|
||||||
|
});
|
||||||
|
|
||||||
|
// Sync the model with the database
|
||||||
|
const syncDb = async () => {
|
||||||
|
try {
|
||||||
|
await sequelize.sync({ alter: false });
|
||||||
|
console.log('assessmentQuestion table created (or already exists)');
|
||||||
|
} catch (err) {
|
||||||
|
console.error('Error syncing the database:', err);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
// Call the sync function
|
||||||
|
syncDb();
|
||||||
|
|
||||||
|
export { assessmentQuestion };
|
56
server/model/training.js
Normal file
56
server/model/training.js
Normal file
|
@ -0,0 +1,56 @@
|
||||||
|
import { sequelize } from './../loaders/index.js';
|
||||||
|
import { DataTypes } from 'sequelize';
|
||||||
|
|
||||||
|
const Training = sequelize.define('training', {
|
||||||
|
id: {
|
||||||
|
type: DataTypes.INTEGER,
|
||||||
|
primaryKey: true,
|
||||||
|
autoIncrement: true,
|
||||||
|
},
|
||||||
|
user_id: {
|
||||||
|
type: DataTypes.INTEGER,
|
||||||
|
allowNull: false,
|
||||||
|
|
||||||
|
},
|
||||||
|
drills: {
|
||||||
|
type: DataTypes.ARRAY(DataTypes.STRING), // Defines an array of strings,
|
||||||
|
allowNull: true,
|
||||||
|
},
|
||||||
|
recommendation: {
|
||||||
|
type: DataTypes.TEXT,
|
||||||
|
allowNull: true,
|
||||||
|
},
|
||||||
|
videos: {
|
||||||
|
type: DataTypes.ARRAY(DataTypes.STRING),
|
||||||
|
allowNull: true,
|
||||||
|
},
|
||||||
|
yoga_exercises: {
|
||||||
|
type: DataTypes.ARRAY(DataTypes.STRING),
|
||||||
|
allowNull: true,
|
||||||
|
},
|
||||||
|
weekly_plan: {
|
||||||
|
type: DataTypes.JSONB, // Defines a JSONB column,
|
||||||
|
allowNull: true,
|
||||||
|
},
|
||||||
|
videos_watched: {
|
||||||
|
type: DataTypes.ARRAY(DataTypes.STRING),
|
||||||
|
allowNull: true,
|
||||||
|
},
|
||||||
|
}, {
|
||||||
|
tableName: 'training_content', // Define the name of the table
|
||||||
|
});
|
||||||
|
|
||||||
|
// Sync the model with the database
|
||||||
|
const syncDb = async () => {
|
||||||
|
try {
|
||||||
|
await sequelize.sync({ alter: false });
|
||||||
|
console.log('training table created (or already exists)');
|
||||||
|
} catch (err) {
|
||||||
|
console.error('Error syncing the database:', err);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
// Call the sync function
|
||||||
|
syncDb();
|
||||||
|
|
||||||
|
export { Training };
|
70
server/model/user.js
Normal file
70
server/model/user.js
Normal file
|
@ -0,0 +1,70 @@
|
||||||
|
import { sequelize } from './../loaders/index.js'; // Import the sequelize instance
|
||||||
|
import { DataTypes } from 'sequelize';
|
||||||
|
|
||||||
|
// Define the 'User' model
|
||||||
|
const User = sequelize.define('User', {
|
||||||
|
id: {
|
||||||
|
type: DataTypes.INTEGER,
|
||||||
|
primaryKey: true,
|
||||||
|
autoIncrement: true,
|
||||||
|
},
|
||||||
|
username: {
|
||||||
|
type: DataTypes.STRING(100),
|
||||||
|
allowNull: false,
|
||||||
|
},
|
||||||
|
full_name: {
|
||||||
|
type: DataTypes.STRING(100),
|
||||||
|
allowNull: false,
|
||||||
|
},
|
||||||
|
email: {
|
||||||
|
type: DataTypes.STRING(255),
|
||||||
|
allowNull: false,
|
||||||
|
unique: true, // Email should be unique
|
||||||
|
validate: {
|
||||||
|
isEmail: true, // Validate email format
|
||||||
|
},
|
||||||
|
},
|
||||||
|
password: {
|
||||||
|
type: DataTypes.STRING(255),
|
||||||
|
allowNull: false, // Password must be provided
|
||||||
|
},
|
||||||
|
profile_pic: {
|
||||||
|
type: DataTypes.TEXT, // Profile picture can be a URL or base64 string
|
||||||
|
allowNull: true,
|
||||||
|
},
|
||||||
|
phone_number: {
|
||||||
|
type: DataTypes.STRING(15),
|
||||||
|
allowNull: true, // Phone number is optional
|
||||||
|
},
|
||||||
|
age: {
|
||||||
|
type: DataTypes.STRING(15),
|
||||||
|
allowNull: true,
|
||||||
|
},
|
||||||
|
gender: {
|
||||||
|
type: DataTypes.STRING(15),
|
||||||
|
allowNull: true,
|
||||||
|
},
|
||||||
|
location: {
|
||||||
|
type: DataTypes.STRING(255),
|
||||||
|
allowNull: true,
|
||||||
|
}
|
||||||
|
}, {
|
||||||
|
tableName: 'users', // Define the name of the table
|
||||||
|
timestamps: true,
|
||||||
|
});
|
||||||
|
|
||||||
|
// Sync the model with the database (creates the table if it doesn't exist)
|
||||||
|
const syncDb = async () => {
|
||||||
|
try {
|
||||||
|
await sequelize.sync({ force: false });
|
||||||
|
console.log('User table created (or already exists)');
|
||||||
|
} catch (err) {
|
||||||
|
console.error('Error syncing the database:', err);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
// Call the sync function
|
||||||
|
syncDb();
|
||||||
|
|
||||||
|
// Export the model to use it in other parts of your application
|
||||||
|
export { User };
|
48
server/model/userAnswered.js
Normal file
48
server/model/userAnswered.js
Normal file
|
@ -0,0 +1,48 @@
|
||||||
|
import { sequelize } from './../loaders/index.js';
|
||||||
|
import { DataTypes } from 'sequelize';
|
||||||
|
import { User } from './user.js'; // Assuming your User model is in a file named user.model.js
|
||||||
|
|
||||||
|
|
||||||
|
const UserAnswers = sequelize.define('UserAnswers', {
|
||||||
|
id: {
|
||||||
|
type: DataTypes.INTEGER,
|
||||||
|
primaryKey: true,
|
||||||
|
autoIncrement: true,
|
||||||
|
},
|
||||||
|
user_id: {
|
||||||
|
type: DataTypes.INTEGER,
|
||||||
|
allowNull: false,
|
||||||
|
references: {
|
||||||
|
model: 'users', // The name of the referenced table (ensure it matches the table name)
|
||||||
|
key: 'id', // The primary key of the User model
|
||||||
|
},
|
||||||
|
onDelete: 'CASCADE', // If the user is deleted, their answers are also deleted
|
||||||
|
},
|
||||||
|
answers: {
|
||||||
|
type: DataTypes.TEXT,
|
||||||
|
allowNull: false,
|
||||||
|
}
|
||||||
|
}, {
|
||||||
|
tableName: 'user_answers', // Define the name of the table
|
||||||
|
timestamps: true, // If you don't want Sequelize to automatically create `createdAt` and `updatedAt` columns
|
||||||
|
});
|
||||||
|
|
||||||
|
UserAnswers.belongsTo(User, {
|
||||||
|
foreignKey: 'user_id',
|
||||||
|
as: 'user' // Optional alias for the reverse relation
|
||||||
|
});
|
||||||
|
|
||||||
|
// Sync the model with the database
|
||||||
|
const syncDbnow = async () => {
|
||||||
|
try {
|
||||||
|
await sequelize.sync({ force: false });
|
||||||
|
console.log('User Answer table created (or already exists)');
|
||||||
|
} catch (err) {
|
||||||
|
console.error('Error syncing the database:', err);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
// Call the sync function
|
||||||
|
syncDbnow();
|
||||||
|
|
||||||
|
export { UserAnswers };
|
99
server/model/userUpload.js
Normal file
99
server/model/userUpload.js
Normal file
|
@ -0,0 +1,99 @@
|
||||||
|
import { sequelize } from './../loaders/index.js';
|
||||||
|
import { DataTypes } from 'sequelize';
|
||||||
|
import { User } from './user.js'; // Assuming your User model is in a file named user.model.js
|
||||||
|
|
||||||
|
// Define the 'UserUploads' model
|
||||||
|
const UserUploads = sequelize.define(
|
||||||
|
'UserUploads',
|
||||||
|
{
|
||||||
|
id: {
|
||||||
|
type: DataTypes.INTEGER,
|
||||||
|
primaryKey: true,
|
||||||
|
autoIncrement: true,
|
||||||
|
},
|
||||||
|
user_id: {
|
||||||
|
type: DataTypes.INTEGER,
|
||||||
|
allowNull: false,
|
||||||
|
references: {
|
||||||
|
model: 'users', // The name of the referenced table (ensure it matches the table name)
|
||||||
|
key: 'id', // The primary key of the User model
|
||||||
|
},
|
||||||
|
onDelete: 'CASCADE', // If the user is deleted, their uploads are also deleted
|
||||||
|
},
|
||||||
|
file_name: {
|
||||||
|
type: DataTypes.TEXT,
|
||||||
|
allowNull: false,
|
||||||
|
},
|
||||||
|
file_path: {
|
||||||
|
type: DataTypes.TEXT,
|
||||||
|
allowNull: false,
|
||||||
|
},
|
||||||
|
file_type: {
|
||||||
|
type: DataTypes.TEXT,
|
||||||
|
allowNull: false,
|
||||||
|
},
|
||||||
|
total_duration: {
|
||||||
|
type: DataTypes.TEXT,
|
||||||
|
allowNull: true,
|
||||||
|
},
|
||||||
|
upload_day: {
|
||||||
|
type: DataTypes.STRING(100),
|
||||||
|
allowNull: false,
|
||||||
|
},
|
||||||
|
upload_time: {
|
||||||
|
type: DataTypes.DATE,
|
||||||
|
defaultValue: DataTypes.NOW,
|
||||||
|
},
|
||||||
|
overall_metrics: {
|
||||||
|
type: DataTypes.TEXT,
|
||||||
|
allowNull: true,
|
||||||
|
},
|
||||||
|
backhand: {
|
||||||
|
type: DataTypes.TEXT,
|
||||||
|
allowNull: true,
|
||||||
|
},
|
||||||
|
forehand: {
|
||||||
|
type: DataTypes.TEXT,
|
||||||
|
allowNull: true,
|
||||||
|
},
|
||||||
|
service: {
|
||||||
|
type: DataTypes.TEXT,
|
||||||
|
allowNull: true,
|
||||||
|
},
|
||||||
|
smash: {
|
||||||
|
type: DataTypes.TEXT,
|
||||||
|
allowNull: true,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
{
|
||||||
|
tableName: 'user_uploads', // Define the name of the table
|
||||||
|
timestamps: false, // If you don't want Sequelize to automatically create `createdAt` and `updatedAt` columns
|
||||||
|
}
|
||||||
|
);
|
||||||
|
|
||||||
|
// User model associations
|
||||||
|
User.hasMany(UserUploads, {
|
||||||
|
foreignKey: 'user_id',
|
||||||
|
onDelete: 'CASCADE',
|
||||||
|
as: 'uploads', // Add alias 'uploads' here
|
||||||
|
});
|
||||||
|
|
||||||
|
UserUploads.belongsTo(User, {
|
||||||
|
foreignKey: 'user_id',
|
||||||
|
as: 'user', // Optional alias for the reverse relation
|
||||||
|
});
|
||||||
|
|
||||||
|
// Sync the model with the database
|
||||||
|
const syncDb = async () => {
|
||||||
|
try {
|
||||||
|
await sequelize.sync({ force: false });
|
||||||
|
console.log('UserUploads table created (or already exists)');
|
||||||
|
} catch (err) {
|
||||||
|
console.error('Error syncing the database:', err);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
// Call the sync function
|
||||||
|
syncDb();
|
||||||
|
|
||||||
|
export { UserUploads };
|
43
server/models/index.js
Normal file
43
server/models/index.js
Normal file
|
@ -0,0 +1,43 @@
|
||||||
|
'use strict';
|
||||||
|
|
||||||
|
const fs = require('fs');
|
||||||
|
const path = require('path');
|
||||||
|
const Sequelize = require('sequelize');
|
||||||
|
const process = require('process');
|
||||||
|
const basename = path.basename(__filename);
|
||||||
|
const env = process.env.NODE_ENV || 'development';
|
||||||
|
const config = require(__dirname + '/../config/config.json')[env];
|
||||||
|
const db = {};
|
||||||
|
|
||||||
|
let sequelize;
|
||||||
|
if (config.use_env_variable) {
|
||||||
|
sequelize = new Sequelize(process.env[config.use_env_variable], config);
|
||||||
|
} else {
|
||||||
|
sequelize = new Sequelize(config.database, config.username, config.password, config);
|
||||||
|
}
|
||||||
|
|
||||||
|
fs
|
||||||
|
.readdirSync(__dirname)
|
||||||
|
.filter(file => {
|
||||||
|
return (
|
||||||
|
file.indexOf('.') !== 0 &&
|
||||||
|
file !== basename &&
|
||||||
|
file.slice(-3) === '.js' &&
|
||||||
|
file.indexOf('.test.js') === -1
|
||||||
|
);
|
||||||
|
})
|
||||||
|
.forEach(file => {
|
||||||
|
const model = require(path.join(__dirname, file))(sequelize, Sequelize.DataTypes);
|
||||||
|
db[model.name] = model;
|
||||||
|
});
|
||||||
|
|
||||||
|
Object.keys(db).forEach(modelName => {
|
||||||
|
if (db[modelName].associate) {
|
||||||
|
db[modelName].associate(db);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
db.sequelize = sequelize;
|
||||||
|
db.Sequelize = Sequelize;
|
||||||
|
|
||||||
|
module.exports = db;
|
14
server/routes/assessment.js
Normal file
14
server/routes/assessment.js
Normal file
|
@ -0,0 +1,14 @@
|
||||||
|
import express from "express";
|
||||||
|
import { addAssessmentQuestions, getAssessmentQuestions, updateAssessmentQuestionById, getUserAnswers, addUserAssessmentResponse, getUserDayTraining, updateVideoWatchByUser } from "../controller/assessment/index.js";
|
||||||
|
import auth from "../middlewares/auth.js";
|
||||||
|
|
||||||
|
const router = express.Router();
|
||||||
|
|
||||||
|
router.post("/questions", addAssessmentQuestions);
|
||||||
|
router.patch("/update-question/:id", updateAssessmentQuestionById );
|
||||||
|
router.get("/get-all-questions", getAssessmentQuestions);
|
||||||
|
router.post("/add-user-answers", auth, addUserAssessmentResponse);
|
||||||
|
router.get("/get-user-answers", auth, getUserAnswers);
|
||||||
|
router.get("/get-day-wise-training", auth, getUserDayTraining);
|
||||||
|
router.patch("/video-watch-for-day", auth, updateVideoWatchByUser);
|
||||||
|
export default router;
|
15
server/routes/index.js
Normal file
15
server/routes/index.js
Normal file
|
@ -0,0 +1,15 @@
|
||||||
|
import express from 'express';
|
||||||
|
import userRoute from './users/user.js';
|
||||||
|
import assessmentRoute from './assessment.js';
|
||||||
|
|
||||||
|
const router = express.Router();
|
||||||
|
|
||||||
|
/* GET home page. */
|
||||||
|
router.get('/', function (req, res, next) {
|
||||||
|
res.render('index', { title: 'Express' });
|
||||||
|
});
|
||||||
|
|
||||||
|
router.use('/users', userRoute);
|
||||||
|
router.use('/assessment', assessmentRoute);
|
||||||
|
|
||||||
|
export default router;
|
58
server/routes/users/user.js
Normal file
58
server/routes/users/user.js
Normal file
|
@ -0,0 +1,58 @@
|
||||||
|
import express from "express";
|
||||||
|
import {
|
||||||
|
checkUploadCount,
|
||||||
|
getAnalysisOfVideo,
|
||||||
|
getUserLastRecorderVideo,
|
||||||
|
getUserPerformanceStats,
|
||||||
|
getUserRecorderVideo,
|
||||||
|
isQuizPlayed,
|
||||||
|
updateUserProfileHandler,
|
||||||
|
updateUserRecorderMiniVideo,
|
||||||
|
updateUserRecorderVideo,
|
||||||
|
uploadManualEntry,
|
||||||
|
userLogin,
|
||||||
|
userSignUp
|
||||||
|
} from "../../controller/users/index.js";
|
||||||
|
import auth from "../../middlewares/auth.js";
|
||||||
|
import upload from "../../middlewares/upload.js";
|
||||||
|
import { refreshAccessToken } from "../../helper/index.js";
|
||||||
|
|
||||||
|
const router = express.Router(); // Create a new router instance
|
||||||
|
|
||||||
|
// Route to refresh the access token
|
||||||
|
router.post('/refresh-token', refreshAccessToken);
|
||||||
|
|
||||||
|
// Route for user sign-up
|
||||||
|
router.post('/sign-up', userSignUp);
|
||||||
|
|
||||||
|
// Route for user login
|
||||||
|
router.post('/login-in', userLogin);
|
||||||
|
|
||||||
|
// Route to update user profile, requires authentication and file upload
|
||||||
|
router.patch('/update-user-profile', auth, upload.fields([{ name: 'profilePic', maxCount: 1 }]), updateUserProfileHandler);
|
||||||
|
|
||||||
|
// Route to upload a recorder video, requires authentication and checks upload count
|
||||||
|
router.post('/upload-recorder-video', auth, checkUploadCount, upload.fields([{ name: 'userVideo', maxCount: 1 }]), updateUserRecorderVideo);
|
||||||
|
|
||||||
|
// Route to get user uploaded videos, requires authentication
|
||||||
|
router.get('/get-user-uploaded', auth, getUserRecorderVideo);
|
||||||
|
|
||||||
|
// Route to get analysis by uploaded video ID, requires authentication
|
||||||
|
router.get('/get-analysis-by-uploaded-id/:id', auth, getAnalysisOfVideo);
|
||||||
|
|
||||||
|
// Route to get the last recorded video, requires authentication
|
||||||
|
router.get('/last-recorded-video', auth, getUserLastRecorderVideo);
|
||||||
|
|
||||||
|
// Route to get performance stats by week, requires authentication
|
||||||
|
router.get('/performance-stats-by-week', auth, getUserPerformanceStats);
|
||||||
|
|
||||||
|
// Route to check if a quiz has been played by the user, requires authentication
|
||||||
|
router.get('/quiz-played-by-user', auth, isQuizPlayed);
|
||||||
|
|
||||||
|
// Route to upload a manual entry, requires authentication
|
||||||
|
router.post('/upload-manual-entry', auth, uploadManualEntry);
|
||||||
|
|
||||||
|
// Route to upload a mini recorder video, requires authentication and checks upload count
|
||||||
|
router.post('/upload-mini-recorder-video', auth, checkUploadCount, upload.fields([{ name: 'userVideo', maxCount: 1 }]), updateUserRecorderMiniVideo);
|
||||||
|
|
||||||
|
export default router; // Export the router for use in other parts of the application
|
94
server/services/assessment.js
Normal file
94
server/services/assessment.js
Normal file
|
@ -0,0 +1,94 @@
|
||||||
|
import { Op } from "sequelize";
|
||||||
|
import { assessmentQuestion } from "../model/assessment.js";
|
||||||
|
import { Training } from "../model/training.js";
|
||||||
|
import { User } from "../model/user.js";
|
||||||
|
import { UserAnswers } from "../model/userAnswered.js";
|
||||||
|
|
||||||
|
export const createQuestions = async (payload) => {
|
||||||
|
try {
|
||||||
|
// Insert all questions at once using bulkCreate
|
||||||
|
const createdQuestions = await assessmentQuestion.bulkCreate(payload);
|
||||||
|
return createdQuestions; // Return the user object
|
||||||
|
} catch (err) {
|
||||||
|
console.error(err);
|
||||||
|
throw new Error('Error creating questions');
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
export const getQuestionById = async (id) => {
|
||||||
|
try {
|
||||||
|
const question = await assessmentQuestion.findByPk(id);
|
||||||
|
return question; // Return the user object
|
||||||
|
} catch (err) {
|
||||||
|
console.error(err);
|
||||||
|
throw new Error('Error getting questions');
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
export const updateQuestionById = async (question) => {
|
||||||
|
try {
|
||||||
|
return await question.save();
|
||||||
|
|
||||||
|
} catch (err) {
|
||||||
|
console.error(err);
|
||||||
|
throw new Error('Error getting questions');
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
// Function to get all questions
|
||||||
|
export const getAllQuestions = async () => {
|
||||||
|
try {
|
||||||
|
return await assessmentQuestion.findAll({
|
||||||
|
order: [['id', 'ASC']],
|
||||||
|
});
|
||||||
|
} catch (err) {
|
||||||
|
console.error('Error fetching questions:', err);
|
||||||
|
throw new Error('Error fetching questions'); // Throw an error if something goes wrong
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
export const addUserAnswersByUserId = async (payload) => {
|
||||||
|
try {
|
||||||
|
const upload = await UserAnswers.create(payload);
|
||||||
|
return upload;
|
||||||
|
} catch (error) {
|
||||||
|
console.error('Error adding res:', error);
|
||||||
|
throw new Error('Error adding res');
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
export const addUserDrillsByUserId = async (payload) => {
|
||||||
|
try {
|
||||||
|
const data = await Training.create(payload);
|
||||||
|
return data;
|
||||||
|
} catch (error) {
|
||||||
|
console.error('Error adding res drills:', error);
|
||||||
|
throw new Error('Error adding res drills');
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
export const getUserAnswersByUserId = async (userId) => {
|
||||||
|
try {
|
||||||
|
// Find the user and include their uploads
|
||||||
|
const user = await UserAnswers.findAll({
|
||||||
|
where: { user_id: userId }, // Filter by user ID
|
||||||
|
});
|
||||||
|
return user;
|
||||||
|
} catch (error) {
|
||||||
|
console.error('Error uploading file:', error);
|
||||||
|
throw new Error('Error uploading file');
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
export const getUserTrainingByUserId = async (userId) => {
|
||||||
|
try {
|
||||||
|
const userTraining = await Training.findOne({
|
||||||
|
where: { user_id: userId }, // Filter by user ID
|
||||||
|
order: [['createdAt', 'DESC']], // Order by createdAt in descending order
|
||||||
|
});
|
||||||
|
return userTraining;
|
||||||
|
} catch (error) {
|
||||||
|
console.error('Error Training:', error);
|
||||||
|
throw new Error('Error Training');
|
||||||
|
}
|
||||||
|
};
|
156
server/services/users.js
Normal file
156
server/services/users.js
Normal file
|
@ -0,0 +1,156 @@
|
||||||
|
import { Op } from "sequelize";
|
||||||
|
import { User } from "../model/user.js";
|
||||||
|
import { UserUploads } from "../model/userUpload.js";
|
||||||
|
|
||||||
|
|
||||||
|
export const countUsers = async () => {
|
||||||
|
try {
|
||||||
|
const count = await User.count();
|
||||||
|
return count;
|
||||||
|
} catch (error) {
|
||||||
|
throw new Error('Error counting users');
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
// Function to get a user by email
|
||||||
|
export const getUserByEmail = async (email) => {
|
||||||
|
try {
|
||||||
|
const user = await User.findOne({ where: { email } });
|
||||||
|
|
||||||
|
return user; // Return the user object
|
||||||
|
} catch (err) {
|
||||||
|
console.error(err);
|
||||||
|
throw new Error('Error while checking user by email');
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
export const getUserByFilter = async (payload) => {
|
||||||
|
try {
|
||||||
|
const user = await User.findOne({ where: payload });
|
||||||
|
|
||||||
|
return user; // Return the user object
|
||||||
|
} catch (err) {
|
||||||
|
console.error(err);
|
||||||
|
throw new Error('Error while checking user by filter');
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
// Function to create a new user
|
||||||
|
export const createUser = async (payload) => {
|
||||||
|
try {
|
||||||
|
const user = await User.create(payload);
|
||||||
|
|
||||||
|
return user; // Return the created user
|
||||||
|
} catch (err) {
|
||||||
|
console.error(err);
|
||||||
|
throw new Error('Error while creating user');
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
// Function to get a user by ID (for example, to fetch profile data)
|
||||||
|
export const getUserById = async (id) => {
|
||||||
|
try {
|
||||||
|
const user = await User.findByPk(id); // Find the user by primary key (ID)
|
||||||
|
|
||||||
|
return user; // Return the user data
|
||||||
|
} catch (err) {
|
||||||
|
console.error(err);
|
||||||
|
throw new Error('Error while fetching user by ID');
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
export const countUserUploadsByUserId = async (userId) => {
|
||||||
|
try {
|
||||||
|
const count = await UserUploads.count({
|
||||||
|
where: {
|
||||||
|
user_id: userId
|
||||||
|
}
|
||||||
|
});
|
||||||
|
console.log(`Total uploads for user ID ${userId}: ${count}`);
|
||||||
|
return count;
|
||||||
|
} catch (error) {
|
||||||
|
console.error('Error counting user uploads:', error);
|
||||||
|
throw new Error('Error counting user uploads');
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
// Update user profile
|
||||||
|
export const updateUserProfile = async (id, updatedFields) => {
|
||||||
|
try {
|
||||||
|
const user = await User.findByPk(id);
|
||||||
|
|
||||||
|
// Dynamically update the fields based on the input
|
||||||
|
await user.update(updatedFields); // Sequelize handles the query and update
|
||||||
|
|
||||||
|
return user; // Return the updated user
|
||||||
|
} catch (err) {
|
||||||
|
console.error(err);
|
||||||
|
throw new Error('Error while updating user profile');
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
export const createUpload = async (payload) => {
|
||||||
|
try {
|
||||||
|
const upload = await UserUploads.create(payload);
|
||||||
|
return upload;
|
||||||
|
} catch (error) {
|
||||||
|
console.error('Error uploading file:', error);
|
||||||
|
throw new Error('Error uploading file');
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
export const getUserUpload = async (userId) => {
|
||||||
|
try {
|
||||||
|
// Find the user and include their uploads
|
||||||
|
const user = await User.findOne({
|
||||||
|
where: { id: userId }, // Filter by user ID
|
||||||
|
include: [
|
||||||
|
{
|
||||||
|
model: UserUploads, // Include the user uploads
|
||||||
|
as: 'uploads', // Alias defined in associations
|
||||||
|
}
|
||||||
|
]
|
||||||
|
});
|
||||||
|
return user;
|
||||||
|
} catch (error) {
|
||||||
|
console.error('Error uploading file:', error);
|
||||||
|
throw new Error('Error uploading file');
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
export const getUploadVideoById = async (payload) => {
|
||||||
|
try {
|
||||||
|
const userData = await UserUploads.findOne({ where: payload });
|
||||||
|
|
||||||
|
return userData;
|
||||||
|
} catch (error) {
|
||||||
|
console.error('Error file:', error);
|
||||||
|
throw new Error('Error file');
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
export const getUserUploadByDateFilter = async (userId, startDate, endDate) => {
|
||||||
|
try {
|
||||||
|
|
||||||
|
// Find the user and include their uploads
|
||||||
|
const user = await User.findOne({
|
||||||
|
where: { id: userId }, // Filter by user ID
|
||||||
|
include: [
|
||||||
|
{
|
||||||
|
model: UserUploads, // Include the user uploads
|
||||||
|
as: 'uploads', // Alias defined in associations
|
||||||
|
where: {
|
||||||
|
upload_time: {
|
||||||
|
[Op.between]: [startDate, endDate], // Filter uploads within the current week
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
]
|
||||||
|
});
|
||||||
|
|
||||||
|
return user;
|
||||||
|
} catch (error) {
|
||||||
|
console.error('Error fetching user uploads:', error);
|
||||||
|
throw new Error('Error fetching user uploads');
|
||||||
|
}
|
||||||
|
};
|
Loading…
Reference in a new issue