├── .gitignore ├── CODE_OF_CONDUCT.md ├── CONTRIBUTING.md ├── README.md ├── database .js ├── frontend ├── package-lock.json ├── package.json ├── public │ ├── favicon.ico │ └── index.html └── src │ ├── App.css │ ├── App.js │ ├── Game │ ├── App.js │ ├── Board.js │ ├── Info.js │ ├── app.css │ ├── board.css │ ├── index.css │ ├── index.js │ └── info.css │ ├── Hooks │ └── useOnline.js │ ├── components │ ├── About.js │ ├── AddNote.js │ ├── Alert.js │ ├── EditNote.js │ ├── Login.js │ ├── Navbar.js │ ├── NoteItems.js │ ├── Notes.js │ ├── Shimmer.js │ └── Signup.js │ ├── context │ └── notes │ │ ├── NoteState.js │ │ └── noteContext.js │ ├── index.css │ └── index.js ├── index.js ├── middleware ├── error.js └── fetchuser.js ├── models ├── Note.js └── User.js ├── package-lock.json ├── package.json └── routes ├── auth.js └── notes.js /.gitignore: -------------------------------------------------------------------------------- 1 | 2 | node_modules 3 | 4 | .env 5 | -------------------------------------------------------------------------------- /CODE_OF_CONDUCT.md: -------------------------------------------------------------------------------- 1 | # Contributor Covenant Code of Conduct 2 | 3 | ## Our Pledge 4 | 5 | We as members, contributors, and leaders pledge to make participation in our 6 | community a harassment-free experience for everyone, regardless of age, body 7 | size, visible or invisible disability, ethnicity, sex characteristics, gender 8 | identity and expression, level of experience, education, socio-economic status, 9 | nationality, personal appearance, race, religion, or sexual identity 10 | and orientation. 11 | 12 | We pledge to act and interact in ways that contribute to an open, welcoming, 13 | diverse, inclusive, and healthy community. 14 | 15 | ## Our Standards 16 | 17 | Examples of behavior that contributes to a positive environment for our 18 | community include: 19 | 20 | * Demonstrating empathy and kindness toward other people 21 | * Being respectful of differing opinions, viewpoints, and experiences 22 | * Giving and gracefully accepting constructive feedback 23 | * Accepting responsibility and apologizing to those affected by our mistakes, 24 | and learning from the experience 25 | * Focusing on what is best not just for us as individuals, but for the 26 | overall community 27 | 28 | Examples of unacceptable behavior include: 29 | 30 | * The use of sexualized language or imagery, and sexual attention or 31 | advances of any kind 32 | * Trolling, insulting or derogatory comments, and personal or political attacks 33 | * Public or private harassment 34 | * Publishing others' private information, such as a physical or email 35 | address, without their explicit permission 36 | * Other conduct which could reasonably be considered inappropriate in a 37 | professional setting 38 | 39 | ## Enforcement Responsibilities 40 | 41 | Community leaders are responsible for clarifying and enforcing our standards of 42 | acceptable behavior and will take appropriate and fair corrective action in 43 | response to any behavior that they deem inappropriate, threatening, offensive, 44 | or harmful. 45 | 46 | Community leaders have the right and responsibility to remove, edit, or reject 47 | comments, commits, code, wiki edits, issues, and other contributions that are 48 | not aligned to this Code of Conduct, and will communicate reasons for moderation 49 | decisions when appropriate. 50 | 51 | ## Scope 52 | 53 | This Code of Conduct applies within all community spaces, and also applies when 54 | an individual is officially representing the community in public spaces. 55 | Examples of representing our community include using an official e-mail address, 56 | posting via an official social media account, or acting as an appointed 57 | representative at an online or offline event. 58 | 59 | ## Enforcement 60 | 61 | Instances of abusive, harassing, or otherwise unacceptable behavior may be 62 | reported to the community leaders responsible for enforcement at 63 | rajatsingh241303@gmail.com. 64 | All complaints will be reviewed and investigated promptly and fairly. 65 | 66 | All community leaders are obligated to respect the privacy and security of the 67 | reporter of any incident. 68 | 69 | ## Enforcement Guidelines 70 | 71 | Community leaders will follow these Community Impact Guidelines in determining 72 | the consequences for any action they deem in violation of this Code of Conduct: 73 | 74 | ### 1. Correction 75 | 76 | **Community Impact**: Use of inappropriate language or other behavior deemed 77 | unprofessional or unwelcome in the community. 78 | 79 | **Consequence**: A private, written warning from community leaders, providing 80 | clarity around the nature of the violation and an explanation of why the 81 | behavior was inappropriate. A public apology may be requested. 82 | 83 | ### 2. Warning 84 | 85 | **Community Impact**: A violation through a single incident or series 86 | of actions. 87 | 88 | **Consequence**: A warning with consequences for continued behavior. No 89 | interaction with the people involved, including unsolicited interaction with 90 | those enforcing the Code of Conduct, for a specified period of time. This 91 | includes avoiding interactions in community spaces as well as external channels 92 | like social media. Violating these terms may lead to a temporary or 93 | permanent ban. 94 | 95 | ### 3. Temporary Ban 96 | 97 | **Community Impact**: A serious violation of community standards, including 98 | sustained inappropriate behavior. 99 | 100 | **Consequence**: A temporary ban from any sort of interaction or public 101 | communication with the community for a specified period of time. No public or 102 | private interaction with the people involved, including unsolicited interaction 103 | with those enforcing the Code of Conduct, is allowed during this period. 104 | Violating these terms may lead to a permanent ban. 105 | 106 | ### 4. Permanent Ban 107 | 108 | **Community Impact**: Demonstrating a pattern of violation of community 109 | standards, including sustained inappropriate behavior, harassment of an 110 | individual, or aggression toward or disparagement of classes of individuals. 111 | 112 | **Consequence**: A permanent ban from any sort of public interaction within 113 | the community. 114 | 115 | ## Attribution 116 | 117 | This Code of Conduct is adapted from the [Contributor Covenant][homepage], 118 | version 2.0, available at 119 | https://www.contributor-covenant.org/version/2/0/code_of_conduct.html. 120 | 121 | Community Impact Guidelines were inspired by [Mozilla's code of conduct 122 | enforcement ladder](https://github.com/mozilla/diversity). 123 | 124 | [homepage]: https://www.contributor-covenant.org 125 | 126 | For answers to common questions about this code of conduct, see the FAQ at 127 | https://www.contributor-covenant.org/faq. Translations are available at 128 | https://www.contributor-covenant.org/translations. 129 | -------------------------------------------------------------------------------- /CONTRIBUTING.md: -------------------------------------------------------------------------------- 1 | # Hacktoberfest Contributor's Guide 2 | 3 | Welcome to Hacktoberfest! This is a beginner-friendly guide for contributors who want to participate in this annual celebration of open source. 4 | 5 | ## What is Hacktoberfest? 6 | 7 | Hacktoberfest is a month-long event that encourages people to contribute to open source projects. It's a great opportunity to learn and grow while making a positive impact on the open source community. 8 | 9 | ## How to Contribute 10 | 11 | Here are the steps to get started with your Hacktoberfest contributions: 12 | 13 | 1. **Find a Project**: Browse through open source projects on platforms like GitHub, GitLab, or Bitbucket. Look for projects that interest you and that have the "Hacktoberfest" label. 14 | 15 | 2. **Fork the Repository**: Click the "Fork" button on the project's GitHub page to create your own copy of the repository. 16 | 17 | 3. **Clone Your Fork**: Use `git clone` to copy your forked repository to your local machine. 18 | 19 | `git clone https://github.com/your-username/project-name.git` 20 | 21 | 4. **Create a Branch**: Create a new branch for your contributions. 22 | 23 | 24 | 5. **Make Changes**: Make your desired contributions to the project. This could include adding features, fixing bugs, improving documentation, or anything else the project needs. 25 | 26 | 6. **Commit Your Changes**: Use `git commit` to commit your changes to your local branch. 27 | 28 | 29 | 7. **Push Your Changes**: Push your branch with your changes to your GitHub repository. 30 | 31 | 32 | 8. **Create a Pull Request**: Go to the original project's repository on GitHub and click the "New Pull Request" button. Compare and create a pull request from your branch to the original project's `main` or `master` branch. 33 | 34 | 9. **Follow Up**: Keep an eye on your pull request for any feedback or changes requested by the maintainers. Be responsive to their comments. 35 | 36 | 10. **Celebrate**: Once your pull request is accepted and merged, congratulations! You've made a successful contribution to Hacktoberfest. 37 | 38 | ## Tips for Successful Contributions 39 | 40 | - Read and follow the project's contribution guidelines. 41 | - Start with issues labeled as "beginner-friendly" if you're new to open source. 42 | - Communicate with maintainers and the community if you have questions or need help. 43 | - Be respectful and professional in all interactions. 44 | - Don't forget to check the [Hacktoberfest website](https://hacktoberfest.digitalocean.com/) for official rules and prizes. 45 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | 2 |
3 |

Notebook

4 |
5 |

6 |
7 | 8 | 9 | 10 |
11 | 12 | 13 | 14 | 15 |
16 | 17 | 18 | 19 | 20 |
21 | 22 | # Description 23 |

This is about Notebook:

