From b35c8b3a6c81692b9c5a2cab7e8454ff2ad7c988 Mon Sep 17 00:00:00 2001 From: Khaled Nashat Date: Sun, 1 Dec 2024 23:08:18 +0200 Subject: [PATCH 1/5] Add URL schema for long and shortened URLs --- models/schema.js | 26 +++++ package-lock.json | 235 ++++++++++++++++++++++++++++++++++++++++++++++ package.json | 1 + 3 files changed, 262 insertions(+) create mode 100644 models/schema.js diff --git a/models/schema.js b/models/schema.js new file mode 100644 index 0000000..51302bb --- /dev/null +++ b/models/schema.js @@ -0,0 +1,26 @@ +const mongoose = require('mongoose'); + +const urlschema = new mongoose.Schema({ + LongURL: { + type: string, + required:true, + }, + ShortenedURL: { + type: string, + required:true, + } +}); + +const URL = mongoose.model('URL',urlschema , 'urls'); //note that if we don't name the collection with (urls) it will automatically call it "urls" beacause of lowercase and ploralize od the model name URL + +URL.insertURL = function (LongURL,ShortenedURL){ + const addedURL = new URL({ + LongURL: LongURL, + ShortenedURL: ShortenedURL, + }); + + addedURL.save().then(()=> console.log("The new URL added successfully")); + +} + +module.exports = URL; diff --git a/package-lock.json b/package-lock.json index 6fd9de3..fbba671 100644 --- a/package-lock.json +++ b/package-lock.json @@ -13,12 +13,37 @@ "ejs": "^3.1.9", "express": "^4.18.2", "http-errors": "~1.6.3", + "mongoose": "^8.8.3", "morgan": "~1.9.1" }, "devDependencies": { "nodemon": "^3.0.1" } }, + "node_modules/@mongodb-js/saslprep": { + "version": "1.1.9", + "resolved": "https://registry.npmjs.org/@mongodb-js/saslprep/-/saslprep-1.1.9.tgz", + "integrity": "sha512-tVkljjeEaAhCqTzajSdgbQ6gE6f3oneVwa3iXR6csiEwXXOFsiC6Uh9iAjAhXPtqa/XMDHWjjeNH/77m/Yq2dw==", + "license": "MIT", + "dependencies": { + "sparse-bitfield": "^3.0.3" + } + }, + "node_modules/@types/webidl-conversions": { + "version": "7.0.3", + "resolved": "https://registry.npmjs.org/@types/webidl-conversions/-/webidl-conversions-7.0.3.tgz", + "integrity": "sha512-CiJJvcRtIgzadHCYXw7dqEnMNRjhGZlYK05Mj9OyktqV8uVT8fD2BFOB7S1uwBE3Kj2Z+4UyPmFw/Ixgw/LAlA==", + "license": "MIT" + }, + "node_modules/@types/whatwg-url": { + "version": "11.0.5", + "resolved": "https://registry.npmjs.org/@types/whatwg-url/-/whatwg-url-11.0.5.tgz", + "integrity": "sha512-coYR071JRaHa+xoEvvYqvnIHaVqaYrLPbsufM9BF63HkwI5Lgmy2QR8Q5K/lYDYo5AK82wOvSOS0UsLTpTG7uQ==", + "license": "MIT", + "dependencies": { + "@types/webidl-conversions": "*" + } + }, "node_modules/abbrev": { "version": "1.1.1", "resolved": "https://registry.npmjs.org/abbrev/-/abbrev-1.1.1.tgz", @@ -195,6 +220,15 @@ "node": ">=8" } }, + "node_modules/bson": { + "version": "6.10.0", + "resolved": "https://registry.npmjs.org/bson/-/bson-6.10.0.tgz", + "integrity": "sha512-ROchNosXMJD2cbQGm84KoP7vOGPO6/bOAW0veMMbzhXLqoZptcaYRVLitwvuhwhjjpU1qP4YZRWLhgETdgqUQw==", + "license": "Apache-2.0", + "engines": { + "node": ">=16.20.1" + } + }, "node_modules/bytes": { "version": "3.1.2", "resolved": "https://registry.npmjs.org/bytes/-/bytes-3.1.2.tgz", @@ -823,6 +857,15 @@ "node": ">=10" } }, + "node_modules/kareem": { + "version": "2.6.3", + "resolved": "https://registry.npmjs.org/kareem/-/kareem-2.6.3.tgz", + "integrity": "sha512-C3iHfuGUXK2u8/ipq9LfjFfXFxAZMQJJq7vLS45r3D9Y2xQ/m4S8zaR4zMLFWh9AsNPXmcFfUDhTEO8UIC/V6Q==", + "license": "Apache-2.0", + "engines": { + "node": ">=12.0.0" + } + }, "node_modules/lru-cache": { "version": "6.0.0", "resolved": "https://registry.npmjs.org/lru-cache/-/lru-cache-6.0.0.tgz", @@ -843,6 +886,12 @@ "node": ">= 0.6" } }, + "node_modules/memory-pager": { + "version": "1.5.0", + "resolved": "https://registry.npmjs.org/memory-pager/-/memory-pager-1.5.0.tgz", + "integrity": "sha512-ZS4Bp4r/Zoeq6+NLJpP+0Zzm0pR8whtGPf1XExKLJBAczGMnSi3It14OiNCStjQjM6NU1okjQGSxgEZN8eBYKg==", + "license": "MIT" + }, "node_modules/merge-descriptors": { "version": "1.0.1", "resolved": "https://registry.npmjs.org/merge-descriptors/-/merge-descriptors-1.0.1.tgz", @@ -897,6 +946,90 @@ "node": "*" } }, + "node_modules/mongodb": { + "version": "6.10.0", + "resolved": "https://registry.npmjs.org/mongodb/-/mongodb-6.10.0.tgz", + "integrity": "sha512-gP9vduuYWb9ZkDM546M+MP2qKVk5ZG2wPF63OvSRuUbqCR+11ZCAE1mOfllhlAG0wcoJY5yDL/rV3OmYEwXIzg==", + "license": "Apache-2.0", + "dependencies": { + "@mongodb-js/saslprep": "^1.1.5", + "bson": "^6.7.0", + "mongodb-connection-string-url": "^3.0.0" + }, + "engines": { + "node": ">=16.20.1" + }, + "peerDependencies": { + "@aws-sdk/credential-providers": "^3.188.0", + "@mongodb-js/zstd": "^1.1.0", + "gcp-metadata": "^5.2.0", + "kerberos": "^2.0.1", + "mongodb-client-encryption": ">=6.0.0 <7", + "snappy": "^7.2.2", + "socks": "^2.7.1" + }, + "peerDependenciesMeta": { + "@aws-sdk/credential-providers": { + "optional": true + }, + "@mongodb-js/zstd": { + "optional": true + }, + "gcp-metadata": { + "optional": true + }, + "kerberos": { + "optional": true + }, + "mongodb-client-encryption": { + "optional": true + }, + "snappy": { + "optional": true + }, + "socks": { + "optional": true + } + } + }, + "node_modules/mongodb-connection-string-url": { + "version": "3.0.1", + "resolved": "https://registry.npmjs.org/mongodb-connection-string-url/-/mongodb-connection-string-url-3.0.1.tgz", + "integrity": "sha512-XqMGwRX0Lgn05TDB4PyG2h2kKO/FfWJyCzYQbIhXUxz7ETt0I/FqHjUeqj37irJ+Dl1ZtU82uYyj14u2XsZKfg==", + "license": "Apache-2.0", + "dependencies": { + "@types/whatwg-url": "^11.0.2", + "whatwg-url": "^13.0.0" + } + }, + "node_modules/mongoose": { + "version": "8.8.3", + "resolved": "https://registry.npmjs.org/mongoose/-/mongoose-8.8.3.tgz", + "integrity": "sha512-/I4n/DcXqXyIiLRfAmUIiTjj3vXfeISke8dt4U4Y8Wfm074Wa6sXnQrXN49NFOFf2mM1kUdOXryoBvkuCnr+Qw==", + "license": "MIT", + "dependencies": { + "bson": "^6.7.0", + "kareem": "2.6.3", + "mongodb": "~6.10.0", + "mpath": "0.9.0", + "mquery": "5.0.0", + "ms": "2.1.3", + "sift": "17.1.3" + }, + "engines": { + "node": ">=16.20.1" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/mongoose" + } + }, + "node_modules/mongoose/node_modules/ms": { + "version": "2.1.3", + "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.3.tgz", + "integrity": "sha512-6FlzubTLZG3J2a/NVCAleEhjzq5oxgHyaCU9yYXvcLsvoVaHJq/s5xXI6/XXP6tz7R9xAOtHnSO/tXtF3WRTlA==", + "license": "MIT" + }, "node_modules/morgan": { "version": "1.9.1", "resolved": "https://registry.npmjs.org/morgan/-/morgan-1.9.1.tgz", @@ -912,6 +1045,50 @@ "node": ">= 0.8.0" } }, + "node_modules/mpath": { + "version": "0.9.0", + "resolved": "https://registry.npmjs.org/mpath/-/mpath-0.9.0.tgz", + "integrity": "sha512-ikJRQTk8hw5DEoFVxHG1Gn9T/xcjtdnOKIU1JTmGjZZlg9LST2mBLmcX3/ICIbgJydT2GOc15RnNy5mHmzfSew==", + "license": "MIT", + "engines": { + "node": ">=4.0.0" + } + }, + "node_modules/mquery": { + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/mquery/-/mquery-5.0.0.tgz", + "integrity": "sha512-iQMncpmEK8R8ncT8HJGsGc9Dsp8xcgYMVSbs5jgnm1lFHTZqMJTUWTDx1LBO8+mK3tPNZWFLBghQEIOULSTHZg==", + "license": "MIT", + "dependencies": { + "debug": "4.x" + }, + "engines": { + "node": ">=14.0.0" + } + }, + "node_modules/mquery/node_modules/debug": { + "version": "4.3.7", + "resolved": "https://registry.npmjs.org/debug/-/debug-4.3.7.tgz", + "integrity": "sha512-Er2nc/H7RrMXZBFCEim6TCmMk02Z8vLC2Rbi1KEBggpo0fS6l0S1nnapwmIi3yW/+GOJap1Krg4w0Hg80oCqgQ==", + "license": "MIT", + "dependencies": { + "ms": "^2.1.3" + }, + "engines": { + "node": ">=6.0" + }, + "peerDependenciesMeta": { + "supports-color": { + "optional": true + } + } + }, + "node_modules/mquery/node_modules/ms": { + "version": "2.1.3", + "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.3.tgz", + "integrity": "sha512-6FlzubTLZG3J2a/NVCAleEhjzq5oxgHyaCU9yYXvcLsvoVaHJq/s5xXI6/XXP6tz7R9xAOtHnSO/tXtF3WRTlA==", + "license": "MIT" + }, "node_modules/ms": { "version": "2.0.0", "resolved": "https://registry.npmjs.org/ms/-/ms-2.0.0.tgz", @@ -1062,6 +1239,15 @@ "integrity": "sha512-77DZwxQmxKnu3aR542U+X8FypNzbfJ+C5XQDk3uWjWxn6151aIMGthWYRXTqT1E5oJvg+ljaa2OJi+VfvCOQ8w==", "dev": true }, + "node_modules/punycode": { + "version": "2.3.1", + "resolved": "https://registry.npmjs.org/punycode/-/punycode-2.3.1.tgz", + "integrity": "sha512-vYt7UD1U9Wg6138shLtLOvdAu+8DsC/ilFtEVHcH+wydcSpNE20AfSOduf6MkRFahL5FY7X1oU7nKVZFtfq8Fg==", + "license": "MIT", + "engines": { + "node": ">=6" + } + }, "node_modules/qs": { "version": "6.11.0", "resolved": "https://registry.npmjs.org/qs/-/qs-6.11.0.tgz", @@ -1288,6 +1474,12 @@ "url": "https://github.com/sponsors/ljharb" } }, + "node_modules/sift": { + "version": "17.1.3", + "resolved": "https://registry.npmjs.org/sift/-/sift-17.1.3.tgz", + "integrity": "sha512-Rtlj66/b0ICeFzYTuNvX/EF1igRbbnGSvEyT79McoZa/DeGhMyC5pWKOEsZKnpkqtSeovd5FL/bjHWC3CIIvCQ==", + "license": "MIT" + }, "node_modules/simple-update-notifier": { "version": "2.0.0", "resolved": "https://registry.npmjs.org/simple-update-notifier/-/simple-update-notifier-2.0.0.tgz", @@ -1300,6 +1492,15 @@ "node": ">=10" } }, + "node_modules/sparse-bitfield": { + "version": "3.0.3", + "resolved": "https://registry.npmjs.org/sparse-bitfield/-/sparse-bitfield-3.0.3.tgz", + "integrity": "sha512-kvzhi7vqKTfkh0PZU+2D2PIllw2ymqJKujUcyPMd9Y75Nv4nPbGJZXNhxsgdQab2BmlDct1YnfQCguEvHr7VsQ==", + "license": "MIT", + "dependencies": { + "memory-pager": "^1.0.2" + } + }, "node_modules/statuses": { "version": "1.4.0", "resolved": "https://registry.npmjs.org/statuses/-/statuses-1.4.0.tgz", @@ -1352,6 +1553,18 @@ "nodetouch": "bin/nodetouch.js" } }, + "node_modules/tr46": { + "version": "4.1.1", + "resolved": "https://registry.npmjs.org/tr46/-/tr46-4.1.1.tgz", + "integrity": "sha512-2lv/66T7e5yNyhAAC4NaKe5nVavzuGJQVVtRYLyQ2OI8tsJ61PMLlelehb0wi2Hx6+hT/OJUWZcw8MjlSRnxvw==", + "license": "MIT", + "dependencies": { + "punycode": "^2.3.0" + }, + "engines": { + "node": ">=14" + } + }, "node_modules/type-is": { "version": "1.6.18", "resolved": "https://registry.npmjs.org/type-is/-/type-is-1.6.18.tgz", @@ -1394,6 +1607,28 @@ "node": ">= 0.8" } }, + "node_modules/webidl-conversions": { + "version": "7.0.0", + "resolved": "https://registry.npmjs.org/webidl-conversions/-/webidl-conversions-7.0.0.tgz", + "integrity": "sha512-VwddBukDzu71offAQR975unBIGqfKZpM+8ZX6ySk8nYhVoo5CYaZyzt3YBvYtRtO+aoGlqxPg/B87NGVZ/fu6g==", + "license": "BSD-2-Clause", + "engines": { + "node": ">=12" + } + }, + "node_modules/whatwg-url": { + "version": "13.0.0", + "resolved": "https://registry.npmjs.org/whatwg-url/-/whatwg-url-13.0.0.tgz", + "integrity": "sha512-9WWbymnqj57+XEuqADHrCJ2eSXzn8WXIW/YSGaZtb2WKAInQ6CHfaUUcTyyver0p8BDg5StLQq8h1vtZuwmOig==", + "license": "MIT", + "dependencies": { + "tr46": "^4.1.1", + "webidl-conversions": "^7.0.0" + }, + "engines": { + "node": ">=16" + } + }, "node_modules/yallist": { "version": "4.0.0", "resolved": "https://registry.npmjs.org/yallist/-/yallist-4.0.0.tgz", diff --git a/package.json b/package.json index 4c3762d..a4ab974 100644 --- a/package.json +++ b/package.json @@ -11,6 +11,7 @@ "ejs": "^3.1.9", "express": "^4.18.2", "http-errors": "~1.6.3", + "mongoose": "^8.8.3", "morgan": "~1.9.1" }, "devDependencies": { From b2420e0033212d5d0c3cc157d581f5e69c3d2745 Mon Sep 17 00:00:00 2001 From: Khaled Nashat Date: Sun, 1 Dec 2024 23:56:37 +0200 Subject: [PATCH 2/5] Add checking of connection to database --- check/check.js | 16 ++++++++++++++++ models/schema.js | 6 +++--- routes/index.js | 4 ++++ 3 files changed, 23 insertions(+), 3 deletions(-) create mode 100644 check/check.js diff --git a/check/check.js b/check/check.js new file mode 100644 index 0000000..a46659d --- /dev/null +++ b/check/check.js @@ -0,0 +1,16 @@ +const mongoose = require('mongoose'); +const mongoURI = 'mongodb://localhost:27017/URLs'; + +const connectdatabase = async function(){ + try{ + + await mongoose.connect(mongoURI); + console.log("connect database succesfully") + } catch(err){ + console.error("Error connecting to MongoDB:", err) + } +} + + +module.exports = connectdatabase; + diff --git a/models/schema.js b/models/schema.js index 51302bb..9937c8c 100644 --- a/models/schema.js +++ b/models/schema.js @@ -2,18 +2,18 @@ const mongoose = require('mongoose'); const urlschema = new mongoose.Schema({ LongURL: { - type: string, + type: String, required:true, }, ShortenedURL: { - type: string, + type: String, required:true, } }); const URL = mongoose.model('URL',urlschema , 'urls'); //note that if we don't name the collection with (urls) it will automatically call it "urls" beacause of lowercase and ploralize od the model name URL -URL.insertURL = function (LongURL,ShortenedURL){ +URL.inputURL = function (LongURL,ShortenedURL){ const addedURL = new URL({ LongURL: LongURL, ShortenedURL: ShortenedURL, diff --git a/routes/index.js b/routes/index.js index 5890929..2110ac0 100644 --- a/routes/index.js +++ b/routes/index.js @@ -1,4 +1,8 @@ const router = require('express').Router(); +const schema = require('../models/schema'); +const connection_to_database = require('../check/check'); + +connection_to_database(); /* GET home page. */ router.get('/', function (req, res, next) { From 6328c31f9c85356e57bfacf10216aa036fdd5a01 Mon Sep 17 00:00:00 2001 From: Khaled Nashat Date: Wed, 4 Dec 2024 02:13:41 +0200 Subject: [PATCH 3/5] feat: render dynamic URLs in homepage from database --- routes/index.js | 12 ++++++++---- views/index.ejs | 18 ++++++------------ 2 files changed, 14 insertions(+), 16 deletions(-) diff --git a/routes/index.js b/routes/index.js index 2110ac0..1f721da 100644 --- a/routes/index.js +++ b/routes/index.js @@ -4,9 +4,13 @@ const connection_to_database = require('../check/check'); connection_to_database(); -/* GET home page. */ -router.get('/', function (req, res, next) { - res.render('index', { title: 'Express' }); -}); +router.get('/', async function(req,res,next){ + const urls = await schema.find(); + res.render('index',{ + title: 'Express', + URLs: urls + } + ); +}) module.exports = router; diff --git a/views/index.ejs b/views/index.ejs index 70b7faa..0087f93 100644 --- a/views/index.ejs +++ b/views/index.ejs @@ -75,18 +75,12 @@ Original URL Shortened URL - - https://github.com/cat-backend-nodejs/nodejs-roadmap - http://localhost:3000/nodejs - - - https://stackoverflow.com/users/11936196/seif-el-din-sweilam - http://localhost:3000/stackoverflow - - - https://drive.google.com/drive/u/1/folders/1bpiBKNBd9n0Z79wrZvHVT1KyA9OBROLx - http://localhost:3000/himym - + <% for (let url in URLs) { %> + + <%= url.LongURL%> + http://localhost:3000/<%= url.ShortenedURL%> + + <% } %> From dccae3adca1a0becf71a6b28ea98348010d7efd9 Mon Sep 17 00:00:00 2001 From: Khaled Nashat Date: Wed, 4 Dec 2024 03:48:08 +0200 Subject: [PATCH 4/5] fixing some error in dynamic view & Add POST route to save URL and alias, render updated list --- models/schema.js | 14 ++------------ routes/index.js | 38 +++++++++++++++++++++++++++++++++++--- views/index.ejs | 6 +++--- 3 files changed, 40 insertions(+), 18 deletions(-) diff --git a/models/schema.js b/models/schema.js index 9937c8c..b872dfa 100644 --- a/models/schema.js +++ b/models/schema.js @@ -1,11 +1,11 @@ const mongoose = require('mongoose'); const urlschema = new mongoose.Schema({ - LongURL: { + urlInput: { type: String, required:true, }, - ShortenedURL: { + aliasInput: { type: String, required:true, } @@ -13,14 +13,4 @@ const urlschema = new mongoose.Schema({ const URL = mongoose.model('URL',urlschema , 'urls'); //note that if we don't name the collection with (urls) it will automatically call it "urls" beacause of lowercase and ploralize od the model name URL -URL.inputURL = function (LongURL,ShortenedURL){ - const addedURL = new URL({ - LongURL: LongURL, - ShortenedURL: ShortenedURL, - }); - - addedURL.save().then(()=> console.log("The new URL added successfully")); - -} - module.exports = URL; diff --git a/routes/index.js b/routes/index.js index 1f721da..d413388 100644 --- a/routes/index.js +++ b/routes/index.js @@ -1,11 +1,16 @@ const router = require('express').Router(); -const schema = require('../models/schema'); +const URL = require('../models/schema'); const connection_to_database = require('../check/check'); +const express = require('express'); + connection_to_database(); -router.get('/', async function(req,res,next){ - const urls = await schema.find(); +router.use(express.urlencoded({ extended: true })); // For parsing form data +router.use(express.json()); // For parsing JSON data + +router.get('/', async function(req,res){ + const urls = await URL.find(); res.render('index',{ title: 'Express', URLs: urls @@ -13,4 +18,31 @@ router.get('/', async function(req,res,next){ ); }) +router.post('/', async (req,res)=>{ + + const {urlInput,aliasInput} = req.body; + + if(!urlInput || !aliasInput) { + res.status(400).json({error : "both urls is required"}); + } + + try { + const addedURL = new URL ({ + urlInput:urlInput, + aliasInput: aliasInput, + }); + + await addedURL.save(); + + const urls = await URL.find(); + res.render('index', { + title: 'Express', + URLs: urls, + }); + } catch (err) { + console.error("Error saving URL:", err); + res.status(500).send("Error saving URL"); + } +}) + module.exports = router; diff --git a/views/index.ejs b/views/index.ejs index 0087f93..34dd8a5 100644 --- a/views/index.ejs +++ b/views/index.ejs @@ -75,10 +75,10 @@ Original URL Shortened URL - <% for (let url in URLs) { %> + <% for (let url of URLs) { %> - <%= url.LongURL%> - http://localhost:3000/<%= url.ShortenedURL%> + <%= url.urlInput%> + http://localhost:3000/<%= url.aliasInput%> <% } %> From 646a6262eee3afb24b1fef3eca2b6e3065ecba9a Mon Sep 17 00:00:00 2001 From: Khaled Nashat Date: Wed, 4 Dec 2024 04:02:13 +0200 Subject: [PATCH 5/5] Adding a middleware which redirect to url Input when clicking on alias --- routes/index.js | 16 +++++++++++----- 1 file changed, 11 insertions(+), 5 deletions(-) diff --git a/routes/index.js b/routes/index.js index d413388..07e2c10 100644 --- a/routes/index.js +++ b/routes/index.js @@ -1,14 +1,9 @@ const router = require('express').Router(); const URL = require('../models/schema'); const connection_to_database = require('../check/check'); -const express = require('express'); - connection_to_database(); -router.use(express.urlencoded({ extended: true })); // For parsing form data -router.use(express.json()); // For parsing JSON data - router.get('/', async function(req,res){ const urls = await URL.find(); res.render('index',{ @@ -45,4 +40,15 @@ router.post('/', async (req,res)=>{ } }) +router.get('/:aliasInput' , async function (req,res) { + const { aliasInput } = req.params; + const bothURLs = await URL.findOne({aliasInput : aliasInput}) + + if (!bothURLs) { + // Handle case where no matching shortened URL is found + return res.status(404).send(" aliasInput not found."); + } + res.redirect(bothURLs.urlInput) +}) + module.exports = router;