11 changed files with 209 additions and 111 deletions
@ -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 |
|||
This is a simple application to short you urls with your own web server. |
|||
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. |
|||
To start the server simply execute `index.js`: |
|||
You can change the public ports `8080` (web UI) and `8081` (websocket) in the `docker-compose.yml` file |
|||
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 |
|||
|
@ -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 |
@ -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", |
|||
"version": "1.0.0", |
|||
"name": "surl", |
|||
"version": "1.1.0", |
|||
"dependencies": { |
|||
"mime": "^2.4.6", |
|||
"redis": "^3.0.2", |
@ -1,90 +1,90 @@ |
|||
<html> |
|||
<head> |
|||
<title>sURL</title> |
|||
<meta charset="utf-8"> |
|||
<!-- PWA support --> |
|||
<link rel="manifest" href="/manifest.json"/> |
|||
<meta name="theme-color" content="#2F3BA2"/> |
|||
<meta name="description" content="short your url"/> |
|||
<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"/> |
|||
<script src="./sym/script.js"></script> |
|||
<script> |
|||
const wsurl = 'ws://127.0.0.1:8081' |
|||
const socket = new WebSocket(wsurl) |
|||
|
|||
socket.onopen = () => { |
|||
socket.send('new session') |
|||
} |
|||
socket.onerror = (error) => { |
|||
console.log(`WebSocket error: ${error}`) |
|||
} |
|||
socket.onclose = error => show_error('session timed out (refresh)') |
|||
socket.onmessage = (e) => { |
|||
console.log(e.data) |
|||
let msg = e.data.split(";", 2) |
|||
if (msg[0] === 'error') show_error(msg[1]) |
|||
else if (msg[0] === 'surl') { |
|||
document.getElementById('surl-input').value = msg[1] |
|||
document.getElementById('surl-popup').style.display = 'block' |
|||
} |
|||
} |
|||
function copy_url() { |
|||
document.getElementById('surl-input').select() |
|||
document.execCommand("copy") |
|||
} |
|||
//document.surl_form.action = surl_submit() |
|||
function lurl_submit() { |
|||
socket.send('long_url;'+document.getElementById('longurl-input').value) |
|||
} |
|||
function show_error(msg) { |
|||
let error_style = document.getElementById('error-box').style |
|||
document.getElementById('error-message').innerText = msg |
|||
error_style.display = "block" |
|||
error_style.animation = "slide-from-left alternate 0.2s" |
|||
setTimeout(to => {error_style.animation = ""}, 200) |
|||
} |
|||
</script> |
|||
</head> |
|||
<body> |
|||
<div class="input-box"> |
|||
<div class="title">short your url</div> |
|||
<form name="surl_form" action="javascript:lurl_submit()"> |
|||
<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"> |
|||
</div> |
|||
<input type="hidden" value="search" name="login"> |
|||
<a href="javascript:lurl_submit()"> |
|||
<div class="btn-blue" id="short-btn"> |
|||
<div class="btn-text">short</div> |
|||
</div> |
|||
</a> |
|||
</form> |
|||
</div> |
|||
|
|||
<div class="box-dark" id="surl-popup"> |
|||
<div class="input-box"> |
|||
<div class="title2">your sURL:</div> |
|||
<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"> |
|||
</div> |
|||
<input type="hidden" value="search" name="login"> |
|||
<div class="btn-green" id="copy-btn" onclick="copy_url()"> |
|||
<div class="btn-text">copy</div> |
|||
</div> |
|||
</div> |
|||
<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" /> |
|||
</div> |
|||
</div> |
|||
|
|||
<div id="error-box" class="error"> |
|||
<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"> |
|||
</div> |
|||
<div id="error-message" class="btn-text"> |
|||
</div> |
|||
</div> |
|||
</body> |
|||
</html> |
|||
<html> |
|||
<head> |
|||
<title>sURL</title> |
|||
<meta charset="utf-8"> |
|||
<!-- PWA support --> |
|||
<link rel="manifest" href="/manifest.json"/> |
|||
<meta name="theme-color" content="#2F3BA2"/> |
|||
<meta name="description" content="short your url"/> |
|||
<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"/> |
|||
<script src="./sym/script.js"></script> |
|||
<script> |
|||
const wsurl = 'ws://127.0.0.1:8081' |
|||
const socket = new WebSocket(wsurl) |
|||
|
|||
socket.onopen = () => { |
|||
socket.send('new session') |
|||
} |
|||
socket.onerror = (error) => { |
|||
console.log(`WebSocket error: ${error}`) |
|||
} |
|||
socket.onclose = error => show_error('session timed out (refresh)') |
|||
socket.onmessage = (e) => { |
|||
console.log(e.data) |
|||
let msg = e.data.split(";", 2) |
|||
if (msg[0] === 'error') show_error(msg[1]) |
|||
else if (msg[0] === 'surl') { |
|||
document.getElementById('surl-input').value = msg[1] |
|||
document.getElementById('surl-popup').style.display = 'block' |
|||
} |
|||
} |
|||
function copy_url() { |
|||
document.getElementById('surl-input').select() |
|||
document.execCommand("copy") |
|||
} |
|||
//document.surl_form.action = surl_submit() |
|||
function lurl_submit() { |
|||
socket.send('long_url;'+document.getElementById('longurl-input').value) |
|||
} |
|||
function show_error(msg) { |
|||
let error_style = document.getElementById('error-box').style |
|||
document.getElementById('error-message').innerText = msg |
|||
error_style.display = "block" |
|||
error_style.animation = "slide-from-left alternate 0.2s" |
|||
setTimeout(to => {error_style.animation = ""}, 200) |
|||
} |
|||
</script> |
|||
</head> |
|||
<body> |
|||
<div class="input-box"> |
|||
<div class="title">short your url</div> |
|||
<form name="surl_form" action="javascript:lurl_submit()"> |
|||
<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"> |
|||
</div> |
|||
<input type="hidden" value="search" name="login"> |
|||
<a href="javascript:lurl_submit()"> |
|||
<div class="btn-blue" id="short-btn"> |
|||
<div class="btn-text">short</div> |
|||
</div> |
|||
</a> |
|||
</form> |
|||
</div> |
|||
|
|||
<div class="box-dark" id="surl-popup"> |
|||
<div class="input-box"> |
|||
<div class="title2">your sURL:</div> |
|||
<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"> |
|||
</div> |
|||
<input type="hidden" value="search" name="login"> |
|||
<div class="btn-green" id="copy-btn" onclick="copy_url()"> |
|||
<div class="btn-text">copy</div> |
|||
</div> |
|||
</div> |
|||
<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" /> |
|||
</div> |
|||
</div> |
|||
|
|||
<div id="error-box" class="error"> |
|||
<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"> |
|||
</div> |
|||
<div id="error-message" class="btn-text"> |
|||
</div> |
|||
</div> |
|||
</body> |
|||
</html> |
Before Width: | Height: | Size: 268 B After Width: | Height: | Size: 265 B |
Loading…
Reference in new issue