From 856674475a5e6f8e647fc661cea17dc1f6758cc9 Mon Sep 17 00:00:00 2001 From: Simran Saini Date: Fri, 7 Jun 2024 19:32:36 +0530 Subject: [PATCH 1/2] Added Url-Shortener project under Node-JS-Projects --- .../Advanced/Url-Shortener/README.md | 82 ++++++++++++++ .../Advanced/Url-Shortener/package.json | 18 +++ .../Url-Shortener/public/css/styles.css | 106 ++++++++++++++++++ .../Advanced/Url-Shortener/public/index.html | 26 +++++ .../Advanced/Url-Shortener/public/js/main.js | 30 +++++ .../Advanced/Url-Shortener/screenshot.webp | Bin 0 -> 20372 bytes .../src/controllers/urlController.js | 43 +++++++ .../Advanced/Url-Shortener/src/db.js | 16 +++ .../Url-Shortener/src/models/urlModel.js | 22 ++++ .../Url-Shortener/src/routes/urlRoutes.js | 10 ++ .../Advanced/Url-Shortener/src/server.js | 22 ++++ .../Url-Shortener/src/utils/urlShortener.js | 9 ++ Node-JS-Projects/README.md | 1 + 13 files changed, 385 insertions(+) create mode 100644 Node-JS-Projects/Advanced/Url-Shortener/README.md create mode 100644 Node-JS-Projects/Advanced/Url-Shortener/package.json create mode 100644 Node-JS-Projects/Advanced/Url-Shortener/public/css/styles.css create mode 100644 Node-JS-Projects/Advanced/Url-Shortener/public/index.html create mode 100644 Node-JS-Projects/Advanced/Url-Shortener/public/js/main.js create mode 100644 Node-JS-Projects/Advanced/Url-Shortener/screenshot.webp create mode 100644 Node-JS-Projects/Advanced/Url-Shortener/src/controllers/urlController.js create mode 100644 Node-JS-Projects/Advanced/Url-Shortener/src/db.js create mode 100644 Node-JS-Projects/Advanced/Url-Shortener/src/models/urlModel.js create mode 100644 Node-JS-Projects/Advanced/Url-Shortener/src/routes/urlRoutes.js create mode 100644 Node-JS-Projects/Advanced/Url-Shortener/src/server.js create mode 100644 Node-JS-Projects/Advanced/Url-Shortener/src/utils/urlShortener.js diff --git a/Node-JS-Projects/Advanced/Url-Shortener/README.md b/Node-JS-Projects/Advanced/Url-Shortener/README.md new file mode 100644 index 00000000..415ff3a2 --- /dev/null +++ b/Node-JS-Projects/Advanced/Url-Shortener/README.md @@ -0,0 +1,82 @@ +

đŸ’Ĩ URL-Shortner đŸ’Ĩ

+ + + +

Tech Stack Used 🎮

