├── .vscode └── settings.json ├── CONTRIBUTING.md ├── README.md ├── backend ├── .gitignore ├── README.md ├── config │ ├── dev.js │ ├── keys.js │ └── prod.js ├── controllers │ ├── Auth.js │ ├── post.js │ ├── upload.js │ ├── user.js │ └── verifyemail.js ├── helper │ ├── cookie.js │ ├── gen_code.js │ ├── mail.js │ ├── mailverifymail.js │ ├── reportmail.js │ ├── token.js │ └── validation.js ├── index.js ├── middleware │ ├── auth.js │ └── req.js ├── models │ ├── Code.js │ ├── Post.js │ ├── User.js │ └── emailverify.js ├── package-lock.json ├── package.json ├── routes │ ├── post.js │ ├── upload.js │ └── user.js └── servises │ └── passport.js └── client ├── .gitignore ├── README.md ├── ads.txt ├── package-lock.json ├── package.json ├── public ├── Facebook.png ├── OIG.svg ├── best.jpg ├── google.jpg ├── index.html └── sp.gif └── src ├── App.css ├── App.js ├── components ├── Auth │ └── intogoogle.js ├── Navbar.js ├── article │ ├── Article.js │ └── article.css ├── footer │ ├── Footer.js │ └── footer.css ├── home │ ├── breaker │ │ ├── Breaker.js │ │ └── breaker.css │ ├── card │ │ ├── Card.js │ │ └── card.css │ └── post │ │ ├── PostCard.js │ │ ├── Posts.js │ │ └── post.css ├── profile │ ├── UserProfile.js │ └── userprofile.css ├── profileOftherUser │ ├── ProfileOfOtherUser.js │ └── profileOfOtherUser.css └── write │ ├── Editor.js │ ├── EditorP.js │ └── editor.css ├── helpers └── index.js ├── index.css ├── index.js ├── pages ├── ArticlePage.js ├── Auth.js ├── HomePage.js ├── Profile.js ├── ResetPassword.js ├── TopicPage.js ├── not_found.js ├── resetPassword.css └── write │ ├── Editpost.js │ └── WritePost.js ├── reducers ├── index.js ├── postReducer.js └── userReducer.js └── routes ├── LoggedInRoutes.js └── NotAllowedLoggedInRoutes.js /.vscode/settings.json: -------------------------------------------------------------------------------- 1 | { 2 | "files.associations": { 3 | "ostream": "cpp" 4 | } 5 | } -------------------------------------------------------------------------------- /CONTRIBUTING.md: -------------------------------------------------------------------------------- 1 | # Hacktoberfest2024 2 | 3 | **[hacktoberfest](https://hacktoberfest.digitalocean.com/). If you don't know where to start, feel free to watch the videos linked below, and read the contribution rules. Happy Contributing!** 4 | 5 | P.S. Star⭐️ this repo or follow [github](https://github.com/Prashant0664/) if you find this project interesting😁😍!
6 | 7 | # 📌 Videos 📽️: 8 | 9 | - [Hacktoberfest Intro](https://youtu.be/K5nzruz1FpA?si=ehf0mrrJ8xsGvxNe) 10 | - [Other Official Resources](https://hacktoberfest.com/participation/#beginner-resources) 11 | 12 | ## Contributors 13 | Here’s what you need to know to participate and complete Hacktoberfest: 14 | - Register anytime between september 26 and october 31 15 | 16 | - Pull requests can be made in any github or gitlab hosted project that’s participating in hacktoberfest (look for the “hacktoberfest” topic) 17 | 18 | - Project maintainers must accept your pull/merge requests for them to count toward your total 19 | 20 | - Have 4 pull/merge requests accepted between october 1 and october 31 to complete hacktoberfest 21 | 22 | ## Mandatory Step : 23 | - You have to star ⭐ this repository 24 | 25 | ## Getting Started 🚀: 26 | 27 | 1. Complete the registartion over https://hacktoberfest.com/ 28 | 2. Fork this repository. 29 | 3. Clone on your local machine. 30 | ```bash 31 | git clone https://github.com//Blog-website.git 32 | ``` 33 | 4. Navigate to the project directory. 34 | ```bash 35 | cd Blog-website 36 | ``` 37 | 5. Create a new branch. 38 | ```bash 39 | git checkout -b my-new-branch 40 | ``` 41 | 6. Add changes (system installation has been explained in README.md with detail 42 | 7. Add your contribution. 43 | ```bash 44 | git add . 45 | ``` 46 | 8. Commit your changes. 47 | ```bash 48 | git commit -m "Relevant message" 49 | ``` 50 | 9. Push your changes. 51 | ```bash 52 | git push origin my-new-branch 53 | ``` 54 | 10. Create a new pull request from your forked repository. 55 | 56 | *Congratulations 🎉 you just made a pull request!* 57 | 58 | # 59 |
60 | 61 | ## Avoid Conflicts {Syncing your fork} 62 | 63 | An easy way to avoid conflicts is to add an 'upstream' for your git repo, as other PR's may be merged while you're working on your branch/fork. 64 | 65 | 66 | ``` 67 | git remote add upstream https://github.com/Prashant0664/Blog-website.git 68 | ``` 69 | 70 | 71 | You can verify that the new remote has been added by typing 72 | ``` 73 | git remote -v 74 | ``` 75 | 76 | To pull any new changes from your parent repo simply run 77 | ``` 78 | git merge upstream/master 79 | ``` 80 | 81 |

🛡 Notice

82 |

⚔️ All contributors who have followed above rules correctly will meet merge pull request successfully.

83 |

⚔️ For any further issues, you can contact me in the comments

84 |

⚔️ Always make more than 4 total pull requests for hacktoberfest for safer side.

85 |
86 |

🖥️ Happy Coding 🔥 Happy Hacking...

