initial commit
This commit is contained in:
parent
f962b2f9da
commit
5a0501d110
75
app.js
Normal file
75
app.js
Normal file
@ -0,0 +1,75 @@
|
|||||||
|
var express = require('express');
|
||||||
|
var app = express();
|
||||||
|
var path = require('path');
|
||||||
|
var bodyParser = require('body-parser');
|
||||||
|
var mongoose = require('mongoose');
|
||||||
|
var config = require('./config');
|
||||||
|
var base58 = require('./base58.js');
|
||||||
|
|
||||||
|
// grab the url model
|
||||||
|
var Url = require('./models/url');
|
||||||
|
|
||||||
|
mongoose.connect('mongodb://' + config.db.host + '/' + config.db.name);
|
||||||
|
|
||||||
|
app.use(bodyParser.json());
|
||||||
|
app.use(bodyParser.urlencoded({ extended: true }));
|
||||||
|
|
||||||
|
app.use(express.static(path.join(__dirname, 'public')));
|
||||||
|
|
||||||
|
app.get('/', function(req, res){
|
||||||
|
res.sendFile(path.join(__dirname, 'views/index.html'));
|
||||||
|
});
|
||||||
|
|
||||||
|
app.post('/api/shorten', function(req, res){
|
||||||
|
var longUrl = req.body.url;
|
||||||
|
var shortUrl = '';
|
||||||
|
|
||||||
|
// check if url already exists in database
|
||||||
|
Url.findOne({long_url: longUrl}, function (err, doc){
|
||||||
|
if (doc){
|
||||||
|
shortUrl = config.webhost + base58.encode(doc._id);
|
||||||
|
|
||||||
|
// the document exists, so we return it without creating a new entry
|
||||||
|
res.send({'shortUrl': shortUrl});
|
||||||
|
} else {
|
||||||
|
// since it doesn't exist, let's go ahead and create it:
|
||||||
|
var newUrl = Url({
|
||||||
|
long_url: longUrl
|
||||||
|
});
|
||||||
|
|
||||||
|
// save the new link
|
||||||
|
newUrl.save(function(err) {
|
||||||
|
if (err){
|
||||||
|
console.log(err);
|
||||||
|
}
|
||||||
|
|
||||||
|
shortUrl = config.webhost + base58.encode(newUrl._id);
|
||||||
|
|
||||||
|
res.send({'shortUrl': shortUrl});
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
});
|
||||||
|
|
||||||
|
});
|
||||||
|
|
||||||
|
app.get('/:encoded_id', function(req, res){
|
||||||
|
|
||||||
|
var base58Id = req.params.encoded_id;
|
||||||
|
|
||||||
|
var id = base58.decode(base58Id);
|
||||||
|
|
||||||
|
// check if url already exists in database
|
||||||
|
Url.findOne({_id: id}, function (err, doc){
|
||||||
|
if (doc) {
|
||||||
|
res.redirect(doc.long_url);
|
||||||
|
} else {
|
||||||
|
res.redirect(config.webhost);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
});
|
||||||
|
|
||||||
|
var server = app.listen(3000, function(){
|
||||||
|
console.log('Server listening on port 3000');
|
||||||
|
});
|
26
base58.js
Normal file
26
base58.js
Normal file
@ -0,0 +1,26 @@
|
|||||||
|
var alphabet = "123456789abcdefghijkmnopqrstuvwxyzABCDEFGHJKLMNPQRSTUVWXYZ";
|
||||||
|
var base = alphabet.length;
|
||||||
|
|
||||||
|
function encode(num){
|
||||||
|
var encoded = '';
|
||||||
|
while (num){
|
||||||
|
var remainder = num % base;
|
||||||
|
num = Math.floor(num / base);
|
||||||
|
encoded = alphabet[remainder].toString() + encoded;
|
||||||
|
}
|
||||||
|
return encoded;
|
||||||
|
}
|
||||||
|
|
||||||
|
function decode(str){
|
||||||
|
var decoded = 0;
|
||||||
|
while (str){
|
||||||
|
var index = alphabet.indexOf(str[0]);
|
||||||
|
var power = str.length - 1;
|
||||||
|
decoded += index * (Math.pow(base, power));
|
||||||
|
str = str.substring(1);
|
||||||
|
}
|
||||||
|
return decoded;
|
||||||
|
}
|
||||||
|
|
||||||
|
module.exports.encode = encode;
|
||||||
|
module.exports.decode = decode;
|
9
config.js
Normal file
9
config.js
Normal file
@ -0,0 +1,9 @@
|
|||||||
|
var config = {};
|
||||||
|
|
||||||
|
config.db = {};
|
||||||
|
config.webhost = 'http://localhost:3000/';
|
||||||
|
|
||||||
|
config.db.host = 'localhost';
|
||||||
|
config.db.name = 'url_shortener';
|
||||||
|
|
||||||
|
module.exports = config;
|
31
models/url.js
Normal file
31
models/url.js
Normal file
@ -0,0 +1,31 @@
|
|||||||
|
var mongoose = require('mongoose');
|
||||||
|
var Schema = mongoose.Schema;
|
||||||
|
|
||||||
|
var CounterSchema = Schema({
|
||||||
|
_id: {type: String, required: true},
|
||||||
|
seq: { type: Number, default: 0 }
|
||||||
|
});
|
||||||
|
|
||||||
|
var counter = mongoose.model('counter', CounterSchema);
|
||||||
|
|
||||||
|
// create a schema for our links
|
||||||
|
var urlSchema = new Schema({
|
||||||
|
_id: {type: Number, index: true},
|
||||||
|
long_url: String,
|
||||||
|
created_at: Date
|
||||||
|
});
|
||||||
|
|
||||||
|
urlSchema.pre('save', function(next){
|
||||||
|
var doc = this;
|
||||||
|
counter.findByIdAndUpdate({_id: 'url_count'}, {$inc: {seq: 1} }, function(error, counter) {
|
||||||
|
if (error)
|
||||||
|
return next(error);
|
||||||
|
doc.created_at = new Date();
|
||||||
|
doc._id = counter.seq;
|
||||||
|
next();
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
var Url = mongoose.model('Url', urlSchema);
|
||||||
|
|
||||||
|
module.exports = Url;
|
16
package.json
Normal file
16
package.json
Normal file
@ -0,0 +1,16 @@
|
|||||||
|
{
|
||||||
|
"name": "url-shortener",
|
||||||
|
"version": "1.0.0",
|
||||||
|
"description": "A NodeJS + Express + MongoDB based URL shortener",
|
||||||
|
"main": "app.js",
|
||||||
|
"scripts": {
|
||||||
|
"test": "echo \"Error: no test specified\" && exit 1"
|
||||||
|
},
|
||||||
|
"author": "coligo.io",
|
||||||
|
"license": "MIT",
|
||||||
|
"dependencies": {
|
||||||
|
"body-parser": "^1.14.1",
|
||||||
|
"express": "^4.13.3",
|
||||||
|
"mongoose": "4.2.9"
|
||||||
|
}
|
||||||
|
}
|
87
public/css/styles.css
Normal file
87
public/css/styles.css
Normal file
@ -0,0 +1,87 @@
|
|||||||
|
.btn:focus, .btn-shorten:focus{
|
||||||
|
outline: 0 !important;
|
||||||
|
}
|
||||||
|
|
||||||
|
html,
|
||||||
|
body {
|
||||||
|
height: 100%;
|
||||||
|
background-color: #4791D2;
|
||||||
|
}
|
||||||
|
|
||||||
|
body {
|
||||||
|
color: #fff;
|
||||||
|
text-align: center;
|
||||||
|
font-family: 'Raleway', sans-serif;
|
||||||
|
}
|
||||||
|
|
||||||
|
.btn-shorten {
|
||||||
|
color: #ffffff;
|
||||||
|
background-color: #F89406;
|
||||||
|
border: none;
|
||||||
|
}
|
||||||
|
|
||||||
|
.btn-shorten:hover,
|
||||||
|
.btn-shorten:focus,
|
||||||
|
.btn-shorten:active,
|
||||||
|
.btn-shorten.active {
|
||||||
|
color: #ffffff;
|
||||||
|
background-color: #FA8900;
|
||||||
|
border: none;
|
||||||
|
}
|
||||||
|
|
||||||
|
.site-wrapper {
|
||||||
|
display: table;
|
||||||
|
width: 100%;
|
||||||
|
height: 100%;
|
||||||
|
min-height: 100%;
|
||||||
|
}
|
||||||
|
|
||||||
|
.site-wrapper-inner {
|
||||||
|
display: table-cell;
|
||||||
|
vertical-align: top;
|
||||||
|
}
|
||||||
|
|
||||||
|
.main-container {
|
||||||
|
margin-right: auto;
|
||||||
|
margin-left: auto;
|
||||||
|
margin-top: 80px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.inner {
|
||||||
|
padding: 30px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.inner h4 {
|
||||||
|
padding-bottom: 30px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.glyphicon-link {
|
||||||
|
font-size: 2em;
|
||||||
|
}
|
||||||
|
|
||||||
|
.inner h1 {
|
||||||
|
margin-top: 5px;
|
||||||
|
}
|
||||||
|
|
||||||
|
#link {
|
||||||
|
display: none;
|
||||||
|
padding-top: 15px;
|
||||||
|
}
|
||||||
|
|
||||||
|
#link a{
|
||||||
|
color: #F89406;
|
||||||
|
font-size: 1.5em;
|
||||||
|
margin-right: 20px;
|
||||||
|
}
|
||||||
|
|
||||||
|
@media (min-width: 768px) {
|
||||||
|
.main-container {
|
||||||
|
width: 100%;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@media (min-width: 992px) {
|
||||||
|
.main-container {
|
||||||
|
width: 700px;
|
||||||
|
}
|
||||||
|
}
|
16
public/javascripts/shorten.js
Normal file
16
public/javascripts/shorten.js
Normal file
@ -0,0 +1,16 @@
|
|||||||
|
$('.btn-shorten').on('click', function(){
|
||||||
|
|
||||||
|
$.ajax({
|
||||||
|
url: '/api/shorten',
|
||||||
|
type: 'POST',
|
||||||
|
dataType: 'JSON',
|
||||||
|
data: {url: $('#url-field').val()},
|
||||||
|
success: function(data){
|
||||||
|
var resultHTML = '<a class="result" href="' + data.shortUrl + '">'
|
||||||
|
+ data.shortUrl + '</a>';
|
||||||
|
$('#link').html(resultHTML);
|
||||||
|
$('#link').hide().fadeIn('slow');
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
});
|
55
views/index.html
Normal file
55
views/index.html
Normal file
@ -0,0 +1,55 @@
|
|||||||
|
<!DOCTYPE html>
|
||||||
|
<html>
|
||||||
|
<head>
|
||||||
|
<meta charset="utf-8">
|
||||||
|
<meta http-equiv="X-UA-Compatible" content="IE=edge">
|
||||||
|
<meta name="viewport" content="width=device-width, initial-scale=1">
|
||||||
|
|
||||||
|
<title>URL Shortener - coligo.io</title>
|
||||||
|
<link href='https://fonts.googleapis.com/css?family=Raleway' rel='stylesheet' type='text/css'>
|
||||||
|
<link rel="stylesheet" href="https://maxcdn.bootstrapcdn.com/bootstrap/3.3.6/css/bootstrap.min.css">
|
||||||
|
<link href="css/styles.css" rel="stylesheet">
|
||||||
|
</head>
|
||||||
|
<body>
|
||||||
|
|
||||||
|
<div class="site-wrapper">
|
||||||
|
|
||||||
|
<div class="site-wrapper-inner">
|
||||||
|
|
||||||
|
<div class="main-container">
|
||||||
|
|
||||||
|
<div class="inner cover">
|
||||||
|
<span class="glyphicon glyphicon-link"></span>
|
||||||
|
<h1>URL Shortener</h1>
|
||||||
|
<h4>coligo.io</h4>
|
||||||
|
|
||||||
|
<div class="row">
|
||||||
|
|
||||||
|
<div class="col-lg-12">
|
||||||
|
<div class="input-group input-group-lg">
|
||||||
|
<input id="url-field" type="text" class="form-control" placeholder="Paste a link...">
|
||||||
|
<span class="input-group-btn">
|
||||||
|
<button class="btn btn-shorten" type="button">SHORTEN</button>
|
||||||
|
</span>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div class="col-lg-12">
|
||||||
|
<div id="link"></div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
</div>
|
||||||
|
|
||||||
|
</div>
|
||||||
|
|
||||||
|
</div>
|
||||||
|
|
||||||
|
</div>
|
||||||
|
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<script src="https://code.jquery.com/jquery-2.2.0.min.js"></script>
|
||||||
|
<script src="https://maxcdn.bootstrapcdn.com/bootstrap/3.3.6/js/bootstrap.min.js"></script>
|
||||||
|
<script src="javascripts/shorten.js"></script>
|
||||||
|
</body>
|
||||||
|
</html>
|
Loading…
Reference in New Issue
Block a user