├── .github
├── ISSUE_TEMPLATE
│ ├── bug_report.md
│ ├── custom.md
│ └── feature_request.md
└── workflows
│ ├── greetings.yml
│ └── pr_checker.yaml
├── .gitignore
├── README.md
├── app.js
├── package-lock.json
├── package.json
├── public
├── css
│ └── style.css
├── img
│ ├── hero-image.webp
│ └── img-noise-361x370.png
└── js
│ └── script.js
├── server
├── config
│ ├── db.js
│ ├── nodemailerConfig.js
│ └── passport.js
├── helpers
│ └── routeHelpers.js
├── middlewares
│ ├── admin.js
│ ├── auth.js
│ ├── authValidator.js
│ └── restrictAuthRoute.js
├── models
│ ├── Post.js
│ ├── Tag.js
│ ├── User.js
│ └── contactMessage.js
└── routes
│ ├── admin.js
│ └── main.js
└── views
├── 404.ejs
├── about.ejs
├── admin
├── add-post.ejs
├── dashboard.ejs
├── edit-post.ejs
└── tags.ejs
├── contact.ejs
├── index.ejs
├── layouts
├── admin.ejs
└── main.ejs
├── login.ejs
├── partials
├── footer.ejs
├── header.ejs
├── header_admin.ejs
└── search.ejs
├── post.ejs
├── posts.ejs
└── register.ejs
/.github/ISSUE_TEMPLATE/bug_report.md:
--------------------------------------------------------------------------------
1 | ---
2 | name: Bug report
3 | about: Create a report to help us improve
4 | title: ''
5 | labels: ''
6 | assignees: ''
7 |
8 | ---
9 |
10 | **Describe the bug**
11 | A clear and concise description of what the bug is.
12 |
13 | **To Reproduce**
14 | Steps to reproduce the behavior:
15 | 1. Go to '...'
16 | 2. Click on '....'
17 | 3. Scroll down to '....'
18 | 4. See error
19 |
20 | **Expected behavior**
21 | A clear and concise description of what you expected to happen.
22 |
23 | **Screenshots**
24 | If applicable, add screenshots to help explain your problem.
25 |
26 | **Desktop (please complete the following information):**
27 | - OS: [e.g. iOS]
28 | - Browser [e.g. chrome, safari]
29 | - Version [e.g. 22]
30 |
31 | **Smartphone (please complete the following information):**
32 | - Device: [e.g. iPhone6]
33 | - OS: [e.g. iOS8.1]
34 | - Browser [e.g. stock browser, safari]
35 | - Version [e.g. 22]
36 |
37 | **Additional context**
38 | Add any other context about the problem here.
39 |
--------------------------------------------------------------------------------
/.github/ISSUE_TEMPLATE/custom.md:
--------------------------------------------------------------------------------
1 | ---
2 | name: Custom issue template
3 | about: Describe this issue template's purpose here.
4 | title: ''
5 | labels: ''
6 | assignees: ''
7 |
8 | ---
9 |
10 |
11 |
--------------------------------------------------------------------------------
/.github/ISSUE_TEMPLATE/feature_request.md:
--------------------------------------------------------------------------------
1 | ---
2 | name: Feature request
3 | about: Suggest an idea for this project
4 | title: ''
5 | labels: ''
6 | assignees: ''
7 |
8 | ---
9 |
10 | **Is your feature request related to a problem? Please describe.**
11 | A clear and concise description of what the problem is. Ex. I'm always frustrated when [...]
12 |
13 | **Describe the solution you'd like**
14 | A clear and concise description of what you want to happen.
15 |
16 | **Describe alternatives you've considered**
17 | A clear and concise description of any alternative solutions or features you've considered.
18 |
19 | **Additional context**
20 | Add any other context or screenshots about the feature request here.
21 |
--------------------------------------------------------------------------------
/.github/workflows/greetings.yml:
--------------------------------------------------------------------------------
1 | name: Greetings
2 |
3 | on:
4 | pull_request_target:
5 | types: [opened, reopened, closed]
6 | issues:
7 | types: [opened, closed]
8 |
9 | jobs:
10 | greeting:
11 | runs-on: ubuntu-latest
12 | permissions:
13 | issues: write
14 | pull-requests: write
15 | steps:
16 | - uses: actions/first-interaction@v1
17 | with:
18 | repo-token: ${{ secrets.GITHUB_TOKEN }}
19 | issue-message: "👋 Hey there, rockstar! Thanks for dropping an issue! The BlogLog team is on it like pineapple on pizza (love it or hate it). Stick around, magic's about to happen!"
20 | pr-message: "🎉 Boom! Your pull request just flew into the BlogLog HQ. High fives all around! Our team of tech wizards will check it out and get back to you faster than you can say 'code ninja!' Thanks for leveling up the project!"
21 |
22 | congratulate:
23 | if: github.event.action == 'closed' && github.event.issue != null
24 | runs-on: ubuntu-latest
25 | permissions:
26 | issues: write
27 | steps:
28 | - name: Congratulate on Issue Closure
29 | run: |
30 | curl -X POST \
31 | -H "Authorization: token ${{ secrets.GITHUB_TOKEN }}" \
32 | -H "Accept: application/vnd.github.v3+json" \
33 | https://api.github.com/repos/${{ github.repository }}/issues/${{ github.event.issue.number }}/comments \
34 | -d '{"body": "🎉 Congratulations @'${{ github.event.issue.user.login }}'! Your issue has been successfully closed! Thanks for your contribution! If you enjoyed contributing, please consider giving us a ⭐ and following us for updates!"}'
35 |
--------------------------------------------------------------------------------
/.github/workflows/pr_checker.yaml:
--------------------------------------------------------------------------------
1 | name: PR Validation
2 |
3 | # Created by smog-root
4 |
5 | on:
6 | pull_request:
7 | types: [opened, edited]
8 |
9 | jobs:
10 | validate-pr:
11 | runs-on: ubuntu-latest
12 |
13 | steps:
14 | - name: Check out code
15 | uses: actions/checkout@v3
16 |
17 | - name: Set up Node.js
18 | uses: actions/setup-node@v3
19 | with:
20 | node-version: '14'
21 |
22 | - name: Validate PR Description
23 | id: pr-check
24 | run: |
25 | # Fetch PR information
26 | PR_DESCRIPTION=$(jq -r .pull_request.body < "$GITHUB_EVENT_PATH")
27 | PR_TITLE=$(jq -r .pull_request.title < "$GITHUB_EVENT_PATH")
28 |
29 | # Define file paths for the output variables
30 | PR_VALID_FILE=$(mktemp)
31 | ERROR_MESSAGE_FILE=$(mktemp)
32 | SUCCESS_MESSAGE_FILE=$(mktemp)
33 |
34 | # Default value for PR_VALID
35 | PR_VALID="true"
36 |
37 | # Check if PR description is empty
38 | if [ -z "$PR_DESCRIPTION" ] || [ "$PR_DESCRIPTION" == "null" ]; then
39 | echo "Empty PR description"
40 | PR_VALID="false"
41 | echo '❌ Error: PR description is empty!' > "$ERROR_MESSAGE_FILE"
42 | fi
43 |
44 | # Check for issue reference in the description
45 | ISSUE_PATTERN="(Fixes|Close|Closes|Closed|Fix|Fixed|Resolve|Resolves) #[0-9]+"
46 | if [[ ! "$PR_DESCRIPTION" =~ $ISSUE_PATTERN ]]; then
47 | echo "Invalid or missing issue reference"
48 | PR_VALID="false"
49 | echo '❌ Error: PR must reference an issue with the format Fixes ,Close ,Closes ,Closed ,Fix ,Fixed ,Resolve ,Resolves #Issue_Number' > "$ERROR_MESSAGE_FILE"
50 | fi
51 |
52 | # If both checks pass
53 | if [ "$PR_VALID" == "true" ]; then
54 | echo '✅ Success: PR is valid!' > "$SUCCESS_MESSAGE_FILE"
55 | fi
56 |
57 | # Save the outputs to environment files
58 | echo "PR_VALID=$PR_VALID" >> $GITHUB_ENV
59 | echo "ERROR_MESSAGE=$(cat $ERROR_MESSAGE_FILE)" >> $GITHUB_ENV
60 | echo "SUCCESS_MESSAGE=$(cat $SUCCESS_MESSAGE_FILE)" >> $GITHUB_ENV
61 |
62 | - name: Post comment on PR
63 | uses: actions/github-script@v6
64 | with:
65 | github-token: ${{ secrets.GITHUB_TOKEN }}
66 | script: |
67 | const prValid = process.env.PR_VALID;
68 | const errorMessage = process.env.ERROR_MESSAGE;
69 | const successMessage = process.env.SUCCESS_MESSAGE;
70 | const prNumber = context.payload.pull_request.number;
71 |
72 | if (prValid === 'false') {
73 | github.rest.issues.createComment({
74 | issue_number: prNumber,
75 | owner: context.repo.owner,
76 | repo: context.repo.repo,
77 | body: errorMessage
78 | });
79 | core.setFailed(errorMessage);
80 | } else {
81 | github.rest.issues.createComment({
82 | issue_number: prNumber,
83 | owner: context.repo.owner,
84 | repo: context.repo.repo,
85 | body: successMessage
86 | });
87 | }
88 |
89 | - name: Fail if validation failed
90 | if: env.PR_VALID == 'false'
91 | run: exit 1
92 |
--------------------------------------------------------------------------------
/.gitignore:
--------------------------------------------------------------------------------
1 | node_modules
2 | .env
3 | logs
--------------------------------------------------------------------------------
/README.md:
--------------------------------------------------------------------------------
1 | # BlogLog 📝
2 |
3 | Welcome to **BlogLog**, your personalized blogging companion designed to help you effortlessly keep track of your thoughts, experiences, and reflections all in one convenient log. Built with **Node.js**, **Express**, and **MongoDB**, BlogLog provides a user-friendly interface to create, read, update, and delete (CRUD) your blog posts.
4 |
5 |
6 |
7 |
8 |
🌟 Stars
9 |
🍴 Forks
10 |
🐛 Issues
11 |
🔔 Open PRs
12 |
🔕 Close PRs
13 |
14 |
15 |
16 |
17 |
18 |
19 |
20 |
21 |
22 |
23 |
24 |
25 |
26 | ## Key Features 🌟
27 |
28 | - **📝 Create New Blog Posts**: Easily compose and publish your thoughts.
29 | - **👁️ Read & View**: Browse through your blog posts with a simple and intuitive design.
30 | - **✏️ Update Posts**: Modify existing blog entries to reflect your current thoughts.
31 | - **❌ Delete Posts**: Remove any blog posts you no longer wish to keep.
32 | - **📱 Responsive Design**: Enjoy a seamless experience across all devices.
33 | - **🖱️ User-Friendly Interface**: Navigate effortlessly with an easy-to-use interface.
34 |
35 | ## Installation 🚀
36 |
37 | To get started with BlogLog, follow these steps:
38 |
39 | 1. **Clone the Repository**:
40 | ```bash
41 | git clone https://github.com/yourusername/bloglog.git
42 | cd bloglog
43 | ```
44 |
45 | 2. **Install Dependencies**:
46 | ```bash
47 | npm install
48 | ```
49 |
50 | 3. Set Up Environment Variables:
51 |
52 | Create a `.env` file in the root directory and add the following line:
53 | ```bash
54 | ADMIN_USERNAME=username of admins seprarated by ","
55 |
56 | MONGODB_URI=mongodb://localhost:27017/bloglog
57 | EMAIL_USERNAME=
58 | EMAIL_APP_PASSWORD=
59 | JWT_SECRET=
60 |
61 | CLOUDINARY_CLOUD_NAME=
62 | CLOUDINARY_API_KEY=
63 | CLOUDINARY_API_SECRET=
64 | ```
65 |
66 | 4. Start the Server:
67 | ```bash
68 | npm start
69 | ```
70 |
71 | The application will be available at `http://localhost:5000` 🌐
72 |
73 | ## Usage 🛠️
74 | After starting the server, open your browser and navigate to `http://localhost:5000`. From there, you can create, view, update, and delete your blog posts.
75 |
76 | ## Contributing 🤝
77 | We welcome contributions! Here’s how you can contribute:
78 |
79 | 1. **Fork the Repository**: Create a personal copy of the repository on your GitHub account.
80 |
81 | 2. **Create Your Branch**: Develop your feature or fix.
82 | ```bash
83 | git checkout -b feature/YourFeatureName
84 | ```
85 |
86 | 3. **Commit Your Changes**: Make sure to write meaningful commit messages.
87 | ```bash
88 | git commit -m "Add some feature"
89 | ```
90 |
91 | 4. **Open a Pull Request**: Push your changes to your fork and submit a pull request.
92 |
93 | 5. Add Detailed Descriptions: Include any relevant information and screenshots if applicable.
94 |
95 |
96 | ## License 📜
97 | This project is licensed under the [MIT License](LICENSE)
98 |
99 | ## Acknowledgments 🙌
100 |
101 | - Thanks to the open-source community for their valuable resources.
102 | - Special thanks to the contributors who have made this project better.
103 |
104 |
105 | ## Our Contributors 👀
106 |
107 | - We extend our heartfelt gratitude for your invaluable contribution to our project! Your efforts play a pivotal role in elevating Ratna-Supermarket to greater heights.
108 | - Make sure you show some love by giving ⭐ to our repository.
109 |
110 |
186 | `,
187 | }
188 | await transporter.sendMail(mailOptions);
189 |
190 | // Render the contact page with a success message
191 | res.render('contact', {
192 | currentRoute: '/contact',
193 | message: 'Thank you for reaching out! We will get back to you soon.',
194 | });
195 | } catch (error) {
196 | console.error(error);
197 | res.render('contact', {
198 | currentRoute: '/contact',
199 | message: 'There was an error sending your message. Please try again later.',
200 | });
201 | }
202 | });
203 |
204 |
205 | /* Authentication */
206 |
207 | router.get('/login', async (req, res) => {
208 | try {
209 | const locals = {
210 | title: 'Login',
211 | description: 'Simple Blog created with NodeJs, Express & MongoDb.',
212 | };
213 |
214 | res.render('login', { locals, currentRoute: '/login' });
215 | } catch (error) {
216 | console.log(error);
217 | }
218 | });
219 |
220 | router.post('/login', async (req, res, next) => {
221 | passport.authenticate('local', async (err, user, info) => {
222 | if (err) {
223 | return res.status(500).json({ message: 'Internal server error' });
224 | }
225 | if (!user) {
226 | return res.status(401).json({ message: 'Unauthorized' });
227 | }
228 | req.logIn(user, async (err) => {
229 | if (err) {
230 | return res.status(500).json({ message: 'Error logging in' });
231 | }
232 |
233 | const data = {
234 | id: user._id,
235 | username: user.username,
236 | admin: process.env.ADMIN_USERNAME.split(",").some(x => x === user.username),
237 | }
238 |
239 | const token = jwt.sign(data, jwtSecret, { expiresIn: '1h' });
240 | res.cookie('user', Object.assign(data, { token }))
241 |
242 | return res.redirect('/');
243 | });
244 | })(req, res, next);
245 | });
246 |
247 |
248 | router.get('/register', (req, res) => {
249 | // Initialize messages object, you can adjust it according to your error handling logic
250 | const locals = {
251 | title: 'Admin',
252 | description: 'Simple Blog created with NodeJs, Express & MongoDb.',
253 | };
254 |
255 | res.render('register', { locals, currentRoute: '/register' });
256 | });
257 |
258 |
259 |
260 | router.post('/register', validateRegistration, async (req, res) => {
261 | const { username, password } = req.body;
262 |
263 | // Simple validation
264 | if (!username || !password) {
265 | req.flash('error', 'All fields are required');
266 | return res.redirect('/register'); // Change to '/register'
267 | }
268 |
269 | if (!/^[a-zA-Z0-9]+$/.test(username) || username.length < 3) {
270 | req.flash('error', 'Username must be at least 3 characters long and contain only alphanumeric characters.');
271 | return res.redirect('/register');
272 | }
273 |
274 | if (password.length < 8 || !/\d/.test(password) || !/[!@#$%^&*]/.test(password)) {
275 | req.flash('error', 'Password must be at least 8 characters long, contain a number, and a special character.');
276 | return res.redirect('/register');
277 | }
278 |
279 | try {
280 | const existingUser = await User.findOne({ username });
281 |
282 | if (existingUser) {
283 | req.flash('error', 'Username already taken');
284 | return res.redirect('/register'); // Change to '/register'
285 | }
286 |
287 | // Hash password and create new user
288 | const hashedPassword = await bcrypt.hash(password, 10);
289 | const user = new User({ username, password: hashedPassword });
290 | await user.save();
291 |
292 | // Automatically log the user in
293 | req.login(user, (err) => {
294 | if (err) return res.status(500).json({ message: 'Error logging in after registration' });
295 |
296 | const data = {
297 | id: user._id,
298 | username: user.username,
299 | admin: process.env.ADMIN_USERNAME.split(",").some(x => x === user.username),
300 | }
301 |
302 | const token = jwt.sign(data, jwtSecret, { expiresIn: '1h' });
303 | res.cookie('user', Object.assign(data, { token }), { httpOnly: true });
304 |
305 | return res.redirect('/');
306 | });
307 | } catch (error) {
308 | console.log(error);
309 | res.status(500).json({ message: 'Internal server error' });
310 | }
311 | });
312 |
313 |
314 | /**
315 | * GET /logout
316 | * Admin Logout Route
317 | */
318 | router.get('/logout', (req, res) => {
319 |
320 | req.logout((err) => {
321 | if (err) {
322 | return next(err);
323 | }
324 | res.clearCookie('user');
325 | res.redirect('/');
326 | });
327 | });
328 |
329 | // function insertPostData() {
330 | // Post.insertMany([
331 | // {
332 | // title: "Understanding the Basics of HTML and CSS",
333 | // body: "HTML (HyperText Markup Language) and CSS (Cascading Style Sheets) are the fundamental technologies for building web pages. HTML provides the structure of the page, while CSS is used to control the presentation, formatting, and layout. This blog post will guide you through the essential concepts and elements of HTML and CSS, providing examples and best practices for creating well-structured and visually appealing web pages.",
334 | // author: "Rishabh"
335 | // },
336 | // {
337 | // title: "An Introduction to JavaScript for Beginners",
338 | // body: "JavaScript is a versatile programming language that allows you to create dynamic and interactive web content. This post will cover the basics of JavaScript, including variables, data types, functions, and control structures. You'll learn how to add interactivity to your web pages and understand how JavaScript interacts with HTML and CSS to enhance user experiences.",
339 | // author: "Rishabh"
340 | // },
341 | // {
342 | // title: "Building Responsive Web Designs with CSS Grid and Flexbox",
343 | // body: "Responsive web design ensures that your website looks great on all devices, from desktops to mobile phones. CSS Grid and Flexbox are powerful layout modules that help you create flexible and responsive designs. In this blog, we'll explore the concepts and practical implementations of CSS Grid and Flexbox, with code examples and tips for creating responsive layouts.",
344 | // author: "Rishabh"
345 | // },
346 | // {
347 | // title: "Getting Started with React: A JavaScript Library for Building User Interfaces",
348 | // body: "React is a popular JavaScript library developed by Facebook for building user interfaces. It allows developers to create large web applications that can update and render efficiently in response to data changes. This post will introduce you to the core concepts of React, including components, JSX, state, and props, and guide you through setting up a basic React application.",
349 | // author: "Rishabh"
350 | // },
351 | // {
352 | // title: "Understanding RESTful APIs and How to Integrate Them into Your Web Applications",
353 | // body: "RESTful APIs (Application Programming Interfaces) are a set of rules and conventions for building and interacting with web services. They allow different software systems to communicate with each other. This blog post will explain what RESTful APIs are, how they work, and how to integrate them into your web applications using JavaScript and AJAX.",
354 | // author: "Rishabh"
355 | // },
356 | // {
357 | // title: "A Guide to Modern JavaScript Frameworks: Angular, Vue, and Svelte",
358 | // body: "Modern JavaScript frameworks like Angular, Vue, and Svelte have revolutionized web development by providing robust tools for building complex applications. This post will compare these three frameworks, discussing their unique features, strengths, and use cases. By the end of this guide, you'll have a better understanding of which framework might be the best fit for your next project.",
359 | // author: "Rishabh"
360 | // },
361 | // {
362 | // title: "Enhancing Web Performance with Lazy Loading and Code Splitting",
363 | // body: "Web performance is crucial for user experience and SEO. Lazy loading and code splitting are techniques that can significantly improve the load times of your web pages. This blog will explain how lazy loading defers the loading of non-critical resources and how code splitting breaks down your code into smaller bundles. Examples and implementation strategies will be provided to help you optimize your web performance.",
364 | // author: "Rishabh"
365 | // },
366 | // {
367 | // title: "Mastering Git and GitHub for Version Control",
368 | // body: "Git is a distributed version control system that helps developers track changes in their code. GitHub is a platform for hosting Git repositories. This post will cover the basics of Git and GitHub, including how to create repositories, commit changes, branch, merge, and collaborate with others. Understanding these tools is essential for modern web development and team collaboration.",
369 | // author: "Rishabh"
370 | // },
371 | // {
372 | // title: "Implementing Authentication in Web Applications with JWT",
373 | // body: "JSON Web Tokens (JWT) are a compact and secure way to transmit information between parties as a JSON object. They are commonly used for authentication and authorization in web applications. This blog post will guide you through the process of implementing JWT authentication in a web application, including generating tokens, securing endpoints, and managing user sessions.",
374 | // author: "Rishabh"
375 | // },
376 | // {
377 | // title: "Exploring the Power of CSS Preprocessors: Sass and LESS",
378 | // body: "CSS preprocessors like Sass and LESS extend the capabilities of standard CSS by adding features like variables, nesting, and mixins. This post will introduce you to Sass and LESS, showing you how to install and use them to write more efficient and maintainable CSS code. Examples and practical tips will help you get started with these powerful tools.",
379 | // author: "Rishabh"
380 | // }
381 | // ])
382 | // }
383 |
384 | // // Call the function to insert 10 posts
385 | // insertPostData();
386 |
387 |
388 | module.exports = router;
389 |
--------------------------------------------------------------------------------
/views/404.ejs:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 | 404 PAGE
7 |
8 |
9 |
10 |
41 |
42 |
43 |
44 |
45 |
46 |
47 |
BlogLog is your personalized blogging companion tailored to keep track of your thoughts, experiences, and reflections in one convenient log.
4 |
5 |
Key Features:
6 |
7 |
Create new blog posts effortlessly.
8 |
Read and view individual blog posts with ease.
9 |
Update existing blog posts in a few simple steps.
10 |
Delete blog posts when they're no longer needed.
11 |
Responsive design for a seamless experience on any device.
12 |
User-friendly interface for intuitive navigation and interaction.
13 |
14 |
15 |
Why Choose BlogLog?
16 |
BlogLog is designed to make blogging enjoyable and stress-free. Whether you're a seasoned writer or a first-time blogger, you'll find the tools you need to express yourself creatively.
17 |
18 |
19 |
20 |
Get Started Today!
21 |
Join our growing community of bloggers and start your journey with BlogLog today! It’s quick and easy to create your first post.
22 | Create Your First Post
23 |
24 |
--------------------------------------------------------------------------------
/views/admin/add-post.ejs:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 | Add a New Blog
5 |
252 |
253 |
254 |
255 | ← Back
256 |