From fe6dff022068bf920a0aedc62ec8488f879246a2 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Killian=C2=B4Dal-Cin?= Date: Thu, 27 Jan 2022 10:23:11 +0100 Subject: [PATCH] First commit --- .DS_Store | Bin 0 -> 6148 bytes .env.example | 2 + .gitignore | 5 ++ LICENSE | 21 ++++++++ README.MD | 118 +++++++++++++++++++++++++++++++++++++++++++++ config/database.js | 16 ++++++ docs.html | 37 ++++++++++++++ index.html | 71 +++++++++++++++++++++++++++ index.js | 25 ++++++++++ models/Url.js | 27 +++++++++++ package.json | 17 +++++++ routes/index.js | 27 +++++++++++ routes/urls.js | 39 +++++++++++++++ utils/utils.js | 7 +++ 14 files changed, 412 insertions(+) create mode 100644 .DS_Store create mode 100644 .env.example create mode 100644 .gitignore create mode 100644 LICENSE create mode 100644 README.MD create mode 100644 config/database.js create mode 100644 docs.html create mode 100644 index.html create mode 100644 index.js create mode 100644 models/Url.js create mode 100644 package.json create mode 100644 routes/index.js create mode 100644 routes/urls.js create mode 100644 utils/utils.js diff --git a/.DS_Store b/.DS_Store new file mode 100644 index 0000000000000000000000000000000000000000..d26ed7a649eaf91afb35d708ed390fe09f7e59b8 GIT binary patch literal 6148 zcmeHLJxc>Y5S`UTgQf^pmWN;~q!Vn;aCUZLVP(jNF_6mzP3(mG1r}N;C|KBum7SfS zts+?4Y2m+cW@nS^a9j`s~W`E(jX3q1C!yrF70l%P z`;)WVx2N?aR&5McXP;J!{?lk3f*Gx(4SUdp2CpXlyxJMf+gdL2AK#a(B{GH|43(sD z5_S0O2(;jEM8}=hVdZTd`L6pq&=KJbb)<0;bmXCJbnF6;)>Ihqd2Wt3VR5C@_TO!IAl_WzCa^MACH+=&C?z`t@pWxOq~gdfT5t+5YB wd#!_|jD>=DA*1esmD!H<1#QLjzk&|TIdp;O8Vnh+1w(!aXd9#w2maK7AJ>A}00000 literal 0 HcmV?d00001 diff --git a/.env.example b/.env.example new file mode 100644 index 0000000..76d87ae --- /dev/null +++ b/.env.example @@ -0,0 +1,2 @@ +MONGO_URI=YOUR_MONGO_COLLECTION_URI +BASE=YOUR_BASE_URL \ No newline at end of file diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..b578afa --- /dev/null +++ b/.gitignore @@ -0,0 +1,5 @@ +.vscode +node_modules +package-lock.json +log_rotate.sh +.env \ No newline at end of file diff --git a/LICENSE b/LICENSE new file mode 100644 index 0000000..8f110d8 --- /dev/null +++ b/LICENSE @@ -0,0 +1,21 @@ +MIT License + +Copyright (c) 2022 Mr¤KayJayDee + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in all +copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +SOFTWARE. diff --git a/README.MD b/README.MD new file mode 100644 index 0000000..66d4235 --- /dev/null +++ b/README.MD @@ -0,0 +1,118 @@ + +# MrKayJayDee Shortener + +### A simple url shortener frontend and api using mongodb + +
+ +# Setup + +Clone the repo +```shell +git clone https://github.com/Mr-KayJayDee/simple-url-shortener +``` + +CD Into the directory +``` +cd simple-url-shortener +``` + +Install the modules +``` +npm install +``` + +Change the configuration + +- Rename the .env.example to .env +- Change MONGO_URI to your mongodb local or cluster url +- Change BASE to your base url (localhost:8080 for local testing) + +Change some others things + +- Go to the index.html file and change the line 50 column 35 to be https://yourdomain.com/api/short + +Start the server +``` +node index.js +``` + +# API Reference + +### Shorten a link + +```http + POST https://mrkayjaydee.xyz/api/short +``` + +| Body | Type | Description | +| :-------- | :------- | :------------------------- | +| `origUrl` | `string` | **Required**. The url you want to shorten | + +Returns + +| Key | Type | Description | +| :-------- | :------- | :------------------------- | +| `urlId` | `String` | The URL ID | +| `origUrl` | `String` | The original URL | +| `shortUrl` | `String` | The shortened URL | +| `clicks` | `Number` | The amount of time this link has been clicked | +| `date` | `Date` | The creation date of this link | +| `_id` | `String` | The databse id of this link | +| `__v` | `Number` | The version number | + + +```json +{ + "urlId": String, + "origUrl": String, + "shortUrl": String, + "clicks": Number, + "date": Date, + "_id": String, + "__v": Number +} +``` + +## Example + +```http + POST https://mrkayjaydee.xyz/api/short +``` + +Body: + +```json +{ + "origUrl": "http://google.com" +} +``` +Returns: + +```json +{ + "urlId": "9bcF-giN0", + "origUrl": "http://google.com", + "shortUrl": "https://mrkayjaydee.xyz/9bcF-giN0", + "clicks": 0, + "date": "2022-01-26T15:37:26.977Z", + "_id": "61f16ab602262b5f179d3336", + "__v": 0 +} +``` + +# Contact Me: + +- Discord: Mr¤KayJayDee#8961 +- Support Server: https://discord.gg/5ZSGFYtnqw +- Shop Server: https://discord.gg/X2CHqpFPjw +- Instagram: [@killian.dalcin](https://www.instagram.com/killian.dalcin) +- Snapchat: [@killian.dalcin](https://www.snapchat.com/add/killian.dalcin) +- Twitter: [@killiandalcin](https://twitter.com/killiandalcin) +- Github: https://github.com/Mr-KayJayDee/ +- Portfolio: https://portfolio.mrkayjaydee.xyz +- Fiverr: https://www.fiverr.com/mrkayjaydee +- Shoppy: https://shoppy.gg/@MrKayJayDee +- Email: [killian.dalcin@gmail.com](mailto:killian.dalcin@gmail.com) +- Email pro: [mrkayjaydee@gmail.com](mailto:mrkayjaydee@gmail.com) +- Email business: [contact@mrkayjaydee.xyz](mailto:contact@mrkayjaydee.xyz) \ No newline at end of file diff --git a/config/database.js b/config/database.js new file mode 100644 index 0000000..2f9ecf1 --- /dev/null +++ b/config/database.js @@ -0,0 +1,16 @@ +const mongoose = require('mongoose'); + +const connectDB = async () => { + try { + await mongoose.connect(process.env.MONGO_URI, { + useNewUrlParser: true, + useUnifiedTopology: true, + }); + console.log('Database Connected'); + } catch (err) { + console.error(err.message); + process.exit(1); + } +}; + +module.exports = connectDB; \ No newline at end of file diff --git a/docs.html b/docs.html new file mode 100644 index 0000000..c7220c5 --- /dev/null +++ b/docs.html @@ -0,0 +1,37 @@ + + + + + + Documentation - MrKayJayDee URL Shortener + + + + +
+
+

Documentation

+ +
+
+
+ + \ No newline at end of file diff --git a/index.html b/index.html new file mode 100644 index 0000000..52169de --- /dev/null +++ b/index.html @@ -0,0 +1,71 @@ + + + + + + MrKayJayDee URL Shortener + + + + +
+
+

+ + +

+ +
+
+
+ + + \ No newline at end of file diff --git a/index.js b/index.js new file mode 100644 index 0000000..b50e4a7 --- /dev/null +++ b/index.js @@ -0,0 +1,25 @@ +/** + * Install mogodb locally + * https://docs.mongodb.com/manual/tutorial/install-mongodb-on-os-x/ + */ + +const express = require('express'); +const app = express(); +const connectDB = require('./config/database'); +require('dotenv').config(); + +connectDB(); + +// Body Parser +app.use(express.urlencoded({ extended: true })); +app.use(express.json()); +app.use(express.static(__dirname + '/')); + +app.use('/', require('./routes/index')); +app.use('/api', require('./routes/urls')); + +// Server Setup +const PORT = 8080; +app.listen(PORT, () => { + console.log(`Server is running at PORT ${PORT}`); +}); diff --git a/models/Url.js b/models/Url.js new file mode 100644 index 0000000..2e2cc4d --- /dev/null +++ b/models/Url.js @@ -0,0 +1,27 @@ +const mongoose = require('mongoose'); + +const UrlSchema = new mongoose.Schema({ + urlId: { + type: String, + required: true, + }, + origUrl: { + type: String, + required: true, + }, + shortUrl: { + type: String, + required: true, + }, + clicks: { + type: Number, + required: true, + default: 0, + }, + date: { + type: String, + default: Date.now, + }, +}); + +module.exports = mongoose.model('Url', UrlSchema); \ No newline at end of file diff --git a/package.json b/package.json new file mode 100644 index 0000000..fb3da1b --- /dev/null +++ b/package.json @@ -0,0 +1,17 @@ +{ + "name": "link-shortener", + "version": "1.0.0", + "main": "index.js", + "scripts": { + "test": "echo \"Error: no test specified\" && exit 1" + }, + "author": "Killian' DAL-CIN contact@mrkayjaydee.xyz", + "license": "ISC", + "dependencies": { + "dotenv": "^14.3.2", + "express": "^4.17.2", + "mongoose": "^6.1.8", + "shortid": "^2.2.16" + }, + "description": "A simple link shortener API" +} diff --git a/routes/index.js b/routes/index.js new file mode 100644 index 0000000..7e0b2c0 --- /dev/null +++ b/routes/index.js @@ -0,0 +1,27 @@ +const express = require('express'); +const index = express.Router(); +const Url = require('../models/Url'); + +index.get('/', async (req, res) => { + res.sendFile('index.html', { root: './' }) +}); + +index.get('/docs', async (req, res) => { + res.sendFile('docs.html', { root: './' }) +}); + +index.get('/:urlId', async (req, res) => { + try { + const url = await Url.findOne({ urlId: req.params.urlId }); + if (url) { + url.clicks++; + url.save(); + return res.redirect(url.origUrl); + } else res.status(404).json('Not found'); + } catch (err) { + console.log(err); + res.status(500).json('Server Error 2'); + } +}); + +module.exports = index; \ No newline at end of file diff --git a/routes/urls.js b/routes/urls.js new file mode 100644 index 0000000..9ddb867 --- /dev/null +++ b/routes/urls.js @@ -0,0 +1,39 @@ +const express = require('express'); +const urls = express.Router(); +const shortid = require('shortid'); +const Url = require('../models/Url'); +const utils = require('../utils/utils'); + +urls.post('/short', async (req, res) => { + const { origUrl } = req.body; + const base = process.env.BASE; + + const urlId = shortid.generate(); + if (utils.validateUrl(origUrl)) { + try { + let url = await Url.findOne({ origUrl }); + if (url) { + res.json(url); + } else { + const shortUrl = `${base}/${urlId}`; + + url = new Url({ + origUrl, + shortUrl, + urlId, + date: new Date().toISOString(), + }); + + await url.save(); + res.json(url); + } + } catch (err) { + console.log(err); + res.status(500).json('Server Error'); + } + } else { + res.status(400).json('Invalid Provided Url'); + } +}); + +module.exports = urls; \ No newline at end of file diff --git a/utils/utils.js b/utils/utils.js new file mode 100644 index 0000000..d75f43a --- /dev/null +++ b/utils/utils.js @@ -0,0 +1,7 @@ +function validateUrl(value) { + return /^(http|https|ftp)\:\/\/([a-zA-Z0-9\.\-]+(\:[a-zA-Z0-9\.&%\$\-]+)*@)*((25[0-5]|2[0-4][0-9]|[0-1]{1}[0-9]{2}|[1-9]{1}[0-9]{1}|[1-9])\.(25[0-5]|2[0-4][0-9]|[0-1]{1}[0-9]{2}|[1-9]{1}[0-9]{1}|[1-9]|0)\.(25[0-5]|2[0-4][0-9]|[0-1]{1}[0-9]{2}|[1-9]{1}[0-9]{1}|[1-9]|0)\.(25[0-5]|2[0-4][0-9]|[0-1]{1}[0-9]{2}|[1-9]{1}[0-9]{1}|[0-9])|localhost|([a-zA-Z0-9\-]+\.)*[a-zA-Z0-9\-]+\.([a-zA-Z]{1,}))(\:[0-9]+)*(\/($|[a-zA-Z0-9\.\,\?\'\\\+&%\$#\=~_\-]+))*$/gm.test( + value + ); +} + +module.exports = { validateUrl }; \ No newline at end of file