87 | 88 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # Blogging Website Version 2.0 2 | *For **Hacktoberfest** please refer [Contributing.md](https://github.com/Prashant0664/Blog-website/blob/master/CONTRIBUTING.md)*
3 | 4 | For older version checkout the branch named *version1* 5 | 6 | ## Table of Contents 7 | - [Introduction](#introduction) 8 | - [New Features](#new-features) 9 | - [Demo](#demo-link) 10 | - [Features](#features) 11 | - [Technologies Used](#technologies-used) 12 | - [Getting Started](#getting-started) 13 | - [Prerequisites](#prerequisites) 14 | - [Installation](#installation) 15 | - [Usage](#usage) 16 | - [Contributing](#contributing) 17 | - [License](#license) 18 | 19 | --- 20 | 21 | ## Introduction 22 | Welcome to the Blogging Website project! This is a web application built using the MERN (MongoDB, Express.js, React.js, Node.js) stack. It allows users to create and manage their blogs, post projects, follow other users, comment on blogs, save content, bookmark blogs, like blogs and a lot more. The application also includes features such as email verification for user registration and Redux for state management. 23 | 24 | --- 25 | 26 | ## New Features: 27 | 🔥 **Like:** Users can now like Blogs and can see their liked blogs in profile. 28 | 🔥 **Filter:** User can now filter the content from dropdown menu below Navbar. 29 | 🔥 **Share:** User can now share blogs on social media using share button. 30 | 🔥 **Use of Params:** Blog Article now use params decreasing the coupling and increasing speed. 31 | 🔥 **Optimised:** More optimised than ever. 32 | 🔥 **User Frinedly:** More user friendly tha ever. 33 | 34 | --- 35 | 36 | ## Demo Link: 37 | 38 | Full Demo Video Drive link: [https://drive.google.com/file/d/1zPDw9Q28q-86CVlG62k5363gzIeGL7YX/view?usp=sharing 39 | ](https://drive.google.com/file/d/1As5DVfGSCfBOT9dqbX7MIYm69XllfGra/view?usp=sharing) 40 | 41 | --- 42 | 43 | 44 | ## Features 45 | Here are some of the key features of this Blogging Website: 46 | 47 | 🔥 **User Authentication and Profiles:** Users can create and manage their profiles, with email verification for account security. 48 | 49 | 🔥 **Blogging:** Users can create and publish their blogs with rich text formatting. 50 | 51 | 🔥 **Sharing:** Users can share any blogs on social media platforms; 52 | 53 | 🔥 **Filter Blogs:** Users can search other users and filter content according to category. 54 | 55 | 🔥 **Project Posting:** Users can share and showcase their projects on their profile. 56 | 57 | 🔥 **Social Features:** Users can follow other users, comment on blogs, and save, like and bookmark content they like. 58 | 59 | 🔥 **Responsive Design:** The website is designed to work seamlessly on various screen sizes and devices. 60 | 61 | 🔥 **Secure:** The application follows best practices for security, including password hashing and user authentication. 62 | 63 | 🔥 **Content Management:** Users can easily edit, delete, or download their own posts. 64 | 65 | --- 66 | 67 | ## Technologies Used 68 | The Blogging Website is built using the following technologies: 69 | 70 | - **MERN Stack:** 71 |
72 | 💫 MongoDB: A NoSQL database used to store user data, blogs, and other application data.
73 | 💫 Express.js: A Node.js web application framework used for building the server.
74 | 💫 React.js: A JavaScript library for building the user interface.
75 | 💫 Node.js: A JavaScript runtime used for server-side code execution.
76 | 77 | - **Additional Technologies:**
78 | 💫 Redux: Used for state management within the React application.
79 | 💫 Nodemailer: Used for email verification and sending email notifications.
80 | 81 | --- 82 | 83 | ## Getting Started 84 | To set up this project locally, follow the instructions below. 85 | 86 | ### Prerequisites 87 | Before you begin, make sure you have the following installed on your system: 88 | - Node.js and npm (Node Package Manager) 89 | - Git 90 | 91 | ### Installation 92 | (Request: **Please Star⭐️ the Repo or follow [github](https://github.com/Prashant0664/) if you find this project interesting😁!**
) 93 | 1. Clone this GitHub repository to your local machine: 94 | ``` 95 | git clone https://github.com/Prashant0664/All-Blogs-V2.git 96 | ``` 97 | 98 | 2. Change into the project directory: 99 | ``` 100 | cd Blog-website 101 | ``` 102 | 103 | 3. Install the backend dependencies: 104 | ``` 105 | cd backend 106 | npm install 107 | ``` 108 | 109 | 4. Install the frontend dependencies: 110 | ``` 111 | cd ../client 112 | npm install 113 | ``` 114 | 115 | 5. Set up your MongoDB database and configure the connection details in the backend's `.env` file. 116 | 117 | _IMP: Seperate setup of both frontend and backend is given in *client* and *backend* folders *Readme.md*_ 118 | 119 | 6. Start the backend server: 120 | ``` 121 | cd ../backend 122 | npm start 123 | ``` 124 | 125 | 7. Start the frontend development server: 126 | ``` 127 | cd ../client 128 | npm start 129 | ``` 130 | 131 | 8. Open your web browser and navigate to `http://localhost:3000` to access the Blogging Website. 132 | 133 | --- 134 | 135 | ## Usage 136 | You can now use the Blogging Website to create, like, save, share, and discover blogs, projects, and much more. Use Google Signin or Register an account, verify your email, and start enjoying the features of the application. 137 | 138 | --- 139 | 140 | ## Contributing 141 | **Please Star⭐️ the Repo or follow [github](https://github.com/Prashant0664/) if you find this project interesting😁!**
142 | Contributions to this project are welcome! If you'd like to contribute, please follow these steps: 143 | 1. Fork the repository. 144 | 2. Create a new branch for your feature or bug fix. 145 | 3. Make your changes and commit them with clear and descriptive commit messages. (Installation and Setup has been Explained in [Getting-Started](#getting-started) ) 146 | 4. Push your changes to your fork. 147 | 5. Submit a pull request to the main repository. 148 |
149 | 150 | --- 151 | 152 | ## License 153 | This project is licensed under the MIT License. See the [LICENSE](LICENSE) file for details. 154 | 155 | --- 156 | 157 | ### *Note: For any doubt or question you can open an issue. I will reply ASAP.* 158 | 159 | Thank you for using and contributing to the Blogging Website project! If you have any questions or need assistance, please don't hesitate to reach out to the maintainers. -------------------------------------------------------------------------------- /backend/.gitignore: -------------------------------------------------------------------------------- 1 | # See https://help.github.com/articles/ignoring-files/ for more about ignoring files. 2 | 3 | # dependencies 4 | /node_modules 5 | /.pnp 6 | .pnp.js 7 | 8 | # testing 9 | /coverage 10 | 11 | # production 12 | /build 13 | 14 | # misc 15 | .DS_Store 16 | .env 17 | .env.local 18 | .env.development.local 19 | .env.test.local 20 | .env.production.local 21 | 22 | npm-debug.log* 23 | yarn-debug.log* 24 | yarn-error.log* 25 | -------------------------------------------------------------------------------- /backend/README.md: -------------------------------------------------------------------------------- 1 | You will only be needed to setup ```.env``` file to run the server. 2 | 3 | _Note: You do not need to keep production variables different to run in the localhost or local machine, they both can be same._ 4 | 5 | *NOTE: If you face any challenges, error or have any doubt, just create a PR(recommended) or email me, I will respond as soon as possible:* 6 | 7 | ```.env``` file contains
8 | 9 | ``` 10 | 11 | # FOR DEVELOPMENT ( to run on local machine/ localhost ) 12 | MONGO_URI = 13 | GOOGLE_CLIENT = 14 | GOOGLE_SECRET = 15 | COOKIE_KEY = 16 | TOKEN_SECRET = 17 | CLOUD_NAME = 18 | CLOUD_API_KEY = 19 | CLOUD_API_SECRET = 20 | PASS = 21 | PORT = 22 | REACT_APP_BACKEND_URL = 23 | REACT_APP_FRONTEND_URL = 24 | EMAIL_ID = 25 | 26 | # FOR PRODUCTION ( to run on server after deployment) 27 | MONGO_URI_PRODUCTION = 28 | GOOGLE_CLIENT_PRODUCTION = 29 | GOOGLE_SECRET_PRODUCTION = 30 | COOKIE_KEY_PRODUCTION = 31 | TOKEN_SECRET_PRODUCTION = 32 | CLOUD_NAME_PRODUCTION = 33 | CLOUD_API_KEY_PRODUCTION = 34 | CLOUD_API_SECRET_PRODUCTION = 35 | PASS_PRODUCTION = 36 | PORT_PRODUCTION = 37 | EMAIL_ID_PRODUCTION = 38 | REACT_APP_BACKEND_URL_PRODUCTION = 39 | REACT_APP_FRONTEND_URL_PRODUCTION = 40 | 41 | ``` 42 | 43 | where
44 | 45 | - MONGO_URI=URI of the mongodb database 46 | - GOOGLE_CLIENT=google client id for google oauth generated from google console 47 | - GOOGLE_SECRET=google secret for google oauth generated from google console 48 | - COOKIE_KEY=key of the cookie 49 | - TOKEN_SECRET=token secret (random value like 120days) 50 | - CLOUD_NAME=cloudnary cloud name for storing images 51 | - CLOUD_API_KEY=cloudnary cloud key for storing images 52 | - CLOUD_API_SECRET=cloudnary cloud secret for storing images 53 | - PASS=Google app password which generated after 2 factor verification for sending mails related to verification, forgot password, report etc. 54 | - EMAIL_ID=id for which PASS is generated 55 | - PORT=5002 56 | - REACT_APP_BACKEND_URL=http://localhost:5002 (backend url) 57 | - REACT_APP_FRONTEND_URL=http://localhost:3000 (frontend url) 58 | -------------------------------------------------------------------------------- /backend/config/dev.js: -------------------------------------------------------------------------------- 1 | require('dotenv').config(); 2 | 3 | if (process.env.NODE_ENV === 'production') { 4 | module.exports = { 5 | MONGO_URI: process.env.MONGO_URI_PRODUCTION, 6 | GOOGLE_CLIENT_ID: process.env.GOOGLE_CLIENT_PRODUCTION, 7 | GOOGLE_CLIENT_SECRET: process.env.GOOGLE_SECRET_PRODUCTION, 8 | COOKIE_KEY: process.env.COOKIE_KEY_PRODUCTION, 9 | TOKEN_SECRET: process.env.TOKEN_SECRET_PRODUCTION, 10 | CLOUD_NAME: process.env.CLOUD_NAME_PRODUCTION, 11 | CLOUD_API_KEY: process.env.CLOUD_API_KEY_PRODUCTION, 12 | CLOUD_API_SECRET: process.env.CLOUD_API_SECRET_PRODUCTION, 13 | PASS: process.env.PASS_PRODUCTION, 14 | EMAIL_ID: process.env.EMAIL_ID_PRODUCTION, 15 | PORT: process.env.PORT_PRODUCTION, 16 | BACKEND_URL: process.env.REACT_APP_BACKEND_URL_PRODUCTION, 17 | FRONTEND_URL: process.env.REACT_APP_FRONTEND_URL_PRODUCTION 18 | } 19 | } 20 | else { 21 | module.exports = { 22 | MONGO_URI: process.env.MONGO_URI, 23 | GOOGLE_CLIENT_ID: process.env.GOOGLE_CLIENT, 24 | GOOGLE_CLIENT_SECRET: process.env.GOOGLE_SECRET, 25 | COOKIE_KEY: process.env.COOKIE_KEY, 26 | TOKEN_SECRET: process.env.TOKEN_SECRET, 27 | CLOUD_NAME: process.env.CLOUD_NAME, 28 | CLOUD_API_KEY: process.env.CLOUD_API_KEY, 29 | CLOUD_API_SECRET: process.env.CLOUD_API_SECRET, 30 | PASS: process.env.PASS, 31 | EMAIL_ID: process.env.EMAIL_ID, 32 | PORT: process.env.PORT, 33 | BACKEND_URL: process.env.REACT_APP_BACKEND_URL, 34 | FRONTEND_URL: process.env.REACT_APP_FRONTEND_URL 35 | } 36 | } 37 | -------------------------------------------------------------------------------- /backend/config/keys.js: -------------------------------------------------------------------------------- 1 | 2 | module.exports = require('./dev') -------------------------------------------------------------------------------- /backend/config/prod.js: -------------------------------------------------------------------------------- 1 | module.exports = { 2 | GOOGLE_CLIENT_ID: process.env.GOOGLE_CLIENT, 3 | GOOGLE_CLIENT_SECRET: process.env.GOOGLE_SECRET, 4 | mongoURI: process.env.MONGO_URI, 5 | cookieKey: process.env.COOKIE_KEY 6 | } -------------------------------------------------------------------------------- /backend/controllers/Auth.js: -------------------------------------------------------------------------------- 1 | const { validateEmail, validateLength } = require("../helper/validation"); 2 | const User = require("../models/User"); 3 | const Post = require("../models/Post"); 4 | const bcrypt = require("bcrypt"); 5 | const { generateToken } = require("../helper/token"); 6 | const Code = require('../models/Code'); 7 | const { sendResetCode } = require("../helper/mail"); 8 | const { sendReportMail } = require("../helper/reportmail"); 9 | const generateCode = require("../helper/gen_code"); 10 | const keys = require("../config/keys"); 11 | 12 | 13 | const CLIENT_URL = `${keys.FRONTEND_URL}`; 14 | // const CLIENT_URL = "https://allblogapp-project.vercel.app"; 15 | 16 | 17 | exports.google_auth = async (req, res) => { 18 | try { 19 | res.redirect('/'); 20 | } catch (error) { 21 | // console.log(error); 22 | return res.status(400).json({ msg: "Bad Request" }) 23 | } 24 | } 25 | 26 | exports.google_auth_callback = async (req, res) => { 27 | try { 28 | // res.redirect(CLIENT_URL); 29 | } catch (error) { 30 | // console.log(error); 31 | return res.status(400).json({ msg: "Bad Request" }) 32 | } 33 | } 34 | 35 | -------------------------------------------------------------------------------- /backend/controllers/post.js: -------------------------------------------------------------------------------- 1 | const Post = require("../models/Post"); 2 | const User = require("../models/User"); 3 | 4 | exports.newPost = async (req, res) => { 5 | try { 6 | const newPost = await new Post(req.body).save(); 7 | const datas = await User.findById(req.body.user); 8 | datas.posts.push(newPost._id) 9 | await datas.save(); 10 | await newPost.populate("user", "name picture"); 11 | res.json(newPost); 12 | } catch (error) { 13 | // console.log(error) 14 | return res.status(500).json({ message: error.message }); 15 | } 16 | }; 17 | exports.editPost = async (req, res) => { 18 | try { 19 | const newPost = await Post.findById(req.body.id); 20 | newPost.title = req.body.title; 21 | newPost.description = req.body.description; 22 | newPost.content = req.body.content; 23 | newPost.category = req.body.category; 24 | newPost.image = req.body.image; 25 | await newPost.save(); 26 | return res.status(200).json(newPost); 27 | } catch (error) { 28 | // console.log(error) 29 | return res.status(500).json({ message: error.message }); 30 | } 31 | }; 32 | exports.increaseView = async (req, res) => { 33 | try { 34 | const increaseView = await Post.findById(req.body.id); 35 | if (!increaseView) { 36 | return res.status(404).json({ message: "Post not found" }); 37 | } 38 | increaseView.views += 1; 39 | await increaseView.save(); 40 | res.json({ msg: "ok" }); 41 | } catch (error) { 42 | // console.log(error) 43 | return res.status(500).json({ message: error.message }); 44 | } 45 | }; 46 | exports.getView = async (req, res) => { 47 | try { 48 | const increaseView = await Post.findById(req.body.id); 49 | res.json({ msg: "ok", view: increaseView.views }); 50 | } catch (error) { 51 | // console.log(error) 52 | return res.status(500).json({ message: error.message }); 53 | } 54 | }; 55 | exports.getLikes = async (req, res) => { 56 | try { 57 | const getLikesView = await Post.findById(req.body.id); 58 | res.json({ msg: "ok", likes: getLikesView.likes }); 59 | } catch (error) { 60 | // console.log(error) 61 | return res.status(500).json({ message: error.message }); 62 | } 63 | }; 64 | exports.increaseLike = async (req, res) => { 65 | try { 66 | 67 | const increaseLike = await Post.findById(req.body.id); 68 | 69 | increaseLike.likes += 1; 70 | await increaseLike.save(); 71 | res.json({ msg: "ok" }); 72 | } 73 | catch (error) { 74 | // console.log(error) 75 | return res.status(500).json({ message: error.message }); 76 | } 77 | } 78 | exports.decreastLike = async (req, res) => { 79 | try { 80 | const increaseLike = await Post.findById(req.body.id); 81 | if (increaseLike) { 82 | increaseLike.likes -= 1; 83 | if (increaseLike.likes < 0) { 84 | increaseLike.likes = 0; 85 | } 86 | await increaseLike.save(); 87 | } 88 | return res.json({ msg: "ok" }); 89 | } 90 | catch (error) { 91 | // console.log(error) 92 | return res.status(500).json({ message: error.message }); 93 | 94 | } 95 | } 96 | exports.postcomment = async (req, res) => { 97 | try { 98 | const { name, 99 | image, 100 | content, 101 | id1, 102 | id2 } = req.body; 103 | const user = await Post.findOne({ user: id2 }); 104 | // var n = user.comment.size(); 105 | const date = new Date(); 106 | 107 | var ndata = { 108 | comment: content, 109 | image: image, 110 | commentBy: id1, 111 | commentAt: date, 112 | name: name 113 | } 114 | var datas = user.comments; 115 | datas.push(ndata) 116 | user.comments = datas; 117 | user.save(); 118 | res.status(201).json({ msg: "ok" }); 119 | } catch (error) { 120 | // console.log(error) 121 | res.status(401).json({ msg: "An Error Occurred" }) 122 | } 123 | } 124 | exports.getallpostdata = async (req, res) => { 125 | try { 126 | const { id } = req.body; 127 | var data = await Post.findById(id); 128 | return res.status(200).json({ msg: data }) 129 | } catch (error) { 130 | return res.status(400).json({ msg: "error" }); 131 | } 132 | } 133 | exports.getarticle = async (req, res) => { 134 | try { 135 | const { id } = req.body; 136 | var data = await Post.findById(id); 137 | if (!data) { 138 | return res.status(404).json({ msg: "!article" }); 139 | } 140 | const user = await User.findById(data.user).select("name picture about"); 141 | if (!user) { 142 | return res.status(404).json({ msg: "!user" }); 143 | } 144 | data.user = user; 145 | return res.status(200).json({ msg: data }) 146 | } catch (error) { 147 | // console.log(error); 148 | return res.status(400).json({ msg: "error" }); 149 | } 150 | } 151 | exports.getcomment = async (req, res) => { 152 | try { 153 | const { id } = req.body; 154 | const data = await Post.findOne({ user: id }); 155 | const user = data.comments 156 | // console.log(user); 157 | res.status(201).json(user); 158 | } catch (error) { 159 | // console.log(error) 160 | res.status(400).json({ msg: "error" }) 161 | } 162 | } 163 | exports.allPost = async (req, res) => { 164 | try { 165 | const page = parseInt(req.query.page); 166 | const size = parseInt(req.query.size); 167 | const cat = req.body.mpost; 168 | if (!cat || cat == "" || cat == "all") { 169 | const skip = (page - 1) * size; 170 | const total = await Post.countDocuments(); 171 | const posts = await Post.find().skip(skip).limit(size); 172 | await Promise.all( 173 | posts.map((post) => post.populate("user", "name picture about")) 174 | ); 175 | 176 | res.status(201).send({ 177 | posts, 178 | total, 179 | page, 180 | size, 181 | }); 182 | } 183 | else { 184 | const skip = (page - 1) * size; 185 | const posts = await Post.find({ category: cat }).skip(skip).limit(size); 186 | const total = posts.length; 187 | await Promise.all( 188 | posts.map((post) => post.populate("user", "name picture about")) 189 | ); 190 | 191 | res.status(201).send({ 192 | posts, 193 | total, 194 | page, 195 | size, 196 | }); 197 | } 198 | } catch (error) { 199 | // console.log(error); 200 | res.status(400).json(error); 201 | } 202 | }; 203 | -------------------------------------------------------------------------------- /backend/controllers/upload.js: -------------------------------------------------------------------------------- 1 | const cloudinary = require("cloudinary"); 2 | const fs = require("fs"); 3 | const keys = require("../config/keys"); 4 | 5 | cloudinary.config({ 6 | cloud_name: keys.CLOUD_NAME, 7 | api_key: keys.CLOUD_API_KEY, 8 | api_secret: keys.CLOUD_API_SECRET, 9 | }); 10 | 11 | 12 | exports.uploadImages = async (req, res) => { 13 | try { 14 | const { path } = req.body; 15 | let images = []; 16 | const url = await uploadToCloudinary(req.files.file, path); 17 | images.push(url); 18 | removeTmp(req.files.file); 19 | res.json(images); 20 | } catch (error) { 21 | // console.log(error) 22 | return res.status(500).json({ message: error.message }); 23 | } 24 | }; 25 | 26 | 27 | 28 | const uploadToCloudinary = async (file, path) => { 29 | return new Promise((resolve) => { 30 | cloudinary.v2.uploader.upload( 31 | file.tempFilePath, 32 | { 33 | folder: path, 34 | }, 35 | (err, res) => { 36 | if (err) { 37 | removeTmp(file); 38 | return res.status(400).json({ message: "Upload image failed." }); 39 | } 40 | resolve({ 41 | url: res.secure_url, 42 | }); 43 | } 44 | ); 45 | }); 46 | }; 47 | 48 | const removeTmp = (file) => { 49 | fs.unlink(file.tempFilePath, (err) => { 50 | if (err) throw err; 51 | }); 52 | }; 53 | -------------------------------------------------------------------------------- /backend/controllers/verifyemail.js: -------------------------------------------------------------------------------- 1 | const StatusCodes = require("http-status-codes"); 2 | const Verify = require("../models/emailverify") 3 | require('dotenv').config(); 4 | const { sendVerifyCode } = require("../helper/mailverifymail") 5 | const User = require("../models/User"); 6 | const generateCode = require("../helper/gen_code"); 7 | const checkotpv = async (req, res) => { 8 | try { 9 | const { mail, otp } = req.body; 10 | const data = await Verify.findOne({ mail: mail }); 11 | if (data) { 12 | if (data.otp == otp) { 13 | return res.status(200).json({ msg: "ok" }); 14 | } 15 | else { 16 | return res.status(200).json({ msg: "not" }); 17 | } 18 | } 19 | else { 20 | return res.status(200).json({ msg: "not" }); 21 | } 22 | // sendVerifyCode(mail, name, code); 23 | } catch (error) { 24 | // console.log("error in mail send code"); 25 | return res.status(400).json({ msg: "error in sending mail" }); 26 | } 27 | } 28 | const sendmail = async (req, res) => { 29 | try { 30 | const { mail, name } = req.body; 31 | const code = generateCode(6); 32 | const data = await Verify.findOne({ mail: mail }); 33 | if (data) { 34 | data.otp = code; 35 | data.mail = mail; 36 | await data.save(); 37 | } 38 | else { 39 | const user = await Verify.create({ 40 | mail: mail, 41 | otp: code, 42 | }) 43 | await user.save(); 44 | } 45 | sendVerifyCode(mail, name, code); 46 | return res.status(200).json({ msg: "ok" }); 47 | } catch (error) { 48 | // console.log("error in mail send code"); 49 | return res.status(400).json({ msg: "error in sending mail" }); 50 | 51 | } 52 | } 53 | const checkifverify = async (req, res) => { 54 | 55 | try { 56 | const { mail } = req.body 57 | const data = await User.findOne({ email: mail }); 58 | if (!data) { 59 | return res.status(200).json({ msg: "ne" }); 60 | } 61 | if (data.verify === true) { 62 | return res.status(200).json({ msg: "ok" }); 63 | } 64 | else { 65 | return res.status(200).json({ msg: "not" }); 66 | 67 | } 68 | } catch (error) { 69 | return res.status(400).json({ msg: "error" }); 70 | } 71 | } 72 | 73 | const verifycode = async (req, res) => { 74 | 75 | try { 76 | const { mail } = req.body 77 | const data = await User.findOne({ email: mail }); 78 | if (!data) { 79 | return res.status(200).json({ msg: "ne" }); 80 | } 81 | if (data.verify === true) { 82 | return res.status(200).json({ msg: "ok" }); 83 | } 84 | else { 85 | return res.status(200).json({ msg: "not" }); 86 | 87 | } 88 | } catch (error) { 89 | return res.status(400).json({ msg: "error" }); 90 | } 91 | } 92 | 93 | module.exports = { 94 | sendmail, 95 | checkifverify, 96 | verifycode, 97 | checkotpv 98 | } -------------------------------------------------------------------------------- /backend/helper/cookie.js: -------------------------------------------------------------------------------- 1 | 2 | exports.clearCookie = (cookieName) => { 3 | document.cookie = `${cookieName}=; expires=Thu, 01 Jan 1970 00:00:00 UTC; path=/;`; 4 | } 5 | 6 | -------------------------------------------------------------------------------- /backend/helper/gen_code.js: -------------------------------------------------------------------------------- 1 | function generateCode(length) { 2 | let code = ""; 3 | let schema = "0123456789"; 4 | 5 | for (let i = 0; i < length; i++) { 6 | code += schema.charAt(Math.floor(Math.random() * schema.length)); 7 | } 8 | 9 | return code; 10 | } 11 | module.exports = generateCode; 12 | -------------------------------------------------------------------------------- /backend/helper/mail.js: -------------------------------------------------------------------------------- 1 | const nodemailer = require("nodemailer"); 2 | const keys = require("../config/keys"); 3 | 4 | exports.sendResetCode = (email, name, code) => { 5 | const transporter = nodemailer.createTransport({ 6 | service: "gmail", 7 | auth: { 8 | user: keys.EMAIL_ID, 9 | pass: keys.PASS, 10 | }, 11 | }); 12 | // console.log(email) 13 | const mailOptions = { 14 | from: keys.EMAIL_ID, 15 | to: email, 16 | subject: "ALL Blogs-Forgot password varification code", 17 | html: `
28 | 29 | Action required : RESET your PASSWORD 30 | 31 |
32 |
41 | 42 | Hello ${name} 43 | 44 |
47 | 48 | To change your password copy the code below and paste it to the confirmation box 49 | 50 |
51 | 59 | ${code} 60 | 61 |
62 |
63 | 64 | 65 |
66 |
`, 67 | }; 68 | 69 | transporter.sendMail(mailOptions, (error, info) => { 70 | if (error) { 71 | } 72 | }); 73 | }; 74 | -------------------------------------------------------------------------------- /backend/helper/mailverifymail.js: -------------------------------------------------------------------------------- 1 | const nodemailer = require("nodemailer"); 2 | const keys = require("../config/keys"); 3 | 4 | exports.sendVerifyCode = (email, name, code) => { 5 | const transporter = nodemailer.createTransport({ 6 | service: "gmail", 7 | auth: { 8 | user: keys.EMAIL_ID, 9 | pass: keys.PASS, 10 | }, 11 | }); 12 | // console.log(email) 13 | const mailOptions = { 14 | from: keys.EMAIL_ID, 15 | to: email, 16 | subject: "ALL Blogs Email Verification Code", 17 | html: `
28 | 29 | Action required : Verify Your Email 30 | 31 |
32 |
41 | 42 | Hello ${name} 43 | 44 |
47 | 48 | To Verify your Email, put below code into otp box 49 | 50 |
51 | 59 | Verification Code: ${code} 60 | 61 |
62 |
63 | 64 | 65 |
66 |
`, 67 | }; 68 | 69 | transporter.sendMail(mailOptions, (error, info) => { 70 | if (error) { 71 | } 72 | }); 73 | }; 74 | -------------------------------------------------------------------------------- /backend/helper/reportmail.js: -------------------------------------------------------------------------------- 1 | const nodemailer = require("nodemailer"); 2 | const keys = require("../config/keys"); 3 | 4 | exports.sendReportMail = (email1, email2, name1, name2, reason, postid) => { 5 | const transporter = nodemailer.createTransport({ 6 | service: "gmail", 7 | auth: { 8 | user: keys.EMAIL_ID, 9 | pass: keys.PASS, 10 | }, 11 | }); 12 | const mailOptions3 = { 13 | from: keys.EMAIL_ID, 14 | to: keys.EMAIL_ID, 15 | subject: "Someone Requested Report", 16 | html: `
27 | 28 | Someone has requested an report action 29 | 30 |
31 |
40 | 41 | Hello Admin 42 | 43 |
46 | 47 | ${name2} has requested action on post id ${postid} 48 | 49 |
50 | 58 | Request Action 59 | 60 |
61 |
62 | Request User Mail: ${email2}
63 | Reported user Mail: ${email1}
64 | Reported content id: ${postid}
65 | Reason: ${reason} 66 |
67 |
68 | 69 | 70 |
71 |
`, 72 | }; 73 | const mailOptions = { 74 | from: keys.EMAIL_ID, 75 | to: keys.EMAIL_ID, 76 | subject: "Report on one of your Blogs", 77 | html: `
88 | 89 | Someone has reported on One of your blog 90 | 91 |
92 |
101 | 102 | Hello ${name1} 103 | 104 |
107 | 108 | Someone has reported on your blog(blog link). If you have violated our terms and conditions then we have to remove your blog, and repeating this violations may lead to permanent ban on your account. 109 | 110 |
111 | 114 | To Request Action reply to this email 115 | 116 |
117 |
118 | 119 | 120 |
121 |
`, 122 | }; 123 | const mailOptions2 = { 124 | from: keys.EMAIL_ID, 125 | to: email2, 126 | subject: "All Blogs Support Team", 127 | html: `
138 | 139 | All blog Support Team 140 | 141 |
142 |
151 | 152 | Hello ${name2} 153 | 154 |
157 | 158 | Your complaint has successfully been received, Our team will review your complaint and will take action accordingly as soon as possible. 159 |
160 | 161 |
162 |
163 | 171 | Request Action 172 | 173 |
174 |
175 | 176 | 177 |
178 |
`, 179 | }; 180 | 181 | transporter.sendMail(mailOptions, (error, info) => { 182 | if (error) { 183 | // console.log(error); 184 | } 185 | }); 186 | transporter.sendMail(mailOptions3, (error, info) => { 187 | if (error) { 188 | // console.log(error); 189 | } 190 | }); 191 | transporter.sendMail(mailOptions2, (error, info) => { 192 | if (error) { 193 | // console.log(error); 194 | } 195 | }); 196 | }; 197 | -------------------------------------------------------------------------------- /backend/helper/token.js: -------------------------------------------------------------------------------- 1 | const jwt = require("jsonwebtoken"); 2 | const keys = require("../config/keys"); 3 | 4 | 5 | exports.generateToken = (payload, expired) => { 6 | return jwt.sign(payload, keys.TOKEN_SECRET, { 7 | expiresIn: expired, 8 | }); 9 | }; 10 | 11 | -------------------------------------------------------------------------------- /backend/helper/validation.js: -------------------------------------------------------------------------------- 1 | 2 | 3 | exports.validateEmail = (email) => { 4 | return String(email) 5 | .toLowerCase() 6 | .match(/^([a-z\d\.-]+)@([a-z\d-]+)\.([a-z]{2,12})(\.[a-z]{2,12})?$/); 7 | }; 8 | 9 | 10 | exports.validateLength = (text, min, max) => { 11 | if (text.length > max || text.length < min) { 12 | return false; 13 | } 14 | return true; 15 | }; 16 | 17 | -------------------------------------------------------------------------------- /backend/index.js: -------------------------------------------------------------------------------- 1 | const dotenv = require("dotenv").config(); 2 | const keys = require("./config/keys"); 3 | const Port = keys.PORT || 5002; 4 | const express = require("express"); 5 | const app = express(); 6 | const mongoose = require("mongoose"); 7 | const passport = require("passport"); 8 | const session = require("express-session"); 9 | // const session = require('cookie-session'); 10 | const cors = require("cors"); 11 | const fileUpload = require("express-fileupload"); 12 | const userRoutes = require("./routes/user.js"); 13 | const uploadRoutes = require("./routes/upload.js"); 14 | const postRoutes = require("./routes/post.js"); 15 | var cookieParser = require('cookie-parser') 16 | var cookieSession = require("cookie-session"); 17 | var MongoDBStore = require("connect-mongodb-session")(session); 18 | require('dotenv').config(); 19 | app.use( 20 | cors({ 21 | origin: [keys.BACKEND_URL, keys.FRONTEND_URL], 22 | // origin: [keys.REACT_APP_BACKEND_URL, keys.REACT_APP_FRONTEND_URL], 23 | methods: "GET,POST,PUT,DELETE", 24 | credentials: true, 25 | }) 26 | ); 27 | 28 | mongoose.set("strictQuery", false); 29 | mongoose.connect(keys.MONGO_URI) 30 | 31 | var store = new MongoDBStore( 32 | { 33 | uri: keys.MONGO_URI, 34 | collection: "mySessions", 35 | }, 36 | function (error) { 37 | if (error) { 38 | // console.log("err", error); 39 | } 40 | } 41 | ); 42 | 43 | app.use((req, res, next) => { 44 | res.setHeader('Access-Control-Allow-Credentials', 'true'); 45 | next(); 46 | }); 47 | 48 | app.use(express.json({ limit: "10mb" })); 49 | app.use(express.urlencoded({ limit: "10mb", extended: true })); 50 | 51 | 52 | app.set("trust proxy", 1) 53 | app.use(cookieParser()) 54 | app.use(session({ 55 | proxy: true, 56 | secret: keys.COOKIE_KEY, 57 | resave: false, 58 | saveUninitialized: true, 59 | cookie: { 60 | maxAge: 15 * 24 * 60 * 60 * 1000, // Uncomment if needed for cookie lifespan 61 | sameSite: process.env.NODE_ENV === 'production' ? "none" : "lax", // "none" for cross-site cookies in production 62 | secure: process.env.NODE_ENV === 'production', // Secure should be true in production (HTTPS) 63 | }, 64 | })) 65 | 66 | app.use(passport.initialize()); 67 | app.use(passport.session()); 68 | 69 | app.use( 70 | fileUpload({ 71 | useTempFiles: true, 72 | }) 73 | ); 74 | 75 | 76 | app.use("/", userRoutes); 77 | require("./servises/passport"); 78 | app.use("/", uploadRoutes); 79 | app.use("/", postRoutes); 80 | 81 | app.listen(Port, () => { 82 | console.log(`server running ${Port}`); 83 | }); 84 | -------------------------------------------------------------------------------- /backend/middleware/auth.js: -------------------------------------------------------------------------------- 1 | const jwt = require("jsonwebtoken"); 2 | const keys = require("../config/keys"); 3 | 4 | exports.authUser = async (req, res, next) => { 5 | try { 6 | let tmp = req.header("Authorization"); 7 | const token = tmp ? tmp.slice(7, tmp.length) : ""; 8 | if (!token) { 9 | return res.status(400).json({ message: "Invalid Authentification" }); 10 | } 11 | jwt.verify(token, keys.TOKEN_SECRET, (err, user) => { 12 | if (err) { 13 | return res.status(400).json({ message: "Invalid Authentificationn" }); 14 | } 15 | req.user = user; 16 | next(); 17 | }); 18 | } catch (error) { 19 | return res.status(500).json({ message: error.message }); 20 | } 21 | }; 22 | -------------------------------------------------------------------------------- /backend/middleware/req.js: -------------------------------------------------------------------------------- 1 | const passport = require("passport"); 2 | 3 | exports.attachUserToRequest = (req, res, next) => { 4 | req.user = req.user || null; 5 | next(); 6 | }; -------------------------------------------------------------------------------- /backend/models/Code.js: -------------------------------------------------------------------------------- 1 | const mongoose = require("mongoose"); 2 | 3 | const { ObjectId } = mongoose.Schema; 4 | 5 | const codeSchema = new mongoose.Schema({ 6 | code: { 7 | type: String, 8 | required: true, 9 | }, 10 | user: { 11 | type: ObjectId, 12 | ref: "User", 13 | required: true, 14 | }, 15 | }); 16 | codeSchema.index({ createdAt: 1 }, { expireAfterSeconds: 1800 }); 17 | module.exports = mongoose.model("Code", codeSchema); 18 | -------------------------------------------------------------------------------- /backend/models/Post.js: -------------------------------------------------------------------------------- 1 | const mongoose = require("mongoose"); 2 | 3 | const { ObjectId } = mongoose.Schema; 4 | 5 | const postSchema = new mongoose.Schema( 6 | { 7 | title: { 8 | type: String, 9 | required: true 10 | }, 11 | description: { 12 | type: String, 13 | required: true 14 | }, 15 | category: { 16 | type: String, 17 | required: true, 18 | enum: ["food", "travelling", "lifestyle", "tech"] 19 | }, 20 | image: { 21 | type: String, 22 | required: true, 23 | }, 24 | views: { 25 | type: Number, 26 | default: 0 27 | }, 28 | likes: { 29 | type: Number, 30 | default: 0 31 | }, 32 | user: { 33 | type: ObjectId, 34 | ref: "User", 35 | required: true, 36 | }, 37 | content: { 38 | type: String, 39 | required: true 40 | }, 41 | comments: [ 42 | { 43 | comment: { 44 | type: String, 45 | }, 46 | image: { 47 | type: String, 48 | }, 49 | commentBy: { 50 | type: ObjectId, 51 | ref: "User", 52 | }, 53 | commentAt: { 54 | type: Date, 55 | required: true, 56 | }, 57 | name: { 58 | type: String, 59 | default: "An User" 60 | } 61 | 62 | }, 63 | ], 64 | }, 65 | { 66 | timestamps: true, 67 | } 68 | ); 69 | 70 | module.exports = mongoose.model("Post", postSchema); 71 | -------------------------------------------------------------------------------- /backend/models/User.js: -------------------------------------------------------------------------------- 1 | const { model, Schema } = require("mongoose"); 2 | const userSchema = new Schema( 3 | { 4 | name: { 5 | type: String, 6 | // required: true, 7 | }, 8 | email: { 9 | type: String, 10 | required: true, 11 | }, 12 | password: { 13 | type: String, 14 | required: function () { return !this.googleId }, 15 | }, 16 | verify: { 17 | type: Boolean, 18 | default: false 19 | }, 20 | googleId: { 21 | type: String, 22 | required: function () { return !this.password }, 23 | }, 24 | picture: { 25 | type: String, 26 | trim: true, 27 | default: 28 | "https://res.cloudinary.com/dmhcnhtng/image/upload/v1643044376/avatars/default_pic_jeaybr.png", 29 | }, 30 | about: { 31 | type: String 32 | }, 33 | bookmarks: { 34 | type: Array, 35 | default: [] 36 | }, 37 | likes: { 38 | type: Array, 39 | default: [] 40 | }, 41 | posts: { 42 | type: Array, 43 | default: [] 44 | }, 45 | following: { 46 | type: Array, 47 | default: [], 48 | }, 49 | followerscount: { 50 | type: Number, 51 | default: 0, 52 | }, 53 | followingcount: { 54 | type: Number, 55 | default: 0, 56 | }, 57 | likeslist: { 58 | type: Map, 59 | of: Boolean, 60 | }, 61 | bookmarkslist: { 62 | type: Map, 63 | of: Boolean, 64 | } 65 | 66 | }, 67 | { timestamps: true } 68 | ); 69 | 70 | module.exports = model("User", userSchema); 71 | 72 | -------------------------------------------------------------------------------- /backend/models/emailverify.js: -------------------------------------------------------------------------------- 1 | const mongoose = require('mongoose') 2 | 3 | const UserSchema = new mongoose.Schema({ 4 | mail: { 5 | type: String, 6 | minlength: 3, 7 | }, 8 | isVerify: { 9 | type: Boolean, 10 | default: false, 11 | }, 12 | otp: { 13 | type: Number, 14 | } 15 | }, 16 | { timestamps: true }) 17 | 18 | UserSchema.index({ createdAt: 1 }, { expireAfterSeconds: 1800 }); 19 | module.exports = mongoose.model('VerifyRegister', UserSchema); 20 | 21 | -------------------------------------------------------------------------------- /backend/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "blog", 3 | "version": "2.13.0", 4 | "description": "multi-user blog application with mern", 5 | "main": "index.js", 6 | "scripts": { 7 | "start": "nodemon index.js" 8 | }, 9 | "author": "Prashant0664", 10 | "license": "MIT", 11 | "dependencies": { 12 | "@react-oauth/google": "^0.12.1", 13 | "bcrypt": "^5.1.1", 14 | "cloudinary": "^2.5.0", 15 | "connect-mongo": "^5.1.0", 16 | "connect-mongodb-session": "^5.0.0", 17 | "cookie-parser": "^1.4.6", 18 | "cookie-session": "^2.1.0", 19 | "cors": "^2.8.5", 20 | "dotenv": "^16.4.5", 21 | "express": "^4.21.0", 22 | "express-fileupload": "^1.5.1", 23 | "express-session": "^1.18.0", 24 | "google-auth-library": "^9.14.1", 25 | "http-status-codes": "^2.3.0", 26 | "jsonwebtoken": "^9.0.2", 27 | "memory-cache": "^0.2.0", 28 | "mongoose": "^8.7.0", 29 | "nodemailer": "^6.9.15", 30 | "nodemon": "^3.1.7", 31 | "npm-check-updates": "^17.1.3", 32 | "passport": "^0.7.0", 33 | "passport-google-oauth2": "^0.2.0", 34 | "passport-google-oauth20": "^2.0.0", 35 | "passport-google-oidc": "^0.1.0", 36 | "passport-oauth": "^1.0.0" 37 | } 38 | } 39 | -------------------------------------------------------------------------------- /backend/routes/post.js: -------------------------------------------------------------------------------- 1 | const express = require("express"); 2 | 3 | const { getarticle, getLikes, decreastLike, increaseLike, getallpostdata, newPost, allPost, postcomment, getcomment, increaseView, getView, editPost, } = require("../controllers/post"); 4 | const { authUser } = require('../middleware/auth'); 5 | 6 | const router = express.Router(); 7 | 8 | router.post("/post", authUser, newPost); 9 | router.post("/editPost", authUser, editPost); 10 | router.post("/getarticle", getarticle); 11 | router.post("/getallpost", allPost); 12 | router.post("/postcomment", postcomment) 13 | router.post("/getcomment", getcomment) 14 | router.post("/increaseView", increaseView) 15 | router.post("/getView", getView) 16 | router.post("/getallpostdata", getallpostdata) 17 | router.post("/increaseLike", increaseLike) 18 | router.post("/decreastLike", decreastLike) 19 | router.post("/getLikes", getLikes) 20 | module.exports = router; 21 | -------------------------------------------------------------------------------- /backend/routes/upload.js: -------------------------------------------------------------------------------- 1 | const express = require("express"); 2 | const { uploadImages } = require("../controllers/upload"); 3 | const { authUser } = require('../middleware/auth'); 4 | 5 | const router = express.Router(); 6 | 7 | router.post("/uploadImages", authUser, uploadImages); 8 | 9 | module.exports = router; 10 | -------------------------------------------------------------------------------- /backend/routes/user.js: -------------------------------------------------------------------------------- 1 | 2 | const express = require("express"); 3 | const keys = require("../config/keys"); 4 | 5 | const { generateToken } = require("../helper/token"); 6 | const { 7 | getallLikes, 8 | register, 9 | login, 10 | getallBookmarks, 11 | uploadprofile, 12 | getUser, 13 | findOutUser, 14 | sendResetPasswordCode, 15 | validateResetCode, 16 | changePassword, 17 | bookmark, 18 | deletebookmark, 19 | checkbookmark, 20 | sendreportmails, 21 | followercount, 22 | followingcount, 23 | showbookmark, 24 | fetchprof, 25 | showmyposts, 26 | deletepost, 27 | fetchfollowing, 28 | follow, 29 | checkfollowing, 30 | unfollow, 31 | searchresult, 32 | changeabout, 33 | likes, 34 | checklikes, 35 | deletelikes, 36 | showLikemark 37 | } = require("../controllers/user"); 38 | const { 39 | sendmail, 40 | checkifverify, 41 | verifycode, 42 | checkotpv 43 | } = require("../controllers/verifyemail") 44 | 45 | const { 46 | google_auth, 47 | google_auth_callback, 48 | } = require("../controllers/Auth") 49 | 50 | var passport = require('passport') 51 | const OAuthStrategy = require('passport-oauth').OAuthStrategy; 52 | var GoogleStrategy = require('passport-google-oidc'); 53 | 54 | const router = express.Router(); 55 | const app = express(); 56 | const { authUser } = require("../middleware/auth"); 57 | // app.use(passport.initialize()); 58 | // app.use(passport.session()); 59 | router.post("/register", register); 60 | router.post("/checkotpv", checkotpv); 61 | 62 | router.post("/checkifverify", checkifverify); 63 | router.post("/login", login); 64 | router.post("/sendmail", sendmail); 65 | router.post("/verifycode", verifycode); 66 | router.put("/uploadprofile", authUser, uploadprofile); 67 | router.get("/getUser/:userId", getUser); 68 | router.post("/findOutUser", findOutUser); 69 | router.post("/getallBookmarks", getallBookmarks); 70 | router.post("/sendResetPasswordCode", sendResetPasswordCode); 71 | router.post("/validateResetCode", validateResetCode); 72 | router.post("/changePassword", changePassword); 73 | router.post("/setbookmark", bookmark); 74 | router.post("/setlikes", likes); 75 | router.post("/getallLikes", getallLikes); 76 | router.post("/deletelikes", deletelikes); 77 | router.post("/checklikes", checklikes); 78 | router.post("/deletebookmark", deletebookmark); 79 | router.post("/checkbookmark", checkbookmark); 80 | router.post("/reportcontent", sendreportmails); 81 | router.post("/countfollower", followercount); 82 | router.post("/countfollowing", followingcount); 83 | router.post("/showbookmarks", showbookmark); 84 | router.post("/showLikemarks", showLikemark); 85 | router.post("/fetchprof", fetchprof); 86 | router.post("/showmyposts", showmyposts); 87 | router.post("/deletepost", deletepost); 88 | router.post("/fetchfollowing", fetchfollowing); 89 | router.post("/startfollow", follow); 90 | router.post("/unfollow", unfollow); 91 | router.post("/searchresult", searchresult); 92 | router.post("/checkfollow", checkfollowing); 93 | router.post("/changeabout", changeabout); 94 | 95 | 96 | const register_google = async (req) => { 97 | try { 98 | const { name, temail, password, image } = req.body; 99 | 100 | const check = await User.findOne({ temail }); 101 | if (check) { 102 | return res.status(400).json({ 103 | message: 104 | "This email already exists,try again with a different email", 105 | }); 106 | } 107 | 108 | const hashed_password = await bcrypt.hash(password, 10); 109 | const user = await new User({ 110 | name: name, 111 | email: temail, 112 | password: hashed_password, 113 | verify: true, 114 | picture: image 115 | }).save(); 116 | const token = generateToken({ id: user._id.toString() }, "15d"); 117 | res.send({ 118 | id: user._id, 119 | name: user.name, 120 | picture: user.picture, 121 | token: token, 122 | message: "Register Success !", 123 | }); 124 | } catch (error) { 125 | // console.log(error); 126 | return res.status(500).json({ message: error.message }); 127 | } 128 | } 129 | 130 | // passport.use(new GoogleStrategy({ 131 | // clientID: process.env.GOOGLE_CLIENT, 132 | // clientSecret: process.env.GOOGLE_SECRET, 133 | // callbackURL: `${process.env.REACT_APP_FRONTEND_URL}`, 134 | // passReqToCallback: true 135 | // }, 136 | // function (req, acc, ref, profile, done) { 137 | // CSSConditionRule.log(profile) 138 | // return done(null, profile) 139 | // } 140 | // )) 141 | 142 | // router.get( 143 | // "/auth/google", 144 | // passport.authenticate("google", { scope: ['profile', 'email'] }, { failureRedirect: '/login/failed', failureMessage: true }), 145 | // google_auth 146 | // ); 147 | 148 | // router.get( 149 | // "/auth/google/callback", 150 | // passport.authenticate("google", { 151 | // failureRedirect: "/login/failed" 152 | // }), 153 | // google_auth_callback 154 | // ); 155 | 156 | router.get("/auth/google", passport.authenticate("google", { scope: ["profile", "email"] })); 157 | 158 | router.get("/auth/google/callback", passport.authenticate("google", { 159 | successRedirect: `${keys.FRONTEND_URL}/`, 160 | failureRedirect: `${keys.FRONTEND_URL}/login` 161 | })) 162 | 163 | 164 | router.get("/login/failed", (req, res) => { 165 | res.status(401).json({ 166 | success: false, 167 | message: " Authentication has been failded ! ", 168 | }); 169 | }); 170 | 171 | router.post("/login/success", async (req, res) => { 172 | if (req.isAuthenticated()) { 173 | const token = generateToken({ id: req.user._id.toString() }, "15d"); 174 | return res.status(201).send({ 175 | id: req.user._id, 176 | name: req.user.name, 177 | picture: req.user.picture, 178 | token: token, 179 | likes: req.user.likes, 180 | bookmarks: req.user.bookmarks, 181 | }); 182 | // res.status(200).json({ 183 | // success: true, 184 | // message: "successfull", 185 | // user: { id: req.user._id, name: req.user.name, email: req.user.email, googleId: req.user.googleId, picture: req.user.picture } 186 | // }); 187 | } else { 188 | // console.log("failed"); 189 | return res.status(401).json({ 190 | success: false, 191 | message: "Un-successfull", 192 | user: null, 193 | }); 194 | } 195 | }); 196 | //Logout 197 | router.get("/logout", async (req, res) => { 198 | try { 199 | req.logout((err) => { 200 | if (err) { 201 | return res.status(400).json("Couldn't logout"); 202 | } 203 | }); 204 | res.cookie('session', '', { expires: new Date(0), }); 205 | res.clearCookie("sessionId"); 206 | res.status(200).json({ success: true }); 207 | } catch (error) { 208 | return res.status(500).json({ message: error.message }); 209 | } 210 | }); 211 | 212 | 213 | 214 | module.exports = router; 215 | -------------------------------------------------------------------------------- /backend/servises/passport.js: -------------------------------------------------------------------------------- 1 | const GoogleStrategy = require('passport-google-oauth20').Strategy; 2 | const passport = require("passport"); 3 | const keys = require("../config/keys") 4 | const User = require('../models/User'); 5 | 6 | passport.use(new GoogleStrategy({ 7 | clientID: keys.GOOGLE_CLIENT_ID, 8 | clientSecret: keys.GOOGLE_CLIENT_SECRET, 9 | callbackURL: "/auth/google/callback", 10 | scope: ["profile", "email"] 11 | }, 12 | async (accessToken, refreshToken, profile, done) => { 13 | try { 14 | User.findOne({ email: profile.emails[0].value }).then((user) => { 15 | if (user && user.picture == "https://res.cloudinary.com/dmhcnhtng/image/upload/v1643044376/avatars/default_pic_jeaybr.png") { 16 | user.picture = profile.photos[0].value;; 17 | } 18 | if (user && !user.googleId) { 19 | user.googleId = profile.id; 20 | user.password = "crnkefn"; 21 | return done(null, user); 22 | } 23 | if (user && !user.likeslist) { 24 | user.likeslist = {}; 25 | } 26 | if (user && !user.bookmarkslist) { 27 | user.bookmarkslist = {}; 28 | } 29 | if (user) user.save(); 30 | } 31 | ) 32 | User.findOne({ googleId: profile.id }).then((existingUser) => { 33 | if (existingUser) { 34 | return done(null, existingUser) 35 | } else { 36 | var url = profile.photos[0].value; 37 | new User({ googleId: profile.id, email: profile.emails[0].value, picture: url, name: profile.displayName, likeslist: {}, bookmarkslist: {} }).save().then((user) => { 38 | return done(null, user) 39 | // done(null, user) 40 | }); 41 | } 42 | }) 43 | } 44 | catch (error) { 45 | // console.log(error) 46 | } 47 | } 48 | )); 49 | 50 | // passport.serializeUser((user, done) => { 51 | // done(null, user.id) 52 | // }) 53 | 54 | // passport.deserializeUser((id, done) => { 55 | // User.findById(id).then((user) => { 56 | // done(null, user) 57 | // }) 58 | // }) 59 | 60 | passport.serializeUser((user, done) => { 61 | done(null, user); 62 | 63 | }) 64 | 65 | // used to deserialize the user 66 | passport.deserializeUser((user, done) => { 67 | done(null, user); 68 | }) 69 | // passport.initialize(); 70 | // passport.session(); 71 | -------------------------------------------------------------------------------- /client/.gitignore: -------------------------------------------------------------------------------- 1 | # See https://help.github.com/articles/ignoring-files/ for more about ignoring files. 2 | 3 | # dependencies 4 | /node_modules 5 | /.pnp 6 | .pnp.js 7 | 8 | 9 | 10 | # testing 11 | /coverage 12 | 13 | # production 14 | /build 15 | 16 | # misc 17 | .DS_Store 18 | .env 19 | .env.local 20 | .env.development.local 21 | .env.test.local 22 | .env.production.local 23 | 24 | npm-debug.log* 25 | yarn-debug.log* 26 | yarn-error.log* 27 | -------------------------------------------------------------------------------- /client/README.md: -------------------------------------------------------------------------------- 1 | _*NOTE: IF YOU FACE ANY ERROR, CHALLENGE OR HAVE ANY DOUBT, JUST CREATE AN PR(HIGHLY RECOMMENDED) OR EMAIL ME, I WILL RESPOND AS SOON AS POSSIBLE*_ 2 | 3 | *DON'T FORGET TO `STAR` THE REPO AND FOLLOW [ME](https://github.com/prashant0664)🥹[PRASHANT0664](https://github.com/prashant0664)* 4 | 5 | ## To setup .env file: 6 | 7 | ``.env`` file contains only two values: 8 | 9 | ``` 10 | REACT_APP_BACKEND_URL="http://localhost:5002" 11 | REACT_APP_FRONTEND_URL=http://localhost:3000 12 | ``` 13 | 14 | - `REACT_APP_BACKEND_URL` contain the backend URL 15 | - `REACT_APP_FRONTEND_URL` contain the frontend URL 16 | 17 | # Getting Started with Create React App 18 | 19 | This project was bootstrapped with [Create React App](https://github.com/facebook/create-react-app). 20 | 21 | ## Available Scripts 22 | 23 | In the project directory, you can run: 24 | 25 | ### `npm start` 26 | 27 | Runs the app in the development mode.\ 28 | Open [http://localhost:3000](http://localhost:3000) to view it in your browser. 29 | 30 | The page will reload when you make changes.\ 31 | You may also see any lint errors in the console. 32 | 33 | ### `npm test` 34 | 35 | Launches the test runner in the interactive watch mode.\ 36 | See the section about [running tests](https://facebook.github.io/create-react-app/docs/running-tests) for more information. 37 | 38 | ### `npm run build` 39 | 40 | Builds the app for production to the `build` folder.\ 41 | It correctly bundles React in production mode and optimizes the build for the best performance. 42 | 43 | The build is minified and the filenames include the hashes.\ 44 | Your app is ready to be deployed! 45 | 46 | See the section about [deployment](https://facebook.github.io/create-react-app/docs/deployment) for more information. 47 | 48 | ### `npm run eject` 49 | 50 | **Note: this is a one-way operation. Once you `eject`, you can't go back!** 51 | 52 | If you aren't satisfied with the build tool and configuration choices, you can `eject` at any time. This command will remove the single build dependency from your project. 53 | 54 | Instead, it will copy all the configuration files and the transitive dependencies (webpack, Babel, ESLint, etc) right into your project so you have full control over them. All of the commands except `eject` will still work, but they will point to the copied scripts so you can tweak them. At this point you're on your own. 55 | 56 | You don't have to ever use `eject`. The curated feature set is suitable for small and middle deployments, and you shouldn't feel obligated to use this feature. However we understand that this tool wouldn't be useful if you couldn't customize it when you are ready for it. 57 | 58 | ## Learn More 59 | 60 | You can learn more in the [Create React App documentation](https://facebook.github.io/create-react-app/docs/getting-started). 61 | 62 | To learn React, check out the [React documentation](https://reactjs.org/). 63 | 64 | ### Code Splitting 65 | 66 | This section has moved here: [https://facebook.github.io/create-react-app/docs/code-splitting](https://facebook.github.io/create-react-app/docs/code-splitting) 67 | 68 | ### Analyzing the Bundle Size 69 | 70 | This section has moved here: [https://facebook.github.io/create-react-app/docs/analyzing-the-bundle-size](https://facebook.github.io/create-react-app/docs/analyzing-the-bundle-size) 71 | 72 | ### Making a Progressive Web App 73 | 74 | This section has moved here: [https://facebook.github.io/create-react-app/docs/making-a-progressive-web-app](https://facebook.github.io/create-react-app/docs/making-a-progressive-web-app) 75 | 76 | ### Advanced Configuration 77 | 78 | This section has moved here: [https://facebook.github.io/create-react-app/docs/advanced-configuration](https://facebook.github.io/create-react-app/docs/advanced-configuration) 79 | 80 | ### Deployment 81 | 82 | This section has moved here: [https://facebook.github.io/create-react-app/docs/deployment](https://facebook.github.io/create-react-app/docs/deployment) 83 | 84 | ### `npm run build` fails to minify 85 | 86 | This section has moved here: [https://facebook.github.io/create-react-app/docs/troubleshooting#npm-run-build-fails-to-minify](https://facebook.github.io/create-react-app/docs/troubleshooting#npm-run-build-fails-to-minify) 87 | -------------------------------------------------------------------------------- /client/ads.txt: -------------------------------------------------------------------------------- 1 | google.com, pub-5295167052153349, DIRECT, f08c47fec0942fa0 -------------------------------------------------------------------------------- /client/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "client", 3 | "version": "2.10.0", 4 | "private": true, 5 | "dependencies": { 6 | "@react-oauth/google": "^0.12.1", 7 | "@redux-devtools/extension": "^3.3.0", 8 | "@testing-library/jest-dom": "^6.2.0", 9 | "@testing-library/react": "^14.1.2", 10 | "@testing-library/user-event": "^14.5.2", 11 | "@vercel/analytics": "^1.1.1", 12 | "@vercel/speed-insights": "^1.0.3", 13 | "axios": "^1.6.5", 14 | "dompurify": "^3.0.8", 15 | "html-to-image": "^1.11.11", 16 | "jodit-react": "^4.0.4", 17 | "js-cookie": "^3.0.5", 18 | "jspdf": "^2.5.1", 19 | "react": "^18.2.0", 20 | "react-confirm-alert": "^3.0.6", 21 | "react-dom": "^18.2.0", 22 | "react-hot-toast": "^2.4.1", 23 | "react-icons": "^5.0.1", 24 | "react-infinite-scroll-component": "^6.1.0", 25 | "react-parallax": "^3.5.1", 26 | "react-redux": "^9.1.0", 27 | "react-responsive": "^9.0.2", 28 | "react-router-dom": "^6.21.2", 29 | "react-scripts": "^5.0.1", 30 | "react-spinners": "^0.13.8", 31 | "react-web-share": "^2.0.2", 32 | "reactjs-popup": "^2.0.6", 33 | "redux": "^5.0.1", 34 | "web-vitals": "^3.5.1" 35 | }, 36 | "scripts": { 37 | "start": "react-scripts start", 38 | "build": "react-scripts build", 39 | "test": "react-scripts test", 40 | "eject": "react-scripts eject" 41 | }, 42 | "eslintConfig": { 43 | "extends": [ 44 | "react-app", 45 | "react-app/jest" 46 | ] 47 | }, 48 | "browserslist": { 49 | "production": [ 50 | ">0.2%", 51 | "not dead", 52 | "not op_mini all" 53 | ], 54 | "development": [ 55 | "last 1 chrome version", 56 | "last 1 firefox version", 57 | "last 1 safari version" 58 | ] 59 | } 60 | } 61 | -------------------------------------------------------------------------------- /client/public/Facebook.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Prashant0664/Blog-website/a94bdfd2d9b78309550651e07a5161ada458183e/client/public/Facebook.png -------------------------------------------------------------------------------- /client/public/best.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Prashant0664/Blog-website/a94bdfd2d9b78309550651e07a5161ada458183e/client/public/best.jpg -------------------------------------------------------------------------------- /client/public/google.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Prashant0664/Blog-website/a94bdfd2d9b78309550651e07a5161ada458183e/client/public/google.jpg -------------------------------------------------------------------------------- /client/public/index.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 11 | 12 | 14 | 15 | 16 | 17 | 20 | 21 | 22 | 23 | 24 | 25 | 27 | ALL BLOGS 28 | 29 | 30 | 31 | 32 |
33 | 34 | 35 | -------------------------------------------------------------------------------- /client/public/sp.gif: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Prashant0664/Blog-website/a94bdfd2d9b78309550651e07a5161ada458183e/client/public/sp.gif -------------------------------------------------------------------------------- /client/src/App.css: -------------------------------------------------------------------------------- 1 | .App { 2 | width: 100%; 3 | } 4 | 5 | .auth_wrapper { 6 | position: absolute; 7 | top: 50%; 8 | left: 50%; 9 | width: 300px; 10 | transform: translate(-50%, -50%); 11 | height: fit-content; 12 | border-radius: 3px; 13 | border: 1px solid rgb(181, 179, 179); 14 | background-color: #ffffff; 15 | text-align: center; 16 | box-shadow: 0px 0px 5px 1px black; 17 | } 18 | 19 | .auth_wrapper:hover { 20 | box-shadow: 0px 0px 10px 1px black; 21 | } 22 | 23 | .auth_wrapper .img { 24 | display: flex; 25 | flex-direction: column; 26 | align-items: center; 27 | } 28 | 29 | .auth_wrapper .img span { 30 | font-weight: 500; 31 | font-size: 1.6rem; 32 | font-family: "Times New Roman", Times, serif; 33 | color: rgb(16, 15, 15); 34 | } 35 | 36 | .auth_wrapper .img img { 37 | width: 60px; 38 | object-fit: cover; 39 | margin: auto; 40 | } 41 | 42 | .login-cont { 43 | display: flex; 44 | justify-content: space-around; 45 | } 46 | 47 | .login-cont .tab { 48 | width: 50%; 49 | padding: 20px; 50 | text-align: center; 51 | font-weight: 500; 52 | border-bottom: 1px solid rgb(52, 50, 50); 53 | color: grey; 54 | font-size: small; 55 | cursor: pointer; 56 | } 57 | 58 | .social { 59 | display: flex; 60 | width: 80%; 61 | margin: 10px auto; 62 | border-radius: 3px; 63 | color: white; 64 | background-color: #0058a9; 65 | cursor: pointer; 66 | transition: all 0.1s ease-in-out; 67 | } 68 | 69 | .social:hover { 70 | background-color: #042f58; 71 | } 72 | 73 | .social img { 74 | width: 30px; 75 | height: 30px; 76 | object-fit: cover; 77 | margin: 2px; 78 | } 79 | 80 | .social span { 81 | margin-left: 1.3rem; 82 | font-family: "Times New Roman", Times, serif; 83 | margin-top: 0.6rem; 84 | font-weight: 400; 85 | } 86 | 87 | .or { 88 | margin: auto; 89 | font-size: x-small; 90 | color: grey; 91 | } 92 | 93 | .input { 94 | width: 80%; 95 | margin: 10px auto; 96 | border: 1px solid grey; 97 | padding: 0.7rem; 98 | display: flex; 99 | justify-content: space-around; 100 | border-radius: 3px; 101 | } 102 | 103 | .input input { 104 | border: none; 105 | outline: none; 106 | } 107 | 108 | .dilougue { 109 | width: auto; 110 | background-color: #eee; 111 | color: rgba(0, 0, 0, 0.54); 112 | padding: 1rem; 113 | font-size: xx-small; 114 | font-family: Arial, Helvetica, sans-serif; 115 | } 116 | 117 | .dilougue b { 118 | color: black; 119 | } 120 | 121 | .forget { 122 | width: auto; 123 | background-color: white; 124 | padding: 1rem; 125 | font-size: xx-small; 126 | text-align: center; 127 | font-family: Arial, Helvetica, sans-serif; 128 | } 129 | 130 | .forget a { 131 | color: rgba(0, 0, 0, 0.6); 132 | text-decoration: none; 133 | } 134 | 135 | .forget a:hover { 136 | color: rgba(0, 0, 0, 0.8); 137 | } 138 | 139 | .footer { 140 | height: 70px; 141 | background-color: #042f58; 142 | color: white; 143 | display: grid; 144 | place-items: center; 145 | border-bottom-left-radius: 4px; 146 | border-bottom-right-radius: 4px; 147 | transition: all 0.2s ease-in-out; 148 | cursor: pointer; 149 | } 150 | 151 | .footer:hover { 152 | background-color: #031d35; 153 | font-size: 16px; 154 | } 155 | 156 | /* ----------------home page----------- */ 157 | 158 | .HomePage { 159 | min-height: 100vh; 160 | width: 100%; 161 | background: rgb(218, 218, 218); 162 | } 163 | 164 | .navbar { 165 | background: rgb(243, 243, 243); 166 | width: 100%; 167 | height: 60px; 168 | display: flex; 169 | justify-content: space-between; 170 | align-items: center; 171 | -webkit-box-shadow: 0px 1px 10px 1px black; 172 | -moz-box-shadow: 0px 1px 10px 1px black; 173 | box-shadow: 0px 1px 10px 1px black; 174 | /* rgba(179, 179, 179, 0.38); */ 175 | position: fixed; 176 | top: 0; 177 | z-index: 10000; 178 | } 179 | 180 | .sidebar { 181 | position: absolute; 182 | top: 65px; 183 | right: 0px; 184 | background-color: rgb(239, 239, 239, 0.9); 185 | width: 100%; 186 | display: flex; 187 | justify-content: center; 188 | transition: all 0.3s ease-in-out; 189 | border-top: 0.5px solid rgb(204, 203, 203); 190 | z-index: 11; 191 | color: black; 192 | padding: 1rem; 193 | } 194 | 195 | .sidebar ul { 196 | display: flex; 197 | justify-content: center; 198 | flex-direction: row; 199 | gap: 10px; 200 | text-align: center; 201 | } 202 | 203 | .sidebar ul li { 204 | font-size: xx-small; 205 | list-style-type: none; 206 | cursor: pointer; 207 | font-weight: 400; 208 | } 209 | 210 | .sidebar ul li:first-of-type { 211 | padding: 10px; 212 | background: repeating-linear-gradient(138deg, #5cf5d5, transparent 100px); 213 | } 214 | 215 | .sidebar ul li:last-of-type { 216 | padding: 10px; 217 | background: rgb(255 65 65); 218 | color: black; 219 | border-radius: 0px; 220 | padding: 10px; 221 | } 222 | 223 | .sidebar ul li a { 224 | text-decoration: none; 225 | color: black; 226 | } 227 | 228 | .navbar .img { 229 | width: 60px; 230 | } 231 | 232 | .navbar img { 233 | width: 100%; 234 | height: auto; 235 | object-fit: contain; 236 | } 237 | 238 | .navbar .rocket { 239 | display: flex; 240 | align-items: center; 241 | gap: 16px; 242 | margin: 0.2rem; 243 | position: relative; 244 | left: 0.5rem; 245 | justify-items: center; 246 | cursor: pointer; 247 | z-index: 10000; 248 | } 249 | 250 | .navbar .rocket span { 251 | font-size: medium; 252 | color: rgb(70, 66, 66); 253 | font-weight: 200; 254 | /* margin-bottom: 15px; */ 255 | font-family: "Pacifico", cursive; 256 | cursor: pointer; 257 | } 258 | 259 | .navbar .search { 260 | display: flex; 261 | position: relative; 262 | right: 200px; 263 | align-items: center; 264 | } 265 | 266 | .navbar .search .cat { 267 | margin: 5px; 268 | position: relative; 269 | left: 270px; 270 | font-size: 15px; 271 | /* background-color: red; */ 272 | } 273 | 274 | .navbar .search .cat label { 275 | margin: 5px; 276 | color: rgb(120, 115, 115); 277 | font-weight: 500; 278 | font-family: Arial, Helvetica, sans-serif; 279 | } 280 | 281 | .selectnav { 282 | height: 30px; 283 | font-size: 30px; 284 | border-radius: 7px; 285 | background-color: pink; 286 | } 287 | 288 | .cat { 289 | border: none; 290 | font-size: 30px; 291 | display: none; 292 | } 293 | 294 | .search-result { 295 | z-index: 924792799; 296 | position: relative; 297 | position: absolute; 298 | width: 50%; 299 | background-color: rgb(237, 237, 237); 300 | color: blue; 301 | top: 33px; 302 | margin-left: 20px; 303 | text-decoration: none; 304 | list-style: none; 305 | border-radius: 4px 4px 10% 10%; 306 | border: solid 1px white; 307 | box-shadow: 0px 6px 7px 2px rgb(74, 74, 74); 308 | max-height: 400px; 309 | overflow-y: scroll; 310 | } 311 | 312 | .search-result::-webkit-scrollbar { 313 | display: none; 314 | } 315 | 316 | .search-list { 317 | margin: 10px; 318 | list-style: none; 319 | overflow-x: hidden; 320 | } 321 | 322 | .btnsrch { 323 | display: block; 324 | position: fixed; 325 | z-index: 999999; 326 | height: 100vh; 327 | top: 60px; 328 | width: 100%; 329 | background-color: rgb(73, 73, 73); 330 | opacity: 0.7; 331 | backdrop-filter: blur(5px); 332 | } 333 | 334 | .optionsseleect { 335 | color: red; 336 | overflow-x: hidden; 337 | font-size: 30px; 338 | } 339 | 340 | .blackclr { 341 | color: black; 342 | text-decoration: none; 343 | } 344 | 345 | .lis-item { 346 | display: flex; 347 | gap: 10px; 348 | justify-items: baseline; 349 | border: solid 1px rgb(156, 156, 156); 350 | border-left: none; 351 | border-right: none; 352 | padding-top: 3px; 353 | padding-bottom: 3px; 354 | padding-left: 4px; 355 | text-decoration: none; 356 | color: black; 357 | } 358 | 359 | .noun { 360 | text-decoration: none; 361 | } 362 | 363 | .navbar .search .search_wrap { 364 | padding: 0.2rem; 365 | margin-left: 300px; 366 | 367 | display: flex; 368 | /* background-color: red; */ 369 | } 370 | 371 | .inputnav:focus { 372 | border: 2px solid rgb(1, 1, 139); 373 | } 374 | 375 | /* */ 376 | 377 | .inputnav { 378 | padding: 0.2rem; 379 | margin-left: 300px; 380 | background-color: rgba(209, 203, 203, 0.3); 381 | border-radius: 15px; 382 | display: flex; 383 | width: 400px; 384 | height: 30px; 385 | margin-left: 1rem; 386 | border: none; 387 | outline: none; 388 | position: relative; 389 | /* bottom: 1px; */ 390 | font-size: small; 391 | font-family: cursive; 392 | background: transparent; 393 | background-color: pink; 394 | background-color: rgba(209, 203, 203, 0.3); 395 | border-radius: 15px; 396 | border: solid 2px rgb(214, 214, 214); 397 | } 398 | 399 | .inputnav:hover { 400 | box-shadow: 0px 1px 1px 0px rgb(6, 0, 36); 401 | transition: 300ms; 402 | background-color: white; 403 | border: solid 2px rgba(209, 203, 203, 0.6); 404 | } 405 | 406 | .inputnav:focus { 407 | transition: 340ms; 408 | height: 35px; 409 | border-radius: 10px; 410 | color: black; 411 | border: solid 1px black; 412 | background-color: rgb(255, 255, 255); 413 | /* display: none; */ 414 | } 415 | 416 | .inputnav::placeholder { 417 | position: relative; 418 | color: rgb(86, 86, 86); 419 | font-size: small; 420 | padding: auto; 421 | } 422 | 423 | /* */ 424 | 425 | .imagesearch { 426 | border-radius: 50%; 427 | margin-left: 2px; 428 | width: 35px; 429 | height: 35px; 430 | background-color: rgba(209, 203, 203, 0.3); 431 | display: grid; 432 | border: solid 2px rgb(207, 207, 207); 433 | place-items: center; 434 | z-index: 10000000; 435 | } 436 | 437 | .search .image svg { 438 | fill: black; 439 | } 440 | 441 | .imagesearch { 442 | transition: 400ms; 443 | cursor: pointer; 444 | } 445 | 446 | .imagesearch:hover { 447 | transition: 344ms; 448 | background: transparent; 449 | transform: scale(1.2); 450 | border: none; 451 | } 452 | 453 | .imagesearch:active { 454 | transition: 400ms; 455 | transform: scale(0.7); 456 | } 457 | 458 | .search img { 459 | height: 25px; 460 | width: 25px; 461 | position: relative; 462 | top: 4px; 463 | left: 4px; 464 | object-fit: cover; 465 | } 466 | 467 | .search select { 468 | color: rgb(0, 0, 0); 469 | font-size: 5px; 470 | padding: 3px; 471 | border: none; 472 | outline: 0.2px solid #656565; 473 | border-radius: 15px; 474 | -webkit-box-shadow: 0px 2px 3px 1px rgba(179, 179, 179, 0.38); 475 | -moz-box-shadow: 0px 2px 3px 1px rgba(179, 179, 179, 0.38); 476 | box-shadow: 0px 2px 3px 1px rgba(179, 179, 179, 0.38); 477 | } 478 | 479 | .links { 480 | display: flex; 481 | gap: 5px; 482 | align-items: center; 483 | white-space: nowrap; 484 | justify-content: center; 485 | } 486 | 487 | .links a { 488 | text-decoration: none; 489 | } 490 | 491 | .links a:first-child { 492 | /* position: relative; 493 | right: 60px; 494 | top: 2px; */ 495 | } 496 | 497 | .links a:first-child svg { 498 | color: black; 499 | fill: rgb(0, 0, 0); 500 | } 501 | 502 | .links a:first-child span { 503 | margin: 2px; 504 | color: black; 505 | } 506 | 507 | .links a:first-child span:hover { 508 | margin: 2px; 509 | color: black; 510 | } 511 | 512 | .user { 513 | /* position: relative; */ 514 | right: 35px; 515 | display: flex; 516 | align-items: center; 517 | background: rgb(241, 237, 237); 518 | border-radius: 30px; 519 | padding: 5px; 520 | } 521 | 522 | .user:hover { 523 | background: rgb(231, 224, 224); 524 | } 525 | 526 | .user .user_image { 527 | background-image: url("https://res.cloudinary.com/dttyhvsnv/image/upload/v1677430557/default_pic_gxoa10.png"); 528 | background-position: center center; 529 | border-radius: 50%; 530 | width: 25px; 531 | height: 25px; 532 | overflow: hidden; 533 | background-size: cover; 534 | } 535 | 536 | .write { 537 | display: flex; 538 | justify-items: center; 539 | } 540 | 541 | .add-button { 542 | display: flex; 543 | align-items: "center"; 544 | gap: "4px"; 545 | } 546 | 547 | .BsPencilSquare { 548 | margin: "2px"; 549 | } 550 | 551 | .logout { 552 | font-size: 17px; 553 | font-weight: 400; 554 | /* position: relative; */ 555 | right: 30px; 556 | border-radius: 4px; 557 | color: rgb(0, 0, 0); 558 | display: flex; 559 | justify-items: center; 560 | align-items: center; 561 | /* padding: 5px; */ 562 | /* height: 21px; */ 563 | top: 4px; 564 | text-decoration: none; 565 | transition: all 0.2s ease-in-out; 566 | color: rgb(133, 0, 0); 567 | } 568 | 569 | .user .user_image img { 570 | width: 100%; 571 | background-size: cover; 572 | object-fit: cover; 573 | height: 100%; 574 | } 575 | 576 | .logout:hover { 577 | text-decoration: underline; 578 | color: rgb(209, 0, 0); 579 | } 580 | 581 | .links .write { 582 | font-weight: 400; 583 | /* font-family: cursive; */ 584 | } 585 | 586 | .cc { 587 | margin-top: 57px; 588 | width: 100vw; 589 | height: calc(100vh - 300px); 590 | object-fit: cover; 591 | } 592 | 593 | .errorValidation { 594 | color: red; 595 | font-size: xx-small; 596 | } 597 | 598 | .RegisterSuccess { 599 | color: green; 600 | font-size: xx-small; 601 | } 602 | 603 | @media screen and (max-width: 1231px) { 604 | .navbar { 605 | display: flex; 606 | /* justify-content: space-around; */ 607 | gap: 0; 608 | /* margin-left:10px; */ 609 | width: 100%; 610 | /* position: absolute; */ 611 | } 612 | 613 | .rocket { 614 | margin: 0; 615 | padding: 0; 616 | gap: 0; 617 | } 618 | } 619 | 620 | @media screen and (max-width: 433px) { 621 | .links { 622 | right: 56px; 623 | left: -170px; 624 | /* background-color: pink; */ 625 | /* display: none; */ 626 | } 627 | } 628 | 629 | @media screen and (max-width: 433px) { 630 | .links { 631 | right: 56px; 632 | left: -230px; 633 | /* background-color: pink; */ 634 | /* display: none; */ 635 | } 636 | } 637 | 638 | @media screen and (max-width: 1231px) { 639 | /* .links { 640 | position: relative; 641 | right: 20px; 642 | } */ 643 | 644 | .navbar .search .cat { 645 | display: flex; 646 | gap: 6px; 647 | } 648 | } 649 | 650 | @media screen and (max-width: 1188px) { 651 | } 652 | 653 | @media screen and (max-width: 1153px) { 654 | .navbar .search .search_wrap input { 655 | width: 355px; 656 | } 657 | } 658 | 659 | @media screen and (max-width: 1088px) { 660 | .links { 661 | right: 10%; 662 | } 663 | } 664 | 665 | @media screen and (max-width: 507px) { 666 | .links { 667 | right: 20%; 668 | } 669 | 670 | .pencill { 671 | font-size: 25px; 672 | } 673 | } 674 | 675 | @media screen and (max-width: 472px) { 676 | .links { 677 | right: 29%; 678 | size: 20px; 679 | } 680 | } 681 | 682 | @media screen and (max-width: 1088px) { 683 | .navbar .search { 684 | right: 221px; 685 | } 686 | 687 | .navbar .search .search_wrap input { 688 | width: 331px; 689 | } 690 | 691 | .posts_container .item .right .profile_data { 692 | top: -9px; 693 | } 694 | } 695 | 696 | @media screen and (max-width: 1058px) { 697 | .links a:first-child { 698 | right: 38px; 699 | } 700 | .for-mid-screen{ 701 | margin-left: -100px; 702 | } 703 | .for-mid-screen2{ 704 | margin-left: -150px; 705 | } 706 | .navbar .search .search_wrap input { 707 | width: 319px; 708 | } 709 | } 710 | 711 | @media screen and (max-width: 825px) { 712 | .search { 713 | display: none; 714 | /* background-color: red; */ 715 | } 716 | 717 | .for-mid-screen{ 718 | margin-left: 0px; 719 | } 720 | 721 | .for-mid-screen2{ 722 | margin-left: 0px; 723 | } 724 | .hideonsamllscreen{ 725 | display:none; 726 | } 727 | 728 | .search_wrap { 729 | display: none; 730 | } 731 | 732 | .search-result { 733 | display: none; 734 | } 735 | 736 | .imagesearch { 737 | display: none; 738 | } 739 | 740 | .inputnav { 741 | display: none; 742 | /* background-color: red; */ 743 | } 744 | 745 | .search_wrap { 746 | display: none; 747 | } 748 | 749 | .imagesearch { 750 | display: none; 751 | } 752 | 753 | .write2 { 754 | display: flex; 755 | /* background-color: pink; */ 756 | display: flex; 757 | padding: 0px; 758 | margin: 0px; 759 | justify-items: center; 760 | } 761 | } 762 | -------------------------------------------------------------------------------- /client/src/App.js: -------------------------------------------------------------------------------- 1 | import "./App.css"; 2 | import { Routes, Route } from "react-router-dom"; 3 | import Auth from "./pages/Auth"; 4 | import HomePage from "./pages/HomePage"; 5 | import { useState } from "react"; 6 | import LoggedInRoutes from "./routes/LoggedInRoutes"; 7 | import NotAllowedLoggedInRoutes from "./routes/NotAllowedLoggedInRoutes"; 8 | import { useSelector } from "react-redux"; 9 | import WritePost from "./pages/write/WritePost"; 10 | import Profile from "./pages/Profile"; 11 | import ArticlePage from "./pages/ArticlePage"; 12 | import ResetPassword from "./pages/ResetPassword"; 13 | import ProfileOfOtherUser from "./components/profileOftherUser/ProfileOfOtherUser"; 14 | import { inject } from '@vercel/analytics'; 15 | import Intogoogle from "./components/Auth/intogoogle"; 16 | import Not_found from "./pages/not_found"; 17 | import Editpost from "./pages/write/Editpost"; 18 | import TopicPage from "./pages/TopicPage"; 19 | function App() { 20 | const { user } = useSelector((state) => ({ ...state })); 21 | 22 | inject(); 23 | 24 | return ( 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 | export default App; 52 | -------------------------------------------------------------------------------- /client/src/components/Auth/intogoogle.js: -------------------------------------------------------------------------------- 1 | import React from 'react' 2 | 3 | const Intogoogle = () => { 4 | const gotoauth = () => { 5 | window.location.href = "/auth"; 6 | } 7 | return ( 8 | <> 9 |
10 |
11 |
12 | rfe 13 | Link with Google 14 |

15 | We have found your existing account. By linking your Google account, we will switch your authentication method for accessing your AllBlogs App account from email address to Log in with Google. Moving forward, your credentials, personal information, and two-factor authentication will be managed by your Google account. 16 |

17 |
18 |
19 |
20 |
21 | google 22 | Sign Up with Google 23 |
24 | 25 |
26 | OR 27 |
gotoauth()}> 28 | Cancel 29 |
30 |
31 |
32 | 33 | ) 34 | } 35 | 36 | export default Intogoogle -------------------------------------------------------------------------------- /client/src/components/Navbar.js: -------------------------------------------------------------------------------- 1 | import { TfiWrite } from "react-icons/tfi"; 2 | import { RiShieldUserLine } from "react-icons/ri"; 3 | import { ImMenu } from "react-icons/im"; 4 | import { GiSplitCross } from "react-icons/gi"; 5 | import { BsSearch, BsPencilSquare } from "react-icons/bs"; 6 | 7 | import { Link, useNavigate } from "react-router-dom"; 8 | import { useSelector, useDispatch } from "react-redux"; 9 | 10 | import Cookies from "js-cookie"; 11 | 12 | import { useState } from "react"; 13 | import axios from "axios"; 14 | import { clearCookie } from "../helpers"; 15 | import { useMediaQuery } from "react-responsive"; 16 | import { 17 | searchresult 18 | } from "../helpers/index"; 19 | function Navbar({ postpage }) { 20 | const view1 = useMediaQuery({ query: "(max-width: 564px)" }); 21 | const view2 = useMediaQuery({ query: "(max-width: 420px)" }); 22 | const view3 = useMediaQuery({ query: "(max-width: 825px)" }); 23 | const dispatch = useDispatch(); 24 | const navigate = useNavigate(); 25 | const [searchsel, setsearchsel] = useState(true); 26 | const [sres, searchf] = useState([]); 27 | const [ssw, cssw] = useState(false) 28 | const [scontent, cscontent] = useState("") 29 | 30 | const navigateToHome = () => { 31 | navigate("/"); 32 | }; 33 | const onsearchc = async () => { 34 | try { 35 | if (scontent === "") { 36 | return; 37 | } 38 | const data = await searchresult(scontent) 39 | if (data.msg == []) { 40 | return; 41 | } 42 | searchf(data.msg); 43 | } catch (error) { 44 | // console.log("error in search result"); 45 | } 46 | } 47 | const { user } = useSelector((state) => ({ ...state })); 48 | const handleLoad = () => { 49 | if (user === null || user === undefined) { 50 | fetch(`${process.env.REACT_APP_BACKEND_URL}/login/success`, { 51 | method: "GET", 52 | credentials: "include", 53 | headers: { 54 | Accept: "application/json", 55 | "Content-Type": "application/json", 56 | "Access-Control-Allow-Credentials": true, 57 | }, 58 | }) 59 | .then((response) => { 60 | if (response.status === 200) return response.json(); 61 | throw new Error("Authentication Failed!"); 62 | }) 63 | .then((resObject) => { 64 | dispatch({ type: "LOGIN", payload: resObject.user }); 65 | Cookies.set("user", JSON.stringify(resObject.user), { 66 | expires: 15, 67 | }); 68 | }) 69 | .catch((err) => { 70 | // console.log(err); 71 | }); 72 | } 73 | }; 74 | 75 | const logoutFunction = async (e) => { 76 | e.preventDefault(); 77 | try { 78 | const { data } = await axios.get( 79 | `${process.env.REACT_APP_BACKEND_URL}/logout`, { withCredentials: true } 80 | ); 81 | if (data) { 82 | Cookies.set("user", ""); 83 | Cookies.remove("sessionId"); 84 | clearCookie("sessionId"); 85 | dispatch({ 86 | type: "LOGOUT", 87 | }); 88 | } 89 | } catch (error) { 90 | // console.log("error", error); 91 | } 92 | }; 93 | const select_action = async () => { 94 | setsearchsel((prev) => !prev); 95 | }; 96 | 97 | return ( 98 | 222 | ); 223 | } 224 | 225 | export default Navbar; 226 | -------------------------------------------------------------------------------- /client/src/components/article/article.css: -------------------------------------------------------------------------------- 1 | @import url("https://fonts.googleapis.com/css2?family=Roboto+Slab:wght@600&display=swap"); 2 | @import url("https://fonts.googleapis.com/css2?family=Dancing+Script:wght@700&display=swap"); 3 | @import url("https://fonts.googleapis.com/css2?family=Dancing+Script:wght@700&display=swap"); 4 | 5 | .ArticlePage { 6 | width: 100%; 7 | } 8 | 9 | .article { 10 | background-color: white; 11 | width: 60%; 12 | margin: auto; 13 | position: relative; 14 | top: 95px; 15 | padding: 1rem; 16 | } 17 | 18 | .article .user_info { 19 | display: flex; 20 | gap: 1rem; 21 | } 22 | 23 | .article .user_info .user_image { 24 | height: 60px; 25 | width: 60px; 26 | border-radius: 50%; 27 | overflow: hidden; 28 | flex-shrink: 0; /* Prevent the image from shrinking */ 29 | } 30 | 31 | .article .user_info .user_image img { 32 | width: 100%; 33 | height: 100%; 34 | object-fit: cover; 35 | } 36 | 37 | .article .user_info .user_side { 38 | display: flex; 39 | flex-direction: column; 40 | gap: 2px; 41 | justify-content: center; 42 | width: calc(100% - 80px); 43 | } 44 | @media screen and (max-width: 441px) { 45 | .article .user_info .user_image { 46 | width: 50px; 47 | height: 50px; 48 | } 49 | .article .user_info .user_image img { 50 | width: 100%; 51 | height: 100%; 52 | } 53 | 54 | .userni { 55 | font-size: 17px; 56 | } 57 | .userdi { 58 | /* display: none; */ 59 | /* font-family: 'Lucida Sans', 'Lucida Sans Regular', 'Lucida Grande', 'Lucida Sans Unicode', Geneva, Verdana, sans-serif; */ 60 | font-size: 11px; 61 | /* text-decoration: none; */ 62 | /* line-height: 3px; */ 63 | } 64 | .user_side { 65 | /* font-size:2px; */ 66 | } 67 | } 68 | 69 | @media screen and (max-width: 385px) { 70 | .article .user_info .user_image { 71 | width: 40px; 72 | height: 40px; 73 | } 74 | 75 | .followc { 76 | font-size: 14px; 77 | } 78 | .sizf { 79 | font-size: 12px; 80 | height: 16px; 81 | } 82 | } 83 | .article .user_info .user_side span:first-of-type { 84 | font-weight: 700; 85 | font-family: "Times New Roman", Times, serif; 86 | } 87 | 88 | .article .user_info .user_side span:first-of-type a { 89 | text-decoration: none; 90 | color: black; 91 | font-weight: 700; 92 | } 93 | 94 | .article .user_info .user_side span:first-of-type a:hover { 95 | text-decoration: underline; 96 | } 97 | 98 | .article .user_info .user_side span:last-of-type { 99 | color: grey; 100 | font-size: 1rem; 101 | } 102 | 103 | .user_side { 104 | font-weight: 600; 105 | font-size: 20px; 106 | } 107 | 108 | .article img { 109 | width: 100%; 110 | object-fit: cover; 111 | } 112 | 113 | .article .post_date { 114 | color: rgb(84, 84, 84); 115 | font-size: x-small; 116 | font-weight: 500; 117 | } 118 | 119 | .article .article_title { 120 | margin-top: 1.5rem; 121 | font-size: 35px; 122 | font-family: "Roboto Slab", serif; 123 | font-weight: 700; 124 | } 125 | 126 | .article .article_image { 127 | margin-top: 1.5rem; 128 | } 129 | 130 | .article .article_content { 131 | overflow-x: scroll; 132 | margin-top: 1.5rem; 133 | } 134 | 135 | .article .article_content img { 136 | object-fit: contain; 137 | } 138 | 139 | @media screen and (max-width: 1008px) { 140 | .article { 141 | width: 70%; 142 | } 143 | } 144 | 145 | @media screen and (max-width: 641px) { 146 | .article { 147 | width: 80%; 148 | } 149 | } 150 | 151 | @media screen and (max-width: 500px) { 152 | .article { 153 | width: 85%; 154 | } 155 | } 156 | 157 | @media screen and (max-width: 500px) { 158 | .article { 159 | width: 95%; 160 | } 161 | } 162 | .hidden { 163 | display: none; 164 | } 165 | .com-date-main { 166 | color: grey; 167 | font-size: 12px; 168 | } 169 | .com-img2 { 170 | height: 40px; 171 | width: 40px; 172 | max-width: 40px; 173 | border-radius: 50%; 174 | } 175 | .side-com { 176 | display: flex; 177 | gap: 7px; 178 | } 179 | .com-name-main { 180 | font-size: 14px; 181 | font-weight: 600; 182 | } 183 | .com-head { 184 | cursor: pointer; 185 | font-size: 20px; 186 | font-weight: 700; 187 | font-stretch: extra-expanded; 188 | /* text-transform: ; */ 189 | font-family: "Gill Sans", "Gill Sans MT", Calibri, "Trebuchet MS", sans-serif; 190 | /* font-family: 'Franklin Gothic Medium', 'Arial Narrow', Arial, sans-serif; */ 191 | } 192 | .com-all { 193 | background-color: rgb(220, 242, 251); 194 | margin-top: 5px; 195 | padding: 10px; 196 | border-radius: 10px; 197 | border: solid 1px rgb(121, 121, 162); 198 | } 199 | .com-all:hover { 200 | transition: 200ms; 201 | box-shadow: 0px 0px 2px 1px blue; 202 | } 203 | 204 | .comment-main { 205 | max-height: 350px; 206 | overflow-y: scroll; 207 | } 208 | .comment-main::-webkit-scrollbar { 209 | display: none; 210 | } 211 | .downloadi { 212 | cursor: pointer; 213 | display: flex; 214 | } 215 | .hidden { 216 | display: none; 217 | } 218 | .menus { 219 | border: grey; 220 | box-shadow: 0px 0px 3px 1px rgb(100, 100, 100); 221 | /* max-height:40px; */ 222 | height: fit-content; 223 | padding: 5px; 224 | position: absolute; 225 | /* right:40px; */ 226 | top: 23px; 227 | right: 9px; 228 | margin-right: 0px; 229 | cursor: pointer; 230 | font-size: 13px; 231 | max-width: 150px; 232 | width: fit-content; 233 | z-index: 9999; 234 | } 235 | .buttonr { 236 | background-color: white; 237 | border: none; 238 | } 239 | .flex { 240 | position: relative; 241 | display: flex; 242 | flex-direction: column; 243 | float: right; 244 | justify-content: right; 245 | justify-self: right; 246 | justify-items: right; 247 | } 248 | .myclassr { 249 | border-radius: 10px; 250 | } 251 | .followc { 252 | color: blue; 253 | cursor: pointer; 254 | } 255 | .kbj { 256 | color: red; 257 | cursor: pointer; 258 | } 259 | .formi { 260 | width: 100%; 261 | height: 400px; 262 | border-radius: 10px; 263 | text-align: left; 264 | padding: 0px; 265 | margin-top: 4px; 266 | justify-content: start; 267 | } 268 | .myformclassr { 269 | display: flex; 270 | flex-direction: column; 271 | cursor: pointer; 272 | } 273 | .menubox:hover { 274 | transform: scale(1.1); 275 | } 276 | .bblack { 277 | background-color: black; 278 | } 279 | .com-form { 280 | width: 100%; 281 | } 282 | .com-inp { 283 | margin-left: 10px; 284 | margin-right: 10px; 285 | width: 90%; 286 | padding: 6px; 287 | border-radius: 6px; 288 | min-height: 70px; 289 | } 290 | .com-btn { 291 | width: 90%; 292 | background-color: rgb(180, 224, 255); 293 | border: none; 294 | border-radius: 7px; 295 | font-size: 17px; 296 | cursor: pointer; 297 | padding-top: 3px; 298 | padding-bottom: 3px; 299 | font-weight: 600; 300 | margin: 0px 10px; 301 | } 302 | .com-btn:hover { 303 | background-color: blue; 304 | color: white; 305 | } 306 | -------------------------------------------------------------------------------- /client/src/components/footer/Footer.js: -------------------------------------------------------------------------------- 1 | import React from "react"; 2 | import "./footer.css"; 3 | 4 | function Footer() { 5 | return ( 6 | 92 | ); 93 | } 94 | 95 | export default Footer; 96 | -------------------------------------------------------------------------------- /client/src/components/footer/footer.css: -------------------------------------------------------------------------------- 1 | .desktop-footer { 2 | background: #070c25; 3 | color: white; 4 | padding: 20px 0; 5 | margin-top: 25rem; 6 | width: 100%; 7 | font-size: larger; 8 | } 9 | 10 | .subfot { 11 | color: white; 12 | } 13 | 14 | .footer-container { 15 | max-width: 1200px; 16 | margin: 0 auto; 17 | display: flex; 18 | width: 100%; 19 | justify-content: space-between; 20 | margin-left: 20px; 21 | } 22 | 23 | .footer-column { 24 | width: 20%; 25 | } 26 | 27 | .footer-column h3 { 28 | font-size: 20px; 29 | font-weight: bold; 30 | margin-bottom: 10px; 31 | } 32 | 33 | .footer-column ul { 34 | list-style: none; 35 | margin: 0; 36 | padding: 0; 37 | } 38 | 39 | .footer-column li { 40 | margin-bottom: 5px; 41 | } 42 | 43 | .footer-column a { 44 | text-decoration: none; 45 | font-weight: 400; 46 | font-size: 16px; 47 | color:gray 48 | 49 | } 50 | 51 | .footer-column a:hover { 52 | color:white; 53 | font-size: 20px; 54 | 55 | } 56 | 57 | .footer-info { 58 | margin-top: 40px; 59 | } 60 | 61 | .footer-info p { 62 | font-size: 14px; 63 | color: #e1e1e1; 64 | } 65 | 66 | .footer-container { 67 | display: flex; 68 | width: 100%; 69 | justify-content: center; 70 | } 71 | 72 | .subfot { 73 | transition: 240ms; 74 | } 75 | 76 | .footer-container { 77 | margin: auto; 78 | display: flex; 79 | } 80 | 81 | .subfot:hover { 82 | transition: 300ms; 83 | color: rgb(148, 229, 243); 84 | transform: scale(1.3); 85 | font-size: 14px; 86 | } 87 | 88 | @media (max-width: 767px) { 89 | .footer-container { 90 | flex-wrap: wrap; 91 | justify-content: center; 92 | } 93 | 94 | .footer-column { 95 | width: 40%; 96 | margin-bottom: 20px; 97 | } 98 | } 99 | 100 | .footer-info { 101 | text-align: center; 102 | } 103 | 104 | .footer-info > p{ 105 | padding-bottom: 1rem; 106 | font-size: large; 107 | } 108 | 109 | .footer-info > p > a{ 110 | color: white; 111 | } -------------------------------------------------------------------------------- /client/src/components/home/breaker/Breaker.js: -------------------------------------------------------------------------------- 1 | import React from 'react'; 2 | import './breaker.css' 3 | 4 | function Breaker({ text }) { 5 | return ( 6 |
7 | {text} 8 |
9 | ) 10 | } 11 | 12 | export default Breaker -------------------------------------------------------------------------------- /client/src/components/home/breaker/breaker.css: -------------------------------------------------------------------------------- 1 | .breaker { 2 | margin: .4rem 8rem; 3 | padding: .6rem; 4 | border-bottom: 1px solid black; 5 | font-size: 1.4rem; 6 | font-weight: 700; 7 | /* top:60px; */ 8 | /* margin-top: 390px; */ 9 | /* position: relative; */ 10 | color:black; 11 | } -------------------------------------------------------------------------------- /client/src/components/home/card/Card.js: -------------------------------------------------------------------------------- 1 | import React from "react"; 2 | import "./card.css"; 3 | import { Parallax } from 'react-parallax'; 4 | import { useMediaQuery } from "react-responsive"; 5 | import { useNavigate } from "react-router-dom"; 6 | 7 | function Card({ setmpost, setflag, flag, mpost }) { 8 | const view1 = useMediaQuery({ query: "(max-width: 564px)" }); 9 | const navigate = useNavigate(); 10 | const arr = ["tech", "lifestyle", "food", "travelling"]; 11 | 12 | return ( 13 |
14 | 15 |
16 | 22 |
23 | {mpost !== "all" && ( 24 |
{ 25 | setmpost("all"); setflag(false); navigate("/"); window.location.reload(); 26 | }}> 27 | All 28 |
29 | )} 30 | {arr.map((item, index) => ( 31 |
{ 32 | setmpost(item); setflag(true); navigate(`/topic/${item}`); window.location.reload(); 33 | }}> 34 | {item.toUpperCase()} 35 |
36 | ))} 37 |
38 |
39 | 40 |

41 | {view1 42 | ? "Whether you're a seasoned blogger or just starting out, our website is the ultimate platform to showcase your projects through engaging blogs." 43 | : "Whether you're a seasoned blogger or just starting out, our website is the ultimate platform to showcase your projects through engaging blogs. From sharing creative endeavors to professional projects, our platform offers a seamless experience. Join our community and bring your ideas to life."} 44 |

45 |
46 |
47 | ); 48 | } 49 | 50 | export default Card; 51 | -------------------------------------------------------------------------------- /client/src/components/home/card/card.css: -------------------------------------------------------------------------------- 1 | @import url("https://fonts.googleapis.com/css2?family=Poppins:wght@200;300;400&display=swap"); 2 | 3 | .intro_section { 4 | width: 100%; 5 | margin: auto; 6 | text-align: center; 7 | font-family: "Poppins", sans-serif; 8 | font-size: 14px; 9 | color: rgb(78, 78, 78); 10 | } 11 | 12 | .parallax-img { 13 | top: 65px; 14 | height: 400px; 15 | } 16 | 17 | .card-text { 18 | margin: auto; 19 | color: rgb(255, 255, 255); 20 | font-size: 22px; 21 | padding: 20px; 22 | text-shadow: 0px 1px 6px rgba(0, 0, 0, 0.8); 23 | } 24 | 25 | .bgimg { 26 | height: 100%; 27 | width: 100%; 28 | object-fit: cover; 29 | } 30 | 31 | .dropbtn { 32 | background-color: rgba(0, 0, 0, 0.6); 33 | color: white; 34 | padding: 12px 24px; 35 | font-size: 18px; 36 | border: none; 37 | border-radius: 50px; 38 | display: flex; 39 | align-items: center; 40 | justify-content: center; 41 | gap: 10px; 42 | transition: background-color 0.3s ease; 43 | cursor: pointer; 44 | } 45 | 46 | .dropdown { 47 | position: relative; 48 | display: inline-block; 49 | margin: 20px; 50 | } 51 | 52 | .dropdown-content { 53 | display: none; 54 | position: absolute; 55 | background-color: #f1f1f1; 56 | min-width: 160px; 57 | border-radius: 8px; 58 | box-shadow: 0px 8px 16px rgba(0, 0, 0, 0.2); 59 | z-index: 1; 60 | } 61 | 62 | .dropdown-content .di { 63 | color: black; 64 | padding: 12px; 65 | text-decoration: none; 66 | cursor: pointer; 67 | transition: background-color 0.3s ease; 68 | } 69 | 70 | .dropdown-content .di:hover { 71 | background-color: #ddd; 72 | } 73 | 74 | .dropdown:hover .dropdown-content { 75 | display: block; 76 | } 77 | 78 | .dropdown:hover .dropbtn { 79 | background-color: #cecece; 80 | color: black; 81 | } 82 | 83 | /* Responsive Styling */ 84 | @media screen and (max-width: 733px) { 85 | .card-text { 86 | font-size: 18px; 87 | } 88 | } 89 | 90 | @media screen and (max-width: 564px) { 91 | .parallax-img { 92 | height: 300px; 93 | } 94 | 95 | .card-text { 96 | font-size: 16px; 97 | padding: 10px; 98 | } 99 | 100 | .dropbtn { 101 | font-size: 16px; 102 | padding: 10px 20px; 103 | } 104 | } 105 | -------------------------------------------------------------------------------- /client/src/components/home/post/PostCard.js: -------------------------------------------------------------------------------- 1 | import React, { useState, useEffect } from "react"; 2 | import { useDispatch, useSelector } from "react-redux"; 3 | import { BsDownload, BsHeartFill, BsThreeDotsVertical } from "react-icons/bs" 4 | import { Link, useNavigate } from "react-router-dom"; 5 | import { toast } from 'react-hot-toast'; 6 | import Popup from 'reactjs-popup'; 7 | import { RWebShare } from "react-web-share"; 8 | import { decreastLike, deletelikes, likes, increaseLike, bookmark, deletebookmark, deletepost, checkbookmark, checklikes, } from "../../../helpers"; 9 | import { CiBookmark, CiHeart } from "react-icons/ci"; 10 | import { BsFillBookmarkFill } from "react-icons/bs"; 11 | function PostCard({ post, type }) { 12 | const { user } = useSelector((state) => ({ ...state })); 13 | const dispatch = useDispatch(); 14 | const notify = (notice) => toast({ notice }); 15 | const [book, setbook] = React.useState(false); 16 | const [heart, setheart] = useState(false); 17 | const [menus, showmenu] = React.useState(false); 18 | const [sbook, setsbook] = React.useState(true); 19 | const [sbook2, setsbook2] = React.useState(true); 20 | // console.log(post); . 21 | const utcTimeString = (post && post.createdAt)?post.createdAt:"N/A"; 22 | const date = new Date(utcTimeString); 23 | const localTimeString = date.toLocaleDateString(); 24 | const navigate = useNavigate() 25 | React.useEffect(() => { 26 | x(); 27 | }, []) 28 | const x = async () => { 29 | try { 30 | const checkbook = await checkbookmark(post._id, user.id); 31 | if (checkbook.msg === "ok") { 32 | setbook(true); 33 | setsbook(true); 34 | setsbook2(false); 35 | } 36 | else{ 37 | setsbook(false); 38 | 39 | } 40 | const checklike = await checklikes(post._id, user.id); 41 | if (checklike.msg === "ok") { 42 | setheart(true); 43 | } 44 | } catch (error) { 45 | // console.log(error); 46 | } 47 | } 48 | const navigateToArticle = () => { 49 | navigate(`/article/${post._id}`) 50 | } 51 | const handleDown = () => { 52 | navigate(`/article/${post._id}`) 53 | } 54 | const handleheart = async () => { 55 | try { 56 | await increaseLike(post._id); 57 | await addheart(); 58 | user.likes.push(post._id); 59 | dispatch({ type: "LIKE", payload: user.likes }); 60 | return; 61 | } catch (error) { 62 | // console.log(error); 63 | alert("ERROR on likening"); 64 | return; 65 | } 66 | } 67 | const editposts = async () => { 68 | navigate(`/edit/${post._id}`) 69 | } 70 | const addheart = async () => { 71 | try { 72 | const data = await likes( 73 | post._id, 74 | user.id 75 | ) 76 | if (data.msg === "exists") { 77 | setheart(true); 78 | notify("Liked already") 79 | } 80 | else if ( 81 | data.msg === "ok" 82 | ) { 83 | notify("liked") 84 | setheart(true); 85 | } 86 | else { 87 | notify("An Error Occurred while likening") 88 | } 89 | } catch (error) { 90 | } 91 | } 92 | const handleheartfalse = async () => { 93 | try { 94 | await decreastLike(post._id); 95 | await removeheart(); 96 | setheart(false); 97 | return; 98 | } catch (error) { 99 | // console.log(error); 100 | alert("ERROR on likening"); 101 | return; 102 | } 103 | } 104 | const removeheart = async () => { 105 | try { 106 | const data = await deletelikes( 107 | post._id, 108 | user.id 109 | ) 110 | if (data.msg === 'deleted') { 111 | 112 | } 113 | else { 114 | alert("ERROR OCCURRED"); 115 | } 116 | } catch (error) { 117 | // console.log("ERROR REMOVING likes"); 118 | } 119 | } 120 | const setbookmark = async () => { 121 | try { 122 | const data = await bookmark( 123 | post._id, 124 | user.id 125 | ) 126 | if (data.msg === "exists") { 127 | notify("Bookmark Already Added") 128 | } 129 | else if ( 130 | data.msg === "ok" 131 | ) { 132 | notify("Bookmark Added") 133 | } 134 | else { 135 | // console.log("error in bb"); 136 | notify("An Error Occurred while Adding Bookmark") 137 | } 138 | setbook(true); 139 | setsbook(true); 140 | setsbook2(false); 141 | } catch (error) { 142 | notify("Error adding bookmark"); 143 | } 144 | } 145 | const dellbookmarl = async () => { 146 | try { 147 | const data = await deletebookmark( 148 | post._id, 149 | user.id 150 | ) 151 | if (data.msg === 'deleted') { 152 | setbook(false); 153 | setsbook(false); 154 | } 155 | else { 156 | setsbook(false); 157 | alert("Does not exists"); 158 | return; 159 | } 160 | setsbook2(true); 161 | 162 | 163 | } catch (error) { 164 | // console.log("ERROR REMOVING BOOKMARK"); 165 | } 166 | } 167 | 168 | const deleteposts = async (postid) => { 169 | try { 170 | const data = await deletepost(postid, user.id) 171 | window.location.reload(); 172 | } catch (error) { 173 | // console.log("error in deleting") 174 | } 175 | } 176 | return ( 177 |
178 |
179 | 180 |
181 |
182 |
183 |

184 | {post && post.title} 185 |

186 | { 187 | (!type || type != "powner") ? 188 | <> 189 |
190 | {post && post.views} 191 | 192 | 193 | 194 |   195 | | 196 |   197 |
198 | {/*
*/} 199 | {post && post.likes ? post.likes : 0} 200 |
{"" + ""} 201 |
202 |   203 | 204 | {/*
*/} 205 |
206 |
207 | 208 | : 209 | <> 210 | 211 | 212 | } 213 |
214 |
215 | 216 |
217 | {post && post.description} 218 |
219 | {(type && type === "powner") 220 | ? 221 | <> 222 |
223 |
224 | {/*
*/} 225 | {post && post.views} 226 | 227 | 228 |
{"" + ""} 229 |
230 | {/*
*/} 231 |
232 | | 233 |
234 | {/*
*/} 235 | {post && post.likes ? post.likes : 0} 236 |
{"" + ""} 237 |
238 |   239 | 240 | {/*
*/} 241 |
242 | | 243 |
editposts(post._id)}> Edit
244 | | 245 |
deleteposts(post._id)}> Delete
246 | {/*
*/} 247 | {/*
*/} 248 |
249 | 256 | console.log("shared successfully!") 257 | } 258 | > 259 |
260 | Share 261 |
262 |
263 | 264 | 265 | : 266 | <> 267 |
268 |
269 | {!user ? 270 | 271 | 272 | 273 | : 274 | (post && post.user && post.user._id) && 275 | 276 | 277 | 278 | } 279 | {post && post.user ? 280 | 281 | : 282 | <>} 283 |
284 |
285 | {!user ? 286 | {(post && post.user && post.user.name )? post.user.name : "N/A"} 287 | : 288 | {post && post.user.name} 289 | } 290 | {localTimeString} 291 |
292 |
293 | {!heart ? { if (!user) { navigate("/auth") } else handleheart() }} /> : 294 | { if (!user) { navigate("/auth") } else handleheartfalse() }} width="25px" height="25px" viewBox="0 0 24 24" fill="none" xmlns="http://www.w3.org/2000/svg" stroke="#000000" strokeWidth="0.648"> 295 | } 296 | {type === "main" ? 297 | <> 298 | 299 | {sbook2 && { 300 | if (!user) { navigate("/auth") } else { setbookmark(); setsbook2(false) } 301 | }} 302 | /> 303 | } 304 | {!sbook2 && { if (!user) { navigate("/auth") } else dellbookmarl(); setsbook2(true) }} /> 306 | } 307 | 308 | 309 | : 310 | <> 311 | } 312 | {/* hhhhkk */} 313 | {type && type === "profile_bookmark" ? 314 | <> 315 | {!sbook ? 316 | <> 317 | { 318 | setbookmark(); 319 | }} 320 | /> 321 | 322 | : 323 | <> 324 | { dellbookmarl(); }} /> 326 | 327 | 328 | } 329 | 330 | : 331 | <> 332 | 333 | } 334 |
335 | { 336 | showmenu(false) 337 | }} onMouseOver={() => { showmenu(true); }} /> 338 |
{ showmenu(true); }} 339 | onMouseLeave={() => { 340 | showmenu(false) 341 | }}> 342 | 349 | console.log("shared successfully!") 350 | } 351 | > 352 | 353 | 354 |
355 | 356 | Share 357 |
358 | 359 |
360 |
361 | 362 |
363 |
364 | {/* */} 365 | 366 |
367 | 368 | {/* */} 369 | {/*
370 | 371 |
*/} 372 |
373 | } 374 |
375 |
376 | ); 377 | } 378 | 379 | export default PostCard; 380 | -------------------------------------------------------------------------------- /client/src/components/home/post/Posts.js: -------------------------------------------------------------------------------- 1 | import React, { useEffect, useState } from "react"; 2 | import "./post.css"; 3 | import { getAllPost } from "../../../helpers"; 4 | import PostCard from "./PostCard"; 5 | import InfiniteScroll from "react-infinite-scroll-component"; 6 | import { DotLoader } from "react-spinners"; 7 | import { useDispatch, useSelector } from "react-redux"; 8 | import { PuffLoader } from "react-spinners"; 9 | // import { Puff} 10 | 11 | function Posts({category}) { 12 | const LIMIT = 6; 13 | const [totalPosts, setTotalPost] = useState(0); 14 | const [activePage, setActivePage] = useState(1); 15 | const dispatch = useDispatch() 16 | const { posts } = useSelector(state => ({ ...state })) 17 | useEffect(() => { 18 | if (posts.length === 0) { 19 | x(); 20 | } 21 | }, []); 22 | 23 | const x = async () => { 24 | try { 25 | // setUser(datad.user) 26 | const data = await getAllPost(activePage, LIMIT, category); 27 | dispatch({ type: "SET_POSTS", payload: data.posts }) 28 | setActivePage(activePage + 1); 29 | setTotalPost(data.total); 30 | } catch (error) { 31 | // console.log(error); 32 | throw error; 33 | } 34 | }; 35 | 36 | return ( 37 | 43 | 44 |
45 | } 46 | endMessage={ 47 | posts.length && ( 48 |

49 | Looks like You have reached to the end.?! 50 |

51 | ) 52 | } 53 | > 54 | {(posts && posts.length === 0) ? ( 55 |
56 | 57 |
58 | ) : ( 59 | 60 |
61 | {posts.map((post, i) => { 62 | 63 | return ; 65 | })} 66 |
67 | )} 68 | 69 | ); 70 | } 71 | 72 | export default Posts; -------------------------------------------------------------------------------- /client/src/components/home/post/post.css: -------------------------------------------------------------------------------- 1 | .posts_container { 2 | margin: 1.2rem 8rem; 3 | display: grid; 4 | grid-template-columns: repeat(2, 1fr); 5 | grid-auto-flow: auto; 6 | grid-gap: 1.2rem; 7 | } 8 | 9 | .item { 10 | background-color: whitesmoke; 11 | } 12 | 13 | .posts_container .item { 14 | border: .5px solid rgb(206, 200, 200); 15 | display: flex; 16 | gap: 2.3rem; 17 | transition: all 0.2s; 18 | border-radius: 6px; 19 | overflow: hidden; 20 | -webkit-box-shadow: 0px 1px 12px -2px rgba(0, 0, 0, 0.69); 21 | -moz-box-shadow: 0px 1px 12px -2px rgba(0, 0, 0, 0.69); 22 | box-shadow: 0px 1px 12px -2px rgba(0, 0, 0, 0.69); 23 | } 24 | 25 | .profile_data24 { 26 | /* position: relative; */ 27 | /* display: flex; */ 28 | /* gap:3px; */ 29 | text-decoration: none; 30 | /* margin-top: 10px; */ 31 | /* float: left; */ 32 | /* bottom:0px; */ 33 | /* top:60px; */ 34 | /* left:2px; */ 35 | /* font-size: 15px; */ 36 | } 37 | 38 | .view_post_profile { 39 | font-size: 16px; 40 | color: grey; 41 | display: flex; 42 | justify-items: center; 43 | align-items: center; 44 | } 45 | 46 | .sharepostmy { 47 | width: 100%; 48 | text-align: center; 49 | background-color: rgb(218, 236, 252); 50 | border-radius: 40px; 51 | } 52 | 53 | .sharepostmy:hover { 54 | cursor: pointer; 55 | transform: scale(1.1); 56 | box-shadow:black; 57 | } 58 | 59 | .bluev { 60 | color: blue; 61 | text-decoration: none; 62 | } 63 | 64 | .imgscp { 65 | border: solid 1px black; 66 | border-radius: 50%; 67 | } 68 | 69 | .post_date { 70 | font-size: 20px; 71 | color: rgb(93, 93, 93); 72 | } 73 | 74 | .posts_container .item:hover { 75 | transform: scale(1.02); 76 | -webkit-box-shadow: 1px 2px 6px 0px rgba(161, 152, 161, 1); 77 | -moz-box-shadow: 1px 2px 6px 0px rgba(161, 152, 161, 1); 78 | box-shadow: 1px 2px 6px 0px rgba(161, 152, 161, 1); 79 | } 80 | 81 | .posts_container .item .left { 82 | width: 40%; 83 | height: 250px; 84 | overflow: hidden; 85 | } 86 | 87 | .backwhi { 88 | background: linear-gradient(to bottom, white, red); 89 | /* z-index: 999999; */ 90 | height: 200px; 91 | display: block; 92 | position: absolute; 93 | overflow: hidden; 94 | } 95 | 96 | .posts_container .item .right { 97 | width: 60%; 98 | display: flex; 99 | flex-direction: column; 100 | } 101 | 102 | .posts_container .item .right .title { 103 | color: black; 104 | font-size: 1rem; 105 | line-height: 1.25; 106 | cursor: pointer; 107 | flex: 4; 108 | } 109 | 110 | .posts_container .item .right .title:hover { 111 | color: rgb(71, 65, 65); 112 | } 113 | 114 | .posts_container .item .right .description { 115 | padding-right: 5px; 116 | color: #888; 117 | font-size: 0.9rem; 118 | line-height: 1.5; 119 | font-weight: 400; 120 | flex: 4; 121 | overflow: hidden; 122 | margin-top: -50px; 123 | } 124 | 125 | .posts_container .item .right .profile_data { 126 | display: flex; 127 | padding: 5px; 128 | gap: 0.6rem; 129 | position: relative; 130 | top: -15px; 131 | left: -5px; 132 | cursor: pointer; 133 | flex: 2; 134 | align-items: center; 135 | 136 | } 137 | 138 | 139 | .posts_container .item .right .profile_data .user_image { 140 | background: white; 141 | height: 45px; 142 | width: 45px; 143 | border-radius: 50%; 144 | overflow: hidden; 145 | } 146 | 147 | .posts_container .item .right .profile_data .user_middle { 148 | display: flex; 149 | flex-direction: column; 150 | gap: 0.2rem; 151 | } 152 | 153 | .posts_container .item .right .profile_data .user_middle span:first-of-type { 154 | font-family: "Times New Roman", Times, serif; 155 | } 156 | 157 | .posts_container .item .right .profile_data .user_middle span:first-of-type a { 158 | text-decoration: none; 159 | color: #0e0d0d; 160 | } 161 | 162 | .posts_container .item .right .profile_data .user_middle span:first-of-type a:hover { 163 | text-decoration: underline; 164 | color: #001392; 165 | } 166 | 167 | .posts_container .item .right .profile_data .user_middle span:last-of-type { 168 | color: #7d7d7d; 169 | font-size: small; 170 | } 171 | 172 | .posts_container .item .right .profile_data .savePost { 173 | position: absolute; 174 | right: 10px; 175 | top: 8px; 176 | } 177 | 178 | .posts_container .item .right .profile_data .savePost svg { 179 | fill: black; 180 | } 181 | .shareprofile{ 182 | display: flex; 183 | justify-content: center; 184 | justify-items: center; 185 | align-content: center; 186 | } 187 | .menus2{ 188 | border:grey; 189 | box-shadow: 0px 0px 3px 1px rgb(100, 100, 100); 190 | /* max-height:40px; */ 191 | height:fit-content; 192 | padding:5px; 193 | position: absolute; 194 | /* right:40px; */ 195 | bottom:23px; 196 | right:9px; 197 | margin-right:0px; 198 | cursor:pointer; 199 | font-size:13px; 200 | max-width:150px; 201 | width:fit-content; 202 | z-index:9999; 203 | background-color: white; 204 | } 205 | 206 | .posts_container .item .right .profile_data .savePost svg:hover { 207 | fill: rgb(90, 82, 82); 208 | } 209 | 210 | .posts_container .item .right .profile_data .user_image img { 211 | height: 100%; 212 | width: 100%; 213 | object-fit: cover; 214 | } 215 | 216 | .posts_container .item .left img { 217 | width: 100%; 218 | height: 100%; 219 | object-fit: cover; 220 | } 221 | 222 | .loader-container { 223 | display: flex; 224 | justify-content: center; 225 | align-items: center; 226 | height: 50px; 227 | } 228 | 229 | .post_loader { 230 | display: flex; 231 | justify-content: center; 232 | align-items: center; 233 | height: 50px; 234 | margin-top: 20px; 235 | } 236 | 237 | @media screen and (max-width: 1153px) { 238 | .posts_container .item .right .title { 239 | font-size: 1rem; 240 | } 241 | 242 | .posts_container .item .left { 243 | height: 225px; 244 | } 245 | } 246 | 247 | @media screen and (max-width: 1058px) { 248 | .posts_container .item { 249 | gap: 0.7rem; 250 | } 251 | 252 | .posts_container .item .right .title { 253 | font-size: 1rem; 254 | } 255 | } 256 | 257 | @media screen and (max-width: 1025px) { 258 | .posts_container { 259 | margin: 0.2rem 3rem; 260 | } 261 | 262 | .posts_container .item .right .title { 263 | font-size: 1rem; 264 | } 265 | 266 | .breaker { 267 | margin: 0.4rem 3rem; 268 | } 269 | } 270 | 271 | @media screen and (max-width: 966px) { 272 | .posts_container .item .left { 273 | height: 213px; 274 | } 275 | 276 | .posts_container .item .right .title { 277 | font-size: 0.98rem; 278 | } 279 | } 280 | 281 | @media screen and (max-width: 936px) { 282 | .posts_container .item .right .profile_data { 283 | top: -11px; 284 | } 285 | 286 | .posts_container .item .right .profile_data .user_image { 287 | height: 32px; 288 | width: 32px; 289 | } 290 | 291 | .posts_container .item .right .profile_data { 292 | padding: 2px; 293 | } 294 | 295 | .posts_container .item .left { 296 | height: 198px; 297 | } 298 | } 299 | 300 | @media screen and (max-width: 872px) { 301 | .posts_container { 302 | margin: 0.2rem 2rem; 303 | } 304 | } 305 | 306 | @media screen and (max-width: 820px) { 307 | .posts_container .item .right .description { 308 | font-size: 0.7rem; 309 | line-height: 1.1; 310 | } 311 | 312 | .breaker { 313 | margin: 0.4rem 2rem; 314 | } 315 | } 316 | 317 | @media screen and (max-width: 730px) { 318 | .posts_container .item .right .title { 319 | font-size: 0.79rem; 320 | } 321 | 322 | .links a:first-child span { 323 | font-size: small; 324 | font-weight: 900; 325 | } 326 | 327 | .posts_container .item .right .description { 328 | font-size: 0.4rem; 329 | } 330 | 331 | .posts_container { 332 | margin: 0.2rem 1rem; 333 | } 334 | 335 | .breaker { 336 | margin: 0.4rem 1rem; 337 | } 338 | 339 | .posts_container .item .right .profile_data .savePost { 340 | position: absolute; 341 | right: 11px; 342 | top: 8px; 343 | } 344 | } 345 | 346 | @media screen and (max-width: 682px) { 347 | .posts_container .item .right .title { 348 | font-size: 0.7rem; 349 | margin-top: 13px; 350 | } 351 | 352 | .posts_container .item .right .description { 353 | font-size: 0.4rem; 354 | position: relative; 355 | bottom: 14px; 356 | } 357 | } 358 | 359 | @media screen and (min-width: 682px) { 360 | .posts_container .item .right .title { 361 | margin-top: 13px; 362 | } 363 | } 364 | 365 | @media screen and (min-width: 1160px) { 366 | .posts_container .item .right .title { 367 | font-size: 1.11rem; 368 | } 369 | } 370 | 371 | @media screen and (min-width: 1212px) { 372 | .posts_container .item .right .title { 373 | font-size: 1.22rem; 374 | } 375 | } 376 | 377 | @media screen and (min-width: 1252px) { 378 | .posts_container .item .right .title { 379 | font-size: 1.32rem; 380 | } 381 | } 382 | 383 | @media screen and (max-width: 650px) { 384 | .posts_container .item { 385 | flex-direction: column; 386 | } 387 | 388 | .posts_container .item .left { 389 | width: 100%; 390 | height: 250px; 391 | } 392 | 393 | .posts_container .item .right { 394 | width: 100%; 395 | } 396 | 397 | .posts_container .item .right .title { 398 | font-size: 1.21rem; 399 | margin-top: -7px; 400 | line-height: 20px; 401 | padding: 5px; 402 | } 403 | 404 | .posts_container .item .right .description { 405 | font-size: 1rem; 406 | padding: 5px; 407 | margin-top: 17px; 408 | } 409 | 410 | .posts_container .item .right .profile_data { 411 | left: 0px; 412 | padding: 5px; 413 | } 414 | 415 | z .posts_container .item .right .profile_data .user_image { 416 | height: 45px; 417 | width: 45px; 418 | } 419 | 420 | .posts_container .item .right .profile_data .savePost { 421 | right: 25px; 422 | top: 13px; 423 | } 424 | 425 | .posts_container { 426 | grid-gap: 0.8rem; 427 | } 428 | } 429 | 430 | @media screen and (max-width: 590px) { 431 | .posts_container .item .left { 432 | width: 100%; 433 | height: 185px; 434 | } 435 | 436 | .posts_container .item .right .title { 437 | font-size: 1rem; 438 | line-height: 17px; 439 | } 440 | } 441 | 442 | @media screen and (max-width: 524px) { 443 | .posts_container .item .left { 444 | width: 100%; 445 | height: 149px; 446 | } 447 | 448 | .posts_container .item .right .description { 449 | font-size: 0.7rem; 450 | } 451 | 452 | .posts_container .item .right .profile_data .user_image { 453 | height: 35px; 454 | width: 35px; 455 | } 456 | } 457 | 458 | @media screen and (max-width: 520px) { 459 | .posts_container .item .right .title { 460 | font-size: 0.89rem; 461 | } 462 | 463 | .hidesmall { 464 | display: none; 465 | } 466 | } 467 | 468 | @media screen and (max-width: 437px) { 469 | .posts_container .item .left { 470 | width: 100%; 471 | height: 120px; 472 | } 473 | 474 | .posts_container .item .right .title { 475 | font-size: 0.82rem; 476 | } 477 | } 478 | 479 | @media screen and (max-width: 420px) { 480 | .posts_container { 481 | grid-template-columns: 1fr; 482 | } 483 | 484 | .posts_container .item .left { 485 | width: 100%; 486 | height: 194px; 487 | } 488 | 489 | .posts_container .item .right .title { 490 | font-size: 1.2rem; 491 | line-height: 27px; 492 | } 493 | 494 | .posts_container .item .right .profile_data { 495 | padding: 13px; 496 | } 497 | 498 | .posts_container .item .right .profile_data .savePost { 499 | right: 40px; 500 | top: 15px; 501 | } 502 | 503 | .posts_container p b { 504 | font-size: xx-small; 505 | } 506 | 507 | .posts_container .item { 508 | -webkit-box-shadow: 1px 2px 3px 0px rgba(189, 189, 189, 1); 509 | -moz-box-shadow: 1px 2px 3px 0px rgba(189, 189, 189, 1); 510 | box-shadow: 1px 2px 3px 0px rgba(189, 189, 189, 1); 511 | flex-direction: column; 512 | } 513 | } -------------------------------------------------------------------------------- /client/src/components/profile/userprofile.css: -------------------------------------------------------------------------------- 1 | .profile_container { 2 | background: rgb(57, 62, 0); 3 | background: radial-gradient(circle, rgba(57, 62, 0, 0.3617822128851541) 23%, rgba(0, 247, 103, 0.362) 100%, rgba(60, 164, 163, 0.7903536414565826) 100%); 4 | } 5 | 6 | .profile { 7 | max-width: 600px; 8 | margin: 0 auto; 9 | padding: 20px; 10 | box-shadow: 0px 0px 10px rgba(0, 0, 0, 0.1); 11 | position: relative; 12 | top: 90px; 13 | background: rgb(9, 9, 121); 14 | background: radial-gradient(circle, rgba(9, 9, 121, 1) 0%, rgba(2, 0, 36, 0.6783088235294117) 100%, rgba(0, 212, 255, 1) 100%); 15 | } 16 | 17 | .profile-photo { 18 | margin-bottom: 20px; 19 | position: relative; 20 | text-align: center; 21 | width: 100%; 22 | display: flex; 23 | justify-content: center; 24 | } 25 | .profile-sub{ 26 | margin-top:100px; 27 | margin-left:20px; 28 | margin-right:20px; 29 | margin-bottom: 10px; 30 | } 31 | .bookmark-main{ 32 | margin-bottom: 19px; 33 | /* margin-left:1rem; */ 34 | margin-right: 1rem; 35 | } 36 | .heading1{ 37 | margin-bottom: 10px; 38 | cursor: pointer; 39 | } 40 | 41 | .profile .preview_img { 42 | background-image: url('https://res.cloudinary.com/dttyhvsnv/image/upload/v1677430557/default_pic_gxoa10.png'); 43 | width: 200px; 44 | height: 200px; 45 | background-position: center center; 46 | background-size: cover; 47 | object-fit: contain; 48 | border-radius: 50%; 49 | margin-top: 10px; 50 | overflow: hidden; 51 | } 52 | 53 | 54 | .profile-photo label { 55 | display: block; 56 | font-size: 20px; 57 | margin-bottom: 10px; 58 | } 59 | 60 | 61 | .profile-photo img { 62 | height: 100%; 63 | width: 100%; 64 | object-fit: cover; 65 | } 66 | 67 | .profile-photo .filename { 68 | display: block; 69 | font-size: 16px; 70 | margin-top: 5px; 71 | text-align: center; 72 | } 73 | 74 | .profile form { 75 | display: flex; 76 | flex-direction: column; 77 | align-items: center; 78 | } 79 | 80 | .profile label { 81 | font-size: 20px; 82 | margin-bottom: 10px; 83 | } 84 | 85 | .profile textarea { 86 | width: 100%; 87 | padding: 10px; 88 | font-size: 16px; 89 | border-radius: 5px; 90 | border: 1px solid #ccc; 91 | margin-bottom: 20px; 92 | } 93 | 94 | 95 | .profile .image_button { 96 | display: flex; 97 | justify-content: center; 98 | margin-bottom: 10px; 99 | } 100 | 101 | .profile .image_button button { 102 | color: rgb(255, 255, 255); 103 | padding: 10px; 104 | margin: 5px; 105 | border: none; 106 | outline: none; 107 | border-radius: 4px; 108 | background-color: rgb(15, 15, 87, 0.8); 109 | transition: all .2s; 110 | } 111 | 112 | .profile .image_button button:hover { 113 | background-color: rgb(15, 15, 87); 114 | cursor: pointer; 115 | transform: scale(1.01); 116 | } 117 | 118 | .hreffor { 119 | color: rgb(209, 236, 255); 120 | } 121 | 122 | .profile button[type=submit] { 123 | color: rgb(0, 0, 0); 124 | padding: 10px; 125 | margin: 5px; 126 | border: none; 127 | outline: none; 128 | border-radius: 4px; 129 | background-color: rgba(104, 214, 7, 0.8); 130 | width: 100%; 131 | } 132 | 133 | .profile button[type=submit]:hover { 134 | background-color: rgba(127, 255, 16, 0.8); 135 | } 136 | 137 | .profile .button:hover { 138 | background-color: rgb(15, 15, 87); 139 | } 140 | 141 | 142 | .about_me { 143 | min-height: 60px; 144 | background-color: #ffffff; 145 | width: 100%; 146 | text-align: center; 147 | border-radius: 6px; 148 | } 149 | 150 | .change_about { 151 | color: rgb(255, 255, 255); 152 | padding: 10px; 153 | margin: 5px; 154 | border: none; 155 | outline: none; 156 | border-radius: 4px; 157 | background-color: rgb(15, 15, 87, 0.8); 158 | font-size: 16px; 159 | margin-top: 6px; 160 | transition: all .2s; 161 | } 162 | 163 | .change_about:hover { 164 | background-color: rgb(15, 15, 87); 165 | } 166 | 167 | .my_profile_error { 168 | color: red; 169 | } 170 | 171 | @media screen and (max-width: 480px) { 172 | .profile { 173 | padding: 10px; 174 | } 175 | 176 | label, 177 | textarea, 178 | button { 179 | font-size: 16px; 180 | } 181 | } 182 | 183 | .change_about { 184 | cursor: pointer; 185 | } 186 | 187 | .jenc { 188 | font-size: 17px; 189 | color: white; 190 | cursor: pointer; 191 | background-color: green; 192 | transition: 300ms; 193 | } 194 | 195 | .jenc:hover { 196 | transition: 300ms; 197 | transform: scale(1.03); 198 | } 199 | 200 | .profile { 201 | border-radius: 10px; 202 | } 203 | 204 | 205 | 206 | 207 | 208 | 209 | /* */ 210 | 211 | 212 | .itemp{ 213 | text-decoration: none; 214 | background-color: whitesmoke; 215 | height:200px; 216 | width:340px; 217 | display:flex; 218 | margin-bottom:10px; 219 | border-radius: 10px 10px 10px 10px; 220 | border:solid 1px white; 221 | gap:0.3rem; 222 | } 223 | .leftp{ 224 | width:260px; 225 | overflow: hidden; 226 | height:200px; 227 | margin-bottom:10px; 228 | border-radius: 10px; 229 | } 230 | .leftimgp{ 231 | width:200px; 232 | height:200px; 233 | /* margin:auto; */ 234 | margin-left:-30px; 235 | /* over */ 236 | border-radius: 10px 10px; 237 | } 238 | .post-containerp { 239 | width:100%; 240 | max-width: 100%; 241 | margin: 1.2rem 2rem; 242 | display: flex; 243 | flex-direction: row; 244 | gap:1rem; 245 | overflow-y: hidden; 246 | overflow-x: scroll; 247 | margin-bottom: 10px; 248 | } 249 | .post-containerp::-webkit-scrollbar{ 250 | display:none; 251 | } 252 | .pointer{ 253 | cursor: pointer; 254 | } 255 | .imgscp2{ 256 | cursor: pointer; 257 | height:30px; 258 | width:30px; 259 | border-radius: 50%; 260 | } 261 | .profile_data2{ 262 | position: relative; 263 | display: flex; 264 | gap:8px; 265 | text-decoration: none; 266 | margin-top: 10px; 267 | float: left; 268 | bottom:0px; 269 | top:60px; 270 | left:10px; 271 | cursor: pointer; 272 | font-size: 15px; 273 | } 274 | .view_post{ 275 | font-size: 12px; 276 | color:grey; 277 | display: flex; 278 | justify-items: center; 279 | align-items: center; 280 | } 281 | .view_post_profile{ 282 | font-size: 16px; 283 | color:grey; 284 | display: flex; 285 | justify-items: center; 286 | align-items: center; 287 | } 288 | .profile_data24{ 289 | position: relative; 290 | display: flex; 291 | gap:3px; 292 | text-decoration: none; 293 | margin-top: 10px; 294 | float: left; 295 | bottom:0px; 296 | top:60px; 297 | left:2px; 298 | font-size: 15px; 299 | } 300 | .user_name2{ 301 | color:black; 302 | text-decoration: none; 303 | /* display: none; */ 304 | } 305 | .user_middle2{ 306 | color:black; 307 | text-decoration: none; 308 | /* background-color: pink; */ 309 | } 310 | .itemp:hover{ 311 | transform: scale(1.021); 312 | transition: 200ms; 313 | } 314 | .blackfont{ 315 | color:black; 316 | text-decoration: none; 317 | } 318 | .blackfont:active{ 319 | color:black; 320 | text-decoration: none; 321 | } 322 | 323 | .red{ 324 | color:red; 325 | text-decoration: none; 326 | cursor: pointer; 327 | transition: 200ms; 328 | } 329 | .bluev{ 330 | color:blue; 331 | text-decoration: none; 332 | } 333 | .red:hover{ 334 | color:brown; 335 | font-weight: 700; 336 | 337 | transition: 200ms; 338 | } 339 | .post-containerp2{ 340 | background-color: whitesmoke; 341 | border-radius: 10px; 342 | border:solid 1px white; 343 | margin-top:1rem; 344 | } 345 | .uf22{ 346 | background-color: white; 347 | padding:10px; 348 | border-radius: 10px; 349 | text-decoration: none; 350 | top:0px; 351 | } 352 | .uf2{ 353 | text-decoration: none; 354 | 355 | } 356 | 357 | .pf22{ 358 | display: grid; 359 | gap:20px; 360 | width:100%; 361 | /* height:300px; */ 362 | margin-top:0px; 363 | overflow-x: scroll; 364 | top:0px; 365 | grid-template-columns: auto auto auto auto auto; 366 | } 367 | @media screen and (max-width: 1100px) { 368 | .pf22{ 369 | 370 | grid-template-columns: auto auto auto auto ; 371 | } 372 | } 373 | @media screen and (max-width: 900px) { 374 | .pf22{ 375 | 376 | grid-template-columns: auto auto auto ; 377 | } 378 | } 379 | @media screen and (max-width: 700px) { 380 | .pf22{ 381 | 382 | grid-template-columns: auto auto ; 383 | } 384 | .uf22{ 385 | width: 170px; 386 | overflow: scroll; 387 | justify-content: left; 388 | justify-items: center; 389 | text-decoration: none; 390 | } 391 | .uf22::-webkit-scrollbar{ 392 | display:none 393 | } 394 | .sm{ 395 | font-size:17px; 396 | } 397 | .ssm{ 398 | font-size:12px; 399 | } 400 | } 401 | @media screen and (max-width: 500px) { 402 | .pf22{ 403 | 404 | grid-template-columns: auto auto; 405 | gap:5px 406 | } 407 | } 408 | @media screen and (max-width: 465px) { 409 | .pf22{ 410 | margin:auto; 411 | margin-left:10%; 412 | grid-template-columns: auto; 413 | gap:5px; 414 | } 415 | } 416 | .pf22::-webkit-scrollbar{ 417 | display:none 418 | } 419 | 420 | .post-containerp22{ 421 | border-radius: 10px; 422 | } 423 | .follpage{ 424 | margin:auto; 425 | margin-top: 100px; 426 | text-align: center; 427 | display: flex; 428 | justify-content: center; 429 | gap:60px; 430 | font-weight: 600; 431 | } 432 | .following, .follower{ 433 | background-color: rgb(207, 224, 255); 434 | border-radius: 5px; 435 | padding:5px; 436 | border: solid 1px white; 437 | } -------------------------------------------------------------------------------- /client/src/components/profileOftherUser/ProfileOfOtherUser.js: -------------------------------------------------------------------------------- 1 | import axios from "axios"; 2 | import "./profileOfOtherUser.css"; 3 | import React, { useEffect, useState } from "react"; 4 | import { useParams } from "react-router-dom"; 5 | import { Link, useNavigate } from "react-router-dom"; 6 | import { 7 | getUser, 8 | startfollow, 9 | checkfollowing, 10 | unfollow, 11 | getfollowercount, 12 | getfollowingcount, 13 | } 14 | from "../../helpers"; 15 | 16 | import Navbar from "../Navbar"; 17 | import Footer from "../footer/Footer"; 18 | import * as htmlToImage from 'html-to-image'; 19 | import { toPng, toJpeg, toBlob, toPixelData, toSvg } from 'html-to-image'; 20 | import { jsPDF } from "jspdf"; 21 | import { useDispatch, useSelector } from "react-redux"; 22 | 23 | function ProfileOfOtherUser() { 24 | const { userID } = useParams(); 25 | const { user } = useSelector((state) => ({ ...state })); 26 | const [otherUser, setOtherUser] = useState({}); 27 | const [sc, sfc] = useState(true); 28 | const navigate = useNavigate(); 29 | 30 | const [folc, setfollc] = useState(0); 31 | const [folcd, setfollcd] = useState(0); 32 | useEffect(() => { 33 | userData(); 34 | }, []); 35 | 36 | const userData = async () => { 37 | 38 | if(userID===user.id){ 39 | // const navigateToHome = () => { 40 | navigate("/profile"); 41 | // }; 42 | } 43 | const data = await getUser(userID); 44 | setOtherUser(data._doc); 45 | const sfcr = await checkfollowing(user.id, userID); 46 | if(sfcr){ 47 | if (sfcr.msg === "ok") { 48 | sfc(true); 49 | } 50 | else { 51 | sfc(false); 52 | }} 53 | 54 | const datas = await getfollowercount(userID); 55 | const data2 = await getfollowingcount(userID); 56 | setfollcd(data2.data.msg); 57 | setfollc(datas.data.msg); 58 | }; 59 | 60 | const startfollowfun = async () => { 61 | try { 62 | const data = await startfollow(user.id, otherUser._id); 63 | if(data.msg==="ok"){ 64 | sfc(true); 65 | 66 | setfollc(folc+1) 67 | 68 | } 69 | else{ 70 | alert("Some error occurred, try again later"); 71 | } 72 | } catch (error) { 73 | // console.log("error in following") 74 | } 75 | } 76 | const unfollowfun = async () => { 77 | try { 78 | const data = await unfollow(user.id, otherUser._id); 79 | if(data.msg==="ok"){ 80 | sfc(false); 81 | setfollc(folc-1) 82 | } 83 | else{ 84 | alert("Some error occurred, try again later"); 85 | 86 | } 87 | // window.location.reload(); 88 | } catch (error) { 89 | // console.log(error) 90 | // console.log("error in unfollowing") 91 | } 92 | } 93 | 94 | return ( 95 |
96 | 97 |
98 |
99 | window.open(otherUser?.picture 107 | ? otherUser.picture 108 | : "https://res.cloudinary.com/dttyhvsnv/image/upload/v1677430557/default_pic_gxoa10.png" 109 | , "_blank")} 110 | /> 111 | {otherUser.name} 112 |
113 |
{otherUser?.about}
114 |
115 | 116 | {!user? 117 |
navigate("/auth")}>+FOLLOW
118 | : 119 | <> 120 | {!sc? 121 |
startfollowfun()}>+FOLLOW
122 | : 123 | (
unfollowfun()}>FOLLOWING
) 124 | } 125 | } 126 | 127 | 128 |
129 |
130 | Following: {folcd} 131 |
132 |
133 | Followers: {folc} 134 |
135 |
136 |
137 |
138 | ); 139 | } 140 | 141 | export default ProfileOfOtherUser; 142 | -------------------------------------------------------------------------------- /client/src/components/profileOftherUser/profileOfOtherUser.css: -------------------------------------------------------------------------------- 1 | .ProfileOfOtherUser { 2 | width: 100%; 3 | 4 | } 5 | 6 | .user_wrapper { 7 | text-align: center; 8 | margin-top: 65px; 9 | margin: auto; 10 | } 11 | 12 | .user_wrapper .user_image { 13 | width: 100%; 14 | margin: auto; 15 | margin-top: 75px; 16 | display: flex; 17 | flex-direction: column; 18 | /* justify-content: center; */ 19 | } 20 | 21 | .user_wrapper .user_image img { 22 | margin: auto; 23 | } 24 | 25 | .user_wrapper .user_image span { 26 | font-weight: 600; 27 | color: rgb(39, 39, 39); 28 | margin-top: 7px; 29 | } 30 | 31 | .user_wrapper .user_about { 32 | color: black; 33 | font-size: 15px; 34 | text-align: center; 35 | } 36 | 37 | .aboutn { 38 | font-size: 30px; 39 | } 40 | 41 | @media screen and (max-width:420px) { 42 | .user_wrapper { 43 | width: 92%; 44 | } 45 | } 46 | 47 | @media screen and (max-width:800px) { 48 | .user_wrapper { 49 | width: 85%; 50 | } 51 | } 52 | @media screen and (max-width:440px) { 53 | 54 | .aboutn{ 55 | margin-top:400px; 56 | } 57 | .user_about{ 58 | margin-top:280px; 59 | } 60 | .marg{ 61 | margin-top:300px; 62 | } 63 | } 64 | @media screen and (max-width:440px) { 65 | 66 | .aboutn{ 67 | margin-top:400px; 68 | } 69 | .user_about{ 70 | margin-top:280px; 71 | } 72 | .marg{ 73 | margin-top:350px; 74 | } 75 | } 76 | 77 | .imgpro { 78 | width: 300px; 79 | position: relative; 80 | /* height:200px; */ 81 | /* width:auto; */ 82 | } 83 | 84 | .user_about { 85 | font-family: 'Gill Sans', 'Gill Sans MT', Calibri, 'Trebuchet MS', sans-serif; 86 | } 87 | .cent{ 88 | text-align: center; 89 | cursor: pointer; 90 | margin-top:15px; 91 | } 92 | .user_wrapper { 93 | margin: auto; 94 | } -------------------------------------------------------------------------------- /client/src/components/write/Editor.js: -------------------------------------------------------------------------------- 1 | import React, { useState, useRef, useMemo, useEffect } from "react"; 2 | import JoditEditor from "jodit-react"; 3 | import "./editor.css"; 4 | import { useSelector } from "react-redux"; 5 | import { createPost, dataURItoBlob, uplaodImages } from "../../helpers"; 6 | import { PulseLoader } from "react-spinners"; 7 | import { useNavigate } from "react-router-dom"; 8 | import dompurify from "dompurify"; 9 | import Footer from "../footer/Footer"; 10 | 11 | const Editor = ({ placeholder, prevdata, pflag }) => { 12 | const navigate = useNavigate(); 13 | const { user } = useSelector((state) => ({ ...state })); 14 | const inputref = useRef(null); 15 | const editor = useRef(null); 16 | const [content, setContent] = useState(""); 17 | const [title, setTitle] = useState(""); 18 | const [description, setDescription] = useState(""); 19 | const [category, setCategory] = useState(); 20 | const [image, setimage] = useState(""); 21 | const [error, setError] = useState(""); 22 | const [loading, setLoading] = useState(""); 23 | const [mflag, setmflag] = useState(false); 24 | const scroll = useRef(); 25 | useEffect(() => { 26 | scroll.current?.scrollIntoView({ behavior: "smooth" }); 27 | }, [error]); 28 | 29 | 30 | const config = useMemo(() => { 31 | return { 32 | readonly: false, 33 | placeholder: placeholder || "Start Typing...", 34 | sanitize: dompurify.sanitize, 35 | extraStyles: `img {max-width: 100%}`, 36 | }; 37 | }, [placeholder]); 38 | 39 | const handleChange = (e) => { 40 | const { name, value } = e.target; 41 | if (name === "title") { 42 | setTitle(value); 43 | } else if (name === "description") { 44 | setDescription(value); 45 | } 46 | }; 47 | 48 | const handleSelect = (e) => { 49 | setCategory(e.target.value); 50 | }; 51 | 52 | const handleImage = (e) => { 53 | let file = e.target.files[0]; 54 | 55 | if (file.size > 1024 * 1024) { 56 | setError(`${file.name} size is too large max 1mb allowed.`); 57 | return; 58 | } 59 | 60 | const reader = new FileReader(); 61 | 62 | reader.onload = (readerEvent) => { 63 | const img = new Image(); 64 | img.src = readerEvent.target.result; 65 | 66 | img.onload = function () { 67 | if (this.naturalWidth < 1000 || this.naturalHeight < 600) { 68 | setError( 69 | "Image resolution is too low, please select an image with a resolution of at least 1000x600." 70 | ); 71 | } else { 72 | setimage(readerEvent.target.result); 73 | setError(""); 74 | } 75 | }; 76 | }; 77 | 78 | reader.readAsDataURL(file); 79 | }; 80 | 81 | const handleSubmit = async (e) => { 82 | try { 83 | e.preventDefault(); 84 | if (!title || !description || !content || !category || !image) { 85 | setError("All field are required with image of your post !"); 86 | return; 87 | } else { 88 | setError(""); 89 | } 90 | if (title.length < 10 || title.length > 90) { 91 | setError("Title must be between 10 to 90 characters !"); 92 | return; 93 | } else { 94 | setError(""); 95 | } 96 | if (description.length < 50 || description.length > 120) { 97 | setError("Description must be between 50 to 100 characters !"); 98 | return; 99 | } else { 100 | setError(""); 101 | } 102 | if (image !== "") { 103 | setLoading(true); 104 | const img = dataURItoBlob(image); 105 | const path = `${user.name}/blog_images`; 106 | let formData = new FormData(); 107 | formData.append("path", path); 108 | formData.append("file", img); 109 | const postImg = await uplaodImages(formData, user?.token); 110 | const cleanHtml = dompurify.sanitize(content, { FORCE_BODY: true }); 111 | 112 | const post = await createPost( 113 | title, 114 | description, 115 | postImg[0].url, 116 | category, 117 | user.id, 118 | user?.token, 119 | cleanHtml 120 | ); 121 | if (post) { 122 | navigate("/"); 123 | } 124 | setLoading(false); 125 | } 126 | } catch (error) { 127 | setLoading(false); 128 | // console.log(error) 129 | setError(error.response.data.message); 130 | } 131 | }; 132 | 133 | return ( 134 |
135 |
136 |
137 |
138 |
139 | {image && } 140 |
141 |

{ 144 | inputref.current.click(); 145 | }} 146 | > 147 | Upload an Image 148 |

149 | 156 | 157 | 164 | 167 | 174 | 175 | 184 |
185 | setContent(newContent)} 190 | onChange={(newContent) => { 191 | setContent(newContent); 192 | }} 193 | /> 194 |
195 | {error && ( 196 |
197 | {error} 198 |
199 | )} 200 | 209 |
210 |
211 |
212 |
213 |
214 | ); 215 | }; 216 | 217 | export default Editor; 218 | -------------------------------------------------------------------------------- /client/src/components/write/EditorP.js: -------------------------------------------------------------------------------- 1 | import React, { useState, useRef, useMemo, useEffect } from "react"; 2 | import JoditEditor from "jodit-react"; 3 | import "./editor.css"; 4 | import { useSelector } from "react-redux"; 5 | import { createPost, dataURItoBlob, uplaodImages, editPost } from "../../helpers"; 6 | import { PulseLoader } from "react-spinners"; 7 | import { useNavigate } from "react-router-dom"; 8 | import dompurify from "dompurify"; 9 | import Footer from "../footer/Footer"; 10 | 11 | const EditpostP = ({ placeholder, prevdata, pflag, post }) => { 12 | const navigate = useNavigate(); 13 | const { user } = useSelector((state) => ({ ...state })); 14 | const inputref = useRef(null); 15 | const editor = useRef(null); 16 | const [content, setContent] = useState(""); 17 | const [title, setTitle] = useState(""); 18 | const [description, setDescription] = useState(""); 19 | const [category, setCategory] = useState(); 20 | const [image, setimage] = useState(""); 21 | const [error, setError] = useState(""); 22 | const [loading, setLoading] = useState(""); 23 | const [cimage, setcimage] = useState(false); 24 | const [mflag, setmflag] = useState(false); 25 | const scroll = useRef(); 26 | useEffect(() => { 27 | scroll.current?.scrollIntoView({ behavior: "smooth" }); 28 | }, [error]); 29 | 30 | useEffect(() => { 31 | if (post.user._id == user.id && pflag) { 32 | setmflag(true); 33 | setContent(post.content); 34 | setTitle(post.title); 35 | setDescription(post.description); 36 | setCategory(post.category); 37 | setimage(post.image); 38 | } 39 | else { 40 | navigate("/createpost"); 41 | } 42 | }, []) 43 | 44 | const config = useMemo(() => { 45 | return { 46 | readonly: false, 47 | placeholder: placeholder || "", 48 | sanitize: dompurify.sanitize, 49 | extraStyles: `img {max-width: 100%}`, 50 | }; 51 | }, [placeholder]); 52 | 53 | const handleChange = (e) => { 54 | const { name, value } = e.target; 55 | if (name === "title") { 56 | setTitle(value); 57 | } else if (name === "description") { 58 | setDescription(value); 59 | } 60 | }; 61 | 62 | const handleSelect = (e) => { 63 | setCategory(e.target.value); 64 | }; 65 | 66 | const handleImage = (e) => { 67 | let file = e.target.files[0]; 68 | 69 | if (file.size > 1024 * 1024) { 70 | setError(`${file.name} size is too large max 1mb allowed.`); 71 | return; 72 | } 73 | 74 | const reader = new FileReader(); 75 | 76 | reader.onload = (readerEvent) => { 77 | const img = new Image(); 78 | img.src = readerEvent.target.result; 79 | 80 | img.onload = function () { 81 | if (this.naturalWidth < 1000 || this.naturalHeight < 600) { 82 | setError( 83 | "Image resolution is too low, please select an image with a resolution of at least 1000x600." 84 | ); 85 | } else { 86 | setimage(readerEvent.target.result); 87 | setError(""); 88 | } 89 | }; 90 | }; 91 | setcimage(true); 92 | 93 | reader.readAsDataURL(file); 94 | }; 95 | const handleEdit = async (e) => { 96 | try { 97 | e.preventDefault(); 98 | if (!title || !description || !content || !category || !image) { 99 | setError("All field are required with image of your post !"); 100 | return; 101 | } else { 102 | setError(""); 103 | } 104 | if (title.length < 10 || title.length > 90) { 105 | setError("Title must be between 10 to 90 characters !"); 106 | return; 107 | } else { 108 | setError(""); 109 | } 110 | if (description.length < 50 || description.length > 120) { 111 | setError("Description must be between 50 to 100 characters !"); 112 | return; 113 | } else { 114 | setError(""); 115 | } 116 | if(cimage){ 117 | setLoading(true); 118 | const path = `${user.name}/blog_images`; 119 | const img = dataURItoBlob(image); 120 | let formData = new FormData(); 121 | formData.append("path", path); 122 | formData.append("file", img); 123 | const postImg = await uplaodImages(formData, user?.token); 124 | } 125 | const cleanHtml = dompurify.sanitize(content, { FORCE_BODY: true }); 126 | // const cleanHtml = dompurify.sanitize(post.content, { FORCE_BODY: true }); 127 | const posted = await editPost( 128 | title, 129 | description, 130 | image, 131 | category, 132 | user.id, 133 | user?.token, 134 | cleanHtml, 135 | post._id 136 | ); 137 | if (posted) { 138 | navigate("/"); 139 | } 140 | } catch (error) { 141 | setLoading(false); 142 | // console.log(error) 143 | // setError(error.response.data.message); 144 | } 145 | } 146 | const handleSubmit = async (e) => { 147 | try { 148 | e.preventDefault(); 149 | if (!title || !description || !content || !category || !image) { 150 | setError("All field are required with image of your post !"); 151 | return; 152 | } else { 153 | setError(""); 154 | } 155 | if (title.length < 10 || title.length > 90) { 156 | setError("Title must be between 10 to 90 characters !"); 157 | return; 158 | } else { 159 | setError(""); 160 | } 161 | if (description.length < 50 || description.length > 120) { 162 | setError("Description must be between 50 to 100 characters !"); 163 | return; 164 | } else { 165 | setError(""); 166 | } 167 | 168 | if (image !== "") { 169 | setLoading(true); 170 | const img = dataURItoBlob(image); 171 | const path = `${user.name}/blog_images`; 172 | let formData = new FormData(); 173 | formData.append("path", path); 174 | formData.append("file", img); 175 | const postImg = await uplaodImages(formData, user?.token); 176 | const cleanHtml = dompurify.sanitize(content, { FORCE_BODY: true }); 177 | // const cleanHtml = dompurify.sanitize(post.content, { FORCE_BODY: true }); 178 | const post = await createPost( 179 | title, 180 | description, 181 | postImg[0].url, 182 | category, 183 | user.id, 184 | user?.token, 185 | cleanHtml 186 | ); 187 | if (post) { 188 | 189 | // navigate("/"); 190 | } 191 | } 192 | } catch (error) { 193 | setLoading(false); 194 | console.log(error) 195 | setError(error.response.data.message); 196 | } 197 | }; 198 | 199 | return ( 200 | // <> 201 | 202 | // 203 | 204 |
205 |
206 |
207 |
208 |
209 | {image && } 210 |
211 |

{ 214 | inputref.current.click(); 215 | }} 216 | > 217 | Upload an Image 218 |

219 | 226 | 227 | 234 | 237 | 244 | 245 | 254 |
255 | setContent(newContent)} 260 | onChange={(newContent) => { 261 | setContent(newContent); 262 | }} 263 | /> 264 |
265 | {error && ( 266 |
267 | {error} 268 |
269 | )} 270 | {!pflag 271 | ? 272 | <> 273 | 274 | 284 | 285 | : 286 | <> 287 | 297 | 298 | } 299 |
300 |
301 |
302 |
303 |
304 | ); 305 | }; 306 | 307 | 308 | export default EditpostP -------------------------------------------------------------------------------- /client/src/components/write/editor.css: -------------------------------------------------------------------------------- 1 | .WritePost { 2 | min-height: 100vh; 3 | width: 100%; 4 | position: relative; 5 | 6 | } 7 | 8 | .WritePost .editor { 9 | width: 70%; 10 | margin: auto; 11 | } 12 | 13 | 14 | .WritePost .editor .form { 15 | position: relative; 16 | top: 95px; 17 | } 18 | 19 | .form .photoButton { 20 | text-align: center; 21 | width: 100%; 22 | margin-top: 10px; 23 | margin-bottom: 10px; 24 | background-color: rgba(167, 129, 249, 0.827); 25 | padding: 0.41rem; 26 | transition: all .2s ease-in-out; 27 | cursor: pointer; 28 | font-size: 1.2rem; 29 | font-weight: 500; 30 | } 31 | 32 | .catoo { 33 | background-color: white; 34 | } 35 | 36 | .btnsubt { 37 | color: white; 38 | font-family: 'Segoe UI', Tahoma, Geneva, Verdana, sans-serif; 39 | background-color: green; 40 | } 41 | 42 | .form .photoButton:hover { 43 | background-color: rgb(205, 7, 255); 44 | color: white; 45 | transition: 300ms; 46 | } 47 | 48 | .selectedImg { 49 | height: 400px; 50 | border: 1px solid black; 51 | width: 100%; 52 | } 53 | 54 | .selectedImg img { 55 | height: 100%; 56 | width: 100%; 57 | object-fit: cover; 58 | } 59 | 60 | .WritePost .editor .form input[type="file"] { 61 | display: none; 62 | } 63 | 64 | .WritePost .editor .form label { 65 | display: block; 66 | margin-bottom: 8px; 67 | font-weight: bold; 68 | } 69 | 70 | .WritePost .editor .form input[type="text"] { 71 | display: block; 72 | width: 98.5%; 73 | padding: 8px; 74 | font-size: 16px; 75 | border: 1px solid #ccc; 76 | border-radius: 4px; 77 | margin-bottom: 16px; 78 | } 79 | 80 | .editor select { 81 | font-size: 14px; 82 | padding: 8px; 83 | border-radius: 3px; 84 | border: none; 85 | background-color: #b6b6b6; 86 | color: rgb(0, 0, 0); 87 | cursor: pointer; 88 | transition: all 0.3s ease; 89 | margin-bottom: 10px; 90 | } 91 | 92 | .editor select:hover { 93 | background-color: #9f9f9f; 94 | } 95 | 96 | .editor select:focus { 97 | outline: none; 98 | box-shadow: 0 0 8px 0 rgba(0, 0, 0, 0.2); 99 | } 100 | 101 | .editor button[type=submit] { 102 | width: 100%; 103 | padding: 0.6rem; 104 | margin: 0.2rem; 105 | background: rgb(25, 190, 25); 106 | outline: none; 107 | border: none; 108 | transition: all 0.2s ease-in-out; 109 | font-size: 1.2rem; 110 | font-weight: 550; 111 | cursor: pointer; 112 | margin-top: 10px; 113 | } 114 | 115 | 116 | .editor button[type=submit]:hover { 117 | background: rgb(30, 226, 30); 118 | z-index: 100000; 119 | } 120 | 121 | .WritePost .editor .errorPopup { 122 | text-align: center; 123 | width: 98.5%; 124 | padding: .6rem; 125 | margin: 0.2rem; 126 | background: rgb(255, 0, 0); 127 | outline: none; 128 | border: none; 129 | transition: all 0.2s ease-in-out; 130 | font-size: 1rem; 131 | font-weight: 400; 132 | color: white; 133 | z-index: 100000; 134 | margin-top: 10px; 135 | } 136 | 137 | @media screen and (max-width:645px) { 138 | .WritePost .editor { 139 | width: 78%; 140 | margin: auto; 141 | } 142 | 143 | } 144 | 145 | @media screen and (max-width:535px) { 146 | .WritePost .editor { 147 | width: 83%; 148 | margin: auto; 149 | } 150 | 151 | } 152 | 153 | @media screen and (max-width:431px) { 154 | .selectedImg { 155 | height: 312px; 156 | } 157 | 158 | } -------------------------------------------------------------------------------- /client/src/helpers/index.js: -------------------------------------------------------------------------------- 1 | import axios from "axios"; 2 | 3 | export const clearCookie = (cookieName) => { 4 | document.cookie = `${cookieName}=; expires=Thu, 01 Jan 1970 00:00:00 UTC; path=/;`; 5 | }; 6 | export const checkifverify = async (mail) => { 7 | try { 8 | const { data } = await axios.post( 9 | `${process.env.REACT_APP_BACKEND_URL}/checkifverify`, { 10 | mail 11 | } 12 | ) 13 | return data; 14 | } catch (error) { 15 | return { msg: "error" }; 16 | } 17 | } 18 | 19 | export const checkotpv = async (mail, otp) => { 20 | try { 21 | const { data } = await axios.post( 22 | `${process.env.REACT_APP_BACKEND_URL}/checkotpv`, { 23 | mail, 24 | otp 25 | } 26 | ) 27 | return data; 28 | } catch (error) { 29 | return { msg: "error in sending mail" }; 30 | } 31 | } 32 | export const sendmail = async (mail, name) => { 33 | try { 34 | const { data } = await axios.post( 35 | `${process.env.REACT_APP_BACKEND_URL}/sendmail`, { 36 | mail, 37 | name 38 | } 39 | ) 40 | return data; 41 | } catch (error) { 42 | return { msg: "error in sending mail" }; 43 | } 44 | } 45 | export const increaseView = async (id) => { 46 | try { 47 | const { data } = await axios.post( 48 | `${process.env.REACT_APP_BACKEND_URL}/increaseView`, { 49 | id, 50 | } 51 | ) 52 | return data; 53 | } catch (error) { 54 | return { msg: "error in increasing view" }; 55 | } 56 | } 57 | export const getView = async (id) => { 58 | try { 59 | const { data } = await axios.post( 60 | `${process.env.REACT_APP_BACKEND_URL}/getView`, { 61 | id, 62 | } 63 | ) 64 | return data; 65 | } catch (error) { 66 | return { msg: "error in getting view mail" }; 67 | } 68 | } 69 | export const getLikes = async (id) => { 70 | try { 71 | const { data } = await axios.post( 72 | `${process.env.REACT_APP_BACKEND_URL}/getLikes`, { 73 | id, 74 | } 75 | ) 76 | return data; 77 | } catch (error) { 78 | // console.log(error); 79 | return { msg: "error in getting likes" }; 80 | } 81 | } 82 | export const uplaodImages = async (formData, token = null) => { 83 | try { 84 | const { data } = await axios.post( 85 | `${process.env.REACT_APP_BACKEND_URL}/uploadImages`, 86 | formData, 87 | { 88 | headers: { 89 | Authorization: `Bearer ${token}`, 90 | "content-type": "multipart/form-data", 91 | }, 92 | withCredentials: true, 93 | } 94 | ); 95 | return data; 96 | } catch (error) { 97 | return error.response.data.message; 98 | } 99 | }; 100 | 101 | export const dataURItoBlob = (dataURI) => { 102 | // convert base64/URLEncoded data component to raw binary data held in a string 103 | var byteString; 104 | if (dataURI.split(",")[0].indexOf("base64") >= 0) 105 | byteString = atob(dataURI.split(",")[1]); 106 | else byteString = unescape(dataURI.split(",")[1]); 107 | 108 | // separate out the mime component 109 | var mimeString = dataURI.split(",")[0].split(":")[1].split(";")[0]; 110 | 111 | // write the bytes of the string to a typed array 112 | var ia = new Uint8Array(byteString.length); 113 | for (var i = 0; i < byteString.length; i++) { 114 | ia[i] = byteString.charCodeAt(i); 115 | } 116 | 117 | return new Blob([ia], { type: mimeString }); 118 | }; 119 | 120 | export const createPost = async ( 121 | title, 122 | description, 123 | image, 124 | category, 125 | userId, 126 | token = null, 127 | cleanHtml 128 | ) => { 129 | try { 130 | const { data } = await axios.post( 131 | `${process.env.REACT_APP_BACKEND_URL}/post`, 132 | { 133 | title, 134 | description, 135 | image, 136 | category, 137 | user: userId, 138 | content: cleanHtml, 139 | }, 140 | 141 | { 142 | headers: { 143 | Authorization: `Bearer ${token}`, 144 | }, 145 | withCredentials: true, 146 | } 147 | ); 148 | return data; 149 | } catch (error) { 150 | return error.response.data.message; 151 | } 152 | }; 153 | export const editPost = async ( 154 | title, 155 | description, 156 | image, 157 | category, 158 | userId, 159 | token = null, 160 | cleanHtml, 161 | id 162 | ) => { 163 | try { 164 | const { data } = await axios.post( 165 | `${process.env.REACT_APP_BACKEND_URL}/editPost`, 166 | { 167 | title, 168 | description, 169 | image, 170 | category, 171 | user: userId, 172 | content: cleanHtml, 173 | id:id 174 | }, 175 | 176 | { 177 | headers: { 178 | Authorization: `Bearer ${token}`, 179 | }, 180 | withCredentials: true, 181 | } 182 | ); 183 | return data; 184 | } catch (error) { 185 | return error.response.data.message; 186 | } 187 | }; 188 | 189 | export const createcomment = async ( 190 | 191 | name, 192 | image, 193 | content, 194 | id1, 195 | id2 196 | 197 | ) => { 198 | try { 199 | const { data } = await axios.post( 200 | `${process.env.REACT_APP_BACKEND_URL}/postcomment`, 201 | { 202 | name, 203 | image, 204 | content, 205 | id1, 206 | id2 207 | } 208 | ) 209 | return data 210 | } catch (error) { 211 | // console.log("error in postcomment indexjs ", error) 212 | return; 213 | } 214 | } 215 | 216 | export const fetchprof = async (id) => { 217 | try { 218 | var { data } = await axios.post( 219 | `${process.env.REACT_APP_BACKEND_URL}/fetchprof`, 220 | { 221 | id, 222 | 223 | } 224 | 225 | ) 226 | // data = [...data, { msg: "ok" }]; 227 | return data; 228 | } catch (error) { 229 | // console.log(error); 230 | return { msg: "error" }; 231 | } 232 | } 233 | export const getcomment = async (id) => { 234 | try { 235 | var { data } = await axios.post( 236 | `${process.env.REACT_APP_BACKEND_URL}/getcomment`, 237 | { 238 | id, 239 | 240 | } 241 | 242 | ) 243 | data = [...data, { msg: "ok" }]; 244 | return data; 245 | } catch (error) { 246 | // console.log(error); 247 | return { msg: "error" }; 248 | } 249 | } 250 | export const getfollowercount = async (id) => { 251 | try { 252 | const data = await axios.post( 253 | `${process.env.REACT_APP_BACKEND_URL}/countfollower`, { 254 | id 255 | }) 256 | return data 257 | } catch (error) { 258 | return { msg: "error" } 259 | } 260 | } 261 | 262 | 263 | export const getallpostdata = async (id) => { 264 | try { 265 | const data = await axios.post( 266 | `${process.env.REACT_APP_BACKEND_URL}/getallpostdata`, { 267 | id 268 | }) 269 | return data 270 | } catch (error) { 271 | // console.log(error); 272 | // console.log(error); 273 | return { msg: "error" } 274 | } 275 | } 276 | export const getfollowingcount = async (id) => { 277 | try { 278 | const data = await axios.post( 279 | `${process.env.REACT_APP_BACKEND_URL}/countfollowing`, { 280 | id 281 | }) 282 | return data 283 | } catch (error) { 284 | return { msg: "error" } 285 | } 286 | } 287 | 288 | export const showbookmarks = async (id) => { 289 | try { 290 | const data = await axios.post( 291 | `${process.env.REACT_APP_BACKEND_URL}/showbookmarks`, { 292 | id 293 | }) 294 | return data; 295 | } catch (error) { 296 | return { msg: "error" } 297 | } 298 | } 299 | export const showLikemarks = async (id) => { 300 | try { 301 | const data = await axios.post( 302 | `${process.env.REACT_APP_BACKEND_URL}/showLikemarks`, { 303 | id 304 | }) 305 | return data; 306 | } catch (error) { 307 | return { msg: "error" } 308 | } 309 | } 310 | 311 | export const reportcontent = async (pid, postid, userid, name1, name2, reason) => { 312 | try { 313 | var { data } = await axios.post( 314 | `${process.env.REACT_APP_BACKEND_URL}/reportcontent`, { 315 | pid, 316 | postid, 317 | userid, 318 | name1, 319 | name2, 320 | reason 321 | } 322 | ) 323 | return data; 324 | } catch (error) { 325 | // console.log("error in Reporting", error); 326 | return { msg: error }; 327 | } 328 | } 329 | export const deletebookmark = async (postid, userid) => { 330 | try { 331 | var { data } = await axios.post( 332 | `${process.env.REACT_APP_BACKEND_URL}/deletebookmark`, { 333 | postid, 334 | userid 335 | } 336 | ) 337 | return data; 338 | } catch (error) { 339 | // console.log("error in Bookmark", error); 340 | return { msg: error }; 341 | } 342 | } 343 | export const deletelikes = async (postid, userid) => { 344 | try { 345 | var { data } = await axios.post( 346 | `${process.env.REACT_APP_BACKEND_URL}/deletelikes`, { 347 | postid, 348 | userid 349 | } 350 | ) 351 | return data; 352 | } catch (error) { 353 | // console.log("error in Liked removing", error); 354 | return { msg: error }; 355 | } 356 | } 357 | 358 | export const deletepost = async (postid, userid) => { 359 | try { 360 | var { data } = await axios.post( 361 | `${process.env.REACT_APP_BACKEND_URL}/deletepost`, { 362 | postid, 363 | userid 364 | } 365 | ) 366 | return data; 367 | } catch (error) { 368 | // console.log("error in deleting post", error); 369 | return { msg: error }; 370 | } 371 | } 372 | export const checkbookmark = async (postid, userid) => { 373 | try { 374 | var { data } = await axios.post( 375 | `${process.env.REACT_APP_BACKEND_URL}/checkbookmark`, { 376 | postid, 377 | userid 378 | } 379 | ) 380 | return data; 381 | } catch (error) { 382 | // console.log("error in Bookmark", error); 383 | return { msg: error }; 384 | } 385 | } 386 | export const checklikes = async (postid, userid) => { 387 | try { 388 | var { data } = await axios.post( 389 | `${process.env.REACT_APP_BACKEND_URL}/checklikes`, { 390 | postid, 391 | userid 392 | } 393 | ) 394 | return data; 395 | } catch (error) { 396 | // console.log("error in checklikes", error); 397 | return { msg: error }; 398 | } 399 | } 400 | 401 | export const fetchfollowing = async (id) => { 402 | try { 403 | var { data } = await axios.post( 404 | `${process.env.REACT_APP_BACKEND_URL}/fetchfollowing`, { 405 | id, 406 | } 407 | ) 408 | return data; 409 | } catch (error) { 410 | // console.log("error in fetch followers", error); 411 | return; 412 | } 413 | } 414 | export const changeabout = async (about, id) => { 415 | try { 416 | var { data } = await axios.post( 417 | `${process.env.REACT_APP_BACKEND_URL}/changeabout`, { 418 | about, id, 419 | } 420 | ) 421 | return data; 422 | } catch (error) { 423 | // console.log("error in fetch followers", error); 424 | return; 425 | } 426 | } 427 | export const startfollow = async (id, id2) => { 428 | try { 429 | var { data } = await axios.post( 430 | `${process.env.REACT_APP_BACKEND_URL}/startfollow`, { 431 | id, 432 | id2 433 | } 434 | ) 435 | return data; 436 | } catch (error) { 437 | // console.log("error in start following", error); 438 | return; 439 | } 440 | } 441 | export const unfollow = async (id, id2) => { 442 | try { 443 | var { data } = await axios.post( 444 | `${process.env.REACT_APP_BACKEND_URL}/unfollow`, { 445 | id, 446 | id2 447 | } 448 | ) 449 | return data; 450 | } catch (error) { 451 | // console.log("error in start following", error); 452 | return; 453 | } 454 | } 455 | export const checkfollowing = async (id, id2) => { 456 | try { 457 | var { data } = await axios.post( 458 | `${process.env.REACT_APP_BACKEND_URL}/checkfollow`, { 459 | id, 460 | id2 461 | } 462 | ) 463 | return data; 464 | } catch (error) { 465 | // console.log("error in fetch check following", error); 466 | return; 467 | } 468 | } 469 | export const showmyposts = async (id) => { 470 | try { 471 | var { data } = await axios.post( 472 | `${process.env.REACT_APP_BACKEND_URL}/showmyposts`, { 473 | id 474 | } 475 | ) 476 | return data; 477 | } catch (error) { 478 | // console.log("error in Bookmark", error); 479 | return; 480 | } 481 | } 482 | export const increaseLike = async (id) => { 483 | try { 484 | var { data } = await axios.post( 485 | `${process.env.REACT_APP_BACKEND_URL}/increaseLike`, { 486 | id 487 | } 488 | ) 489 | return data; 490 | } catch (error) { 491 | // console.log("error in likes increase", error); 492 | return; 493 | } 494 | } 495 | export const decreastLike = async (id) => { 496 | try { 497 | var { data } = await axios.post( 498 | `${process.env.REACT_APP_BACKEND_URL}/decreastLike`, { 499 | id 500 | } 501 | ) 502 | return data; 503 | } catch (error) { 504 | // console.log("error in likes decrease", error); 505 | return; 506 | } 507 | } 508 | export const searchresult = async (id2,) => { 509 | try { 510 | var { data } = await axios.post( 511 | `${process.env.REACT_APP_BACKEND_URL}/searchresult`, { 512 | id2 513 | } 514 | ) 515 | return data; 516 | } catch (error) { 517 | // console.log("error in Bookmark", error); 518 | return; 519 | } 520 | } 521 | export const bookmark = async (postid, userid) => { 522 | try { 523 | var { data } = await axios.post( 524 | `${process.env.REACT_APP_BACKEND_URL}/setbookmark`, { 525 | postid, 526 | userid 527 | } 528 | ) 529 | return data; 530 | } catch (error) { 531 | // console.log("error in Bookmark", error); 532 | return; 533 | } 534 | } 535 | 536 | export const likes = async (postid, userid) => { 537 | try { 538 | var { data } = await axios.post( 539 | `${process.env.REACT_APP_BACKEND_URL}/setlikes`, { 540 | postid, 541 | userid 542 | } 543 | ) 544 | return data; 545 | } catch (error) { 546 | // console.log("error in Likes", error); 547 | return; 548 | } 549 | } 550 | export const getallLikes = async (userid) => { 551 | try { 552 | var { data } = await axios.post( 553 | `${process.env.REACT_APP_BACKEND_URL}/getallLikes`, { 554 | userid 555 | } 556 | ) 557 | return data; 558 | } catch (error) { 559 | // console.log("error in Likes", error); 560 | return; 561 | } 562 | } 563 | export const getallBookmarks = async (userid) => { 564 | try { 565 | var { data } = await axios.post( 566 | `${process.env.REACT_APP_BACKEND_URL}/getallBookmarks`, { 567 | userid 568 | } 569 | ) 570 | return data; 571 | } catch (error) { 572 | // console.log("error in Likes", error); 573 | return; 574 | } 575 | } 576 | 577 | export const getAllPost = async (activePage, LIMIT,mpost) => { 578 | try { 579 | const { data } = await axios.post( 580 | `${process.env.REACT_APP_BACKEND_URL}/getallpost`, 581 | { 582 | params: { 583 | page: activePage, 584 | size: LIMIT, 585 | }, 586 | mpost 587 | } 588 | ); 589 | return data; 590 | 591 | } catch (error) { 592 | return error; 593 | } 594 | }; 595 | 596 | export const getarticle = async (id) => { 597 | try { 598 | var { data } = await axios.post( 599 | `${process.env.REACT_APP_BACKEND_URL}/getarticle`, { 600 | id 601 | } 602 | ) 603 | return data; 604 | } catch (error) { 605 | // console.log("error in getting article", error); 606 | return error; 607 | } 608 | } 609 | 610 | export const uploadProfilePicture = async (picture, about, token = null) => { 611 | try { 612 | const { data } = await axios.put( 613 | `${process.env.REACT_APP_BACKEND_URL}/uploadprofile`, 614 | { 615 | picture, 616 | about, 617 | }, 618 | 619 | { 620 | headers: { 621 | Authorization: `Bearer ${token}`, 622 | }, 623 | withCredentials: true, 624 | } 625 | ); 626 | return data; 627 | } catch (error) { 628 | return error.response.data.message; 629 | } 630 | }; 631 | 632 | export const getUser = async (userId) => { 633 | try { 634 | const { data } = await axios.get( 635 | `${process.env.REACT_APP_BACKEND_URL}/getUser/${userId}` 636 | ); 637 | return data; 638 | } catch (error) { 639 | return error.response.data.message; 640 | } 641 | }; 642 | -------------------------------------------------------------------------------- /client/src/index.css: -------------------------------------------------------------------------------- 1 | :root { 2 | --main: #F0A826 3 | } 4 | 5 | * { 6 | margin: 0; 7 | padding: 0; 8 | box-sizing: border-box; 9 | } 10 | 11 | 12 | body { 13 | font-family: -apple-system, BlinkMacSystemFont, 'Segoe UI', 'Roboto', 'Oxygen', 14 | 'Ubuntu', 'Cantarell', 'Fira Sans', 'Droid Sans', 'Helvetica Neue', 15 | sans-serif; 16 | -webkit-font-smoothing: antialiased; 17 | -moz-osx-font-smoothing: grayscale; 18 | 19 | 20 | } 21 | 22 | 23 | code { 24 | font-family: source-code-pro, Menlo, Monaco, Consolas, 'Courier New', 25 | monospace; 26 | } -------------------------------------------------------------------------------- /client/src/index.js: -------------------------------------------------------------------------------- 1 | import React from "react"; 2 | import ReactDOM from "react-dom/client"; 3 | import "./index.css"; 4 | import App from "./App"; 5 | import { BrowserRouter } from "react-router-dom"; 6 | import { Provider } from "react-redux"; 7 | import { createStore, applyMiddleware } from 'redux'; 8 | import { composeWithDevTools } from '@redux-devtools/extension'; 9 | import rootReducer from "./reducers"; 10 | 11 | const store = createStore(rootReducer, composeWithDevTools()); 12 | 13 | 14 | const root = ReactDOM.createRoot(document.getElementById("root")); 15 | root.render( 16 | 17 | 18 | 19 | 20 | 21 | ); 22 | -------------------------------------------------------------------------------- /client/src/pages/ArticlePage.js: -------------------------------------------------------------------------------- 1 | import React, { useEffect, useState } from 'react' 2 | import { useParams, useNavigate } from 'react-router-dom' 3 | import Article from '../components/article/Article'; 4 | import Navbar from '../components/Navbar'; 5 | import { getarticle, increaseView } from '../helpers/index' 6 | import '../components/article/article.css' 7 | function ArticlePage() { 8 | const navigate = useNavigate(); 9 | const [post, setpost] = useState({}); 10 | const { postID } = useParams(); 11 | useEffect(() => { 12 | const init = async () => { 13 | await increaseView(postID); 14 | try { 15 | const dt = await getarticle(postID); 16 | if (dt.response && (dt.response.status === 404 || dt.response.status === 400)) { 17 | navigate("/404"); 18 | } 19 | setpost(dt.msg); 20 | return; 21 | } 22 | catch (error) { 23 | if (error.msg == "!article") { 24 | navigate("/404"); 25 | } 26 | else if (error.msg == "!user") { 27 | navigate("/404"); 28 | } 29 | else if (error.msg === "error") navigate("/404"); 30 | // console.log(error); 31 | return; 32 | } 33 | }; 34 | init(); 35 | }, []) 36 | if (!post || post.image === undefined || post.length === 0) { 37 | return <> 38 |
39 |
40 |
41 |
42 |
43 |

Loading....

44 |

You can comment, save, bookmark and download page

45 |
46 | 47 | } 48 | else 49 | return ( 50 |
51 | 52 |
53 |
54 | ) 55 | // const location = useLocation() 56 | // const { post } = location.state; 57 | // if(location.state.ooo){ 58 | // return ( 59 | //
60 | // 61 | //
62 | //
) 63 | // }else { 64 | // return ( 65 | //
66 | // 67 | //
68 | //
) 69 | // } 70 | } 71 | 72 | export default ArticlePage -------------------------------------------------------------------------------- /client/src/pages/Auth.js: -------------------------------------------------------------------------------- 1 | import { useState } from "react"; 2 | import { TfiEmail } from "react-icons/tfi"; 3 | import { CiLock } from "react-icons/ci"; 4 | import { TiUser } from "react-icons/ti"; 5 | import { Link, useNavigate } from "react-router-dom"; 6 | import axios from "axios"; 7 | import { useDispatch } from "react-redux"; 8 | import Cookies from "js-cookie"; 9 | import { 10 | checkifverify, 11 | sendmail, 12 | checkotpv 13 | } from "../helpers/index" 14 | 15 | function Auth() { 16 | const dispatch = useDispatch(); 17 | const navigate = useNavigate(); 18 | const [state, setState] = useState("Sign Up"); 19 | const [success, setSuccess] = useState(""); 20 | const [error, setError] = useState(""); 21 | const [otpv, cotpv] = useState(""); 22 | const [vo, svo] = useState(false); 23 | const [cs, scs] = useState(""); 24 | const userInfos = { 25 | email: "", 26 | password: "", 27 | name: "", 28 | }; 29 | 30 | const [user, setUser] = useState(userInfos); 31 | const { email, password, name } = user; 32 | 33 | const handleRegisterChange = (e) => { 34 | const { name, value } = e.target; 35 | setUser({ ...user, [name]: value }); 36 | }; 37 | 38 | const handleSubmit = async () => { 39 | var temail = email.toLowerCase() 40 | 41 | if (state === "Log In") { 42 | try { 43 | if (!temail || !password) { 44 | setError('All feilds are required !') 45 | return; 46 | } 47 | const data = await checkifverify(temail); 48 | if (data.msg === "ok") { 49 | 50 | } 51 | else if ( 52 | data.msg === 'ne' 53 | ) { 54 | setError("Please Sign Up First "); 55 | return; 56 | } 57 | else { 58 | setError("Please Sign up and Verify Your Email "); 59 | return; 60 | } 61 | } catch (error) { 62 | // console.log(error); 63 | } 64 | logIn(); 65 | } else { 66 | if (!name || !temail || !password) { 67 | setError('All feilds are required !') 68 | return; 69 | } 70 | if (vo === false) { 71 | setError('An OTP has been send to your mail for verification') 72 | const datas = await sendmail(temail, name); 73 | scs(true); 74 | } 75 | } 76 | }; 77 | const checkotp = async () => { 78 | try { 79 | var temail = email.toLowerCase() 80 | 81 | const data = await checkotpv(temail, otpv); 82 | } catch (error) { 83 | setError('An Error Occurred') 84 | // console.log("cannot verify otp") 85 | } 86 | } 87 | const logIn = async () => { 88 | try { 89 | var temail = email.toLowerCase() 90 | const { data } = await axios.post( 91 | `${process.env.REACT_APP_BACKEND_URL}/login`, 92 | { 93 | temail, 94 | password, 95 | } 96 | ); 97 | setError('') 98 | setSuccess("Success !") 99 | setTimeout(() => { 100 | dispatch({ type: "LOGIN", payload: data }); 101 | Cookies.set("user", JSON.stringify(data), { expires: 15 }); 102 | navigate("/"); 103 | }, 2000); 104 | } catch (error) { 105 | // console.log(error) 106 | setError(error.response.data.message); 107 | } 108 | }; 109 | 110 | const signUp = async () => { 111 | try { 112 | var temail = email.toLowerCase() 113 | const { data } = await axios.post( 114 | `${process.env.REACT_APP_BACKEND_URL}/register`, 115 | { 116 | name, 117 | temail, 118 | password, 119 | } 120 | ); 121 | 122 | setError('') 123 | setSuccess(data.message) 124 | const { message, ...rest } = data; 125 | setTimeout(() => { 126 | dispatch({ type: "LOGIN", payload: rest }); 127 | Cookies.set("user", JSON.stringify(rest), { expires: 15 }); 128 | navigate("/"); 129 | }, 2000); 130 | } catch (error) { 131 | setError(error.response.data.message); 132 | } 133 | }; 134 | const verifyOTP = async () => { 135 | try { 136 | var temail = email.toLowerCase() 137 | const data = await checkotpv(temail, otpv); 138 | if (data.msg === 'ok') { 139 | setError("OTP Matched"); 140 | signUp(); 141 | } 142 | else { 143 | setError("OTP do not match"); 144 | } 145 | } catch (error) { 146 | setError("ERROR OCCURRED!"); 147 | // console.log("error in matching") 148 | } 149 | } 150 | const signUpWithGoogle = () => { 151 | window.open(`${process.env.REACT_APP_BACKEND_URL}/auth/google`, "_self"); 152 | } 153 | 154 | return ( 155 |
156 |
157 |
158 | rfe 159 | {state} 160 |
161 |
162 |
{ 169 | setState("Log In"); 170 | setError("") 171 | }} 172 | > 173 | Log In 174 |
175 |
{ 182 | setState("Sign Up"); 183 | setError("") 184 | 185 | }} 186 | > 187 | Sign Up 188 |
189 |
190 | {state === "Log In" ? ( 191 |
192 | {/*
193 | google 194 | signUpWithGoogle()} >Log in with Google 195 |
196 |
197 | facebook 198 | Log in with Facebook 199 |
*/} 200 |
201 | google 202 | {/* ...; */} 203 | signUpWithGoogle()}>Sign In with Google 204 |
205 |
206 | ) : ( 207 |
208 |
209 | google 210 | {/* ...; */} 211 | signUpWithGoogle()}>Sign Up with Google 212 |
213 |
214 | 215 | )} 216 | {/* or */} 217 | OR 218 |
219 | {state === "Sign Up" ? ( 220 |
221 | 222 | 229 |
230 | ) : ( 231 | "" 232 | )} 233 |
234 | 235 | 242 |
243 |
244 | 245 | 252 |
253 | {(cs) && state === "Sign Up" ? 254 |
255 | { cotpv(e.target.value) }} 261 | /> 262 |
263 | : <>} 264 |
265 | {error && {error}} 266 | {success && {success}} 267 | {state === "Sign Up" ? ( 268 |
269 | By signing up, you agree to our terms of service and 270 | privacy policy. No credit card required. 271 |
272 | ) : ( 273 |
274 | Don't remember your password? 275 |
276 | )} 277 | {(cs) && state === "Sign Up" ? 278 |
279 | {state === "Sign Up" ? "Verify OTP" : "LOG IN"} 280 |
281 | : 282 |
283 | {state === "Sign Up" ? "SIGN UP FOR FREE" : "LOG IN"} 284 |
285 | } 286 |
287 |
288 | ); 289 | } 290 | 291 | export default Auth; 292 | -------------------------------------------------------------------------------- /client/src/pages/HomePage.js: -------------------------------------------------------------------------------- 1 | import React from "react"; 2 | import Breaker from "../components/home/breaker/Breaker"; 3 | import Posts from "../components/home/post/Posts"; 4 | import Navbar from "../components/Navbar"; 5 | import Card from "../components/home/card/Card"; 6 | import Cookies from "js-cookie"; 7 | import { useNavigate } from "react-router-dom"; 8 | import { useDispatch } from "react-redux"; 9 | import { useState } from "react"; 10 | import Footer from "../components/footer/Footer"; 11 | function HomePage({ user, category }) { 12 | const navigate = useNavigate(); 13 | const dispatch = useDispatch(); 14 | const [mpost, setmpost] = useState(category); 15 | const [flag, setflag] = useState(false); 16 | const handleLoad = async () => { 17 | if (user === null || user === undefined) { 18 | await fetch(`${process.env.REACT_APP_BACKEND_URL}/login/success`, { 19 | method: "POST", 20 | credentials: "include", 21 | withCredentials: true, 22 | headers: { 23 | Accept: "application/json", 24 | "Content-Type": "application/json", 25 | "Access-Control-Allow-Credentials": true, 26 | }, 27 | }) 28 | .then((data) => { 29 | if (data.status === 201) return data.json(); 30 | throw new Error("Authentication Failed!"); 31 | }) 32 | .then((data) => { 33 | dispatch({ type: "LOGIN", payload: data }); 34 | Cookies.set("user", JSON.stringify(data), { expires: 15 }); 35 | navigate("/"); 36 | }) 37 | .catch((err) => { 38 | // console.log(err); 39 | }); 40 | 41 | } 42 | }; 43 | React.useEffect(() => { 44 | handleLoad(); 45 | }, [] 46 | ) 47 | return ( 48 |
49 | 50 | 51 | 52 | 53 |
54 |
55 | ); 56 | } 57 | 58 | export default HomePage; 59 | -------------------------------------------------------------------------------- /client/src/pages/Profile.js: -------------------------------------------------------------------------------- 1 | import React from 'react' 2 | import UserProfile from '../components/profile/UserProfile' 3 | 4 | function Profile() { 5 | return ( 6 |
7 | 8 |
9 | ) 10 | } 11 | 12 | export default Profile; -------------------------------------------------------------------------------- /client/src/pages/ResetPassword.js: -------------------------------------------------------------------------------- 1 | import axios from "axios"; 2 | import { useState } from "react"; 3 | import "./resetPassword.css"; 4 | import { Link, useNavigate, Navigate } from "react-router-dom"; 5 | 6 | function ResetPassword() { 7 | const [email, setEmail] = useState(""); 8 | const [code, setcode] = useState(""); 9 | const [pass, setpass] = useState(""); 10 | const [foundUser, setFoundUser] = useState(null); 11 | const [foundsend, setFoundsend] = useState(null); 12 | const [open, setopen] = useState(null); 13 | 14 | const navigate = useNavigate(); 15 | 16 | const handleInputChange = (event) => { 17 | setEmail(event.target.value); 18 | }; 19 | 20 | const handleSearchClick = async () => { 21 | try { 22 | const { data } = await axios.post( 23 | `${process.env.REACT_APP_BACKEND_URL}/findOutUser`, 24 | { 25 | email, 26 | } 27 | ); 28 | 29 | setFoundUser(data[0]) 30 | 31 | 32 | } catch (error) { 33 | if(error.response.status===400){ 34 | alert(error.response.data.message); 35 | return; 36 | } 37 | if(error.response.status===404){ 38 | alert(error.response.data.message); 39 | return; 40 | } 41 | } 42 | }; 43 | const sendCode = async () => { 44 | try { 45 | const { data } = await axios.post( 46 | `${process.env.REACT_APP_BACKEND_URL}/sendResetPasswordCode`, { email: foundUser.email, code: code }); 47 | setFoundsend(true); 48 | } catch (error) { 49 | // console.log(error.message); 50 | } 51 | }; 52 | 53 | const validate = async (e) => { 54 | e.preventDefault() 55 | try { 56 | const { data } = await axios.post( 57 | `${process.env.REACT_APP_BACKEND_URL}/validateResetCode`, { email: foundUser.email, code: code }); 58 | if (data.message === "ok") { 59 | setFoundsend(false); 60 | setopen(true); 61 | } 62 | else { 63 | alert(data.message) 64 | } 65 | 66 | } catch (error) { 67 | // console.log(error.message) 68 | } 69 | } 70 | const changep = async (e) => { 71 | e.preventDefault(); 72 | if (pass.length <= 8) { 73 | alert("PASSWORD LENGTH SHOULD BE MORE THAN 8") 74 | return; 75 | } 76 | if (!pass) { 77 | return; 78 | } 79 | 80 | try { 81 | const { data } = await axios.post( 82 | `${process.env.REACT_APP_BACKEND_URL}/changePassword`, { email: foundUser.email, password: pass }); 83 | if (data.message === "ok") { 84 | alert("Password Changed"); 85 | setTimeout(() => { 86 | navigate("/"); 87 | }, 2000); 88 | } 89 | else { 90 | alert(data.message) 91 | } 92 | } catch (error) { 93 | alert(error.message) 94 | } 95 | } 96 | return ( 97 |
98 | {" "} 99 | {/* Add the "user-search" class to the container */} 100 | 101 | 108 | 111 | {foundUser ? ( 112 |
113 |

Name: {foundUser.name}

114 |

Email: {foundUser.email}

115 |

picture:

116 | 117 |
118 | ) : ( 119 |

120 | No user found with that email address. 121 |

122 | )} 123 |
124 |
125 | 126 | { setcode(e.target.value) }} 133 | className="user-search-input" 134 | /> 135 | 136 | 137 |
138 |
139 |
140 |
141 | 142 | { setpass(e.target.value) }} 148 | className="user-search-input" 149 | /> 150 | 151 | 152 |
153 |
154 |
155 | ); 156 | } 157 | 158 | export default ResetPassword; 159 | -------------------------------------------------------------------------------- /client/src/pages/TopicPage.js: -------------------------------------------------------------------------------- 1 | import React, { useEffect } from 'react' 2 | 3 | import { useParams, useNavigate } from 'react-router-dom' 4 | import { getAllPost } from '../helpers'; 5 | import HomePage from './HomePage'; 6 | 7 | 8 | const TopicPage = ({user}) => { 9 | const { id } = useParams(); 10 | return ( 11 | <> 12 | 13 | 14 | ) 15 | } 16 | 17 | export default TopicPage -------------------------------------------------------------------------------- /client/src/pages/not_found.js: -------------------------------------------------------------------------------- 1 | import React from 'react' 2 | import { Link } from 'react-router-dom' 3 | 4 | const Not_found = () => { 5 | return ( 6 |
16 |

404

22 |

Page Not Found

27 | 28 | e.target.style.backgroundColor = '#0056b3'} 37 | onMouseOut={(e) => e.target.style.backgroundColor = '#007bff'}> 38 | GO TO HOME 39 | 40 | 41 | 56 |
57 | 58 | ) 59 | } 60 | 61 | export default Not_found -------------------------------------------------------------------------------- /client/src/pages/resetPassword.css: -------------------------------------------------------------------------------- 1 | .user-search { 2 | max-width: 500px; 3 | margin: 0 auto; 4 | padding: 20px; 5 | border: 1px solid #ccc; 6 | border-radius: 5px; 7 | } 8 | 9 | .user-search label { 10 | display: block; 11 | margin-bottom: 10px; 12 | } 13 | 14 | .hidden { 15 | display: none; 16 | } 17 | 18 | .user-search-input { 19 | width: 350px; 20 | height: 40px; 21 | font-size: 15px; 22 | border-radius: 6px; 23 | } 24 | 25 | .user-search input[type="text"] { 26 | display: block; 27 | width: 100%; 28 | padding: 10px; 29 | font-size: 16px; 30 | border: 1px solid #ccc; 31 | border-radius: 5px; 32 | } 33 | 34 | .imgres { 35 | width: 300px; 36 | } 37 | 38 | .user-search button { 39 | display: block; 40 | margin-top: 10px; 41 | padding: 10px; 42 | font-size: 16px; 43 | background-color: #007bff; 44 | color: #fff; 45 | border: none; 46 | border-radius: 5px; 47 | cursor: pointer; 48 | transition: background-color 0.2s; 49 | } 50 | 51 | .user-search button:hover { 52 | background-color: #0062cc; 53 | } 54 | 55 | .user-search p { 56 | margin-top: 10px; 57 | } 58 | 59 | @media only screen and (max-width: 600px) { 60 | .user-search { 61 | max-width: 100%; 62 | padding: 10px; 63 | } 64 | 65 | .user-search input[type="text"] { 66 | font-size: 14px; 67 | margin-bottom: 10px; 68 | } 69 | 70 | .user-search button { 71 | font-size: 14px; 72 | padding: 8px; 73 | margin-bottom: 10px; 74 | } 75 | 76 | .user-search p { 77 | margin-top: 10px; 78 | margin-bottom: 10px; 79 | } 80 | } -------------------------------------------------------------------------------- /client/src/pages/write/Editpost.js: -------------------------------------------------------------------------------- 1 | import React, { useState } from 'react' 2 | import EditpostP from '../../components/write/EditorP' 3 | import { useParams, useNavigate } from 'react-router-dom' 4 | import { getarticle } from '../../helpers/index' 5 | import Navbar from '../../components/Navbar' 6 | 7 | const Editpost = () => { 8 | const navigate = useNavigate(); 9 | const [post, setpost] = useState({}); 10 | const { id } = useParams(); 11 | React.useEffect(() => { 12 | x(); 13 | }, []) 14 | const x = async () => { 15 | try { 16 | const dt = await getarticle(id); 17 | if (dt.response && (dt.response.status === 404 || dt.response.status === 400)) { 18 | // navigate("/404"); 19 | } 20 | setpost(dt.msg); 21 | return; 22 | } 23 | catch (error) { 24 | if (error.msg == "!article") { 25 | // navigate("/404"); 26 | } 27 | else if (error.msg == "!user") { 28 | // navigate("/404"); 29 | } 30 | // else if (error.msg === "error") navigate("/404"); 31 | // console.log(error); 32 | return; 33 | } 34 | } 35 | if (!post || !post.user) { 36 | return <> 37 |
38 |
39 |
40 |
41 |
42 |

Loading....

43 |

You can comment, save, bookmark and download page

44 |
45 | 46 | } 47 | else { 48 | return ( 49 | <> 50 |
51 | 52 | 53 |
54 | 55 | ) 56 | } 57 | } 58 | 59 | export default Editpost -------------------------------------------------------------------------------- /client/src/pages/write/WritePost.js: -------------------------------------------------------------------------------- 1 | import React from 'react' 2 | import Editor from '../../components/write/Editor' 3 | import Navbar from '../../components/Navbar' 4 | 5 | function WritePost() { 6 | return ( 7 |
8 | 9 | 10 |
11 | ) 12 | } 13 | 14 | export default WritePost -------------------------------------------------------------------------------- /client/src/reducers/index.js: -------------------------------------------------------------------------------- 1 | import { combineReducers } from "redux"; 2 | import { userReducer } from "./userReducer"; 3 | import { postReducer } from "./postReducer"; 4 | 5 | 6 | 7 | const rootReducer = combineReducers({ 8 | user: userReducer, 9 | posts: postReducer, 10 | 11 | }); 12 | 13 | export default rootReducer; -------------------------------------------------------------------------------- /client/src/reducers/postReducer.js: -------------------------------------------------------------------------------- 1 | 2 | let posts = []; 3 | 4 | export function postReducer(state = posts, action) { 5 | switch (action.type) { 6 | case "SET_POSTS": 7 | try{ 8 | return [...state, ...action.payload]; 9 | } 10 | catch(e){ 11 | return [...state, action.payload]; 12 | } 13 | default: 14 | return state; 15 | } 16 | } 17 | 18 | -------------------------------------------------------------------------------- /client/src/reducers/userReducer.js: -------------------------------------------------------------------------------- 1 | 2 | import Cookies from "js-cookie"; 3 | export function userReducer(state = Cookies.get("user") ? JSON.parse(Cookies.get("user")) : null, action) { 4 | switch (action.type) { 5 | case "LOGIN": 6 | return action.payload; 7 | case "LOGOUT": 8 | return null; 9 | case "UPDATEPICTURE": 10 | return { ...state, picture: action.payload.picture, about: action.payload.about }; 11 | case "VERIFY": 12 | return { ...state, verified: action.payload }; 13 | case "LIKE": 14 | return { likes: action.payload, ...state }; 15 | case "BOOKMARK": 16 | return { ...state, bookmarks: action.payload }; 17 | default: 18 | return state; 19 | } 20 | } 21 | 22 | -------------------------------------------------------------------------------- /client/src/routes/LoggedInRoutes.js: -------------------------------------------------------------------------------- 1 | import { useSelector } from "react-redux"; 2 | import { Navigate, Outlet, useNavigate } from "react-router-dom"; 3 | 4 | export default function LoggedInRoutes() { 5 | const { user } = useSelector((state) => ({ ...state })); 6 | 7 | return user ? : ; 8 | } 9 | -------------------------------------------------------------------------------- /client/src/routes/NotAllowedLoggedInRoutes.js: -------------------------------------------------------------------------------- 1 | import { useSelector } from "react-redux"; 2 | import { Navigate, Outlet } from "react-router-dom"; 3 | 4 | export default function NotAllowedLoggedInRoutes() { 5 | const { user } = useSelector((state) => ({ ...state })); 6 | 7 | return user ? : ; 8 | } 9 | --------------------------------------------------------------------------------