├── fronted └── your-diary-mood │ ├── src │ ├── App.css │ ├── index.css │ ├── main.jsx │ ├── pages │ │ ├── Register.jsx │ │ ├── Login.jsx │ │ └── Profile.jsx │ ├── utils │ │ └── PrivateRoute.jsx │ ├── App.jsx │ ├── components │ │ ├── Navbar.jsx │ │ ├── MoodCard.jsx │ │ ├── MoodCardForm.jsx │ │ ├── LoginForm.jsx │ │ └── RegisterForm.jsx │ └── assets │ │ ├── react.svg │ │ └── smiling-face-with-halo-svgrepo-com.svg │ ├── vite.config.js │ ├── .gitignore │ ├── README.md │ ├── .eslintrc.cjs │ ├── package.json │ ├── index.html │ └── public │ └── vite.svg ├── .gitignore ├── images ├── login.png ├── profile-1.png ├── profile-2.png ├── register.png ├── mobile-login.png ├── mobile-profile.png ├── mobile-register.png ├── Aspose.Words.9a3b1f86-47b0-41d9-b069-e55ca91e40ed.001.png ├── Aspose.Words.9a3b1f86-47b0-41d9-b069-e55ca91e40ed.002.png ├── Aspose.Words.9a3b1f86-47b0-41d9-b069-e55ca91e40ed.003.png ├── Aspose.Words.9a3b1f86-47b0-41d9-b069-e55ca91e40ed.004.png └── Aspose.Words.9a3b1f86-47b0-41d9-b069-e55ca91e40ed.005.png ├── backend ├── v1 │ ├── src │ │ ├── config │ │ │ ├── server.js │ │ │ └── db.js │ │ ├── uploads │ │ │ └── userProfileImages │ │ │ │ ├── 669e3335263bc0ef55c63b35.png │ │ │ │ ├── 669f6f448bb9bd3d37ba17d6.jpeg │ │ │ │ ├── 669f6f448bb9bd3d37ba17d6.png │ │ │ │ ├── 66a0eccdb2a3e0d4aa533dbf.jpeg │ │ │ │ ├── 66a0efb0b2a3e0d4aa533ddf.jpeg │ │ │ │ └── 66a218d6a17a676246c9fa63.jpeg │ │ ├── business │ │ │ ├── DTOs │ │ │ │ ├── GetMoodCardDto.js │ │ │ │ ├── GetUserInfoDto.js │ │ │ │ └── SignInDto.js │ │ │ ├── validations │ │ │ │ └── user.js │ │ │ └── services │ │ │ │ ├── moodCard.js │ │ │ │ ├── user.js │ │ │ │ └── auth.js │ │ ├── server.js │ │ ├── presentation │ │ │ ├── routes │ │ │ │ ├── index.js │ │ │ │ ├── user.js │ │ │ │ ├── auth.js │ │ │ │ └── moodCard.js │ │ │ ├── middlewares │ │ │ │ ├── validateSchema.js │ │ │ │ └── validateJWT.js │ │ │ └── controllers │ │ │ │ ├── user.js │ │ │ │ ├── moodCard.js │ │ │ │ └── auth.js │ │ ├── app.js │ │ ├── data │ │ │ ├── models │ │ │ │ ├── moodCard.js │ │ │ │ └── user.js │ │ │ └── repositories │ │ │ │ ├── user.js │ │ │ │ ├── moodCard.js │ │ │ │ └── auth.js │ │ └── scripts │ │ │ └── utils │ │ │ ├── jwt │ │ │ └── jwt.js │ │ │ └── mail │ │ │ └── sendVerificationEmail.js │ └── tests │ │ └── unit │ │ ├── services │ │ ├── userService.spec.js │ │ ├── moodCardService.spec.js │ │ └── authService.spec.js │ │ └── controllers │ │ └── moodCardController.spec.js └── package.json ├── LICENSE ├── README.en.md └── README.md /fronted/your-diary-mood/src/App.css: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /fronted/your-diary-mood/src/index.css: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | .env 2 | node_modules 3 | uploads/userProfileImages -------------------------------------------------------------------------------- /images/login.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/emrebaranarca/your-diary-mood-web-application/HEAD/images/login.png -------------------------------------------------------------------------------- /images/profile-1.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/emrebaranarca/your-diary-mood-web-application/HEAD/images/profile-1.png -------------------------------------------------------------------------------- /images/profile-2.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/emrebaranarca/your-diary-mood-web-application/HEAD/images/profile-2.png -------------------------------------------------------------------------------- /images/register.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/emrebaranarca/your-diary-mood-web-application/HEAD/images/register.png -------------------------------------------------------------------------------- /images/mobile-login.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/emrebaranarca/your-diary-mood-web-application/HEAD/images/mobile-login.png -------------------------------------------------------------------------------- /images/mobile-profile.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/emrebaranarca/your-diary-mood-web-application/HEAD/images/mobile-profile.png -------------------------------------------------------------------------------- /images/mobile-register.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/emrebaranarca/your-diary-mood-web-application/HEAD/images/mobile-register.png -------------------------------------------------------------------------------- /images/Aspose.Words.9a3b1f86-47b0-41d9-b069-e55ca91e40ed.001.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/emrebaranarca/your-diary-mood-web-application/HEAD/images/Aspose.Words.9a3b1f86-47b0-41d9-b069-e55ca91e40ed.001.png -------------------------------------------------------------------------------- /images/Aspose.Words.9a3b1f86-47b0-41d9-b069-e55ca91e40ed.002.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/emrebaranarca/your-diary-mood-web-application/HEAD/images/Aspose.Words.9a3b1f86-47b0-41d9-b069-e55ca91e40ed.002.png -------------------------------------------------------------------------------- /images/Aspose.Words.9a3b1f86-47b0-41d9-b069-e55ca91e40ed.003.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/emrebaranarca/your-diary-mood-web-application/HEAD/images/Aspose.Words.9a3b1f86-47b0-41d9-b069-e55ca91e40ed.003.png -------------------------------------------------------------------------------- /images/Aspose.Words.9a3b1f86-47b0-41d9-b069-e55ca91e40ed.004.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/emrebaranarca/your-diary-mood-web-application/HEAD/images/Aspose.Words.9a3b1f86-47b0-41d9-b069-e55ca91e40ed.004.png -------------------------------------------------------------------------------- /images/Aspose.Words.9a3b1f86-47b0-41d9-b069-e55ca91e40ed.005.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/emrebaranarca/your-diary-mood-web-application/HEAD/images/Aspose.Words.9a3b1f86-47b0-41d9-b069-e55ca91e40ed.005.png -------------------------------------------------------------------------------- /backend/v1/src/config/server.js: -------------------------------------------------------------------------------- 1 | const dotenv=require('dotenv') 2 | const connectDB=require('./db') 3 | 4 | const serverConfig=()=>{ 5 | dotenv.config() 6 | connectDB() 7 | } 8 | 9 | module.exports=serverConfig -------------------------------------------------------------------------------- /backend/v1/src/uploads/userProfileImages/669e3335263bc0ef55c63b35.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/emrebaranarca/your-diary-mood-web-application/HEAD/backend/v1/src/uploads/userProfileImages/669e3335263bc0ef55c63b35.png -------------------------------------------------------------------------------- /backend/v1/src/uploads/userProfileImages/669f6f448bb9bd3d37ba17d6.jpeg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/emrebaranarca/your-diary-mood-web-application/HEAD/backend/v1/src/uploads/userProfileImages/669f6f448bb9bd3d37ba17d6.jpeg -------------------------------------------------------------------------------- /backend/v1/src/uploads/userProfileImages/669f6f448bb9bd3d37ba17d6.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/emrebaranarca/your-diary-mood-web-application/HEAD/backend/v1/src/uploads/userProfileImages/669f6f448bb9bd3d37ba17d6.png -------------------------------------------------------------------------------- /backend/v1/src/uploads/userProfileImages/66a0eccdb2a3e0d4aa533dbf.jpeg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/emrebaranarca/your-diary-mood-web-application/HEAD/backend/v1/src/uploads/userProfileImages/66a0eccdb2a3e0d4aa533dbf.jpeg -------------------------------------------------------------------------------- /backend/v1/src/uploads/userProfileImages/66a0efb0b2a3e0d4aa533ddf.jpeg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/emrebaranarca/your-diary-mood-web-application/HEAD/backend/v1/src/uploads/userProfileImages/66a0efb0b2a3e0d4aa533ddf.jpeg -------------------------------------------------------------------------------- /backend/v1/src/uploads/userProfileImages/66a218d6a17a676246c9fa63.jpeg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/emrebaranarca/your-diary-mood-web-application/HEAD/backend/v1/src/uploads/userProfileImages/66a218d6a17a676246c9fa63.jpeg -------------------------------------------------------------------------------- /fronted/your-diary-mood/vite.config.js: -------------------------------------------------------------------------------- 1 | import { defineConfig } from 'vite' 2 | import react from '@vitejs/plugin-react' 3 | 4 | // https://vitejs.dev/config/ 5 | export default defineConfig({ 6 | plugins: [react()], 7 | }) 8 | -------------------------------------------------------------------------------- /backend/v1/src/business/DTOs/GetMoodCardDto.js: -------------------------------------------------------------------------------- 1 | class GetMoodCardDto{ 2 | constructor(feel,note,id) { 3 | this.feel = feel; 4 | this.note = note; 5 | this.id=id 6 | } 7 | } 8 | 9 | module.exports=GetMoodCardDto -------------------------------------------------------------------------------- /backend/v1/src/server.js: -------------------------------------------------------------------------------- 1 | const app=require('./app') 2 | const serverConfig=require('./config/server') 3 | serverConfig() 4 | 5 | const PORT=process.env.PORT 6 | app.listen(PORT,()=>{ 7 | console.log(`Server is running on ${PORT} PORT`); 8 | }) 9 | -------------------------------------------------------------------------------- /backend/v1/src/presentation/routes/index.js: -------------------------------------------------------------------------------- 1 | const userRouter=require('./user') 2 | const authRouter=require('../routes/auth') 3 | const moodCardRouter=require('../routes/moodCard') 4 | 5 | module.exports={ 6 | userRouter, 7 | authRouter, 8 | moodCardRouter 9 | } -------------------------------------------------------------------------------- /fronted/your-diary-mood/src/main.jsx: -------------------------------------------------------------------------------- 1 | import React from 'react' 2 | import ReactDOM from 'react-dom/client' 3 | import App from './App.jsx' 4 | import './index.css' 5 | 6 | ReactDOM.createRoot(document.getElementById('root')).render( 7 | 8 | 9 | , 10 | ) 11 | -------------------------------------------------------------------------------- /backend/v1/src/business/DTOs/GetUserInfoDto.js: -------------------------------------------------------------------------------- 1 | class GetUserInfoDto{ 2 | constructor(email,username,fullname,profilePicture) { 3 | this.email = email; 4 | this.username = username; 5 | this.fullname=fullname 6 | this.profilePicture=profilePicture 7 | } 8 | } 9 | 10 | module.exports=GetUserInfoDto -------------------------------------------------------------------------------- /fronted/your-diary-mood/src/pages/Register.jsx: -------------------------------------------------------------------------------- 1 | import Navbar from "../components/Navbar" 2 | import RegisterForm from "../components/RegisterForm" 3 | function Register() { 4 | return ( 5 | <> 6 | 7 | 8 | 9 | ) 10 | } 11 | 12 | export default Register 13 | -------------------------------------------------------------------------------- /backend/v1/src/config/db.js: -------------------------------------------------------------------------------- 1 | const mongoose=require('mongoose') 2 | 3 | const connectDB=async()=>{ 4 | try { 5 | await mongoose.connect(process.env.CONNECTION_STRING) 6 | console.log('connected'); 7 | } catch (error){ 8 | console.log(error) 9 | } 10 | } 11 | 12 | module.exports=connectDB 13 | 14 | 15 | 16 | 17 | 18 | -------------------------------------------------------------------------------- /fronted/your-diary-mood/src/utils/PrivateRoute.jsx: -------------------------------------------------------------------------------- 1 | import { Outlet,Navigate } from "react-router-dom"; 2 | 3 | const PrivateRoute = () => { 4 | const accessToken = sessionStorage.getItem('accessToken'); 5 | if(accessToken){ 6 | return 7 | }else{ 8 | return 9 | } 10 | } 11 | export default PrivateRoute -------------------------------------------------------------------------------- /fronted/your-diary-mood/src/pages/Login.jsx: -------------------------------------------------------------------------------- 1 | import Navbar from "../components/Navbar" 2 | import LoginForm from "../components/LoginForm" 3 | import { useNavigate } from "react-router-dom" 4 | 5 | function Login() { 6 | 7 | return ( 8 | <> 9 | 10 | 11 | 12 | ) 13 | } 14 | 15 | export default Login 16 | -------------------------------------------------------------------------------- /backend/v1/src/presentation/routes/user.js: -------------------------------------------------------------------------------- 1 | const express=require('express') 2 | const router=express.Router() 3 | const userController=require('../controllers/user') 4 | 5 | router.route('/upload-profil-picture').post(userController.validateJWT,userController.uploadProfilePicture) 6 | router.route('/get-user').get(userController.validateJWT,userController.getUser) 7 | 8 | 9 | module.exports=router -------------------------------------------------------------------------------- /fronted/your-diary-mood/.gitignore: -------------------------------------------------------------------------------- 1 | # Logs 2 | logs 3 | *.log 4 | npm-debug.log* 5 | yarn-debug.log* 6 | yarn-error.log* 7 | pnpm-debug.log* 8 | lerna-debug.log* 9 | 10 | node_modules 11 | dist 12 | dist-ssr 13 | *.local 14 | 15 | # Editor directories and files 16 | .vscode/* 17 | !.vscode/extensions.json 18 | .idea 19 | .DS_Store 20 | *.suo 21 | *.ntvs* 22 | *.njsproj 23 | *.sln 24 | *.sw? 25 | -------------------------------------------------------------------------------- /backend/v1/src/presentation/routes/auth.js: -------------------------------------------------------------------------------- 1 | const express=require('express') 2 | const router=express.Router() 3 | const authController=require('../controllers/auth') 4 | 5 | router.route('/sign-up').post(authController.validateUser,authController.signUp) 6 | router.route('/verify-email/:emailToken').get(authController.verifyEmail) 7 | router.route('/sign-in').post(authController.signIn) 8 | 9 | module.exports=router -------------------------------------------------------------------------------- /fronted/your-diary-mood/README.md: -------------------------------------------------------------------------------- 1 | # React + Vite 2 | 3 | This template provides a minimal setup to get React working in Vite with HMR and some ESLint rules. 4 | 5 | Currently, two official plugins are available: 6 | 7 | - [@vitejs/plugin-react](https://github.com/vitejs/vite-plugin-react/blob/main/packages/plugin-react/README.md) uses [Babel](https://babeljs.io/) for Fast Refresh 8 | - [@vitejs/plugin-react-swc](https://github.com/vitejs/vite-plugin-react-swc) uses [SWC](https://swc.rs/) for Fast Refresh 9 | -------------------------------------------------------------------------------- /backend/v1/src/presentation/middlewares/validateSchema.js: -------------------------------------------------------------------------------- 1 | const httpStatus = require('http-status'); 2 | 3 | const validateSchema = (schema) => { 4 | return function verify(req, res, next) { 5 | const { value, error } = schema.validate(req.body); 6 | if (error) { 7 | return res.status(httpStatus.BAD_REQUEST).json({ 8 | error: error.details[0].message 9 | }); 10 | } 11 | Object.assign(req, value); 12 | return next(); 13 | }; 14 | }; 15 | 16 | module.exports = validateSchema; 17 | -------------------------------------------------------------------------------- /backend/v1/src/presentation/routes/moodCard.js: -------------------------------------------------------------------------------- 1 | const express=require('express') 2 | const router=express.Router() 3 | 4 | const moodCardController=require('../controllers/moodCard') 5 | const userContoller=require('../controllers/user') 6 | 7 | router.route('/create-moodCard').post(userContoller.validateJWT,moodCardController.createMoodCard) 8 | router.route('/get-moodCards').get(userContoller.validateJWT,moodCardController.getMoodCards) 9 | router.route('/delete-moodCard/:id').delete(moodCardController.deleteMoodCard) 10 | 11 | 12 | module.exports=router -------------------------------------------------------------------------------- /backend/v1/src/business/validations/user.js: -------------------------------------------------------------------------------- 1 | const Joi=require('joi'); 2 | 3 | const userSchema = Joi.object({ 4 | username: Joi.string() 5 | .alphanum() 6 | .min(3) 7 | .max(30) 8 | .required(), 9 | 10 | password: Joi.string() 11 | .pattern(new RegExp('^[a-zA-Z0-9]{3,30}$')).min(8).required(), 12 | 13 | 14 | email: Joi.string() 15 | .email({ minDomainSegments: 2, tlds: { allow: ['com', 'net'] } }) 16 | .required(), 17 | 18 | fullname: Joi.string().required().min(3), 19 | 20 | }) 21 | 22 | module.exports=userSchema 23 | -------------------------------------------------------------------------------- /backend/v1/src/app.js: -------------------------------------------------------------------------------- 1 | const express=require('express') 2 | const app=express() 3 | const router=require('./presentation/routes') 4 | const fileUpload=require('express-fileupload') 5 | const cors=require('cors') 6 | const path=require('path') 7 | 8 | app.use(cors()) 9 | app.use(express.json()) 10 | app.use(fileUpload()) 11 | app.use('/uploads', express.static(path.join(__dirname,'/uploads')))// uploads dizinini statik olarak sun 12 | 13 | app.use('/api/v1',router.userRouter) 14 | app.use('/api/v1',router.authRouter) 15 | app.use('/api/v1/moodCard',router.moodCardRouter) 16 | 17 | 18 | 19 | 20 | module.exports=app -------------------------------------------------------------------------------- /backend/v1/src/data/models/moodCard.js: -------------------------------------------------------------------------------- 1 | const mongoose=require('mongoose') 2 | 3 | const moodCardSchema=new mongoose.Schema({ 4 | feel:{ 5 | type:String, 6 | required:true 7 | }, 8 | note:{ 9 | type:String, 10 | required:true 11 | }, 12 | date:{ 13 | type:Date, 14 | default:Date.now() 15 | }, 16 | emoji:{ 17 | type:String, 18 | required:false 19 | }, 20 | user:{ 21 | type:mongoose.Schema.Types.ObjectId, 22 | ref:'users', 23 | required:true 24 | }, 25 | 26 | },{ 27 | versionKey:false 28 | }) 29 | 30 | module.exports=mongoose.model('MoodCard',moodCardSchema) -------------------------------------------------------------------------------- /fronted/your-diary-mood/.eslintrc.cjs: -------------------------------------------------------------------------------- 1 | module.exports = { 2 | root: true, 3 | env: { browser: true, es2020: true }, 4 | extends: [ 5 | 'eslint:recommended', 6 | 'plugin:react/recommended', 7 | 'plugin:react/jsx-runtime', 8 | 'plugin:react-hooks/recommended', 9 | ], 10 | ignorePatterns: ['dist', '.eslintrc.cjs'], 11 | parserOptions: { ecmaVersion: 'latest', sourceType: 'module' }, 12 | settings: { react: { version: '18.2' } }, 13 | plugins: ['react-refresh'], 14 | rules: { 15 | 'react/jsx-no-target-blank': 'off', 16 | 'react-refresh/only-export-components': [ 17 | 'warn', 18 | { allowConstantExport: true }, 19 | ], 20 | }, 21 | } 22 | -------------------------------------------------------------------------------- /backend/v1/src/data/repositories/user.js: -------------------------------------------------------------------------------- 1 | const User=require('../models/user') 2 | 3 | const uploadProfilePicture=async(userID,filePath)=>{ 4 | try { 5 | await User.findOneAndUpdate( 6 | { _id: userID }, 7 | { profilePicture: filePath } 8 | ); 9 | } catch (error) { 10 | throw new Error('User update failed'); 11 | } 12 | } 13 | 14 | const readUserInfo=async(userID)=>{ 15 | try { 16 | const user=await User.findById(userID) 17 | return user 18 | } catch (error) { 19 | throw new Error('User update failed') 20 | } 21 | } 22 | 23 | 24 | module.exports={ 25 | uploadProfilePicture, 26 | readUserInfo 27 | } -------------------------------------------------------------------------------- /backend/v1/src/presentation/middlewares/validateJWT.js: -------------------------------------------------------------------------------- 1 | const { verifyToken } = require('../../scripts/utils/jwt/jwt'); 2 | const httpStatus=require('http-status') 3 | 4 | const authMiddleware = (req, res, next) => { 5 | const token = req.header('Authorization').replace('Bearer ', ''); 6 | if (!token) { 7 | return res.status(httpStatus.UNAUTHORIZED) 8 | .json( 9 | { message: 'No token, authorization denied' } 10 | ) 11 | } 12 | 13 | try { 14 | const decoded = verifyToken(token); 15 | req.user = decoded; 16 | next(); 17 | } catch (error) { 18 | res.status(httpStatus.UNAUTHORIZED) 19 | .json( 20 | { message: 'Token is not valid' } 21 | ) 22 | } 23 | }; 24 | 25 | module.exports = authMiddleware; 26 | -------------------------------------------------------------------------------- /fronted/your-diary-mood/src/App.jsx: -------------------------------------------------------------------------------- 1 | import Register from "./pages/Register"; 2 | import Login from "./pages/Login" 3 | import Profile from "./pages/Profile"; 4 | import { BrowserRouter, Routes, Route } from 'react-router-dom'; 5 | import PrivateRoute from "./utils/PrivateRoute"; 6 | 7 | function App() { 8 | 9 | return ( 10 | <> 11 | 12 | 13 | }> 14 | }> 15 | }> 16 | }> 17 | 18 | 19 | 20 | 21 | ) 22 | } 23 | 24 | export default App 25 | -------------------------------------------------------------------------------- /backend/v1/src/data/models/user.js: -------------------------------------------------------------------------------- 1 | const mongoose=require('mongoose') 2 | 3 | const userSchema = new mongoose.Schema({ 4 | fullname:{ 5 | type:String, 6 | required:true 7 | }, 8 | email:{ 9 | type:String, 10 | required:true, 11 | unique:true 12 | }, 13 | username:{ 14 | type:String, 15 | required:true, 16 | unique:true 17 | }, 18 | password:{ 19 | type:String, 20 | required:true 21 | }, 22 | profilePicture:{ 23 | type:String, 24 | required:false 25 | }, 26 | isVerified:{ 27 | type:Boolean, 28 | required:true, 29 | default:false 30 | }, 31 | emailToken:{ 32 | type:String, 33 | required:false 34 | } 35 | },{ 36 | timestamps:true, 37 | versionKey:false 38 | }); 39 | 40 | 41 | module.exports=mongoose.model('User',userSchema) -------------------------------------------------------------------------------- /backend/v1/src/business/DTOs/SignInDto.js: -------------------------------------------------------------------------------- 1 | const Joi = require('joi'); 2 | 3 | class SignInDTO { 4 | constructor(email, password) { 5 | this.email = email; 6 | this.password = password; 7 | } 8 | 9 | static schema() { 10 | return Joi.object({ 11 | email: Joi.string() 12 | .email({ minDomainSegments: 2, tlds: { allow: ['com', 'net'] } }) 13 | .required(), 14 | password: Joi.string() 15 | .pattern(new RegExp('^[a-zA-Z0-9]{3,30}$')) 16 | .min(8) 17 | .required(), 18 | }); 19 | } 20 | 21 | static validate(data) { 22 | const schema = SignInDTO.schema(); 23 | return schema.validate(data, { abortEarly: false }); 24 | } 25 | } 26 | 27 | module.exports = SignInDTO; 28 | -------------------------------------------------------------------------------- /fronted/your-diary-mood/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "your-diary-mood", 3 | "private": true, 4 | "version": "0.0.0", 5 | "type": "module", 6 | "scripts": { 7 | "dev": "vite", 8 | "build": "vite build", 9 | "lint": "eslint . --ext js,jsx --report-unused-disable-directives --max-warnings 0", 10 | "preview": "vite preview" 11 | }, 12 | "dependencies": { 13 | "axios": "^1.7.2", 14 | "react": "^18.3.1", 15 | "react-dom": "^18.3.1", 16 | "react-router-dom": "^6.25.1" 17 | }, 18 | "devDependencies": { 19 | "@types/react": "^18.3.3", 20 | "@types/react-dom": "^18.3.0", 21 | "@vitejs/plugin-react": "^4.3.1", 22 | "eslint": "^8.57.0", 23 | "eslint-plugin-react": "^7.34.3", 24 | "eslint-plugin-react-hooks": "^4.6.2", 25 | "eslint-plugin-react-refresh": "^0.4.7", 26 | "vite": "^5.3.4" 27 | } 28 | } 29 | -------------------------------------------------------------------------------- /backend/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "your-emoji-mood-service", 3 | "version": "1.0.0", 4 | "description": "Your Emoji Mood application backend service", 5 | "main": "server.js", 6 | "scripts": { 7 | "dev": "nodemon ./v1/src/server.js", 8 | "test-unit": "jest" 9 | }, 10 | "author": "Emre Baran ARCA", 11 | "license": "ISC", 12 | "dependencies": { 13 | "bcrypt": "^5.1.1", 14 | "cors": "^2.8.5", 15 | "crypto-js": "^4.2.0", 16 | "dotenv": "^16.4.5", 17 | "express": "^4.19.2", 18 | "express-fileupload": "^1.5.1", 19 | "http-status": "^1.7.4", 20 | "joi": "^17.13.3", 21 | "jsonwebtoken": "^9.0.2", 22 | "mongoose": "^8.5.1", 23 | "nodemailer": "^6.9.14" 24 | }, 25 | "devDependencies": { 26 | "jest": "^29.7.0", 27 | "jest-worker": "^29.7.0", 28 | "mock-fs": "^5.2.0", 29 | "nodemon": "^3.1.4", 30 | "supertest": "^7.0.0" 31 | } 32 | } 33 | -------------------------------------------------------------------------------- /backend/v1/src/scripts/utils/jwt/jwt.js: -------------------------------------------------------------------------------- 1 | const jwt=require('jsonwebtoken') 2 | require('dotenv').config() 3 | const httpStatus=require('http-status') 4 | 5 | const generateJWT=(user)=>{ 6 | if(!user){ 7 | res.status(httpStatus.BAD_REQUEST) 8 | throw new Error("Please Provide User Details") 9 | } 10 | const accessToken=jwt.sign( 11 | { 12 | user:{ 13 | id:user._id, 14 | email:user.email, 15 | username:user.username, 16 | } 17 | 18 | }, 19 | process.env.JWT_SECRET, 20 | {expiresIn:'1h'} 21 | ) 22 | 23 | return accessToken 24 | } 25 | 26 | const verifyToken = (token) => { 27 | try { 28 | return jwt.verify(token, process.env.JWT_SECRET); 29 | } catch (error) { 30 | throw new Error('Invalid token'); 31 | } 32 | }; 33 | 34 | module.exports = { generateJWT, verifyToken }; -------------------------------------------------------------------------------- /backend/v1/src/data/repositories/moodCard.js: -------------------------------------------------------------------------------- 1 | const MoodCard=require('../models/moodCard') 2 | 3 | const createMoodCard=async(moodCardData,userID)=>{ 4 | try { 5 | const moodCard=new MoodCard(moodCardData) 6 | moodCard.user=userID 7 | await moodCard.save() 8 | return moodCard 9 | } catch (error) { 10 | throw new Error(error.message) 11 | } 12 | } 13 | 14 | const readMoodCards=async(userID)=>{ 15 | try { 16 | const moodCards=await MoodCard.find({ 17 | user:userID 18 | }) 19 | return moodCards 20 | } catch (error) { 21 | throw new Error(error.message) 22 | } 23 | } 24 | 25 | const deleteMoodCard=async(id)=>{ 26 | try { 27 | await MoodCard.findByIdAndDelete(id) 28 | } catch (error) { 29 | throw new Error(error.message) 30 | } 31 | } 32 | module.exports={ 33 | createMoodCard, 34 | readMoodCards, 35 | deleteMoodCard 36 | } -------------------------------------------------------------------------------- /fronted/your-diary-mood/src/components/Navbar.jsx: -------------------------------------------------------------------------------- 1 | const Navbar=()=>{ 2 | return( 3 | <> 4 | 16 | 17 | 18 | ) 19 | } 20 | 21 | export default Navbar 22 | -------------------------------------------------------------------------------- /backend/v1/src/scripts/utils/mail/sendVerificationEmail.js: -------------------------------------------------------------------------------- 1 | const nodemailer = require('nodemailer'); 2 | require('dotenv').config() 3 | 4 | const transporter = nodemailer.createTransport({ 5 | service:'gmail', 6 | auth: { 7 | user: process.env.SMTP_USER, 8 | pass: process.env.SMTP_PASS 9 | }, 10 | }); 11 | 12 | const sendVerificationEmail = async (to, verificationLink) => { 13 | const mailOptions = { 14 | from: process.env.SMTP_USER, 15 | to: to, 16 | subject: 'Email Verification', 17 | text: `Please verify your email by clicking on the following link: ${verificationLink}`, 18 | html: `

