├── 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 |
5 |
15 |
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 |
deleteMoodCard(mood.id)}>
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 |
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 | 
16 |
17 | 
18 |
19 | 
20 |
21 | 
22 |
23 | 
24 |
25 | 
26 |
27 | 
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 | 
41 |
42 | 
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 | 
61 |
62 | 
63 |
64 | **UML Class Diagram**
65 |
66 | 
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 |
40 |
41 |
74 |
75 |
76 |
77 | >
78 | )
79 | }
80 |
81 | export default LoginForm
--------------------------------------------------------------------------------
/README.md:
--------------------------------------------------------------------------------
1 | [](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 | 
17 |
18 | 
19 |
20 | 
21 |
22 | 
23 |
24 | 
25 |
26 | 
27 |
28 | 
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 | 
44 |
45 | 
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 | 
66 |
67 | 
68 |
69 |
70 |
71 | UML Class diagram
72 |
73 | 
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 | Upload Profile Pic
87 |
88 |
89 |
90 |
91 |
92 |
93 |
94 |
95 |
96 |
97 |
98 |
101 |
102 |
{userData.fullname}
103 |
104 |
105 |
106 |
107 |
110 |
111 |
{userData.email}
112 |
113 |
114 |
115 |
116 |
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 | Fullname
53 |
54 |
55 |
56 |
57 |
58 |
59 |
60 | Email
61 |
62 |
63 |
64 |
65 |
66 |
67 |
68 | Password
69 |
70 |
71 |
72 |
73 |
74 |
75 |
76 | Username
77 |
78 |
79 |
80 |
81 | Register
82 |
83 |
84 |
85 |
86 |
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 |
--------------------------------------------------------------------------------