adding docker-compose

master
adb-sh 4 years ago
parent 5af8687ebb
commit 8779fee2af

7
.gitignore vendored

@ -0,0 +1,7 @@
.idea/
./redis_conf/
./redis_data/
./node_logs/
./node_app./
node_modules/
./node_app/package-lock.json

@ -1,13 +1,13 @@
# short_url # short_url
This is a simple application to short you urls with your own web server. This is a simple application to short you urls with your own web server.
It is based on Nodejs and Redis. It is based on Nodejs and Redis.
You will have to change the IPs in the `index.js` and `./public/index.html` files to your public IP or domain. You can change the public ports `8080` (web UI) and `8081` (websocket) in the `docker-compose.yml` file
To start the server simply execute `index.js`: To start the server simply use `docker-compose`:
``` ```
screen -A -m -d -S short_url nodejs index.js docker-compose -p surl up -d
``` ```
It's recommended to use a reverse proxy like nginx to manage SSL certificates etc. It's recommended to use a reverse proxy like nginx to manage SSL certificates.
A test instance is running under [s.adb.sh](https://s.adb.sh). A test instance is running at [s.adb.sh](https://s.adb.sh).
//good luck //good luck

@ -0,0 +1,31 @@
version: '3'
services:
node:
image: node
container_name: surl_node
restart: always
ports:
- "8080:8080"
- "8081:8081"
volumes:
- ./node_app/:/home/node/app/
# - ./node_modules/:/home/node/app/node_modules/
- ./node_logs/:/var/log/
working_dir: /home/node/app/
environment:
- NODE_ENV=production
command: sh -c 'npm i && nodejs index.js'
links:
- redis
redis:
image: redis
container_name: surl_redis
expose:
- 6379
restart: always
volumes:
- ./redis_data/:/var/lib/redis/
- ./redis_conf/:/usr/local/etc/redis/
environment:
- REDIS_REPLICATION_MODE=master

@ -5,30 +5,34 @@ const ws = require('ws')
const redis = require('redis') const redis = require('redis')
const mime = require('mime') const mime = require('mime')
const XMLHttpRequest = require("xmlhttprequest").XMLHttpRequest const XMLHttpRequest = require("xmlhttprequest").XMLHttpRequest
const host = 'http://127.0.0.1:8080/' const host = 'http://127.0.0.1:8080/'
const outpath = ['sym', '', 'manifest.json', 'admin', 'stats']
//redis client //redis client
const redis_cli = redis.createClient() const redis_cli = redis.createClient({
host: 'redis',
port: 6379
})
redis_cli.on("error", function (error) { redis_cli.on("error", function (error) {
console.error(error) console.error(error)
}) })
const outpath = ['sym', '', 'manifest.json']
//HTTP server //HTTP server
http.createServer(function (req, res) { http.createServer(function (req, res) {
const q = url.parse(req.url, true); const q = url.parse(req.url, true);
let filename = "./public" + q.pathname; let filename = "./public" + q.pathname;
if (filename === "./public/") filename = "./public/index.html"; let path_split = q.pathname.split("/", 3);
if (path_split[path_split.length - 1] === "") filename += "/index.html";
let file_type = mime.getType(filename) let file_type = mime.getType(filename)
//q.pathname.split("/", 2)[1] === "sym" || ) if (function valid_path(){
if (function valid_path(){// //outpath.forEach( path => {if (request_path === path) return true})
for (let i = 0; i < outpath.length; i++) if (q.pathname.split("/", 2)[1] === outpath[i]) return true for (let i = 0; i < outpath.length; i++) if (path_split[1] === outpath[i]) return true
return false return false
}() === true){ }() === true){
fs.readFile(filename, function(err, data) { fs.readFile(filename, function(err, data) {
if (err) { if (err) {
res.writeHead(404, {'Content-Type': file_type}); res.writeHead(404, {'Content-Type': "text/html"});
return res.end("404 Not Found"); return res.end("404 Not Found");
} }
res.writeHead(200, {'Content-Type': file_type}); res.writeHead(200, {'Content-Type': file_type});
@ -42,7 +46,7 @@ http.createServer(function (req, res) {
res.writeHead(302, {'Location': obj}); res.writeHead(302, {'Location': obj});
return res.end(); return res.end();
}else{ }else{
res.writeHead(404, {'Content-Type': file_type}); res.writeHead(404, {'Content-Type': "text/html"});
return res.end("404 this short-url does not exist :/"); return res.end("404 this short-url does not exist :/");
} }
}); });
@ -113,14 +117,13 @@ wss.on('connection', ws => {
}) })
//random key //random key
const forbidden_array = ['sym', 'admin', 'stats']
function get_key(length) { function get_key(length) {
let forbidden = false; let output = '' let forbidden = false; let output = ''
let characters = 'ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789' let characters = 'ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789'
do{ do{
for (let i = 0; i < length; i++ ) for (let i = 0; i < length; i++ )
output += characters.charAt(Math.floor(Math.random() * characters.length)) output += characters.charAt(Math.floor(Math.random() * characters.length))
for (let i = 0; i < forbidden_array.length; i++) if (output === forbidden_array[i]) forbidden = true for (let i = 0; i < output.length; i++) if (output === outpath[i]) forbidden = true
redis_cli.hget("surl;"+output, "url", function(err, obj) { redis_cli.hget("surl;"+output, "url", function(err, obj) {
if(err) console.log(err) if(err) console.log(err)
if(obj) forbidden = true if(obj) forbidden = true

@ -0,0 +1,57 @@
{
"name": "surl",
"version": "1.1.0",
"lockfileVersion": 1,
"requires": true,
"dependencies": {
"denque": {
"version": "1.4.1",
"resolved": "https://registry.npmjs.org/denque/-/denque-1.4.1.tgz",
"integrity": "sha512-OfzPuSZKGcgr96rf1oODnfjqBFmr1DVoc/TrItj3Ohe0Ah1C5WX5Baquw/9U9KovnQ88EqmJbD66rKYUQYN1tQ=="
},
"mime": {
"version": "2.4.6",
"resolved": "https://registry.npmjs.org/mime/-/mime-2.4.6.tgz",
"integrity": "sha512-RZKhC3EmpBchfTGBVb8fb+RL2cWyw/32lshnsETttkBAyAUXSGHxbEJWWRXc751DrIxG1q04b8QwMbAwkRPpUA=="
},
"redis": {
"version": "3.0.2",
"resolved": "https://registry.npmjs.org/redis/-/redis-3.0.2.tgz",
"integrity": "sha512-PNhLCrjU6vKVuMOyFu7oSP296mwBkcE6lrAjruBYG5LgdSqtRBoVQIylrMyVZD/lkF24RSNNatzvYag6HRBHjQ==",
"requires": {
"denque": "^1.4.1",
"redis-commands": "^1.5.0",
"redis-errors": "^1.2.0",
"redis-parser": "^3.0.0"
}
},
"redis-commands": {
"version": "1.6.0",
"resolved": "https://registry.npmjs.org/redis-commands/-/redis-commands-1.6.0.tgz",
"integrity": "sha512-2jnZ0IkjZxvguITjFTrGiLyzQZcTvaw8DAaCXxZq/dsHXz7KfMQ3OUJy7Tz9vnRtZRVz6VRCPDvruvU8Ts44wQ=="
},
"redis-errors": {
"version": "1.2.0",
"resolved": "https://registry.npmjs.org/redis-errors/-/redis-errors-1.2.0.tgz",
"integrity": "sha1-62LSrbFeTq9GEMBK/hUpOEJQq60="
},
"redis-parser": {
"version": "3.0.0",
"resolved": "https://registry.npmjs.org/redis-parser/-/redis-parser-3.0.0.tgz",
"integrity": "sha1-tm2CjNyv5rS4pCin3vTGvKwxyLQ=",
"requires": {
"redis-errors": "^1.0.0"
}
},
"ws": {
"version": "7.3.1",
"resolved": "https://registry.npmjs.org/ws/-/ws-7.3.1.tgz",
"integrity": "sha512-D3RuNkynyHmEJIpD2qrgVkc9DQ23OrN/moAwZX4L8DfvszsJxpjQuUq3LMx6HoYji9fbIOBY18XWBsAux1ZZUA=="
},
"xmlhttprequest": {
"version": "1.8.0",
"resolved": "https://registry.npmjs.org/xmlhttprequest/-/xmlhttprequest-1.8.0.tgz",
"integrity": "sha1-Z/4HXFwk/vOfnWX197f+dRcZaPw="
}
}
}

@ -1,6 +1,6 @@
{ {
"name": "short_url", "name": "surl",
"version": "1.0.0", "version": "1.1.0",
"dependencies": { "dependencies": {
"mime": "^2.4.6", "mime": "^2.4.6",
"redis": "^3.0.2", "redis": "^3.0.2",

@ -1,90 +1,90 @@
<html> <html>
<head> <head>
<title>sURL</title> <title>sURL</title>
<meta charset="utf-8"> <meta charset="utf-8">
<!-- PWA support --> <!-- PWA support -->
<link rel="manifest" href="/manifest.json"/> <link rel="manifest" href="/manifest.json"/>
<meta name="theme-color" content="#2F3BA2"/> <meta name="theme-color" content="#2F3BA2"/>
<meta name="description" content="short your url"/> <meta name="description" content="short your url"/>
<meta name='viewport' content='width=device-width, initial-scale=1, maximum-scale=1, user-scalable=0'/> <meta name='viewport' content='width=device-width, initial-scale=1, maximum-scale=1, user-scalable=0'/>
<link rel="stylesheet" type="text/css" href="./sym/dark.css"/> <link rel="stylesheet" type="text/css" href="./sym/dark.css"/>
<script src="./sym/script.js"></script> <script src="./sym/script.js"></script>
<script> <script>
const wsurl = 'ws://127.0.0.1:8081' const wsurl = 'ws://127.0.0.1:8081'
const socket = new WebSocket(wsurl) const socket = new WebSocket(wsurl)
socket.onopen = () => { socket.onopen = () => {
socket.send('new session') socket.send('new session')
} }
socket.onerror = (error) => { socket.onerror = (error) => {
console.log(`WebSocket error: ${error}`) console.log(`WebSocket error: ${error}`)
} }
socket.onclose = error => show_error('session timed out (refresh)') socket.onclose = error => show_error('session timed out (refresh)')
socket.onmessage = (e) => { socket.onmessage = (e) => {
console.log(e.data) console.log(e.data)
let msg = e.data.split(";", 2) let msg = e.data.split(";", 2)
if (msg[0] === 'error') show_error(msg[1]) if (msg[0] === 'error') show_error(msg[1])
else if (msg[0] === 'surl') { else if (msg[0] === 'surl') {
document.getElementById('surl-input').value = msg[1] document.getElementById('surl-input').value = msg[1]
document.getElementById('surl-popup').style.display = 'block' document.getElementById('surl-popup').style.display = 'block'
} }
} }
function copy_url() { function copy_url() {
document.getElementById('surl-input').select() document.getElementById('surl-input').select()
document.execCommand("copy") document.execCommand("copy")
} }
//document.surl_form.action = surl_submit() //document.surl_form.action = surl_submit()
function lurl_submit() { function lurl_submit() {
socket.send('long_url;'+document.getElementById('longurl-input').value) socket.send('long_url;'+document.getElementById('longurl-input').value)
} }
function show_error(msg) { function show_error(msg) {
let error_style = document.getElementById('error-box').style let error_style = document.getElementById('error-box').style
document.getElementById('error-message').innerText = msg document.getElementById('error-message').innerText = msg
error_style.display = "block" error_style.display = "block"
error_style.animation = "slide-from-left alternate 0.2s" error_style.animation = "slide-from-left alternate 0.2s"
setTimeout(to => {error_style.animation = ""}, 200) setTimeout(to => {error_style.animation = ""}, 200)
} }
</script> </script>
</head> </head>
<body> <body>
<div class="input-box"> <div class="input-box">
<div class="title">short your url</div> <div class="title">short your url</div>
<form name="surl_form" action="javascript:lurl_submit()"> <form name="surl_form" action="javascript:lurl_submit()">
<div class="input-field" id="longurl"> <div class="input-field" id="longurl">
<input class="input" id="longurl-input" type="text" autocomplete="off" onblur="deselected('longurl');" onfocus="selected('longurl');" name="url" value="" maxlength="2000" placeholder="https://your.long.url/junk"> <input class="input" id="longurl-input" type="text" autocomplete="off" onblur="deselected('longurl');" onfocus="selected('longurl');" name="url" value="" maxlength="2000" placeholder="https://your.long.url/junk">
</div> </div>
<input type="hidden" value="search" name="login"> <input type="hidden" value="search" name="login">
<a href="javascript:lurl_submit()"> <a href="javascript:lurl_submit()">
<div class="btn-blue" id="short-btn"> <div class="btn-blue" id="short-btn">
<div class="btn-text">short</div> <div class="btn-text">short</div>
</div> </div>
</a> </a>
</form> </form>
</div> </div>
<div class="box-dark" id="surl-popup"> <div class="box-dark" id="surl-popup">
<div class="input-box"> <div class="input-box">
<div class="title2">your sURL:</div> <div class="title2">your sURL:</div>
<div class="input-field" id="surl"> <div class="input-field" id="surl">
<input class="input" id="surl-input" type="text" onblur="deselected('surl');" onfocus="selected('surl');" size="30" value="" maxlength="20" placeholder="surl"> <input class="input" id="surl-input" type="text" onblur="deselected('surl');" onfocus="selected('surl');" size="30" value="" maxlength="20" placeholder="surl">
</div> </div>
<input type="hidden" value="search" name="login"> <input type="hidden" value="search" name="login">
<div class="btn-green" id="copy-btn" onclick="copy_url()"> <div class="btn-green" id="copy-btn" onclick="copy_url()">
<div class="btn-text">copy</div> <div class="btn-text">copy</div>
</div> </div>
</div> </div>
<div onclick="this.parentNode.style.display = 'none'" style="position: absolute; top:5px; right: 5px;" class="sym_btn-invisible"> <div onclick="this.parentNode.style.display = 'none'" style="position: absolute; top:5px; right: 5px;" class="sym_btn-invisible">
<img class="icon" src="./sym/ic_close_white_24px.svg" /> <img class="icon" src="./sym/ic_close_white_24px.svg" />
</div> </div>
</div> </div>
<div id="error-box" class="error"> <div id="error-box" class="error">
<div onclick="this.parentNode.style.display = 'none'" style="position: absolute; top:5px; right: 5px;" class="sym_btn-invisible"> <div onclick="this.parentNode.style.display = 'none'" style="position: absolute; top:5px; right: 5px;" class="sym_btn-invisible">
<img class="icon" src="./sym/ic_close_white_24px.svg"> <img class="icon" src="./sym/ic_close_white_24px.svg">
</div> </div>
<div id="error-message" class="btn-text"> <div id="error-message" class="btn-text">
</div> </div>
</div> </div>
</body> </body>
</html> </html>

@ -1,4 +1,4 @@
<svg fill="#FFFFFF" height="24" viewBox="0 0 24 24" width="24" xmlns="http://www.w3.org/2000/svg"> <svg fill="#FFFFFF" height="24" viewBox="0 0 24 24" width="24" xmlns="http://www.w3.org/2000/svg">
<path d="M19 6.41L17.59 5 12 10.59 6.41 5 5 6.41 10.59 12 5 17.59 6.41 19 12 13.41 17.59 19 19 17.59 13.41 12z"/> <path d="M19 6.41L17.59 5 12 10.59 6.41 5 5 6.41 10.59 12 5 17.59 6.41 19 12 13.41 17.59 19 19 17.59 13.41 12z"/>
<path d="M0 0h24v24H0z" fill="none"/> <path d="M0 0h24v24H0z" fill="none"/>
</svg> </svg>

Before

Width:  |  Height:  |  Size: 268 B

After

Width:  |  Height:  |  Size: 265 B

Loading…
Cancel
Save