Please verify your email by clicking on the following link: Click to verify email

` 19 | }; 20 | 21 | try { 22 | const info = await transporter.sendMail(mailOptions); 23 | console.log('Verification email sent: ' + info.response); 24 | } catch (error) { 25 | console.error('Error sending verification email: ', error); 26 | } 27 | }; 28 | 29 | module.exports = sendVerificationEmail; 30 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | MIT License 2 | 3 | Copyright (c) 2024 Emre Baran ARCA 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE. 22 | -------------------------------------------------------------------------------- /fronted/your-diary-mood/index.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | Your Diary Mood 10 | 11 | 12 |
13 | 14 | 15 | 16 | 17 | -------------------------------------------------------------------------------- /backend/v1/src/business/services/moodCard.js: -------------------------------------------------------------------------------- 1 | const moodCardRepository=require('../../data/repositories/moodCard') 2 | const GetMoodCardDto=require('../DTOs/GetMoodCardDto') 3 | 4 | const createMoodCard=async(moodCardData,userID)=>{ 5 | try { 6 | const moodCard=await moodCardRepository.createMoodCard(moodCardData,userID) 7 | return moodCard 8 | } catch (error) { 9 | throw new Error(error.message) 10 | } 11 | } 12 | 13 | const findMoodCards=async(userID)=>{ 14 | try { 15 | const moodCards=await moodCardRepository.readMoodCards(userID) 16 | const getMoodCardDto=[] 17 | moodCards.map((element)=>{ 18 | let moodCard=new GetMoodCardDto(element.feel,element.note,element._id) 19 | getMoodCardDto.push(moodCard) 20 | }) 21 | return getMoodCardDto 22 | } catch (error) { 23 | throw new Error(error.message) 24 | } 25 | } 26 | 27 | const deleteMoodCard=async(id)=>{ 28 | try { 29 | await moodCardRepository.deleteMoodCard(id) 30 | } catch (error) { 31 | throw new Error(error.message) 32 | } 33 | } 34 | 35 | 36 | 37 | module.exports={ 38 | createMoodCard, 39 | findMoodCards, 40 | deleteMoodCard 41 | } -------------------------------------------------------------------------------- /backend/v1/src/presentation/controllers/user.js: -------------------------------------------------------------------------------- 1 | const userService=require('../../business/services/user') 2 | const httpStatus=require('http-status') 3 | const validateJWT=require('../middlewares/validateJWT') 4 | 5 | 6 | const uploadProfilePicture=async(req,res)=>{ 7 | try { 8 | const {user}=req.user 9 | const userID=user.id 10 | const file=req.files.profilePicture 11 | await userService.uploadProfilePicture(userID,file) 12 | res.status(httpStatus.OK).json({ 13 | message: 'Profile picture uploaded successfully!' 14 | }); 15 | } catch (error) { 16 | res.status(httpStatus.INTERNAL_SERVER_ERROR) 17 | .json({ 18 | message:error.message 19 | }) 20 | } 21 | } 22 | 23 | const getUser=async(req,res)=>{ 24 | try { 25 | const {user}=req.user 26 | const userID=user.id 27 | const userData=await userService.findUserInfo(userID) 28 | res.status(httpStatus.OK) 29 | .json(userData) 30 | } catch (error) { 31 | res.status(httpStatus.BAD_REQUEST) 32 | .json({ 33 | message:error.message 34 | }) 35 | } 36 | } 37 | 38 | 39 | module.exports={ 40 | uploadProfilePicture, 41 | validateJWT, 42 | getUser 43 | } -------------------------------------------------------------------------------- /backend/v1/src/data/repositories/auth.js: -------------------------------------------------------------------------------- 1 | const User=require('../models/user') 2 | 3 | const createUser=async(userData)=>{ 4 | try { 5 | const exitingUser=await User.findOne({ 6 | $or:[ 7 | {email:userData.email}, 8 | {username:userData.username} 9 | ] 10 | }) 11 | 12 | if(exitingUser){ 13 | throw new Error('Email or username already exists') 14 | } 15 | const user=new User(userData) 16 | await user.save() 17 | return user 18 | } catch (error) { 19 | throw new Error(error.message) 20 | } 21 | 22 | } 23 | 24 | const verifyEmail=async(emailToken)=>{ 25 | try { 26 | const user=await User.findOneAndUpdate({emailToken:emailToken},{isVerified:true}) 27 | } catch (error) { 28 | throw new Error(error.message) 29 | } 30 | } 31 | 32 | const loginUser=async(userData)=>{ 33 | try { 34 | if(!userData){ 35 | throw new Error('provide user login data ') 36 | } 37 | const{email}=userData 38 | const user=await User.findOne({ 39 | email:email 40 | }) 41 | return user 42 | } catch (error) { 43 | throw new Error(error.message) 44 | } 45 | } 46 | 47 | module.exports={ 48 | createUser, 49 | verifyEmail, 50 | loginUser 51 | } 52 | -------------------------------------------------------------------------------- /fronted/your-diary-mood/public/vite.svg: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /backend/v1/src/business/services/user.js: -------------------------------------------------------------------------------- 1 | const userRepository=require('../../data/repositories/user') 2 | const path=require('path') 3 | const GetUserInfoDto=require('../DTOs/GetUserInfoDto') 4 | 5 | const uploadProfilePicture=async(userID,file)=>{ 6 | try { 7 | if (!file) { 8 | throw new Error('No file uploaded'); 9 | } 10 | if(!userID){ 11 | throw new Error('User ID is required'); 12 | } 13 | 14 | const extension=path.extname(file.name) 15 | const fileName=`${userID}${extension}` 16 | const folderPath=path.join(__dirname,"../../uploads/userProfileImages",fileName) 17 | // Move file to the destination directory 18 | file.mv(folderPath, async (err) => { 19 | if (err) { 20 | throw new Error('File upload failed'); 21 | } 22 | await userRepository.uploadProfilePicture(userID, fileName); 23 | }); 24 | 25 | } catch (error) { 26 | throw new Error(error.message) 27 | } 28 | } 29 | 30 | const findUserInfo=async(userID)=>{ 31 | try { 32 | const userData=await userRepository.readUserInfo(userID) 33 | const getUserInfoDto=new GetUserInfoDto(userData.email,userData.username,userData.fullname,userData.profilePicture) 34 | return getUserInfoDto 35 | } catch (error) { 36 | throw new Error(error.message) 37 | } 38 | } 39 | 40 | module.exports={ 41 | uploadProfilePicture, 42 | findUserInfo 43 | } 44 | 45 | -------------------------------------------------------------------------------- /backend/v1/src/presentation/controllers/moodCard.js: -------------------------------------------------------------------------------- 1 | const httpStatus=require('http-status') 2 | const moodCardService=require('../../business/services/moodCard') 3 | 4 | const createMoodCard=async(req,res)=>{ 5 | try { 6 | const {user}=req.user 7 | const userID=user.id 8 | const moodCardData=req.body 9 | const moodCard=await moodCardService.createMoodCard(moodCardData,userID) 10 | res.status(httpStatus.CREATED) 11 | .json(moodCard) 12 | } catch (error) { 13 | res.status(httpStatus.BAD_REQUEST) 14 | .json({ 15 | message:error.message 16 | }) 17 | } 18 | } 19 | 20 | const getMoodCards=async(req,res)=>{ 21 | try { 22 | const {user}=req.user 23 | const userID=user.id 24 | const moodCards=await moodCardService.findMoodCards(userID) 25 | res.status(httpStatus.OK) 26 | .json(moodCards) 27 | } catch (error) { 28 | res.status(httpStatus.BAD_REQUEST) 29 | .json({ 30 | message:error.message 31 | }) 32 | } 33 | } 34 | 35 | const deleteMoodCard=async(req,res)=>{ 36 | try { 37 | const cardID=req.params.id 38 | await moodCardService.deleteMoodCard(cardID) 39 | res.status(httpStatus.OK) 40 | .json({ 41 | message:'deleting mood card' 42 | }) 43 | } catch (error) { 44 | res.status(httpStatus.BAD_REQUEST) 45 | .json({ 46 | message:error.message 47 | }) 48 | } 49 | } 50 | 51 | 52 | 53 | module.exports={ 54 | createMoodCard, 55 | getMoodCards, 56 | deleteMoodCard 57 | } -------------------------------------------------------------------------------- /backend/v1/tests/unit/services/userService.spec.js: -------------------------------------------------------------------------------- 1 | const path = require('path') 2 | const mockFs = require('mock-fs') 3 | 4 | const userService=require('../../../src/business/services/user') 5 | const userRepository=require('../../../src/data/repositories/user') 6 | 7 | jest.mock('/home/emrebaran/Desktop/software/your-diary-mood-app/backend/v1/src/data/repositories/user.js') 8 | 9 | describe('User Service', () => { 10 | beforeAll(() => { 11 | mockFs({ 12 | 'uploads/userProfileImages': {} 13 | }); 14 | }); 15 | 16 | afterAll(() => { 17 | mockFs.restore(); 18 | }); 19 | 20 | it('should upload a profile picture successfully', async () => { 21 | const userID = '123'; 22 | const file = { 23 | name: 'test.png', 24 | mv: jest.fn((dest, cb) => cb(null)) 25 | }; 26 | 27 | userRepository.uploadProfilePicture.mockResolvedValue(); 28 | 29 | await userService.uploadProfilePicture(userID, file); 30 | 31 | expect(file.mv).toHaveBeenCalledTimes(1); 32 | expect(userRepository.uploadProfilePicture).toHaveBeenCalledWith(userID, `${userID}.png`); 33 | }); 34 | 35 | it('should throw an error when no file is uploaded', async () => { 36 | const userID = '123'; 37 | await expect(userService.uploadProfilePicture(userID, null)).rejects.toThrow('No file uploaded'); 38 | }); 39 | 40 | it('should throw an error when no userID is provided', async () => { 41 | const file = { 42 | name: 'test.png', 43 | mv: jest.fn((dest, cb) => cb(null)) 44 | }; 45 | await expect(userService.uploadProfilePicture(null, file)).rejects.toThrow('User ID is required'); 46 | }); 47 | }); -------------------------------------------------------------------------------- /fronted/your-diary-mood/src/components/MoodCard.jsx: -------------------------------------------------------------------------------- 1 | import axios from 'axios'; 2 | import { useEffect, useState } from 'react'; 3 | 4 | const MoodCard = () => { 5 | const [moodCards, setMoodCards] = useState([]); 6 | 7 | const getData = async () => { 8 | try { 9 | const accessToken = sessionStorage.getItem('accessToken'); 10 | const res = await axios.get('http://localhost:3000/api/v1/moodCard/get-moodCards', { 11 | headers: { 12 | 'Authorization': `Bearer ${accessToken}` 13 | } 14 | }); 15 | setMoodCards(res.data); 16 | } catch (error) { 17 | console.error(error); 18 | } 19 | }; 20 | 21 | const deleteMoodCard = async (cardID) => { 22 | try { 23 | await axios.delete(`http://localhost:3000/api/v1/moodCard/delete-moodCard/${cardID}`); 24 | setMoodCards(moodCards.filter(mood => mood._id !== cardID)); 25 | await getData() 26 | } catch (error) { 27 | console.error('Error deleting mood card:', error); 28 | } 29 | }; 30 | 31 | useEffect(() => { 32 | getData(); 33 | }, []); 34 | 35 | return ( 36 | <> 37 |
38 |
39 | {moodCards.map((mood, index) => ( 40 |
41 |
42 |
43 |
44 |

MOOD CARD

45 |
46 |

{mood.feel}

47 |
48 |

{mood.note}

49 |
50 | 51 | 52 |
53 |
54 |
55 | ))} 56 |
57 | 58 | ); 59 | }; 60 | 61 | export default MoodCard; 62 | -------------------------------------------------------------------------------- /backend/v1/src/business/services/auth.js: -------------------------------------------------------------------------------- 1 | const authRepository=require('../../data/repositories/auth') 2 | const bcrypt=require('bcrypt') 3 | const crypto=require('crypto') 4 | const sendVerificationEmail=require('../../scripts/utils/mail/sendVerificationEmail') 5 | const httpStatus = require('http-status') 6 | const {generateJWT}=require('../../scripts/utils/jwt/jwt') 7 | 8 | const registerUser=async(userData)=>{ 9 | try { 10 | const email=userData.email 11 | const emailToken=await crypto.randomBytes(12).toString('hex'); 12 | userData.emailToken=emailToken 13 | const verificationLink = `http://localhost:3000/api/v1/verify-email/${emailToken}` 14 | sendVerificationEmail(email,verificationLink) 15 | const salt=await bcrypt.genSalt() 16 | const hashedPassword=await bcrypt.hash(userData.password,salt) 17 | userData.password=hashedPassword 18 | await authRepository.createUser(userData) 19 | } catch (error) { 20 | throw new Error(error.message) 21 | } 22 | } 23 | 24 | const verifyEmail=async(emailToken)=>{ 25 | try { 26 | if(!emailToken){ 27 | throw new Error('email token is null') 28 | } 29 | const user=await authRepository.verifyEmail(emailToken) 30 | } catch (error) { 31 | throw new Error(error.message) 32 | } 33 | } 34 | 35 | const loginUser=async(userData)=>{ 36 | try { 37 | const {password}=userData 38 | const user=await authRepository.loginUser(userData) 39 | if(!user){ 40 | throw new Error("User Does Not Exists") 41 | } 42 | if(!user.isVerified){ 43 | throw new Error("Please Verify Your Account") 44 | } 45 | const isMatch=await bcrypt.compare(password,user.password) 46 | if(!isMatch){ 47 | throw new Error("Invalid Credentials") 48 | } 49 | const token = generateJWT(user); 50 | return token 51 | } catch (error) { 52 | throw new Error(error.message) 53 | } 54 | 55 | } 56 | 57 | module.exports={ 58 | registerUser, 59 | verifyEmail, 60 | loginUser 61 | } -------------------------------------------------------------------------------- /backend/v1/src/presentation/controllers/auth.js: -------------------------------------------------------------------------------- 1 | const authService=require('../../business/services/auth') 2 | const httpStatus=require('http-status') 3 | const validateSchema=require('../middlewares/validateSchema') 4 | const userValidation=require('../../business/validations/user') 5 | const validateJWT=require('../middlewares/validateJWT') 6 | const SignInDTO = require('../../business/DTOs/SignInDto') 7 | 8 | const signUp=async(req,res)=>{ 9 | try { 10 | const userData=req.body 11 | console.log(req.file); 12 | const user=await authService.registerUser(userData) 13 | res.status(httpStatus.CREATED) 14 | .json({ 15 | message:'user registered successfully', 16 | user 17 | }) 18 | } catch (error) { 19 | res.status(httpStatus.BAD_REQUEST) 20 | .json({ 21 | message:error.message 22 | }) 23 | } 24 | 25 | } 26 | 27 | const verifyEmail=async(req,res)=>{ 28 | try { 29 | const {emailToken}=req.params 30 | await authService.verifyEmail(emailToken) 31 | res.status(httpStatus.OK) 32 | .json({ 33 | message:'user verified' 34 | }) 35 | } catch (error) { 36 | res.status(httpStatus.BAD_REQUEST) 37 | .json({ 38 | message:error.message 39 | }) 40 | } 41 | } 42 | 43 | const signIn=async(req,res)=>{ 44 | try { 45 | const {error,value}=SignInDTO.validate(req.body) 46 | if(error){ 47 | return res.status(httpStatus.BAD_REQUEST) 48 | .json({ 49 | message:error.message 50 | }) 51 | } 52 | const signInDTO=new SignInDTO(value.email,value.password) 53 | const accessToken=await authService.loginUser(signInDTO) 54 | res.status(httpStatus.OK) 55 | .json( 56 | {"accessToken":accessToken} 57 | ) 58 | } catch (error) { 59 | res.status(httpStatus.BAD_REQUEST) 60 | .json({ 61 | message:error.message 62 | }) 63 | 64 | } 65 | } 66 | 67 | module.exports={ 68 | signUp, 69 | validateUser:validateSchema(userValidation), 70 | verifyEmail, 71 | signIn 72 | } -------------------------------------------------------------------------------- /fronted/your-diary-mood/src/components/MoodCardForm.jsx: -------------------------------------------------------------------------------- 1 | import axios from 'axios' 2 | 3 | 4 | const MoodCardForm=()=>{ 5 | 6 | const handleSubmit=async(e)=>{ 7 | e.preventDefault() 8 | const moodData = { 9 | feel: e.target.elements.feel.value, 10 | note: e.target.elements.note.value 11 | } 12 | try { 13 | const accessToken = sessionStorage.getItem('accessToken'); 14 | const res=await axios.post('http://localhost:3000/api/v1/moodCard/create-moodCard', 15 | moodData, 16 | { 17 | headers: { 18 | 'Authorization': `Bearer ${accessToken}` 19 | } 20 | } 21 | ) 22 | window.location.reload() 23 | } catch (error) { 24 | alert(error.response.data.message) 25 | console.error(error.response.data.message) 26 | } 27 | 28 | } 29 | 30 | return ( 31 | <> 32 |
33 |
34 |
35 |
36 | 37 | 38 |
39 | 40 |
41 | 42 | 43 |
44 | 45 |
46 |
47 | 48 |
49 |
50 |
51 |
52 |
53 | 54 | ) 55 | } 56 | 57 | 58 | export default MoodCardForm -------------------------------------------------------------------------------- /backend/v1/tests/unit/controllers/moodCardController.spec.js: -------------------------------------------------------------------------------- 1 | const request = require('supertest'); 2 | const express = require('express'); 3 | const httpStatus = require('http-status'); 4 | const moodCardController = require('../../../src/presentation/controllers/moodCard'); 5 | const moodCardService = require('../../../src/business/services/moodCard'); 6 | 7 | jest.mock('/home/emrebaran/Desktop/software/your-diary-mood-app/backend/v1/src/business/services/moodCard.js'); 8 | 9 | const app = express(); 10 | app.use(express.json()); 11 | 12 | // Mock route handlers 13 | app.post('/mood-cards', moodCardController.createMoodCard); 14 | app.get('/mood-cards', moodCardController.getMoodCards); 15 | app.delete('/mood-cards/:id', moodCardController.deleteMoodCard); 16 | 17 | describe('MoodCard Controller', () => { 18 | beforeEach(() => { 19 | jest.clearAllMocks(); 20 | }); 21 | 22 | 23 | describe('GET /mood-cards', () => { 24 | it('should return 400 if retrieval fails', async () => { 25 | const userID = 'user123'; 26 | 27 | moodCardService.findMoodCards.mockRejectedValue(new Error('Retrieval failed')); 28 | 29 | const response = await request(app) 30 | .get('/mood-cards') 31 | .set('Authorization', 'Bearer token'); 32 | 33 | expect(response.status).toBe(httpStatus.BAD_REQUEST); 34 | }); 35 | }); 36 | 37 | describe('DELETE /mood-cards/:id', () => { 38 | it('should delete a mood card and return 200 status', async () => { 39 | const cardID = '1'; 40 | 41 | moodCardService.deleteMoodCard.mockResolvedValue(); 42 | 43 | const response = await request(app) 44 | .delete(`/mood-cards/${cardID}`) 45 | .set('Authorization', 'Bearer token'); 46 | 47 | expect(response.status).toBe(httpStatus.OK); 48 | expect(response.body.message).toBe('deleting mood card'); 49 | }); 50 | 51 | it('should return 400 if deletion fails', async () => { 52 | const cardID = '1'; 53 | 54 | moodCardService.deleteMoodCard.mockRejectedValue(new Error('Deletion failed')); 55 | 56 | const response = await request(app) 57 | .delete(`/mood-cards/${cardID}`) 58 | .set('Authorization', 'Bearer token'); 59 | 60 | expect(response.status).toBe(httpStatus.BAD_REQUEST); 61 | expect(response.body.message).toBe('Deletion failed'); 62 | }); 63 | }); 64 | }); 65 | -------------------------------------------------------------------------------- /backend/v1/tests/unit/services/moodCardService.spec.js: -------------------------------------------------------------------------------- 1 | const moodCardService = require('../../../src/business/services/moodCard'); 2 | const moodCardRepository = require('../../../src/data/repositories/moodCard'); 3 | const GetMoodCardDto = require('../../../src/business/DTOs/GetMoodCardDto'); 4 | 5 | jest.mock('../../../src/data/repositories/moodCard'); 6 | 7 | describe('MoodCard Service', () => { 8 | beforeEach(() => { 9 | jest.clearAllMocks(); 10 | }); 11 | 12 | describe('createMoodCard', () => { 13 | it('should create a new mood card', async () => { 14 | const moodCardData = { feel: 'happy', note: 'Great day!' }; 15 | const userID = 'user123'; 16 | const createdMoodCard = { id: '1', ...moodCardData }; 17 | 18 | moodCardRepository.createMoodCard.mockResolvedValue(createdMoodCard); 19 | 20 | const result = await moodCardService.createMoodCard(moodCardData, userID); 21 | 22 | expect(moodCardRepository.createMoodCard).toHaveBeenCalledWith(moodCardData, userID); 23 | expect(result).toEqual(createdMoodCard); 24 | }); 25 | 26 | it('should throw an error if creation fails', async () => { 27 | const moodCardData = { feel: 'happy', note: 'Great day!' }; 28 | const userID = 'user123'; 29 | 30 | moodCardRepository.createMoodCard.mockRejectedValue(new Error('Creation failed')); 31 | 32 | await expect(moodCardService.createMoodCard(moodCardData, userID)).rejects.toThrow('Creation failed'); 33 | }); 34 | }); 35 | 36 | describe('findMoodCards', () => { 37 | it('should return an array of mood cards', async () => { 38 | const userID = 'user123'; 39 | const moodCards = [ 40 | { feel: 'happy', note: 'Great day!', _id: '1' }, 41 | { feel: 'sad', note: 'Bad day', _id: '2' } 42 | ]; 43 | const expectedDto = moodCards.map(card => new GetMoodCardDto(card.feel, card.note, card._id)); 44 | 45 | moodCardRepository.readMoodCards.mockResolvedValue(moodCards); 46 | 47 | const result = await moodCardService.findMoodCards(userID); 48 | 49 | expect(moodCardRepository.readMoodCards).toHaveBeenCalledWith(userID); 50 | expect(result).toEqual(expectedDto); 51 | }); 52 | 53 | it('should throw an error if retrieval fails', async () => { 54 | const userID = 'user123'; 55 | 56 | moodCardRepository.readMoodCards.mockRejectedValue(new Error('Retrieval failed')); 57 | 58 | await expect(moodCardService.findMoodCards(userID)).rejects.toThrow('Retrieval failed'); 59 | }); 60 | }); 61 | 62 | describe('deleteMoodCard', () => { 63 | it('should delete a mood card by id', async () => { 64 | const id = '1'; 65 | 66 | moodCardRepository.deleteMoodCard.mockResolvedValue(); 67 | 68 | await moodCardService.deleteMoodCard(id); 69 | 70 | expect(moodCardRepository.deleteMoodCard).toHaveBeenCalledWith(id); 71 | }); 72 | 73 | it('should throw an error if deletion fails', async () => { 74 | const id = '1'; 75 | 76 | moodCardRepository.deleteMoodCard.mockRejectedValue(new Error('Deletion failed')); 77 | 78 | await expect(moodCardService.deleteMoodCard(id)).rejects.toThrow('Deletion failed'); 79 | }); 80 | }); 81 | }); 82 | -------------------------------------------------------------------------------- /README.en.md: -------------------------------------------------------------------------------- 1 | **This application was prepared using the Rational Unified Process software development process. The entire application is developed with software architecture.** 2 | 3 | **Used Software Architecture:** Layered architecture (monolithic) 4 | 5 | **Used Software Technologies:** Node.js (Express.js), React.js, Jest (test). MongoDB (Mongoose) → MERN STACK 6 | 7 | **Features Included:** API versioning, unit test, layered architecture (presentation, business, data), DTOs, validations, JWT, mail sender (Nodemailer), file upload, Joi, crypto.js, dotenv, authentication, authorization 8 | 9 | --- 10 | 11 | **YOUR DIARY MOOD WEB APPLICATION** 12 | 13 | In today's world, increasing psychological issues and addictions lower people's quality of life and joy. The lack of self-awareness due to the ever-growing sea of technology and content is constantly increasing, harming social and individual lives. With the Your Diary Mood application, users can track their emotional states, thoughts, and feelings throughout the day and view the analysis and results. The application contributes to increasing individual awareness. 14 | 15 | ![](./images/register.png) 16 | 17 | ![](./images/login.png) 18 | 19 | ![](./images/profile-1.png) 20 | 21 | ![](./images/profile-2.png) 22 | 23 | ![](./images/mobile-register.png) 24 | 25 | ![](./images/mobile-login.png) 26 | 27 | ![](./images/mobile-profile.png) 28 | 29 | **INCEPTION PHASE** 30 | 31 | **Vision** 32 | 33 | We will ask users to express their emotional states through the application at specific times. This expression time will be left free for the user, with only reminder notifications permitted as allowed. After reporting their emotions, feelings, and thoughts using emoji expressions, users will be able to see which emotions they experienced throughout the day at the end of the day, raising awareness. Users will be able to track their weekly and monthly emotional changes through the application. 34 | 35 | |**ACTOR**|**GOAL**| 36 | | :- | :- | 37 | |User|

