First commit

main
Killian´Dal-Cin 3 years ago
commit fe6dff0220

BIN
.DS_Store vendored

Binary file not shown.

@ -0,0 +1,2 @@
MONGO_URI=YOUR_MONGO_COLLECTION_URI
BASE=YOUR_BASE_URL

5
.gitignore vendored

@ -0,0 +1,5 @@
.vscode
node_modules
package-lock.json
log_rotate.sh
.env

@ -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.

@ -0,0 +1,118 @@
# MrKayJayDee Shortener
### A simple url shortener frontend and api using mongodb
<br>
# 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)

@ -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;

@ -0,0 +1,37 @@
<!DOCTYPE html>
<html lang="en">
<head>
<meta name="viewport" content="width=device-width, initial-scale=1">
<meta name="description" content="An Easy, Fast and Secured URL shortener">
<title>Documentation - MrKayJayDee URL Shortener</title>
<link rel="icon" href="https://i.imgur.com/Dnmmfqd.png">
<script src="https://cdn.tailwindcss.com"></script>
</head>
<body>
<div class="flex h-screen justify-center items-center">
<div class="bg-white rounded m-2 sm:m-5 p-3 sm:p-80 shadow-md">
<p class="text-center font-bold text-4xl">Documentation</p>
<div class="pt-5" id="shortenedLink">
<p class="font-bold">Shortening a link</p>
<p class="font-medium">POST</p>
<pre class="border-black bg-gray-300 border-2 rounded-lg text-center"><code class="language-plaintext">/api/short</code></pre>
<p class="font-medium">Body</p>
<pre class="border-black bg-gray-300 border-2 rounded-lg"><code class="language-json">{
"origUrl": "http://google.com"
}</code></pre>
<p class="font-medium">Return</p>
<pre class="border-black bg-gray-300 border-2 rounded-lg"><code class="language-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
}</code></pre>
</div>
<div class="pt-5" id="state"></div>
</div>
</div>
</body>
</html>

@ -0,0 +1,71 @@
<!DOCTYPE html>
<html lang="en">
<head>
<meta name="viewport" content="width=device-width, initial-scale=1">
<meta name="description" content="An Easy, Fast and Secured URL shortener">
<title>MrKayJayDee URL Shortener</title>
<link rel="icon" href="https://i.imgur.com/Dnmmfqd.png">
<script src="https://cdn.tailwindcss.com"></script>
</head>
<body>
<div class="flex h-screen justify-center items-center">
<div class="bg-white rounded m-2 sm:m-5 p-3 sm:p-10 shadow-md">
<p class="text-center">
<input id="link" class="border py-2 px-3 text-grey-darkest content-auto rounded-lg border-black" type="text" placeholder="Your URL">
<button id="shorten" class="text-center mt-5 rounded-2xl text-white py-1 px-5 text-base shadow overflow-ellipsis cursor-pointer bg-blue-600" type="submit" onclick="shortenLink()">Get Shortened url</button>
</p>
<div class="pt-5" id="shortenedLink">
<p class="font-bold text-center">Shortened Link:</p>
<p class="text-center">
<a target="_blank" href="" id="shortenedLinkText">None</a>
</p>
<p class="font-bold text-center">Clicks Count</p>
<p class="text-center" id="clicksCount">None</p>
<p class="font-bold text-center">Creation Date</p>
<p class="text-center" id="creationDate">None</p>
<p class="pt-8 text-center">
<a class="text-center mt-5 rounded-2xl text-white py-1 px-5 text-base shadow overflow-ellipsis cursor-pointer bg-blue-600" href="docs">Documentation</a>
</p>
</div>
<div class="pt-5" id="state"></div>
</div>
</div>
</body>
<script>
function shortenLink() {
let url = document.getElementById("link").value;
if (validateUrl(url)) {
let xhr = new XMLHttpRequest();
xhr.open("POST", "http://localhost:8080/api/short", true);
xhr.setRequestHeader("Content-Type", "application/json");
xhr.responseType = 'json';
xhr.send(JSON.stringify({
origUrl: url
}));
xhr.onreadystatechange = function (evt) {
if (xhr.readyState !== 4) {
return;
}
document.getElementById("shortenedLinkText").innerHTML = xhr.response.shortUrl;
document.getElementById("shortenedLinkText").href = xhr.response.shortUrl;
document.getElementById("clicksCount").innerHTML = xhr.response.clicks;
document.getElementById("creationDate").innerHTML = new Date(xhr.response.date).toLocaleString();
document.getElementById("state").innerHTML = "<p class='text-center text-green-600'>URL successfully retrieved</p>";
};
} else {
document.getElementById("state").innerHTML = "<p class='text-center text-red-600'>Invalid URL</p>";
}
}
function validateUrl(value) {
return /^(http|https|ftp)\:\/\/([a-zA-Z0-9\.\-]+(\:[a-zA-Z0-9\.&amp;%\$\-]+)*@)*((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\.\,\?\'\\\+&amp;%\$#\=~_\-]+))*$/gm.test(
value
);
}
document.querySelector("#link").addEventListener("keyup", event => {
if(event.key !== "Enter") return;
document.querySelector("#shorten").click();
event.preventDefault();
});
</script>
</html>

@ -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}`);
});

@ -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);

@ -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"
}

@ -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;

@ -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;

@ -0,0 +1,7 @@
function validateUrl(value) {
return /^(http|https|ftp)\:\/\/([a-zA-Z0-9\.\-]+(\:[a-zA-Z0-9\.&amp;%\$\-]+)*@)*((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\.\,\?\'\\\+&amp;%\$#\=~_\-]+))*$/gm.test(
value
);
}
module.exports = { validateUrl };
Loading…
Cancel
Save