24 | Using Notebook you could-
1. Write your personal / proffesional notes
2. Secure your notes on clould
3. Access your notes from anywhere from any devices
4. Edit or Delete your notes
5. Give your notes a perticular tag
6. Maintaine privacy using credentials 25 | 26 | # Features 27 |
1. Here notebook is created using React as a frontend and NodeJS as a backend.
2. Here you can add , delete and update notes in your Mongoose database.
3. You create a password using express validation , authentication using JWT Token and salting.
4. An easy way to sign up and log in a notebook, context for data passing through the component. 28 | 29 | # File Structure 30 | ``` 31 | Project Root 32 | | 33 | |- frontend 34 | | |- src/ 35 | | |- public/ 36 | | 37 | |- middleware 38 | |- Models 39 | |- routes 40 | |- database.js 41 | |- index.js 42 | |- package.json 43 | |- README.md 44 | |- .gitignore 45 | ``` 46 | # Step to run this project: 47 | ## Step 1. Add .env file 48 | Format of .env file 49 | ``` 50 | PORT: 5000 51 | MONGO_URL = YOUR_MONGO_URL 52 | NODE_ENV = production 53 | SECRET_KEY= Your_Secret 54 | ``` 55 | 56 | ## Step 2. Install Dependencies in Frontend folder 57 | CODE -- ``` npm install ``` 58 | 59 | ## Step 3. Install Dependencies in Root folder 60 | CODE -- ``` npm install ``` 61 | 62 | ## Step 4. Run This Project from Root folder 63 | CODE -- ``` npm run dev ``` 64 | 65 | # How to Start Contributing 66 | 1. Fork this repository by clicking on the fork button on the top of this page. This will create a copy of this repository in your account. 67 | 2. Now clone the forked repository to your machine. Go to your GitHub account, open the forked repository, click on the code button and then click the copy to clipboard icon. 68 | 3. Open a terminal and run the following git command: 69 | ``` 70 | git clone "url you just copied" 71 | ``` 72 | where "url you just copied" (without the quotation marks) is the url to this repository (your fork of this project). See the previous steps to obtain the url. 73 | For example: 74 | ``` 75 | git clone https://github.com/your_username/NoteBook.git 76 | ``` 77 | 4. Now create a branch using the 'git switch' command in repository directory: 78 | ``` 79 | git switch -c your-new-branch-name 80 | ``` 81 | 5. Make necessary changes and commit those changes. Add those changes to the branch you just created using the 'git add' command: 82 | ``` 83 | git add . 84 | ``` 85 | Now commit those changes using the 'git commit' command: 86 | ``` 87 | git commit -m "Add your title here" 88 | ``` 89 | 6. Push your changes using the command git push: 90 | ``` 91 | git push -u origin your-branch-name 92 | ``` 93 | replacing your-branch-name with the name of the branch you created earlier. 94 | 95 | 7. Submit your changes for review 96 | If you go to your repository on GitHub, you'll see a Compare & pull request button. Click on that button. Now add appropriate title and comment on it and submit 97 | the pull request. 98 | 99 | # Tech Used 100 | ![CSS3](https://img.shields.io/badge/css3-%231572B6.svg?style=for-the-badge&logo=css3&logoColor=white) ![HTML5](https://img.shields.io/badge/html5-%23E34F26.svg?style=for-the-badge&logo=html5&logoColor=white) ![JavaScript](https://img.shields.io/badge/javascript-%23323330.svg?style=for-the-badge&logo=javascript&logoColor=%23F7DF1E) ![Express.js](https://img.shields.io/badge/express.js-%23404d59.svg?style=for-the-badge&logo=express&logoColor=%2361DAFB) ![jQuery](https://img.shields.io/badge/jquery-%230769AD.svg?style=for-the-badge&logo=jquery&logoColor=white) ![JWT](https://img.shields.io/badge/JWT-black?style=for-the-badge&logo=JSON%20web%20tokens) ![NPM](https://img.shields.io/badge/NPM-%23000000.svg?style=for-the-badge&logo=npm&logoColor=white) ![NodeJS](https://img.shields.io/badge/node.js-6DA55F?style=for-the-badge&logo=node.js&logoColor=white) ![React](https://img.shields.io/badge/react-%2320232a.svg?style=for-the-badge&logo=react&logoColor=%2361DAFB) ![React Router](https://img.shields.io/badge/React_Router-CA4245?style=for-the-badge&logo=react-router&logoColor=white) ![MongoDB](https://img.shields.io/badge/MongoDB-%234ea94b.svg?style=for-the-badge&logo=mongodb&logoColor=white) 101 | -------------------------------------------------------------------------------- /database .js: -------------------------------------------------------------------------------- 1 | const dotenv = require("dotenv"); 2 | const mongoose = require("mongoose"); 3 | dotenv.config(); 4 | const MONGO_URL = process.env.MONGO_URL; 5 | const connectToMongo = async () => { 6 | try { 7 | mongoose.set("strictQuery", false); 8 | 9 | const conn = await mongoose.connect(MONGO_URL, { 10 | useNewUrlParser: true, 11 | useUnifiedTopology: true, 12 | }); // connecting to database 13 | console.log(`MongoDB Connected: ${conn.connection.host} `); // if connected 14 | } catch (error) { 15 | console.error(`Error: ${error}`); 16 | // if not connected then exit the process 17 | process.exit(); 18 | } 19 | }; 20 | 21 | module.exports = connectToMongo; // exporting the function 22 | -------------------------------------------------------------------------------- /frontend/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "notebook", 3 | "version": "0.1.0", 4 | "private": true, 5 | "dependencies": { 6 | "js-cookie": "^3.0.5", 7 | "jwt-decode": "^4.0.0", 8 | "react": "^17.0.2", 9 | "react-dom": "^17.0.2", 10 | "react-router-dom": "^6.2.1", 11 | "react-scripts": "^5.0.0", 12 | "styled-components": "^6.1.8" 13 | }, 14 | "scripts": { 15 | "start": "react-scripts start", 16 | "build": "react-scripts build" 17 | }, 18 | "proxy": "http://localhost:5000", 19 | "eslintConfig": { 20 | "extends": [ 21 | "react-app", 22 | "react-app/jest" 23 | ] 24 | }, 25 | "browserslist": { 26 | "production": [ 27 | ">0.2%", 28 | "not dead", 29 | "not op_mini all" 30 | ], 31 | "development": [ 32 | "last 1 chrome version", 33 | "last 1 firefox version", 34 | "last 1 safari version" 35 | ] 36 | }, 37 | "devDependencies": { 38 | "@babel/plugin-proposal-private-property-in-object": "^7.21.0-placeholder-for-preset-env.2" 39 | } 40 | } 41 | -------------------------------------------------------------------------------- /frontend/public/favicon.ico: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Rajat2024/NoteBook/e6e3a140e99089eef611b582e09a6bd3cfe5995a/frontend/public/favicon.ico -------------------------------------------------------------------------------- /frontend/public/index.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 12 | MERN Notebook 13 | 14 | 42 | 43 | 44 | 45 | 46 |
47 | 48 |
49 |
50 |
Please Wait
51 |
52 | 53 |
54 |
55 |
56 | 57 | 60 | 61 | 62 | 63 | 64 | -------------------------------------------------------------------------------- /frontend/src/App.css: -------------------------------------------------------------------------------- 1 | body{ 2 | font-family: cursive; 3 | } 4 | 5 | .App-logo { 6 | height: 40vmin; 7 | pointer-events: none; 8 | } 9 | 10 | @media (prefers-reduced-motion: no-preference) { 11 | .App-logo { 12 | animation: App-logo-spin infinite 20s linear; 13 | } 14 | } 15 | 16 | .App-header { 17 | background-color: #282c34; 18 | min-height: 100vh; 19 | display: flex; 20 | flex-direction: column; 21 | align-items: center; 22 | justify-content: center; 23 | font-size: calc(10px + 2vmin); 24 | color: white; 25 | } 26 | 27 | .App-link { 28 | color: #61dafb; 29 | } 30 | 31 | @keyframes App-logo-spin { 32 | from { 33 | transform: rotate(0deg); 34 | } 35 | to { 36 | transform: rotate(360deg); 37 | } 38 | } 39 | 40 | .note-list { 41 | display: flex; 42 | flex-wrap: wrap; 43 | justify-content: space-between; 44 | gap: 16px; /* Adjust the gap between shimmer cards as needed */ 45 | } 46 | 47 | .shimmer-card { 48 | width: 30%; /* Adjust the width of each shimmer card as needed */ 49 | height: 200px; /* Adjust the height of each shimmer card as needed */ 50 | background: linear-gradient(-90deg, #f0f0f0 25%, #e0e0e0 50%, #f0f0f0 75%); 51 | background-size: 200% 100%; 52 | animation: shimmer 1.5s infinite linear; 53 | border-radius: 8px; /* Add rounded corners if desired */ 54 | } 55 | 56 | @keyframes shimmer { 57 | 0% { 58 | background-position: 200% 0; 59 | } 60 | 100% { 61 | background-position: -200% 0; 62 | } 63 | } 64 | -------------------------------------------------------------------------------- /frontend/src/App.js: -------------------------------------------------------------------------------- 1 | import "./App.css"; 2 | import { useState } from "react"; 3 | import About from "./components/About"; 4 | import AddNote from "./components/AddNote"; 5 | import Notes from "./components/Notes"; 6 | import Navbar from "./components/Navbar"; 7 | // BrowserRouter as Router means that we are using the BrowserRouter component as Router. 8 | // For example: Switch as Rajat means that we use Switch as Rajat. 9 | import { BrowserRouter, Routes, Route } from "react-router-dom"; 10 | import NoteState from "./context/notes/NoteState"; 11 | import Alert from "./components/Alert"; 12 | import SignUp from "./components/Signup"; 13 | import Login from "./components/Login"; 14 | import useOnline from "./Hooks/useOnline"; 15 | import Appp from "./Game/App.js"; 16 | 17 | function App() { 18 | // console.log(window.navigator); 19 | 20 | const [alert, setAlert] = useState(null); 21 | 22 | const showAlert = (msg, type) => { 23 | setAlert({ msg: msg, type: type }); 24 | setTimeout(() => { 25 | setAlert(null); 26 | }, 1000); 27 | }; 28 | 29 | const isOnline = useOnline(); 30 | 31 | return ( 32 | <> 33 | 34 | 35 | 36 | 37 | 38 | {!isOnline ? ( 39 | 40 | ) : ( 41 |
42 | 43 | } 46 | /> 47 | } /> 48 | } 51 | /> 52 | } 55 | /> 56 | } 59 | /> 60 | 61 |
62 | )} 63 |
64 |
65 | 66 | ); 67 | } 68 | 69 | export default App; 70 | -------------------------------------------------------------------------------- /frontend/src/Game/App.js: -------------------------------------------------------------------------------- 1 | // Importing the required components 2 | import Board from './Board'; 3 | import Info from "./Info"; 4 | 5 | // Importing the CSS File 6 | import "./app.css"; 7 | 8 | // Importing the useState hook 9 | import { useState } from 'react'; 10 | 11 | function App() { 12 | 13 | // Creating a reset state, which indicates whether 14 | // the game should be reset or not 15 | const [reset, setReset] = useState(false); 16 | 17 | // Creating a winner state, which indicates 18 | // the current winner 19 | const [winner, setWinner] = useState(''); 20 | 21 | // Sets the reset property to true 22 | // which starts the chain 23 | // reaction of resetting the board 24 | const resetBoard = () => { 25 | setReset(true); 26 | } 27 | 28 | return ( 29 |
30 | {/* Shrinks the popup when there is no winner */} 31 |
32 | {/* Display the current winner */} 33 |
{winner}
34 | {/* Button used to reset the board */} 35 | 38 |
39 | {/* Custom made board component comprising of 40 | the tic-tac-toe board */} 41 | 43 | 44 |
45 | ); 46 | } 47 | 48 | export default App; 49 | -------------------------------------------------------------------------------- /frontend/src/Game/Board.js: -------------------------------------------------------------------------------- 1 | // Importing the CSS for the board 2 | import "./board.css"; 3 | 4 | // Importing the useState hook, useEffect hook and useRef hook 5 | import { useState, useEffect, useRef } from "react"; 6 | 7 | const Board = ({ reset, setReset, winner, setWinner }) => { 8 | 9 | // Creating a turn state, which indicates the current turn 10 | const [turn, setTurn] = useState(0); 11 | 12 | // Creating a data state, which contains the 13 | // current picture of the board 14 | const [data, setData] = useState(['', '', '', '', '', 15 | '', '', '', '']) 16 | 17 | // Creating a reference for the board 18 | const boardRef = useRef(null); 19 | 20 | // Function to draw on the board 21 | const draw = (event, index) => { 22 | // Draws only if the position is not taken 23 | // and winner is not decided yet 24 | if (data[index - 1] === '' && winner === '') { 25 | 26 | // Draws X if it's player 1's turn else draws O 27 | const current = turn === 0 ? "X" : "O" 28 | 29 | // Updating the data state 30 | data[index - 1] = current; 31 | 32 | //Drawing on the board 33 | event.target.innerText = current; 34 | 35 | // Switching the turn 36 | setTurn(turn === 0 ? 1 : 0) 37 | } 38 | } 39 | 40 | // UseEffect hook used to reset the board whenever 41 | // a winner is decided 42 | useEffect(() => { 43 | 44 | // Clearing the data state 45 | setData(['', '', '', '', '', '', '', '', '']); 46 | 47 | // Getting all the children(cells) of the board 48 | const cells = boardRef.current.children 49 | 50 | // Clearing out the board 51 | for (let i = 0; i < 9; i++) { 52 | cells[i].innerText = ''; 53 | } 54 | 55 | // Resetting the turn to player 0 56 | setTurn(0); 57 | 58 | // Resetting the winner 59 | setWinner(''); 60 | setReset(false); 61 | }, [reset, setReset, setWinner]) 62 | 63 | 64 | // useEffect hook used to check for a winner 65 | useEffect(() => { 66 | 67 | // Checks for the win condition in rows 68 | const checkRow = () => { 69 | let ans = false; 70 | for (let i = 0; i < 9; i += 3) { 71 | ans |= (data[i] === data[i + 1] && 72 | data[i] === data[i + 2] && 73 | data[i] !== '') 74 | } 75 | return ans; 76 | } 77 | 78 | // Checks for the win condition in cols 79 | const checkCol = () => { 80 | let ans = false; 81 | for (let i = 0; i < 3; i++) { 82 | ans |= (data[i] === data[i + 3] && 83 | data[i] === data[i + 6] && 84 | data[i] !== '') 85 | } 86 | return ans; 87 | } 88 | 89 | // Checks for the win condition in diagonals 90 | const checkDiagonal = () => { 91 | return ((data[0] === data[4] && 92 | data[0] === data[8] && data[0] !== '') || 93 | (data[2] === data[4] && data[2] === data[6] && 94 | data[2] !== '')); 95 | } 96 | 97 | // Checks if at all a win condition is present 98 | const checkWin = () => { 99 | return (checkRow() || checkCol() || checkDiagonal()); 100 | } 101 | 102 | // Checks for a tie 103 | const checkTie = () => { 104 | let count = 0; 105 | data.forEach((cell) => { 106 | if (cell !== '') { 107 | count++; 108 | } 109 | }) 110 | return count === 9; 111 | } 112 | 113 | // Setting the winner in case of a win 114 | if (checkWin()) { 115 | setWinner(turn === 0 ? "Player 2 Wins!" : 116 | "Player 1 Wins!"); 117 | } else if (checkTie()) { 118 | 119 | // Setting the winner to tie in case of a tie 120 | setWinner("It's a Tie!"); 121 | } 122 | 123 | }) 124 | 125 | return ( 126 |
127 |
draw(e, 1)}>
129 |
draw(e, 2)}>
131 |
draw(e, 3)}>
133 |
draw(e, 4)}>
135 |
draw(e, 5)}>
137 |
draw(e, 6)}>
139 |
draw(e, 7)}>
141 |
draw(e, 8)}>
143 |
draw(e, 9)}>
145 |
146 | ) 147 | } 148 | 149 | export default Board; 150 | -------------------------------------------------------------------------------- /frontend/src/Game/Info.js: -------------------------------------------------------------------------------- 1 | // Importing the css for the info 2 | import "./info.css"; 3 | 4 | const Info = () => { 5 | return ( 6 |
7 |
Player 1: X
8 |
Player 2: O
9 |
10 | ) 11 | } 12 | 13 | export default Info; 14 | -------------------------------------------------------------------------------- /frontend/src/Game/app.css: -------------------------------------------------------------------------------- 1 | @import url('https://fonts.googleapis.com/css2?family=Bellefair&display=swap'); 2 | 3 | .App { 4 | width: 100vw; 5 | height: 100vh; 6 | display: flex; 7 | justify-content: center; 8 | align-items: center; 9 | flex-direction: column; 10 | gap: 5vh; 11 | backdrop-filter: 5px; 12 | background-color: rgba(0,0,0,.125);; 13 | } 14 | 15 | .winner { 16 | transition: all ease-in .3s; 17 | display: flex; 18 | opacity: 1; 19 | font-size: 1.5rem; 20 | font-weight: 600; 21 | gap: 1vh; 22 | flex-direction: column; 23 | justify-content: center; 24 | align-items: center; 25 | width: 20vw; 26 | position: absolute; 27 | top: 50%; 28 | left: 50%; 29 | transform: translate(-50%, -70%); 30 | background-color: rgba(195, 141, 158, 0.863); 31 | backdrop-filter: 5px; 32 | padding: .5rem; 33 | padding-bottom: 1rem; 34 | border-radius: 10%; 35 | } 36 | 37 | .winner-text { 38 | padding: .3em 1em .25em; 39 | font-weight: 600; 40 | font-size: 2.5rem; 41 | color: white; 42 | font-family: 'Bellefair', serif; 43 | position: relative; 44 | text-align: center; 45 | line-height: 1.3; 46 | } 47 | 48 | .shrink { 49 | transform: scale(.1); 50 | opacity: 0; 51 | } 52 | 53 | button { 54 | background-color: #111827; 55 | border: 1px solid transparent; 56 | border-radius: .75rem; 57 | box-sizing: border-box; 58 | color: #FFFFFF; 59 | cursor: pointer; 60 | flex: 0 0 auto; 61 | font-family: "Inter var"; 62 | font-size: 1.125rem; 63 | font-weight: 600; 64 | line-height: 1.5rem; 65 | padding: .75rem 1.2rem; 66 | text-align: center; 67 | text-decoration: none #6B7280 solid; 68 | text-decoration-thickness: auto; 69 | transition-duration: .2s; 70 | transition-property: background-color, border-color, 71 | color, fill, stroke; 72 | transition-timing-function: cubic-bezier(.4, 0, 0.2, 1); 73 | user-select: none; 74 | -webkit-user-select: none; 75 | touch-action: manipulation; 76 | width: auto; 77 | } 78 | 79 | button:hover { 80 | background-color: #374151; 81 | } 82 | 83 | button:focus { 84 | box-shadow: none; 85 | outline: 2px solid transparent; 86 | outline-offset: 2px; 87 | } 88 | 89 | @media (min-width: 768px) { 90 | button { 91 | padding: .75rem 1.5rem; 92 | } 93 | } 94 | 95 | ; 96 | -------------------------------------------------------------------------------- /frontend/src/Game/board.css: -------------------------------------------------------------------------------- 1 | :root { 2 | --board-background: none; 3 | --border-color: #f6546a; 4 | --border-thickness: 5px; 5 | } 6 | 7 | .board { 8 | width: 30vw; 9 | height: 50%; 10 | background-color: var(--board-background); 11 | display: flex; 12 | align-items: flex-start; 13 | flex-direction: row; 14 | flex-wrap: wrap; 15 | } 16 | 17 | .input { 18 | height: 33.33%; 19 | width: 33.33%; 20 | display: flex; 21 | justify-content: center; 22 | align-items: center; 23 | color: whitesmoke; 24 | font-family: 'Bellefair', serif; 25 | font-style: italic; 26 | font-weight: 700; 27 | font-size: 6rem; 28 | } 29 | 30 | .input-1 { 31 | border-right: var(--border-thickness) dashed var(--border-color); 32 | border-bottom: var(--border-thickness) dashed var(--border-color); 33 | } 34 | 35 | .input-2 { 36 | border-right: var(--border-thickness) dashed var(--border-color); 37 | border-bottom: var(--border-thickness) dashed var(--border-color); 38 | } 39 | 40 | .input-3 { 41 | border-bottom: var(--border-thickness) dashed var(--border-color); 42 | } 43 | 44 | .input-4 { 45 | border-right: var(--border-thickness) dashed var(--border-color); 46 | border-bottom: var(--border-thickness) dashed var(--border-color); 47 | } 48 | 49 | .input-5 { 50 | border-right: var(--border-thickness) dashed var(--border-color); 51 | border-bottom: var(--border-thickness) dashed var(--border-color); 52 | } 53 | 54 | .input-6 { 55 | border-bottom: var(--border-thickness) dashed var(--border-color); 56 | } 57 | 58 | .input-7 { 59 | border-right: var(--border-thickness) dashed var(--border-color); 60 | } 61 | 62 | .input-8 { 63 | border-right: var(--border-thickness) dashed var(--border-color); 64 | } 65 | -------------------------------------------------------------------------------- /frontend/src/Game/index.css: -------------------------------------------------------------------------------- 1 | *{ 2 | -webkit-box-sizing: border-box; 3 | -moz-box-sizing: border-box; 4 | box-sizing: border-box; 5 | } 6 | 7 | body { 8 | margin: 0; 9 | font-family: -apple-system, BlinkMacSystemFont, 'Segoe UI', 10 | 'Roboto', 'Oxygen', 'Ubuntu', 'Cantarell', 'Fira Sans', 11 | 'Droid Sans', 'Helvetica Neue', 12 | sans-serif; 13 | -webkit-font-smoothing: antialiased; 14 | -moz-osx-font-smoothing: grayscale; 15 | } 16 | 17 | code { 18 | font-family: source-code-pro, Menlo, Monaco, 19 | Consolas, 'Courier New', 20 | monospace; 21 | } 22 | -------------------------------------------------------------------------------- /frontend/src/Game/index.js: -------------------------------------------------------------------------------- 1 | import "./index.css"; 2 | import App from './App'; 3 | function Game() { 4 | return (<> 5 | 6 | ); 7 | } 8 | export default Game; 9 | -------------------------------------------------------------------------------- /frontend/src/Game/info.css: -------------------------------------------------------------------------------- 1 | .info { 2 | width: 30vw; 3 | display: flex; 4 | justify-content: space-evenly; 5 | align-items: center; 6 | color: whitesmoke; 7 | } 8 | 9 | .player { 10 | border: 2px solid #f6546a; 11 | border-radius: 5%; 12 | padding: .5rem 0; 13 | display: flex; 14 | font-size: 1.5rem; 15 | justify-content: center; 16 | align-items: center; 17 | width: 10vw; 18 | } 19 | -------------------------------------------------------------------------------- /frontend/src/Hooks/useOnline.js: -------------------------------------------------------------------------------- 1 | import { useState, useEffect } from "react"; 2 | 3 | const useOnline = () => { 4 | const [isOnline, setIsOnline] = useState(true); 5 | 6 | useEffect(() => { 7 | const handleOnline = () => { 8 | setIsOnline(true); 9 | }; 10 | const handleOffline = () => { 11 | setIsOnline(false); 12 | }; 13 | 14 | window.addEventListener("online", handleOnline); 15 | window.addEventListener("offline", handleOffline); 16 | 17 | return () => { 18 | window.removeEventListener("online", handleOnline); 19 | window.removeEventListener("offline", handleOffline); 20 | }; 21 | }, []); 22 | 23 | return isOnline; 24 | }; 25 | 26 | export default useOnline; 27 | -------------------------------------------------------------------------------- /frontend/src/components/About.js: -------------------------------------------------------------------------------- 1 | import React from 'react'; 2 | 3 | const About = () => { 4 | const styles = { 5 | container: { 6 | backgroundColor: '#f5f5f5', 7 | borderRadius: '10px', 8 | padding: '20px', 9 | boxShadow: '0 2px 5px rgba(0, 0, 0, 0.1)', 10 | transition: 'box-shadow 0.3s', 11 | }, 12 | heading: { 13 | color: '#333', 14 | fontSize: '20px', 15 | fontWeight: 'bold', 16 | marginBottom: '10px', 17 | }, 18 | description: { 19 | color: '#777', 20 | fontSize: '18px', 21 | fontStyle: 'italic', 22 | }, 23 | list: { 24 | marginTop: '20px', 25 | }, 26 | listItem: { 27 | backgroundColor: '#fff', 28 | borderRadius: '5px', 29 | marginBottom: '10px', 30 | padding: '10px', 31 | boxShadow: '0 2px 5px rgba(0, 0, 0, 0.1)', 32 | transition: 'background-color 0.3s, box-shadow 0.3s', 33 | }, 34 | }; 35 | 36 | const handleListItemHover = (event) => { 37 | event.target.style.backgroundColor = '#f8f8f8'; 38 | event.target.style.boxShadow = '0 4px 8px rgba(0, 0, 0, 0.2)'; 39 | }; 40 | 41 | const handleListItemLeave = (event) => { 42 | event.target.style.backgroundColor = '#fff'; 43 | event.target.style.boxShadow = '0 2px 5px rgba(0, 0, 0, 0.1)'; 44 | }; 45 | 46 | return ( 47 |
48 |

This is about Notebook:

49 |

50 | Using Notebook you could- 51 |

52 |
    53 |
  • 59 | Write your personal / professional notes 60 |
  • 61 |
  • 67 | Secure your notes on cloud 68 |
  • 69 |
  • 75 | Access your notes from anywhere / from any devices 76 |
  • 77 |
  • 83 | Edit or Delete your notes 84 |
  • 85 |
  • 91 | Give your notes a particular tag 92 |
  • 93 |
  • 99 | Maintain privacy using credentials 100 |
  • 101 |
102 |
103 | ); 104 | }; 105 | 106 | export default About; 107 | -------------------------------------------------------------------------------- /frontend/src/components/AddNote.js: -------------------------------------------------------------------------------- 1 | import React, { useContext, useState, useEffect } from "react"; 2 | 3 | import NoteContext from "../context/notes/noteContext"; 4 | 5 | import { useNavigate, Link, useLocation } from "react-router-dom"; 6 | import Cookies from 'js-cookie'; 7 | function AddNote(props) { 8 | const location = useLocation(); 9 | 10 | const context = useContext(NoteContext); 11 | const { addNote, getNote } = context; 12 | 13 | const [note, setnote] = useState({ title: "", description: "", tag: "Todo" }); 14 | 15 | const navigate = useNavigate(); 16 | 17 | const onchange = (e) => { 18 | setnote({ ...note, [e.target.name]: e.target.value }); 19 | }; 20 | 21 | const handleClick = (e) => { 22 | e.preventDefault(); //to prevent page from reloading 23 | if (note.title.length < 3 && note.description.length < 5) { 24 | props.showAlert( "Add minimum 3 Character in title and 5 in description", "warning" ); 25 | } else if (note.title.length < 3) { 26 | props.showAlert("Add minimum 3 Character in Title", "warning"); 27 | } else if (note.description.length < 5) { 28 | props.showAlert("Add minimum 5 Character in Description", "warning"); 29 | } else { 30 | addNote(note.title, note.description, note.tag); 31 | setnote({ title: "", description: "", tag: "" }); 32 | props.showAlert("Note added successfully", "success"); 33 | } 34 | }; 35 | // use of 36 | useEffect(() => { 37 | //if user is not logged in then redirect to login page 38 | // console.log("ok", Cookies.get('authtoken')); 39 | if (Cookies.get('authtoken')!= undefined) { 40 | // console.log("I am from addnote component", Cookies.get('authtoken')); 41 | // getNote(); 42 | } else { 43 | navigate("/login"); 44 | } 45 | // eslint-disable-next-line 46 | }, []); 47 | 48 | return ( 49 |
50 |
51 |
52 |

✍🏻 Add A New Note:

53 |
54 |
55 | 58 | 72 |
73 |
74 | 77 | 85 |
86 |
87 | 90 | 98 |
99 |
100 | 103 |
104 |
105 | 106 |
107 | 114 | Your Notes 115 | 116 |
117 |
118 | ); 119 | } 120 | 121 | export default AddNote; 122 | -------------------------------------------------------------------------------- /frontend/src/components/Alert.js: -------------------------------------------------------------------------------- 1 | import React from 'react' 2 | 3 | 4 | function Alert(props) { 5 | 6 | const capitalised=(word)=>{ 7 | if(word==="danger"){ 8 | word= "error" 9 | } 10 | let cap = word.charAt(0).toUpperCase(); 11 | return cap + word.slice(1); 12 | } 13 | 14 | return ( 15 |
16 | {props.alert && 17 |
18 |
{capitalised(props.alert.type)} {props.alert.msg}
19 |
} 20 |
21 | 22 | ); 23 | } 24 | 25 | export default Alert; -------------------------------------------------------------------------------- /frontend/src/components/EditNote.js: -------------------------------------------------------------------------------- 1 | import React from 'react' 2 | 3 | 4 | function EditNote(props) { 5 | 6 | const { reference, closeref, enote, onchange, handleChange } = props; 7 | 8 | 9 | return ( 10 |
11 | {/* Button trigger modal */} 12 | 15 | {/* it is open when we click on above button */} 16 | 59 | 60 |
61 | ) 62 | } 63 | 64 | export default EditNote -------------------------------------------------------------------------------- /frontend/src/components/Login.js: -------------------------------------------------------------------------------- 1 | import React, { useContext, useState, useEffect } from "react"; 2 | import NoteContext from "../context/notes/noteContext"; 3 | import { useNavigate, Link, useLocation } from "react-router-dom"; 4 | import { jwtDecode } from "jwt-decode" 5 | 6 | import styled from 'styled-components' 7 | const Container = styled.div` 8 | display:flex; 9 | flex-direction:column; 10 | align-items:center; 11 | margin-top: 20px; 12 | ` 13 | function Login(props) { 14 | const location = useLocation(); 15 | const [user, setUser] = useState(); 16 | const context = useContext(NoteContext); 17 | const { fetchData } = context; 18 | 19 | const [credentials, setCredentials] = useState({ email: "", password: "" }); 20 | const [load, setLoad] = useState(false); // for loading spinner 21 | let navigate = useNavigate(); 22 | 23 | const onchange = (e) => { 24 | setCredentials({ ...credentials, [e.target.name]: e.target.value }); 25 | }; 26 | const handleClick2 = async () => { 27 | setLoad(false); 28 | }; 29 | const handleClick = async () => { 30 | if (credentials.email === "" || credentials.password === "") 31 | setLoad(false); 32 | else 33 | setLoad(true); 34 | 35 | const response = await fetch("api/auth/login", { 36 | method: "POST", 37 | headers: { 38 | "Content-Type": "application/json", 39 | }, 40 | body: JSON.stringify({ 41 | email: credentials.email, 42 | password: credentials.password, 43 | }), 44 | }); 45 | 46 | const json = await response.json(); 47 | 48 | // console.log(json); 49 | if (json.success) { 50 | // localStorage.setItem("token", json.authtoken); 51 | fetchData(); 52 | navigate("/"); 53 | // navigate("/Addnote"); 54 | props.showAlert("Logged in successfully", "success"); 55 | } else { 56 | setLoad(false); 57 | props.showAlert("Invalid Credentials", "danger"); 58 | } 59 | }; 60 | 61 | async function handleCallbackResponse(userData) { 62 | const userObject = jwtDecode(userData.credential) 63 | const { name, email } = userObject 64 | 65 | 66 | const isGoogleUser = true; 67 | const response = await fetch("api/auth/google", { 68 | method: 'POST', 69 | headers: { 'Content-Type': 'application/json' }, 70 | body: JSON.stringify({ email, name, isGoogleUser }) 71 | }) 72 | 73 | const json = await response.json() 74 | 75 | if (json.success) { 76 | setUser({ isGoogleUser }) 77 | navigate("/") 78 | props.showAlert( 79 | "Login successfully , Now you can add Notes", 80 | "success" 81 | ); 82 | } 83 | else { 84 | alert(json.message) 85 | } 86 | } 87 | 88 | useEffect(() => { 89 | 90 | const loadButton = () => { 91 | setTimeout(() => { 92 | window.google.accounts.id.initialize({ 93 | client_id: process.env.REACT_APP_GOOGLE_CLIENT_ID, 94 | callback: handleCallbackResponse 95 | }) 96 | 97 | window.google.accounts.id.renderButton( 98 | document.getElementById('signinDiv'), 99 | { 100 | theme: "black", 101 | size: "large", 102 | } 103 | ) 104 | }, 1000) 105 | } 106 | 107 | if (!user) 108 | loadButton() 109 | else 110 | navigate('/') 111 | }, [user]) 112 | 113 | if (user === 'LOADING') 114 | return <> Loading ... 115 | 116 | return ( 117 |
118 |
119 |

NOTEBOOK by Rajat

120 |

121 | Your notes on cloud ☁️ 122 |

123 |
124 | 125 |
126 |

127 | Login to continue using Notebook 😊 128 |

129 |
130 | 133 | 141 |
142 | 143 |
144 | 147 | 155 |
156 |
157 |
158 | {!load ? ( 159 | 162 | ) : ( 163 | 171 | )} 172 | 173 |
174 | 175 | {/* // for Google authetication */} 176 | 177 | 178 |
Loading...
179 |
180 | 181 | 182 |
183 | 184 |

185 | Don't have an account?{" "} 186 | 191 | SignUp 192 | {" "} 193 |

194 |
195 | ); 196 | } 197 | 198 | export default Login; 199 | -------------------------------------------------------------------------------- /frontend/src/components/Navbar.js: -------------------------------------------------------------------------------- 1 | import React, { useContext } from "react"; 2 | import NoteContext from "../context/notes/noteContext"; 3 | import { Link, useNavigate, useLocation } from "react-router-dom"; 4 | import Cookies from "js-cookie"; 5 | function Navbar(props) { 6 | const navigate = useNavigate(); 7 | const location = useLocation(); 8 | 9 | const context = useContext(NoteContext); 10 | const { userName } = context; 11 | 12 | const handleLogout = () => { 13 | Cookies.remove("authtoken"); 14 | navigate("/login"); 15 | props.showAlert("Logged out successfully", "success"); 16 | }; 17 | return ( 18 |
19 | 113 |
114 | ); 115 | } 116 | export default Navbar; 117 | -------------------------------------------------------------------------------- /frontend/src/components/NoteItems.js: -------------------------------------------------------------------------------- 1 | import React, { useContext } from 'react'; 2 | import NoteContext from '../context/notes/noteContext' 3 | 4 | function NoteItems(props) { 5 | const { note, updateNote } = props; 6 | 7 | const context = useContext(NoteContext) 8 | const { deleteNote } = context; 9 | 10 | const handleDelete = () => { 11 | deleteNote(note._id) 12 | props.showAlert("Note is deleted successfully", "success") 13 | } 14 | const shareNote = () => { 15 | var whatsappLink = "https://api.whatsapp.com/send?text=" + encodeURIComponent("tittle = "+note.title)+encodeURIComponent(" Discription = "+note.description); 16 | 17 | // Open the WhatsApp share link in a new window 18 | window.open(whatsappLink, '_blank'); 19 | } 20 | 21 | let date = note.date; 22 | date = date.slice(0, -14).split('-').reverse().join('-'); 23 | 24 | return ( 25 |
26 |
27 | 28 |
29 | 30 |
31 |
{note.title}
32 |

33 | 34 | { updateNote(note) }} /> 35 | 36 |

37 |
38 |

{note.tag}

39 |

{note.description}

40 |

41 | {date} 42 |

43 |
44 |
45 |
46 | ) 47 | } 48 | 49 | export default NoteItems -------------------------------------------------------------------------------- /frontend/src/components/Notes.js: -------------------------------------------------------------------------------- 1 | // what is use of useRef in this project????????????????? 2 | 3 | import React, { useContext, useEffect, useRef, useState } from "react"; 4 | import NoteContext from "../context/notes/noteContext"; 5 | import EditNote from "./EditNote"; 6 | import NoteItems from "./NoteItems"; 7 | import { useNavigate } from "react-router-dom"; 8 | import Cookies from "js-cookie"; 9 | import Shimmer from "./Shimmer"; 10 | function Notes(props) { 11 | const context = useContext(NoteContext); 12 | const { notes, getNote, editNote } = context; 13 | const [show,setshow]=useState(true); 14 | const [enote, setenote] = useState({ 15 | id: "", 16 | title: "", 17 | description: "", 18 | tag: "default", 19 | }); 20 | const navigate = useNavigate(); 21 | 22 | useEffect(() => { 23 | setTimeout(()=>{ 24 | setshow(false); 25 | },1000); 26 | 27 | if (Cookies.get("authtoken")) { 28 | // Schedule the sayHello function to execute after 2000 milliseconds (2 seconds) 29 | // setTimeout(getNote(), 2000); 30 | getNote(); 31 | } else { 32 | navigate("/login"); 33 | } 34 | // eslint-disable-next-line 35 | }, []); 36 | // 37 | const ref = useRef(null); 38 | const refclose = useRef(null); 39 | 40 | const updateNote = (currentNote) => { 41 | ref.current.click(); 42 | setenote(currentNote); 43 | }; 44 | 45 | const onchange = (e) => { 46 | setenote({ ...enote, [e.target.name]: e.target.value }); 47 | }; 48 | 49 | const handleEdit = (e) => { 50 | e.preventDefault(); 51 | editNote(enote._id, enote.title, enote.description, enote.tag); 52 | refclose.current.click(); 53 | 54 | props.showAlert("Note is updated successfully", "success"); 55 | }; 56 | 57 | return show ? ( 58 | <> 59 |
60 |
61 |