Register to the system

Log in to the system

Create an emotion state card

| 38 | |System|

Save user registration information

Verify the user

Save the user's emotion state card

Show the user their emotion state card

| 39 | 40 | ![](./images/Aspose.Words.9a3b1f86-47b0-41d9-b069-e55ca91e40ed.001.png) 41 | 42 | ![](./images/Aspose.Words.9a3b1f86-47b0-41d9-b069-e55ca91e40ed.002.png) 43 | 44 | **ELABORATION PHASE** 45 | 46 | **Main Success Scenario** 47 | 48 | 1. The user registers to the system. 49 | 2. The system receives the user information. 50 | 3. The system sends a verification email to the user. 51 | 4. The user verifies the email. 52 | 5. The user logs in to the system. 53 | 6. The user views the profile page. 54 | 7. The user creates an emotion state card. 55 | 8. The system saves the emotion state card. 56 | 9. The user views the emotion state card. 57 | 58 | **System Sequence Diagrams** 59 | 60 | ![](./images/Aspose.Words.9a3b1f86-47b0-41d9-b069-e55ca91e40ed.003.png) 61 | 62 | ![](./images/Aspose.Words.9a3b1f86-47b0-41d9-b069-e55ca91e40ed.004.png) 63 | 64 | **UML Class Diagram** 65 | 66 | ![](./images/Aspose.Words.9a3b1f86-47b0-41d9-b069-e55ca91e40ed.005.png) 67 | 68 | **TRANSITION PHASE** 69 | 70 | It is in the development and completion phase. The technologies listed at the beginning are relevant to this phase. The application code has been created. Unit tests have been conducted. 71 | 72 | **Used Software Architecture:** Layered architecture (monolithic) 73 | 74 | **Used Software Technologies:** Node.js (Express.js), React.js, Jest (test). MongoDB (Mongoose) → MERN STACK 75 | 76 | **Features Included:** API versioning, unit test, layered architecture (presentation, business, data), DTOs, validations, JWT, mail sender (Nodemailer), file upload, Joi, crypto.js, dotenv, authentication, authorization 77 | -------------------------------------------------------------------------------- /fronted/your-diary-mood/src/assets/react.svg: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /fronted/your-diary-mood/src/components/LoginForm.jsx: -------------------------------------------------------------------------------- 1 | import { useNavigate } from "react-router-dom" 2 | import axios from 'axios' 3 | 4 | const LoginForm=()=>{ 5 | const navigate=useNavigate() 6 | 7 | const handleSubmit=async(e)=>{ 8 | e.preventDefault() 9 | const userData = { 10 | email: e.target.elements.email.value, 11 | password: e.target.elements.password.value, 12 | } 13 | try { 14 | const res=await axios.post('http://localhost:3000/api/v1/sign-in', 15 | userData 16 | ) 17 | sessionStorage.setItem('accessToken', res.data.accessToken) 18 | navigate('/profile') 19 | } catch (error) { 20 | if(error.response.data.error){ 21 | alert(error.response.data.error) 22 | } 23 | if(error.response.data.message){ 24 | alert(error.response.data.message) 25 | } 26 | 27 | 28 | console.error('There was an error registering the user:', error) 29 | } 30 | 31 | } 32 | return( 33 | <> 34 |
35 |
36 |
37 |
38 | Sample image 40 |
41 |
42 |
43 |
44 | 46 | 47 |
48 | 49 |
50 | 52 | 53 |
54 | 55 |
56 |
57 | 58 | 61 |
62 |
63 | 64 |
65 | 67 |

Don't have an account? navigate("/register")} >Register

69 | 70 |
71 | 72 |
73 |
74 |
75 |
76 |
77 | 78 | ) 79 | } 80 | 81 | export default LoginForm -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | [![en](https://img.shields.io/badge/lang-en-red.svg)](https://github.com/emrebaranarca/your-diary-mood-web-application/blob/main/README.en.md) 2 | 3 | **Bu uygulama rational unified process yazılım geliştirme süreci ile hazırlanmıştır. Tüm uygulama yazılım mimarisi ile geliştirilmiştir.** 4 | 5 | **Kullanılan yazılım mimarisi:** Layered architecture (monolithic) 6 | 7 | **Kullanılan yazılım teknolojileri:** node.js (express.js), react.js, jest(test). MongoDB (mongoose) →MERN STACK 8 | 9 | **İçerisinde bulunan özellikler:** API versioning, unit test, layered architecture(presentation,business,data), DTOs, validations, JWT, mail sender(nodemailer), file upload, joi, crypto.js, dotenv, authentication, authorization 10 | 11 | 12 | **YOUR DIARY MOOD WEB APPLICATION** 13 | 14 | Günümüzde artan psikolojik sorunlar ve bağımlılıklar insanın yaşam kalitesini ve sevincini düşürmektedir. İnsanın kendini bilme ve tanıma noktasında eksikliği artan teknoloji ve içerik denizinden dolayı sürekli artmakta toplumsal ve bireysel yaşama zarar vermektedir. Your Diary Mood uygulaması sayesinde kullanıcılar gün içerisinde yaşadığı duygu durumlarını, neler düşündüklerini ve neler hissettiklerini takip edebilecek, bunun analiz ve sonuçlarını görüntüleyebileceklerdir. Uygulama insanın bireysel farkındalığını arttırmaya katkı sağlamaktadır. 15 | 16 | ![](./images/register.png) 17 | 18 | ![](./images/login.png) 19 | 20 | ![](./images/profile-1.png) 21 | 22 | ![](./images/profile-2.png) 23 | 24 | ![](./images/mobile-register.png) 25 | 26 | ![](./images/mobile-login.png) 27 | 28 | ![](./images/mobile-profile.png) 29 | 30 | **INCEPTION PHASE** 31 | 32 | **Vision** 33 | 34 | Kullanıcıların belirli saatlerde uygulama üzerinden duygu durumlarını ifade etmesini isteyeceğiz. Bu ifade etme vakti kullanıcıya özgür olarak bırakılacak sadece izinler doğrultusunda hatırlatma bildirimleri yapılabilecektir. Emoji ifadeleri ile duygularını, his ve düşüncelerini duygu kartına bildirdikten sonra gün sonunda gün içerisinde hangi duyguları yaşadıklarını görüntüleyecek bunlar üzerinde bir farkındalık gerçekleştirilecektir. Uygulama üzerinden haftalık ve aylık duygu değişimlerini takip edebileceklerdir. 35 | 36 | 37 | |**ACTOR**|**GOAL**| 38 | | :- | :- | 39 | |User|

Sisteme kayıt ol

Sisteme giriş yap

Duygu durum kartını oluştur

| 40 | |Sistem|

Kullanıcın kayıt bilgilerini sakla

Kullanıcıyı doğrula

Kullanıcının duygu durum kartını kaydet

Kullanıcıya duygu durum kartını göster

| 41 | 42 | 43 | ![](./images/Aspose.Words.9a3b1f86-47b0-41d9-b069-e55ca91e40ed.001.png) 44 | 45 | ![](./images/Aspose.Words.9a3b1f86-47b0-41d9-b069-e55ca91e40ed.002.png) 46 | 47 | 48 | **ELABORATION PHASE** 49 | 50 | **Ana başarı senaryosu** 51 | 52 | 1. Kullanıcı sisteme kayıt olur. 53 | 1. Sistem kullanıcı bilgilerini alır. 54 | 1. Sistem kullanıcı mailine doğrulama gönderir. 55 | 1. Kullanıcı mail doğrulamasını gerçekleştirir 56 | 1. Kullanıcı sisteme giriş yapar. 57 | 1. Kullanıcı profil sayfasını görüntüler. 58 | 1. Kullanıcı duygu durum kartını oluşturur. 59 | 1. Sistem duygu durum kartını kaydeder. 60 | 1. Kullanıcı duygu durum kartını görüntüler. 61 | 62 | 63 | **System Sequence Diagrams** 64 | 65 | ![](./images/Aspose.Words.9a3b1f86-47b0-41d9-b069-e55ca91e40ed.003.png) 66 | 67 | ![](./images/Aspose.Words.9a3b1f86-47b0-41d9-b069-e55ca91e40ed.004.png) 68 | 69 | 70 | 71 | UML Class diagram 72 | 73 | ![](./images/Aspose.Words.9a3b1f86-47b0-41d9-b069-e55ca91e40ed.005.png) 74 | 75 | 76 | 77 | 78 | 79 | 80 | **TRANSITION PHASE** 81 | 82 | Geliştirme ve tamamlama aşamasındadır. En başta yazılan teknolojiler bu fazın bilgileridir. Uygulama kodu oluşturuldu. Unit testler yapıldı. 83 | 84 | **Kullanılan yazılım mimarisi:** Layered architecture (monolithic) 85 | 86 | **Kullanılan yazılım teknolojileri:** node.js (express.js), react.js, jest(test). MongoDB (mongoose) →MERN STACK 87 | 88 | **İçerisinde bulunan özellikler:** API versioning, unit test, layered architecture(presentation,business,data), DTOs, validations, JWT, mail sender(nodemailer), file upload, joi, crypto.js, dotenv, authentication, authorization 89 | 90 | 91 | 92 | 93 | 94 | 95 | 96 | -------------------------------------------------------------------------------- /fronted/your-diary-mood/src/pages/Profile.jsx: -------------------------------------------------------------------------------- 1 | import { useEffect, useState } from "react"; 2 | import Navbar from "../components/Navbar"; 3 | import axios from 'axios'; 4 | import MoodCard from "../components/MoodCard"; 5 | import MoodCardForm from "../components/MoodCardForm"; 6 | 7 | function Profile() { 8 | const [userData, setUserData] = useState(null); 9 | const [profilePicture, setProfilePicture] = useState(null); 10 | 11 | const getData = async () => { 12 | try { 13 | const accessToken = sessionStorage.getItem('accessToken'); 14 | const res = await axios.get('http://localhost:3000/api/v1/get-user', { 15 | headers: { 16 | 'Authorization': `Bearer ${accessToken}` 17 | } 18 | }); 19 | setUserData(res.data); 20 | } catch (error) { 21 | console.error('Error fetching user data:', error); 22 | } 23 | }; 24 | 25 | const handleFileChange = (e) => { 26 | setProfilePicture(e.target.files[0]); 27 | }; 28 | 29 | const handleFileUpload = async () => { 30 | if (!profilePicture) { 31 | alert("Please select a file first!"); 32 | return; 33 | } 34 | 35 | const formData = new FormData(); 36 | formData.append('profilePicture', profilePicture); 37 | 38 | try { 39 | const accessToken = sessionStorage.getItem('accessToken'); 40 | const res = await axios.post('http://localhost:3000/api/v1/upload-profil-picture', formData, { 41 | headers: { 42 | 'Authorization': `Bearer ${accessToken}`, 43 | 'Content-Type': 'multipart/form-data' 44 | } 45 | }); 46 | console.log('File uploaded successfully:', res.data); 47 | setUserData(prevData => ({ 48 | ...prevData, 49 | profilePicture: res.data.fileName // Yeni dosya yolunu userData'ya ekleyin 50 | })); 51 | await getData() 52 | } catch (error) { 53 | console.error('Error uploading file:', error); 54 | } 55 | }; 56 | 57 | 58 | useEffect(() => { 59 | getData(); 60 | }, []); 61 | 62 | useEffect(() => { 63 | console.log('User Data Updated:', userData); 64 | }, [userData]); 65 | 66 | if (!userData) { 67 | return

Loading...

; // Veriler yüklenirken kullanıcıya bilgi ver 68 | } 69 | 70 | return ( 71 | <> 72 | 73 |
74 |
75 |
76 |
77 |
78 |
79 | 81 |
{userData.fullname}
82 |

Full Stack Developer

83 |

Bay Area, San Francisco, CA

84 |
85 | 86 | 87 |
88 |
89 |
90 | 91 | 92 |
93 | 94 |
95 |
96 |
97 |
98 |
99 |

Full Name

100 |
101 |
102 |

{userData.fullname}

103 |
104 |
105 |
106 |
107 |
108 |

Email

109 |
110 |
111 |

{userData.email}

112 |
113 |
114 |
115 |
116 |
117 |

Username

118 |
119 |
120 |

{userData.username}

121 |
122 |
123 |
124 |
125 | 126 | 127 | 128 | 129 | 130 | 131 | 132 | 133 | 134 |
135 |
136 |
137 |
138 | 139 | ); 140 | } 141 | 142 | export default Profile; 143 | -------------------------------------------------------------------------------- /fronted/your-diary-mood/src/components/RegisterForm.jsx: -------------------------------------------------------------------------------- 1 | import axios from 'axios' 2 | import { useNavigate } from 'react-router-dom' 3 | 4 | const RegisterForm=()=>{ 5 | const navigate=useNavigate() 6 | 7 | const handleSubmit=async(e)=>{ 8 | e.preventDefault() 9 | const userData = { 10 | fullname: e.target.elements.fullname.value, 11 | email: e.target.elements.email.value, 12 | password: e.target.elements.password.value, 13 | username: e.target.elements.username.value 14 | } 15 | console.log(userData) 16 | try { 17 | const res=await axios.post('http://localhost:3000/api/v1/sign-up', 18 | userData 19 | ) 20 | alert('registered succesfully. please verify your email') 21 | navigate('/') 22 | } catch (error) { 23 | if(error.response.data.error){ 24 | alert(error.response.data.error) 25 | } 26 | if(error.response.data.message){ 27 | alert(error.response.data.message) 28 | } 29 | 30 | console.error('There was an error registering the user:', error.response.data.message) 31 | } 32 | 33 | } 34 | 35 | return( 36 | <> 37 |
38 |
39 |
40 |
41 |
42 |
43 |
44 |
45 |

Sign up

46 | 47 |
48 |
49 | 50 |
51 | 52 | 53 |
54 |
55 | 56 |
57 | 58 |
59 | 60 | 61 |
62 |
63 | 64 |
65 | 66 |
67 | 68 | 69 |
70 |
71 | 72 |
73 | 74 |
75 | 76 | 77 |
78 |
79 | 80 |
81 | 82 |
83 |
84 |
85 |
86 | Sample image 88 |
89 |
90 |
91 |
92 |
93 |
94 |
95 |
96 | 97 | ) 98 | } 99 | 100 | export default RegisterForm -------------------------------------------------------------------------------- /backend/v1/tests/unit/services/authService.spec.js: -------------------------------------------------------------------------------- 1 | const authService = require('../../../src/business/services/auth'); 2 | const authRepository = require('../../../src/data/repositories/auth'); 3 | const bcrypt = require('bcrypt'); 4 | const crypto = require('crypto'); 5 | const sendVerificationEmail = require('../../../src/scripts/utils/mail/sendVerificationEmail'); 6 | const { generateJWT } = require('../../../src/scripts/utils/jwt/jwt'); 7 | const nodemailer = require('nodemailer'); 8 | 9 | jest.mock('/home/emrebaran/Desktop/software/your-diary-mood-app/backend/v1/src/data/repositories/auth.js'); 10 | jest.mock('bcrypt'); 11 | jest.mock('crypto'); 12 | jest.mock('/home/emrebaran/Desktop/software/your-diary-mood-app/backend/v1/src/scripts/utils/mail/sendVerificationEmail.js'); 13 | jest.mock('/home/emrebaran/Desktop/software/your-diary-mood-app/backend/v1/src/scripts/utils/jwt/jwt.js'); 14 | jest.mock('nodemailer'); 15 | 16 | 17 | describe('Auth Service', () => { 18 | beforeEach(() => { 19 | jest.clearAllMocks(); 20 | }); 21 | 22 | // describe('registerUser', () => { 23 | // it('should register a user and send a verification email', async () => { 24 | // const userData = { 25 | // email: 'test@example.com', 26 | // password: 'password123' 27 | // }; 28 | // const emailToken = '1234567890abcdef'; 29 | // const hashedPassword = 'hashedpassword123'; 30 | // const mockSendMail = jest.fn().mockResolvedValue(true); 31 | 32 | // crypto.randomBytes.mockImplementation((size, callback) => callback(null, Buffer.from(emailToken, 'hex'))); 33 | // bcrypt.genSalt.mockResolvedValue('salt'); 34 | // bcrypt.hash.mockResolvedValue(hashedPassword); 35 | // authRepository.createUser.mockResolvedValue(); 36 | 37 | // // Mock nodemailer 38 | // nodemailer.createTransport.mockReturnValue({ 39 | // sendMail: mockSendMail 40 | // }); 41 | 42 | // await authService.registerUser(userData); 43 | 44 | // expect(crypto.randomBytes).toHaveBeenCalledWith(12, expect.any(Function)); 45 | // expect(mockSendMail).toHaveBeenCalledWith({ 46 | // from: process.env.SMTP_USER, 47 | // to: userData.email, 48 | // subject: 'Email Verification', 49 | // text: `Please verify your email by clicking on the following link: http://localhost:3000/api/v1/verify-email/${emailToken}`, 50 | // html: `

Please verify your email by clicking on the following link: http://localhost:3000/api/v1/verify-email/${emailToken}

` 51 | // }); 52 | // expect(bcrypt.genSalt).toHaveBeenCalled(); 53 | // expect(bcrypt.hash).toHaveBeenCalledWith(userData.password, 'salt'); 54 | // expect(authRepository.createUser).toHaveBeenCalledWith({ ...userData, emailToken, password: hashedPassword }); 55 | // }); 56 | 57 | // }); 58 | 59 | describe('verifyEmail', () => { 60 | it('should verify a user\'s email', async () => { 61 | const emailToken = '1234567890abcdef'; 62 | authRepository.verifyEmail.mockResolvedValue(); 63 | 64 | await authService.verifyEmail(emailToken); 65 | 66 | expect(authRepository.verifyEmail).toHaveBeenCalledWith(emailToken); 67 | }); 68 | 69 | it('should throw an error if email token is null', async () => { 70 | await expect(authService.verifyEmail(null)).rejects.toThrow('email token is null'); 71 | }); 72 | 73 | it('should throw an error if email verification fails', async () => { 74 | const emailToken = '1234567890abcdef'; 75 | const errorMessage = 'Verification failed'; 76 | 77 | authRepository.verifyEmail.mockRejectedValue(new Error(errorMessage)); 78 | 79 | await expect(authService.verifyEmail(emailToken)).rejects.toThrow(errorMessage); 80 | }); 81 | }); 82 | 83 | describe('loginUser', () => { 84 | it('should login a user and return a JWT token', async () => { 85 | const userData = { 86 | email: 'test@example.com', 87 | password: 'password123' 88 | }; 89 | const user = { 90 | isVerified: true, 91 | password: 'hashedpassword123' 92 | }; 93 | const token = 'jwt_token'; 94 | 95 | authRepository.loginUser.mockResolvedValue(user); 96 | bcrypt.compare.mockResolvedValue(true); 97 | generateJWT.mockReturnValue(token); 98 | 99 | const result = await authService.loginUser(userData); 100 | 101 | expect(authRepository.loginUser).toHaveBeenCalledWith(userData); 102 | expect(bcrypt.compare).toHaveBeenCalledWith(userData.password, user.password); 103 | expect(generateJWT).toHaveBeenCalledWith(user); 104 | expect(result).toBe(token); 105 | }); 106 | 107 | it('should throw an error if user does not exist', async () => { 108 | const userData = { 109 | email: 'test@example.com', 110 | password: 'password123' 111 | }; 112 | 113 | authRepository.loginUser.mockResolvedValue(null); 114 | 115 | await expect(authService.loginUser(userData)).rejects.toThrow('User Does Not Exists'); 116 | }); 117 | 118 | it('should throw an error if user is not verified', async () => { 119 | const userData = { 120 | email: 'test@example.com', 121 | password: 'password123' 122 | }; 123 | const user = { 124 | isVerified: false 125 | }; 126 | 127 | authRepository.loginUser.mockResolvedValue(user); 128 | 129 | await expect(authService.loginUser(userData)).rejects.toThrow('Please Verify Your Account'); 130 | }); 131 | 132 | it('should throw an error if password is invalid', async () => { 133 | const userData = { 134 | email: 'test@example.com', 135 | password: 'password123' 136 | }; 137 | const user = { 138 | isVerified: true, 139 | password: 'hashedpassword123' 140 | }; 141 | 142 | authRepository.loginUser.mockResolvedValue(user); 143 | bcrypt.compare.mockResolvedValue(false); 144 | 145 | await expect(authService.loginUser(userData)).rejects.toThrow('Invalid Credentials'); 146 | }); 147 | 148 | it('should throw an error if login fails', async () => { 149 | const userData = { 150 | email: 'test@example.com', 151 | password: 'password123' 152 | }; 153 | const errorMessage = 'Login failed'; 154 | 155 | authRepository.loginUser.mockRejectedValue(new Error(errorMessage)); 156 | 157 | await expect(authService.loginUser(userData)).rejects.toThrow(errorMessage); 158 | }); 159 | }); 160 | }) 161 | 162 | -------------------------------------------------------------------------------- /fronted/your-diary-mood/src/assets/smiling-face-with-halo-svgrepo-com.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | 22 | 23 | 24 | 25 | 26 | 27 | 28 | 29 | 30 | 31 | 32 | 33 | 34 | 35 | 36 | 37 | 38 | 39 | 40 | 41 | 42 | 43 | 44 | 45 | 46 | 47 | 48 | 49 | 50 | 51 | 52 | 53 | 54 | 55 | 56 | 57 | 58 | 59 | 60 | 61 | 62 | 63 | 64 | 65 | 66 | 67 | 68 | 69 | 70 | 71 | 72 | 73 | 74 | 75 | 76 | 77 | 78 | 79 | 80 | 81 | 82 | 83 | 84 | 85 | 86 | 87 | 88 | 89 | 90 | 91 | 92 | 93 | 94 | 95 | 96 | 97 | 98 | 99 | 100 | 101 | 102 | 103 | 104 | 105 | 106 | 107 | 108 | 109 | 110 | 111 | 112 | 113 | 114 | 115 | 116 | 117 | 118 | 119 | 120 | 121 | 122 | 123 | 124 | 125 | 126 | 127 | 128 | 129 | 130 | 131 | 132 | 133 | --------------------------------------------------------------------------------