+ + +
+ HTML5 + CSS3 + JavaScript + Node.js + Express.js + MongoDB +
+ + + +![Line](https://github.com/Avdhesh-Varshney/WebMasterLog/assets/114330097/4b78510f-a941-45f8-a9d5-80ed0705e847) + + + +## :zap: Description 📃 + +
+ +

A simple Node.js URL shortener using ExpressJS and MongoDB. The primary aim of a URL shortener is to convert long URLs into shorter, more manageable links. The shortened URL redirects users to the original, longer URL when accessed.

+
+ + + + +## :zap: How to run it? 🕹ī¸ + + +Follow these steps to set up the project locally: + +### Clone the Repository + +First, clone the repository to your local machine: + +```bash +git clone https://github.com/1simransaini/Url-Shortener.git +``` +Navigate to the project directory and install the required dependencies: +```bash +cd Url-Shortener +npm install +``` +Create a .env file in the root of the project directory and add the following environment variables: +```bash +PORT=3000 +MONGO_URL=yours +``` +Run the program as +```bash +node src/server.js +``` + + +## :zap: Screenshots 📸 + +![1](https://github.com/Avdhesh-Varshney/WebMasterLog/assets/91106038/26624ca0-5639-4fe4-9c7f-f0ec4241406a) + +![Line](https://github.com/Avdhesh-Varshney/WebMasterLog/assets/114330097/4b78510f-a941-45f8-a9d5-80ed0705e847) + + + +

Developed By Simran Saini 👩

+

+ + + + + + +

+ +

Happy Coding 👩

+ +

Show some  â¤ī¸  by  đŸŒŸ  this repository!

+ diff --git a/Node-JS-Projects/Advanced/Url-Shortener/package.json b/Node-JS-Projects/Advanced/Url-Shortener/package.json new file mode 100644 index 00000000..bbf9ad45 --- /dev/null +++ b/Node-JS-Projects/Advanced/Url-Shortener/package.json @@ -0,0 +1,18 @@ +{ + "name": "urlshortner", + "version": "1.0.0", + "description": "", + "main": "index.js", + "scripts": { + "dev": "nodemon src/server.js", + "start": "nodemon src/server.js" + }, + "author": "Simran Saini", + "license": "ISC", + "dependencies": { + "body-parser": "^1.20.2", + "dotenv": "^16.3.1", + "express": "^4.18.2", + "mongoose": "^7.5.1" + } +} \ No newline at end of file diff --git a/Node-JS-Projects/Advanced/Url-Shortener/public/css/styles.css b/Node-JS-Projects/Advanced/Url-Shortener/public/css/styles.css new file mode 100644 index 00000000..457def46 --- /dev/null +++ b/Node-JS-Projects/Advanced/Url-Shortener/public/css/styles.css @@ -0,0 +1,106 @@ +body { + font-family: 'Arial', sans-serif; + background-color: #e0e0e0; /* background color */ + margin: 0; + padding: 0; + line-height: 1.6; +} + +.container { + max-width: 600px; + margin: 50px auto; + background-color: #ffffff; /* background color */ + padding: 20px; + border-radius: 5px; + box-shadow: 0px 0px 10px rgba(0, 0, 0, 0.1); +} + +header { + text-align: center; + margin-bottom: 20px; + background-color: #007BFF; /* Added background color */ + padding: 10px; + border-radius: 5px 5px 0 0; +} + +header h1 { + color: #ffffff; /* text color */ + margin: 0; + font-size: 2em; +} + +header p { + color: #dddddd; /* text color */ + font-size: 1em; +} + +input[type="url"] { + width: 100%; + padding: 10px; + margin-bottom: 10px; + border: 1px solid #ddd; + border-radius: 5px; + box-sizing: border-box; + transition: border-color 0.3s; +} + +input[type="url"]:focus { + border-color: #007BFF; + outline: none; +} + +button { + width: 100%; + padding: 10px; + background-color: #28a745; /* background color */ + color: #fff; + border: none; + border-radius: 5px; + cursor: pointer; + transition: background-color 0.3s, transform 0.3s; +} + +button:hover { + background-color: #218838; /* hover background color */ + transform: scale(1.05); +} + +button:active { + background-color: #1e7e34; /* active background color */ +} + +#result { + margin-top: 20px; + text-align: center; + font-size: 18px; + color: #28a745; /* text color */ +} + +#result a { + color: #155724; /* link color */ + text-decoration: none; + transition: color 0.3s; +} + +#result a:hover { + color: #0c3d12; /* hover link color */ +} + +@media (max-width: 600px) { + .container { + margin: 20px; + padding: 15px; + } + + header h1 { + font-size: 1.5em; + } + + header p { + font-size: 0.9em; + } + + button { + padding: 12px; + } +} diff --git a/Node-JS-Projects/Advanced/Url-Shortener/public/index.html b/Node-JS-Projects/Advanced/Url-Shortener/public/index.html new file mode 100644 index 00000000..4c9bb7dc --- /dev/null +++ b/Node-JS-Projects/Advanced/Url-Shortener/public/index.html @@ -0,0 +1,26 @@ + + + + + + + URL SHORTENER + + + + +
+
+

URL SHORTENER

+

Shorten your lengthy URLs in a snap!!

+
+
+ + +
+
+ +
+ + + \ No newline at end of file diff --git a/Node-JS-Projects/Advanced/Url-Shortener/public/js/main.js b/Node-JS-Projects/Advanced/Url-Shortener/public/js/main.js new file mode 100644 index 00000000..113d47e3 --- /dev/null +++ b/Node-JS-Projects/Advanced/Url-Shortener/public/js/main.js @@ -0,0 +1,30 @@ +document.getElementById('shortenBtn').addEventListener('click', async () => { + const originalUrl = document.getElementById('originalUrl').value; + if (!originalUrl || !isValidUrl(originalUrl)) { + alert('Please enter a valid URL'); + return; + } + try { + const response = await fetch('/shorten', { + method: 'POST', + headers: { + 'Content-Type': 'application/json' + }, + body: JSON.stringify({ originalUrl }) + }); + const data = await response.json(); + document.getElementById('result').textContent = `Shortened URL : ${window.location.href}${data.shortUrl}`; + } catch (err) { + console.error('Error shortening the URL : ', err); + alert('Error shortening the URL , PLEASE TRY AGAIN'); + } + +}); +function isValidUrl(string) { + try { + new URL(string); + return true; + } catch (_) { + return false; + } +} \ No newline at end of file diff --git a/Node-JS-Projects/Advanced/Url-Shortener/screenshot.webp b/Node-JS-Projects/Advanced/Url-Shortener/screenshot.webp new file mode 100644 index 0000000000000000000000000000000000000000..0d34e5f0de76403f86b5515b5f858f689eb71131 GIT binary patch literal 20372 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 RX7YCzH@128Yu8r?{y(u~J?8)b literal 0 HcmV?d00001 diff --git a/Node-JS-Projects/Advanced/Url-Shortener/src/controllers/urlController.js b/Node-JS-Projects/Advanced/Url-Shortener/src/controllers/urlController.js new file mode 100644 index 00000000..a8322f58 --- /dev/null +++ b/Node-JS-Projects/Advanced/Url-Shortener/src/controllers/urlController.js @@ -0,0 +1,43 @@ +const Url = require('../models/urlModel'); +const UrlShortener = require('../utils/urlShortener'); +class UrlController { + static async shortenUrl(req, res) { + try { + const { originalUrl } = req.body; + if (!originalUrl) + return res.status(400).json({ message: "URL is required" }); + //check if the url is already shortened + let url = await Url.findOne({ originalUrl }); + if (url) { + return res.json(url); + } + //generate a short url + const shortUrl = UrlShortener.generateShortUrl(); + //save to the database + url = new Url({ + originalUrl, shortUrl + }); + await url.save(); + res.json(url); + } catch (err) { + res.status(500).json({ message: 'Sever error : ' + err.message }); + } + } + static async redirectToOriginalUrl(req, res) { + try { + const { shortUrl } = req.params; + const url = await Url.findOne({ shortUrl: shortUrl }); + if (!url) { + return res.status(404).send('URL NOT FOUND'); + } + //increase the count + url.clicks += 1; + await url.save(); + res.redirect(url.originalUrl); + } catch (err) { + console.error(err); + res.status(500).send('Server Error'); + } + } +} +module.exports = UrlController; \ No newline at end of file diff --git a/Node-JS-Projects/Advanced/Url-Shortener/src/db.js b/Node-JS-Projects/Advanced/Url-Shortener/src/db.js new file mode 100644 index 00000000..b1ff180f --- /dev/null +++ b/Node-JS-Projects/Advanced/Url-Shortener/src/db.js @@ -0,0 +1,16 @@ +const mongoose = require('mongoose'); +//requing the .env file +require('dotenv').config(); +const connectDB = async () => { + try { + await mongoose.connect(process.env.MONGO_URL, { + useNewUrlParser: true, + useUnifiedTopology: true + }); + console.log("Connected to MONGODB ...") + } catch (err) { + console.error('Error connecting to database : ', err); + } +} + +module.exports = connectDB; \ No newline at end of file diff --git a/Node-JS-Projects/Advanced/Url-Shortener/src/models/urlModel.js b/Node-JS-Projects/Advanced/Url-Shortener/src/models/urlModel.js new file mode 100644 index 00000000..ed67c7f9 --- /dev/null +++ b/Node-JS-Projects/Advanced/Url-Shortener/src/models/urlModel.js @@ -0,0 +1,22 @@ +const mongoose = require('mongoose'); +const urlSchema = new mongoose.Schema({ + originalUrl: { + type: String, + required: true + }, + shortUrl: { + type: String, + required: true, + unique: true + }, + clicks: { + type: Number, + default: 0 + }, + createdAt: { + type: Date, + default: Date.now + } +}); +const Url = mongoose.model('Url', urlSchema); +module.exports = Url; \ No newline at end of file diff --git a/Node-JS-Projects/Advanced/Url-Shortener/src/routes/urlRoutes.js b/Node-JS-Projects/Advanced/Url-Shortener/src/routes/urlRoutes.js new file mode 100644 index 00000000..afffb8c8 --- /dev/null +++ b/Node-JS-Projects/Advanced/Url-Shortener/src/routes/urlRoutes.js @@ -0,0 +1,10 @@ +const express = require('express'); +const urlController = require('../controllers/urlController'); + + +const router = express.Router(); + +router.post('/shorten', urlController.shortenUrl); +router.get('/:shortUrl', urlController.redirectToOriginalUrl); + +module.exports = router; \ No newline at end of file diff --git a/Node-JS-Projects/Advanced/Url-Shortener/src/server.js b/Node-JS-Projects/Advanced/Url-Shortener/src/server.js new file mode 100644 index 00000000..1dcfa9dd --- /dev/null +++ b/Node-JS-Projects/Advanced/Url-Shortener/src/server.js @@ -0,0 +1,22 @@ +const express = require('express'); +const bodyParser = require('body-parser'); +const connectDB = require('./db'); +const urlRoutes = require('./routes/urlRoutes'); +// const path = require('path'); +const app = express(); +const PORT = 3000; + +//connect to the database +connectDB(); + +//middleware +app.use(bodyParser.json()); + + +//middleware to serv static files +app.use('/', urlRoutes); +app.use(express.static('public')); + +app.listen(PORT, () => { + console.log(`Server is running fine on ${PORT}`); +}) \ No newline at end of file diff --git a/Node-JS-Projects/Advanced/Url-Shortener/src/utils/urlShortener.js b/Node-JS-Projects/Advanced/Url-Shortener/src/utils/urlShortener.js new file mode 100644 index 00000000..82cf41fb --- /dev/null +++ b/Node-JS-Projects/Advanced/Url-Shortener/src/utils/urlShortener.js @@ -0,0 +1,9 @@ +const crypto = require('crypto'); + +class UrlShortener { + static generateShortUrl() { + return crypto.randomBytes(6).toString('hex'); + } +} + +module.exports = UrlShortener; \ No newline at end of file diff --git a/Node-JS-Projects/README.md b/Node-JS-Projects/README.md index 99239c6d..214b8369 100644 --- a/Node-JS-Projects/README.md +++ b/Node-JS-Projects/README.md @@ -29,6 +29,7 @@ | 2 | [Infinite Scrolling](./Basic/Infinite-Scrolling) | ![Basic](https://img.shields.io/badge/Basic-00FF00?style=for-the-badge) | | 3 | [CRUD Operations](./Basic/Crud-Operations) | ![Basic](https://img.shields.io/badge/Basic-00FF00?style=for-the-badge) | | 4 | [YouTube Video Downloader](./Intermediate/YouTube-Video-Downloader) | ![Intermediate](https://img.shields.io/badge/Intermediate-FFD700?style=for-the-badge) | +| 5 | [Url Shortener](./Advanced/Url-Shortener) | ![Advanced](https://img.shields.io/badge/Advanced-FF0000?style=for-the-badge)| From 1ad1493645f0481c5dc2c048eff9209784a83865 Mon Sep 17 00:00:00 2001 From: Simran Saini Date: Fri, 7 Jun 2024 19:57:16 +0530 Subject: [PATCH 2/2] Added rate limiting and safe database queries to resolve security issues --- .../src/controllers/urlController.js | 41 ++++++++++++------- .../Url-Shortener/src/routes/urlRoutes.js | 17 ++++++-- 2 files changed, 39 insertions(+), 19 deletions(-) diff --git a/Node-JS-Projects/Advanced/Url-Shortener/src/controllers/urlController.js b/Node-JS-Projects/Advanced/Url-Shortener/src/controllers/urlController.js index a8322f58..35d9a3ac 100644 --- a/Node-JS-Projects/Advanced/Url-Shortener/src/controllers/urlController.js +++ b/Node-JS-Projects/Advanced/Url-Shortener/src/controllers/urlController.js @@ -1,36 +1,46 @@ +// src/controllers/urlController.js const Url = require('../models/urlModel'); const UrlShortener = require('../utils/urlShortener'); + class UrlController { static async shortenUrl(req, res) { try { const { originalUrl } = req.body; - if (!originalUrl) - return res.status(400).json({ message: "URL is required" }); - //check if the url is already shortened - let url = await Url.findOne({ originalUrl }); + if (!originalUrl || typeof originalUrl !== 'string') { + return res.status(400).json({ message: "A valid URL is required" }); + } + + // Check if the URL is already shortened + const url = await Url.findOne({ originalUrl }); if (url) { return res.json(url); } - //generate a short url + + // Generate a short URL const shortUrl = UrlShortener.generateShortUrl(); - //save to the database - url = new Url({ - originalUrl, shortUrl - }); - await url.save(); - res.json(url); + + // Save to the database + const newUrl = new Url({ originalUrl, shortUrl }); + await newUrl.save(); + res.json(newUrl); } catch (err) { - res.status(500).json({ message: 'Sever error : ' + err.message }); + res.status(500).json({ message: 'Server error: ' + err.message }); } } + static async redirectToOriginalUrl(req, res) { try { const { shortUrl } = req.params; - const url = await Url.findOne({ shortUrl: shortUrl }); + if (!shortUrl || typeof shortUrl !== 'string') { + return res.status(400).send('Invalid short URL'); + } + + const url = await Url.findOne({ shortUrl }); if (!url) { return res.status(404).send('URL NOT FOUND'); } - //increase the count + + // Increase the count url.clicks += 1; await url.save(); res.redirect(url.originalUrl); @@ -40,4 +50,5 @@ class UrlController { } } } -module.exports = UrlController; \ No newline at end of file + +module.exports = UrlController; diff --git a/Node-JS-Projects/Advanced/Url-Shortener/src/routes/urlRoutes.js b/Node-JS-Projects/Advanced/Url-Shortener/src/routes/urlRoutes.js index afffb8c8..64fe3ef4 100644 --- a/Node-JS-Projects/Advanced/Url-Shortener/src/routes/urlRoutes.js +++ b/Node-JS-Projects/Advanced/Url-Shortener/src/routes/urlRoutes.js @@ -1,10 +1,19 @@ +// src/routes/urlRoutes.js const express = require('express'); +const rateLimit = require('express-rate-limit'); const urlController = require('../controllers/urlController'); - const router = express.Router(); -router.post('/shorten', urlController.shortenUrl); -router.get('/:shortUrl', urlController.redirectToOriginalUrl); +// Define rate limit rule +const limiter = rateLimit({ + windowMs: 15 * 60 * 1000, // 15 minutes + max: 100, // limit each IP to 100 requests per windowMs + message: "Too many requests from this IP, please try again later." +}); + +// Apply the rate limiting middleware to relevant routes +router.post('/shorten', limiter, urlController.shortenUrl); +router.get('/:shortUrl', limiter, urlController.redirectToOriginalUrl); -module.exports = router; \ No newline at end of file +module.exports = router;