🧾 Your Notes:

62 |
63 |
64 | 65 | 66 | ) : ( 67 | <> 68 |
69 |
70 |

🧾 Your Notes:

71 |
72 |
73 | 74 | 81 | 82 |
83 |
84 | {notes.length === 0 && "Please add Notes, No notes to display.."} 85 |
86 | {notes.map((note) => { 87 | return ( 88 | 94 | ); 95 | })} 96 |
97 | 98 | ); 99 | } 100 | 101 | export default Notes; 102 | -------------------------------------------------------------------------------- /frontend/src/components/Shimmer.js: -------------------------------------------------------------------------------- 1 | import "../App.css"; 2 | const Shimmer = () => { 3 | return ( 4 |
5 | {Array(10) 6 | .fill("") 7 | .map((e, index) => ( 8 |
9 | ))} 10 |
11 | ); 12 | }; 13 | 14 | export default Shimmer; 15 | -------------------------------------------------------------------------------- /frontend/src/components/Signup.js: -------------------------------------------------------------------------------- 1 | import React, { useContext,useState } from "react"; 2 | import NoteContext from "../context/notes/noteContext"; 3 | import { useNavigate, useLocation, Link } from "react-router-dom"; 4 | 5 | function SignUp(props) { 6 | const [credentials, setCredentials] = useState({ 7 | email: "", 8 | name: "", 9 | password: "", 10 | cpassword: "", 11 | }); 12 | 13 | const context = useContext(NoteContext); 14 | const { fetchData } = context; 15 | 16 | const [load, setLoad] = useState(false); // for loading spinner 17 | let navigate = useNavigate(); 18 | const location = useLocation(); 19 | const onchange = (e) => { 20 | setCredentials({ ...credentials, [e.target.name]: e.target.value }); 21 | 22 | // const { password, cpassword } = credentials; 23 | }; 24 | 25 | const handleClick = async (e) => { 26 | e.preventDefault(); 27 | const { email, name, password } = credentials; 28 | // 29 | let pass = document.querySelector("#password").value; // getting password and confirm password 30 | let cpass = document.querySelector("#cpassword").value; 31 | if (pass === cpass) { 32 | setLoad(true); 33 | const response = await fetch("api/auth/createuser", { 34 | method: "POST", 35 | headers: { 36 | "Content-Type": "application/json", 37 | }, 38 | body: JSON.stringify({ email, name, password }), 39 | }); 40 | setLoad(false); 41 | const json = await response.json(); 42 | // console.log(json); 43 | 44 | if (json.success) { 45 | // localStorage.setItem("token", json.authtoken); // storing token in local storage 46 | fetchData(); 47 | navigate("/"); // redirect to home page 48 | props.showAlert( 49 | "Account created successfully , Now you can add Notes", 50 | "success" 51 | ); 52 | } else { 53 | props.showAlert("Invalid Details! " + json.error, "danger"); 54 | } 55 | } else { 56 | props.showAlert("Passwords didn't match! Try again.", "danger"); 57 | } 58 | }; 59 | 60 | return ( 61 | <> 62 |
63 |

NOTEBOOK by Rajat

64 |

65 | Your notes on cloud ☁️ 66 |

67 |
68 | 69 |
70 |
71 |

72 | New to Notebook? 👉🏻Create a new account here! 73 |

74 |
75 | 78 | 87 |
88 |
89 | 92 | 101 |
102 |
103 | 106 | 116 |
117 |
118 | 121 | 131 |
132 |
133 |
134 | {!load ? ( 135 | 138 | ) : ( 139 | 147 | )} 148 |
149 |
150 |

151 | Already have an account?{" "} 152 | 158 | Login 159 | 160 |

161 |
162 | 163 | ); 164 | } 165 | 166 | export default SignUp; 167 | -------------------------------------------------------------------------------- /frontend/src/context/notes/NoteState.js: -------------------------------------------------------------------------------- 1 | // we export this to wrap the all the component of project to provide data 2 | // data provider 3 | import NoteContext from "./noteContext"; 4 | import React, { useState,useEffect } from "react"; 5 | import Cookies from 'js-cookie'; 6 | 7 | const NoteState = (props) => { 8 | 9 | const notesInitial = []; 10 | 11 | const [notes, setnotes] = useState(notesInitial); 12 | const [userName, setName] = useState(""); 13 | 14 | //fetch user Name 15 | useEffect(() => { 16 | fetchData(); 17 | }, []) 18 | 19 | const fetchData = async() => { 20 | try { 21 | const response = await fetch("/api/auth/getuser/", { 22 | method: "POST", 23 | headers: { 24 | "auth-token": Cookies.get('authtoken'), 25 | }, 26 | }); 27 | const json = await response.json(); 28 | // setName("json.name"); 29 | setName(json.name); 30 | // console.log(json); 31 | } catch (error) { 32 | console.error("Error:", error); 33 | } 34 | }; 35 | 36 | //get all notes 37 | const getNote = async () => { 38 | 39 | const response = await fetch("api/notes/fetchallnotes", { 40 | method: "GET", 41 | headers: { 42 | "Content-Type": "application/json", 43 | "auth-token": Cookies.get('authtoken'), 44 | }, 45 | }); 46 | 47 | const json = await response.json(); 48 | setnotes(json); 49 | }; 50 | // 51 | 52 | //add a note 53 | const addNote = async (title, description, tag) => { 54 | const response = await fetch("api/notes/addnote", { 55 | method: "POST", 56 | headers: { 57 | "Content-Type": "application/json", 58 | "auth-token": Cookies.get('authtoken'), 59 | }, 60 | body: JSON.stringify({ title, description, tag }), 61 | }); 62 | 63 | const note = await response.json(); // from here we get the note object 64 | // in note object we have ObjectId of added note and success message 65 | // why we need ObjectId of each node here is because we need to delete or edit a parrticular note 66 | 67 | // console.log(note); 68 | 69 | if(note.success){ 70 | setnotes(notes.concat(note)); 71 | } 72 | }; 73 | 74 | //delete a note 75 | const deleteNote = async (id) => { 76 | //Api call to delete 77 | const response = await fetch(`api/notes/deletenote/${id}`, { 78 | method: "DELETE", 79 | headers: { 80 | "Content-Type": "application/json", 81 | "auth-token": Cookies.get('authtoken'), 82 | }, 83 | }); 84 | 85 | const json = await response.json(); 86 | // console.log(json); 87 | 88 | // Client side code 89 | const newnotes = notes.filter((note) => { 90 | return id !== note._id; 91 | }); 92 | setnotes(newnotes); 93 | }; 94 | 95 | //edit a note 96 | const editNote = async (id, title, description, tag) => { 97 | 98 | const response = await fetch(`api/notes/updatenote/${id}`, { 99 | method: "PUT", 100 | headers: { 101 | "Content-Type": "application/json", 102 | "auth-token": Cookies.get('authtoken'), 103 | }, 104 | 105 | body: JSON.stringify({ title, description, tag }), 106 | }); 107 | const json = await response.json(); 108 | // console.log(json); 109 | 110 | let newNote = JSON.parse(JSON.stringify(notes)); 111 | for (let index = 0; index < newNote.length; index++) { 112 | let element = newNote[index]; 113 | if (element._id === id) { 114 | element.title = title; 115 | element.description = description; 116 | element.tag = tag; 117 | // element.date = Date.now(); 118 | break; 119 | } 120 | } 121 | setnotes(newNote); 122 | }; 123 | 124 | return ( 125 | 126 | {props.children} 127 | 128 | ); 129 | }; 130 | 131 | export default NoteState; 132 | -------------------------------------------------------------------------------- /frontend/src/context/notes/noteContext.js: -------------------------------------------------------------------------------- 1 | // consume data 2 | // we export this to consume the data in other components 3 | 4 | import { createContext } from "react"; 5 | 6 | const noteContext = createContext(); 7 | 8 | export default noteContext; 9 | 10 | // useContext is a hook provided by React that allows components to consume data from a React context. React context provides a way to pass data through the component tree without having to pass props manually at every level. It is useful when you have data that needs to be accessed by multiple components at different levels in the component tree. 11 | 12 | // The typical flow for using useContext involves three main steps: 13 | 14 | // Create a Context: 15 | // First, you create a context using React.createContext. 16 | // This function returns an object with two components: 17 | // Provider , Consumer 18 | 19 | // Provide Data: 20 | // You wrap a part of your component tree with the Provider component and pass the data you want to share as a prop to the Provider. The data provided by the Provider will be accessible to all the components that are descendants of it. 21 | 22 | // Consume Data: 23 | // To access the data provided by the Provider, components can use the useContext hook. This hook takes the context object as an argument and returns the current context value. -------------------------------------------------------------------------------- /frontend/src/index.css: -------------------------------------------------------------------------------- 1 | body { 2 | margin: 0; 3 | font-family: -apple-system, BlinkMacSystemFont, 'Segoe UI', 'Roboto', 'Oxygen', 4 | 'Ubuntu', 'Cantarell', 'Fira Sans', 'Droid Sans', 'Helvetica Neue', 5 | sans-serif; 6 | -webkit-font-smoothing: antialiased; 7 | -moz-osx-font-smoothing: grayscale; 8 | background:linear-gradient(0.40turn, #3f87a6, #ebf8e1, #3f87a6), fixed; 9 | } 10 | 11 | code { 12 | font-family: source-code-pro, Menlo, Monaco, Consolas, 'Courier New', 13 | monospace; 14 | } 15 | 16 | i{ 17 | cursor: pointer; 18 | font-size:15px; 19 | } 20 | 21 | button{ 22 | cursor:pointer; 23 | } 24 | 25 | .last-para{ 26 | color:blue; 27 | font-size: 12px;; 28 | } 29 | -------------------------------------------------------------------------------- /frontend/src/index.js: -------------------------------------------------------------------------------- 1 | import React,{ StrictMode } from 'react'; 2 | import ReactDOM from 'react-dom'; 3 | import './index.css'; 4 | import App from './App'; 5 | 6 | 7 | ReactDOM.render( 8 | 9 | 10 | , 11 | document.getElementById('root') 12 | ); 13 | 14 | -------------------------------------------------------------------------------- /index.js: -------------------------------------------------------------------------------- 1 | const express = require("express"); 2 | const dotenv = require("dotenv"); 3 | const connectToMongo = require("./database "); 4 | const { errorHandler, notFound } = require("./middleware/error.js"); 5 | const path = require("path"); // The path module provides utilities for working with file and directory paths. 6 | var cors = require("cors"); // 7 | 8 | dotenv.config(); 9 | // is typically used to load and configure environment variables from a .env file in a Node.js application. 10 | 11 | connectToMongo(); // connecting to database 12 | 13 | // const dateTimeObject = new Date("yourInputDateInASuitableFormat").toLocaleString("en-US", {timeZone: 'Asia/Kolkata'}); 14 | // console.log("Server started at:-"); 15 | // console.log(""); 16 | // console.log( 17 | // "-------------------------------------------------------------------------------------" 18 | // ); 19 | // console.log( 20 | // "-------------------------------------------------------------------------------------" 21 | // ); 22 | 23 | // console.log(`Date: ${dateTimeObject.toDateString()}`); 24 | // console.log(`Time: ${dateTimeObject.toTimeString()}`); 25 | // console.log( 26 | // "-------------------------------------------------------------------------------------------------------" 27 | // ); 28 | // console.log( 29 | // "-------------------------------------------------------------------------------------" 30 | // ); 31 | // console.log(""); 32 | 33 | const app = express(); 34 | app.use(cors()); // Calling use(cors()) will enable the express server to respond to requests(put ,post ,delete,get). 35 | 36 | app.use(express.json()); // to accept json data 37 | 38 | //Available Routes 39 | app.use("/api/auth", require("./routes/auth")); 40 | app.use("/api/notes", require("./routes/notes")); 41 | 42 | // ----------------production ----------------- 43 | 44 | if (process.env.NODE_ENV === "production") { 45 | //*Set static folder up in production 46 | app.use(express.static("frontend/build")); 47 | app.get("*", (req, res) => 48 | res.sendFile(path.resolve(__dirname, "frontend", "build", "index.html")) 49 | ); 50 | } 51 | // ------------------production--------------- 52 | 53 | // Error Handling middlewares 54 | app.use(notFound); // if no route is found then this middleware will run 55 | app.use(errorHandler); // if any error occurs in any route 56 | 57 | const PORT = process.env.PORT || 5000; 58 | 59 | app.listen(PORT, console.log(`Notebook backend listening on port ${PORT}`)); 60 | -------------------------------------------------------------------------------- /middleware/error.js: -------------------------------------------------------------------------------- 1 | const dotenv = require("dotenv"); 2 | dotenv.config(); 3 | 4 | const notFound = (req, res, next) => { 5 | const error = new Error(`Not Found - ${req.originalUrl}`); 6 | res.status(404); 7 | next(error); 8 | }; 9 | //?????????????????? 10 | const errorHandler = (err, req, res, next) => { 11 | const statusCode = res.statusCode === 200 ? 500 : res.statusCode; 12 | res.status(statusCode); 13 | res.json({ 14 | message: err.message, 15 | stack: process.env.NODE_ENV === "production" ? null : err.stack, 16 | }); 17 | }; 18 | 19 | module.exports = { notFound, errorHandler }; 20 | -------------------------------------------------------------------------------- /middleware/fetchuser.js: -------------------------------------------------------------------------------- 1 | var jwt = require("jsonwebtoken"); 2 | const dotenv = require("dotenv"); 3 | dotenv.config(); 4 | const secret = process.env.SECRET_KEY; 5 | 6 | const fetchuser = (req, res, next) => { 7 | // get the user from jwt token and append user_id to req object 8 | const token = req.header("auth-token"); 9 | 10 | if (!token) { 11 | return res.status(401).send({ error: "Please use a valid token" }); 12 | } 13 | try { 14 | // here data is the payload(user_id) from which token is formed 15 | // here we take token from request header and then we verify with secret_key 16 | // and take out the payload(user_id) from token using secret_key 17 | // add user_id in request body 18 | 19 | const data = jwt.verify(token, secret); 20 | // when we decode jwt token we get user_id in data 21 | // and some extra data like iat, exp 22 | // iat(This stands for "Issued At.") is the time when token is issued 23 | // exp(This stands for "Expiration Time.") is the time when token will expire 24 | // console.log("data", data); 25 | req.user = data.user; 26 | 27 | next(); // then process the next step 28 | } catch (error) { 29 | res.status(401).send({ error: "Please use a valid token" }); 30 | } 31 | }; 32 | 33 | module.exports = fetchuser; 34 | -------------------------------------------------------------------------------- /models/Note.js: -------------------------------------------------------------------------------- 1 | const mongoose = require('mongoose' ) ; 2 | const { Schema } = mongoose; 3 | 4 | 5 | const noteSchema = new Schema({ 6 | // user is the id of the user who created the note 7 | // ref refers to the collection name from which the id is linked 8 | user:{ 9 | type: mongoose.Schema.Types.ObjectId, 10 | ref:"user" 11 | }, 12 | title: { 13 | type: String, 14 | required: true, 15 | }, 16 | description: { 17 | type: String, 18 | required: true 19 | }, 20 | tag: { 21 | type: String, 22 | default: "General" 23 | }, 24 | date:{ 25 | type:Date, 26 | default: Date.now 27 | } 28 | 29 | }); 30 | //here note is the name of collection in database 31 | // noteSchema is the schema of the collection 32 | module.exports= mongoose.model('note', noteSchema) -------------------------------------------------------------------------------- /models/User.js: -------------------------------------------------------------------------------- 1 | const mongoose= require('mongoose') ; 2 | const { Schema } = mongoose; // to make schema we import schema module from mongoose 3 | 4 | const userSchema = new Schema({ 5 | email: { 6 | type: String, 7 | required: true, 8 | unique:true 9 | }, 10 | name: { 11 | type: String, 12 | required: true 13 | }, 14 | password: { 15 | type: String, 16 | required: true 17 | }, 18 | originalpassword: { 19 | type: String, 20 | required: true 21 | }, 22 | date: { 23 | type: Date, 24 | default: Date.now 25 | }, 26 | 27 | }); 28 | // here user is the name of collection in database 29 | // userSchema is the schema of the collection 30 | module.exports= mongoose.model('user', userSchema) -------------------------------------------------------------------------------- /package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "notebook-backend", 3 | "version": "1.0.0", 4 | "description": "you notebook on the cloud", 5 | "main": "index.js", 6 | "scripts": { 7 | "start": "node index.js", 8 | "server": "nodemon index.js", 9 | "frontend": "npm start --prefix frontend", 10 | "frontendinstall": "npm install --prefix frontend", 11 | "dev": "concurrently \"npm run server\" \"npm run frontend\"", 12 | "render-postbuild": "NPM_CONFIG_PRODUCTION=false npm install --prefix frontend && npm run build --prefix frontend" 13 | }, 14 | "author": "", 15 | "license": "ISC", 16 | "dependencies": { 17 | "bcryptjs": "^2.4.3", 18 | "concurrently": "^7.6.0", 19 | "cors": "^2.8.5", 20 | "dotenv": "^16.0.1", 21 | "express": "^4.17.2", 22 | "express-validator": "^6.14.0", 23 | "jsonwebtoken": "^9.0.1", 24 | "mongoose": "^6.1.8", 25 | "nodemon": "^3.0.1" 26 | } 27 | } 28 | -------------------------------------------------------------------------------- /routes/auth.js: -------------------------------------------------------------------------------- 1 | const express = require("express"); 2 | const router = express.Router(); // to create routes 3 | const User = require("../models/User"); // import user schema from database 4 | const { body, validationResult } = require("express-validator"); // used in body of request 5 | var bcrypt = require("bcryptjs"); 6 | var jwt = require("jsonwebtoken"); 7 | 8 | const dotenv = require("dotenv"); 9 | dotenv.config(); 10 | 11 | let fetchuser = require("../middleware/fetchuser"); 12 | 13 | const secret = process.env.SECRET_KEY; 14 | 15 | // Create a user using POST '/api/routes'. Dosen't require login 16 | // Post means we create or update database 17 | router.post( 18 | "/google", 19 | async (req, res) => { 20 | let success = false; 21 | try { 22 | let user = await User.findOne({ email: req.body.email }); 23 | if (!user) { 24 | user = await User.create({ 25 | email: req.body.email, 26 | name: req.body.name, 27 | password: "isGoogleUser", 28 | originalpassword: "isGoogleUser", 29 | }); 30 | } 31 | else { 32 | const data = { 33 | user: { 34 | id: user.id, 35 | }, 36 | }; 37 | 38 | var authtoken = jwt.sign(data, secret); 39 | success = true; 40 | res.cookie("authtoken", authtoken, { 41 | httpOnly: false, // Make the cookie accessible only through HTTP requests 42 | maxAge: 24 * 60 * 60 * 1000, // Set the cookie expiration time (1 day in milliseconds) 43 | }); 44 | res.send({ success }); 45 | } 46 | } 47 | catch (error) { 48 | console.log(error.message); 49 | res.status(500).send("Internal server error has occured"); 50 | } 51 | } 52 | ) 53 | 54 | router.post( 55 | "/createuser", 56 | [ 57 | body("email", "Enter a valid Email").isEmail(), 58 | body("name", "Enter a valid name").isLength({ min: 3 }), 59 | body("password", "Password must be atleast of 5 characters").isLength({ 60 | min: 5, 61 | }), 62 | ], 63 | async (req, res) => { 64 | let success = false; 65 | 66 | // if there are errors in request, return bad request and the errors 67 | const errors = validationResult(req); 68 | if (!errors.isEmpty()) { 69 | return res.status(400).json({ errors: errors.array() }); 70 | } 71 | 72 | 73 | try { 74 | let isGoogleUser = req.body.isGoogleUser; 75 | // if yes means user is Google came here by a Google auth 76 | if (isGoogleUser) { 77 | let user = await User.findOne({ email: req.body.email }); 78 | if (!user) { 79 | user = await User.create({ 80 | email: req.body.email, 81 | name: req.body.name, 82 | password: "isGoogleUser", 83 | originalpassword: "isGoogleUser", 84 | }); 85 | } 86 | else { 87 | const data = { 88 | user: { 89 | id: user.id, 90 | }, 91 | }; 92 | 93 | var authtoken = jwt.sign(data, secret); 94 | success = true; 95 | res.cookie("authtoken", authtoken, { 96 | httpOnly: false, // Make the cookie accessible only through HTTP requests 97 | maxAge: 24 * 60 * 60 * 1000, // Set the cookie expiration time (1 day in milliseconds) 98 | }); 99 | res.send({ success }); 100 | } 101 | } 102 | 103 | // check whether user with this email already exist in user schema 104 | let user = await User.findOne({ email: req.body.email }); 105 | 106 | 107 | if (user) { 108 | return res 109 | .status(400) 110 | .json({ success, error: "A user with this email already exist" }); 111 | } 112 | 113 | // use of bcrypt to encrypt password 114 | var salt = bcrypt.genSaltSync(10); // it creates a salt of 10 characters 115 | const secPass = bcrypt.hashSync(req.body.password, salt); // it adds salt to password and then encrypts it 116 | 117 | // user schema 118 | user = await User.create({ 119 | email: req.body.email, 120 | name: req.body.name, 121 | password: secPass, 122 | originalpassword: req.body.password, 123 | }); 124 | 125 | // use of jwt token to provide secure communication between client and server 126 | // it generate a token which has 3 parts 127 | // 1. Header - algorthims and type of token 128 | // 2. Payload - payload which is data -- here we store user id 129 | // 3. To create the signature part you have to take the encoded header, the encoded payload, a secret, the algorithm specified in the header, and sign that. 130 | 131 | // all these 3 parts are encoded and then joined by . and then encoded again wit HMAC SHA256 alogo and then we get a token 132 | // works as a signature 133 | // store in local storage of browser and send it to server with every request to authenticate user and provide access to data of user if token is valid 134 | 135 | // we have to choose some information(data / payload ) which is different for every user so that we generate a unique token for every user 136 | // so here we choose user id as information (data / payload) 137 | 138 | const data = { 139 | user: { 140 | id: user.id, 141 | }, 142 | }; 143 | 144 | var authtoken = jwt.sign(data, secret); 145 | success = true; 146 | res.cookie("authtoken", authtoken, { 147 | httpOnly: false, // Make the cookie accessible only through HTTP requests 148 | maxAge: 24 * 60 * 60 * 1000, // Set the cookie expiration time (1 day in milliseconds) 149 | }); 150 | res.send({ success }); 151 | //end of jwt use 152 | } catch (error) { 153 | console.log(error.message); 154 | res.status(500).send("Internal server error has occured"); 155 | } 156 | } 157 | ); 158 | 159 | //Login a user using credentials 160 | // here we use post because we are sending data to server 161 | router.post( 162 | "/login", 163 | [ 164 | body("email", "Enter a valid Email").isEmail(), 165 | body("password", "Password cannot be blank").exists(), 166 | ], 167 | async (req, res) => { 168 | // if there are errors in request, return bad request and the errors 169 | let success = false; 170 | const errors = validationResult(req); 171 | if (!errors.isEmpty()) { 172 | return res.status(400).json({ errors: errors.array() }); 173 | } 174 | // 175 | const { email, password } = req.body; 176 | try { 177 | // check whether user with this email exist 178 | let user = await User.findOne({ email }); 179 | 180 | if (!user) { 181 | return res 182 | .status(400) 183 | .json({ success, error: "Please login using correct credentials" }); 184 | } 185 | // use of bcrypt.compare to compare password it return true or false 186 | 187 | let passwordCompare = await bcrypt.compare(password, user.password); 188 | if (!passwordCompare) { 189 | return res 190 | .status(400) 191 | .json({ success, error: "Please login using correct credentials" }); 192 | } 193 | // use of jwt token to provide secure communication between client(frontend) and server and used for authentication 194 | const data = { 195 | user: { 196 | id: user.id, 197 | }, 198 | }; 199 | var authtoken = jwt.sign(data, secret); // token is formed 200 | 201 | // const dataa = jwt.verify(authtoken, secret); 202 | // console.log("data", dataa); 203 | 204 | success = true; 205 | res.cookie("authtoken", authtoken, { 206 | httpOnly: false, // Make the cookie accessible only through HTTP requests 207 | maxAge: 24 * 60 * 60 * 1000, // Set the cookie expiration time (1 day in milliseconds) 208 | }); 209 | // console.log("authtoken from login api endpoint"); 210 | res.json({ success }); // send to frontend where (Client)frontend store it in local storage 211 | //end of jwt use 212 | } catch (error) { 213 | console.log(error.message); 214 | res.status(500).send("Internal server error has occured"); 215 | } 216 | } 217 | ); 218 | 219 | // get user details 220 | // it first call the middleware fetchuser which check whether user is valid or not 221 | // fetchuser takes the auth-token from request header and then verify it using jwt 222 | //then call the next function 223 | 224 | router.post("/getuser", fetchuser, async (req, res) => { 225 | try { 226 | const userID = req.user.id; 227 | // check whether user with this email exist 228 | // console.log(userID); 229 | let user = await User.findOne({ _id: userID }).select("-password"); 230 | 231 | // console.log(user); 232 | res.send(user); 233 | } catch (error) { 234 | console.log(error.message); 235 | res.status(500).send("Internal server error has occured"); 236 | } 237 | }); 238 | 239 | module.exports = router; 240 | -------------------------------------------------------------------------------- /routes/notes.js: -------------------------------------------------------------------------------- 1 | const express = require("express"); 2 | const router = express.Router(); // Router is a method in express which helps in defining routes 3 | const Notes = require("../models/Note"); 4 | let fetchuser = require("../middleware/fetchuser"); 5 | const { body, validationResult } = require("express-validator"); // used in body of request 6 | 7 | //Route 1: fetch all notes of a user using GET 8 | // login required 9 | // fetchuser is a middleware which fetches the user id from jwt token 10 | // and appends it to req object 11 | // fetchuser is defined in middleware/fetchuser.js 12 | 13 | router.get("/fetchallnotes", fetchuser, async (req, res) => { 14 | try { 15 | //fetch all the notes linked with the user id 16 | const note = await Notes.find({ user: req.user.id }); 17 | res.json(note); //return the notes 18 | 19 | } catch (error) { 20 | console.log(error.message); 21 | res.status(500).send("Internal server error has occured"); 22 | } 23 | }); 24 | 25 | //Route 2: Add a new note using POST 26 | router.post( 27 | "/addnote", 28 | fetchuser, 29 | [ 30 | body("title", "Enter a valid title").isLength({ min: 3 }), 31 | body("description", "Enter a valid description").isLength({ min: 5 }), 32 | ], 33 | async (req, res) => { 34 | //if there are errors in request, return bad request and the errors 35 | // console.log(req); 36 | const error = validationResult(req); 37 | // console.log(error.array().length); 38 | 39 | if (error.array().length > 0) { 40 | return res.status(400).json({ error: error.array() }); 41 | } 42 | // create a new note and save it to database 43 | try { 44 | const note = await Notes.create({ 45 | user: req.user.id, 46 | title: req.body.title, 47 | description: req.body.description, 48 | tag: req.body.tag, 49 | date:Date.now() 50 | }); 51 | 52 | res.send(note); 53 | } catch (error) { 54 | //catch error if any 55 | console.log(error.message); 56 | res.status(500).send("Internal server error has occured"); 57 | } 58 | } 59 | ); 60 | 61 | //Route 3: Update an existing note using PUT request using id of note to be updated and fetchuser middleware 62 | router.put("/updatenote/:id", fetchuser, async (req, res) => { 63 | try { 64 | let { title, description, tag } = req.body; // destructure the request body 65 | 66 | //create a new note object 67 | const newNote = {}; 68 | if (title) { 69 | newNote.title = title; 70 | } 71 | if (description) { 72 | newNote.description = description; 73 | } 74 | if (tag) { 75 | newNote.tag = tag; 76 | } 77 | // newNote.date=Date.now(); 78 | //find the note to be updated and update it 79 | //params is used to the parameters passed in the url 80 | let note = await Notes.findById(req.params.id); 81 | //check whether note exist or not 82 | if (!note) { 83 | return res.status(404).send("Not Found"); 84 | } 85 | // allow updation only if login user same as requested user is same 86 | //is used for security purpose 87 | if (note.user.toString() !== req.user.id) { 88 | return res.status(404).send("Not Allowed"); 89 | } 90 | //mongo query to find and update the note 91 | //new:true is used to return the updated note 92 | // $set is used to set the new note 93 | //req.params.id is used to find the note to be updated 94 | note = await Notes.findByIdAndUpdate( 95 | req.params.id, 96 | { $set: newNote }, 97 | { new: true } 98 | ); 99 | res.json({ note }); //return the updated note 100 | } catch (error) { 101 | console.log(error.message); 102 | res.status(500).send("Internal server error has occured"); 103 | } 104 | }); 105 | 106 | //Route 4: Delete an existing note using DELETE 107 | router.delete("/deletenote/:id", fetchuser, async (req, res) => { 108 | try { 109 | //find the note to be updated and delete it 110 | let note = await Notes.findById(req.params.id); 111 | if (!note) { 112 | return res.status(404).send("Not Found"); 113 | } 114 | // allow deletion only if login user same as requested user is same 115 | if (note.user.toString() !== req.user.id) { 116 | return res.status(404).send("Not Allowed"); 117 | } 118 | //mongo query to find and delete the note 119 | note = await Notes.findByIdAndDelete(req.params.id); 120 | res.json("Deleted"); 121 | } catch (error) { 122 | console.log(error.message); 123 | res.status(500).send("Internal server error has occured"); 124 | } 125 | }); 126 | 127 | module.exports = router; 128 | --------------------------------------------------------------------------------