diff --git a/Dockerfile b/Dockerfile index 2cf867d..1c22bac 100644 --- a/Dockerfile +++ b/Dockerfile @@ -59,9 +59,9 @@ ENV PATH $PYENV_ROOT/bin:$PATH ## Python #######################################################################33 RUN pyenv install $PYTHON_VERSION && \ - pyenv global $PYTHON_VERISON && \ - pip3 install pipenv + pyenv global $PYTHON_VERISON +RUN ls $PYENV_ROOT/bin/ #######################################################################33 ## Install Deps #######################################################################33 diff --git a/Makefile b/Makefile index ece822b..b462c41 100644 --- a/Makefile +++ b/Makefile @@ -1,4 +1,6 @@ -export PYTHONPATH =: $(PWD) +export DOCKER_HOST := ssh://datavi.be +export PYTHONPATH := $(PWD) +export SQLITE_FILENAME := ./db.sqlite default: docker diff --git a/tvid/server.py b/tvid/server.py index cad2385..4fec629 100644 --- a/tvid/server.py +++ b/tvid/server.py @@ -15,6 +15,7 @@ from sqlalchemy.ext.declarative import declarative_base from bottle.ext import sqlalchemy from pprint import pprint from sqlalchemy import create_engine +import urllib.parse import os import random import string @@ -25,7 +26,7 @@ PORT = os.environ.get('PORT', 8080) DEBUG = os.environ.get('DEBUG', False) SQLITE_FILENAME = os.environ.get('SQLITE_FILENAME','/data/db.sqlite') DATABASE_URL = os.environ.get('DATABASE_URL','sqlite:///' + SQLITE_FILENAME) -ADMINPSK = os.environ.get('ADMINPSK','hunter2') +ADMIN_PSK = os.environ.get('ADMIN_PSK','hunter2') # sorry for global SQLBASE = declarative_base() @@ -109,22 +110,57 @@ def serve(): # FIXME make this use sessions instead of just storing PSK in a cookie # https://bottlepy.org/docs/dev/recipes.html @app.get('/admin') - def adminpage(): - c = request.get_cookie("adminpw") + def adminpage(db): + c = request.get_cookie("psk") + if not c: + redirect('/login') + return + if c != ADMIN_PSK: + redirect('/logout') + return + tvs = db.query(TV).order_by(TV.lastSeen) + return template('adminpanel', tvs=tvs, version=VERSION) - # FIXME check their 'adminpw' cookie here, redirect to /loign - return "Hello World!" - - # here we ask for a password and cookie them and bounce them back to /admin - @app.get('/login') - def checklogin(): + @app.post('/admin') + def savesettings(): + c = request.get_cookie("psk") + if not c: + redirect('/login') + return + if c != ADMIN_PSK: + redirect('/logout') + return raise NotImplementedError() - #response.set_cookie("adminpw", whatever) - redirect('/login') - @app.get('/logut') + # here we ask for a password: + @app.get('/login') + def loginform(): + msg = request.GET.msg + return template('loginform', version=VERSION, msg=msg) + + @app.post('/checklogin') + def checklogin(): + attemptedPass = request.forms.get('password') + if not attemptedPass: + redirect( + '/login?msg=' + + urllib.parse.quote_plus(u"Incorrect password.") + ) + return + if attemptedPass != ADMIN_PSK: + redirect( + '/login?msg=' + + urllib.parse.quote_plus(u"Incorrect password.") + ) + return + # password is right, cookie them: + response.set_cookie("psk", attemptedPass) + redirect('/admin') + return + + @app.get('/logout') def logout(): - response.set_cookie("adminpw", "") + response.set_cookie("psk", "") redirect('/login') app.run(host='0.0.0.0', port=PORT, debug=DEBUG) diff --git a/views/adminpanel.tpl b/views/adminpanel.tpl new file mode 100644 index 0000000..a8aa0c8 --- /dev/null +++ b/views/adminpanel.tpl @@ -0,0 +1,4 @@ +% rebase('base.tpl', refresh=None, title='tvid administration') + + +

Powered by tvid v{{version}}

diff --git a/views/base.tpl b/views/base.tpl new file mode 100644 index 0000000..dc302f5 --- /dev/null +++ b/views/base.tpl @@ -0,0 +1,16 @@ + + + + {{ get('title','tvid') }} + + + % if defined('refresh'): + + % end + % include('htmlheader.tpl') + + + + {{!base}} + + diff --git a/views/displayid.tpl b/views/displayid.tpl index 8d3f926..d52582d 100644 --- a/views/displayid.tpl +++ b/views/displayid.tpl @@ -1,20 +1,12 @@ - - - tv info page - - - - -
-

- Display ID: -

-

{{id}}

+% rebase('base.tpl', refresh='60') +
+

+ Display ID: +

+

{{id}}

- (They're only letters. I like India, O like - Oscar.) + (They're only letters. I like India, O like + Oscar.) -

Powered by tvid v{{version}}

-
- - +

Powered by tvid v{{version}}

+
diff --git a/views/htmlheader.tpl b/views/htmlheader.tpl new file mode 100644 index 0000000..5846f27 --- /dev/null +++ b/views/htmlheader.tpl @@ -0,0 +1,5 @@ + + + + + diff --git a/views/loginform.tpl b/views/loginform.tpl new file mode 100644 index 0000000..6b82752 --- /dev/null +++ b/views/loginform.tpl @@ -0,0 +1,24 @@ +% rebase('base.tpl', refresh=None, title=None) + +% if defined('msg') and msg: +
{{msg}}
+% end + +
+
+ + +
+

tvid administration

+
+ + +
+ + +
+ +
+
+

Powered by tvid v{{version}}

diff --git a/views/nocookie.tpl b/views/nocookie.tpl index a6354c8..64222f0 100644 --- a/views/nocookie.tpl +++ b/views/nocookie.tpl @@ -1,10 +1,2 @@ - - - tv info page - - - - -

Please enable cookies, they're required.

- - +% rebase('base.tpl', refresh='60') +

Please enable cookies, they're required.

diff --git a/views/style.tpl b/views/style.tpl index ef0985b..601bbb2 100644 --- a/views/style.tpl +++ b/views/style.tpl @@ -8,3 +8,249 @@ body { h1 { font-size: 48pt; } + +a { + color: #92badd; + display:inline-block; + text-decoration: none; + font-weight: 400; +} + +h2 { + text-align: center; + font-size: 16px; + font-weight: 600; + text-transform: uppercase; + display:inline-block; + margin: 40px 8px 10px 8px; + color: #cccccc; +} + + +/* STRUCTURE */ + +.wrapper { + display: flex; + align-items: center; + flex-direction: column; + justify-content: center; + width: 100%; + min-height: 100%; + padding: 20px; +} + +#formContent { + -webkit-border-radius: 10px 10px 10px 10px; + border-radius: 10px 10px 10px 10px; + background: #fff; + padding: 30px; + width: 90%; + max-width: 450px; + position: relative; + padding: 0px; + -webkit-box-shadow: 0 30px 60px 0 rgba(0,0,0,0.3); + box-shadow: 0 30px 60px 0 rgba(0,0,0,0.3); + text-align: center; +} + +#formFooter { + background-color: #f6f6f6; + border-top: 1px solid #dce8f1; + padding: 25px; + text-align: center; + -webkit-border-radius: 0 0 10px 10px; + border-radius: 0 0 10px 10px; +} + + +/* TABS */ + +h2.inactive { + color: #cccccc; +} + +h2.active { + color: #0d0d0d; + border-bottom: 2px solid #5fbae9; +} + + +/* FORM TYPOGRAPHY*/ + +input[type=button], input[type=submit], input[type=reset] { + background-color: #56baed; + border: none; + color: white; + padding: 15px 80px; + text-align: center; + text-decoration: none; + display: inline-block; + text-transform: uppercase; + font-size: 13px; + -webkit-box-shadow: 0 10px 30px 0 rgba(95,186,233,0.4); + box-shadow: 0 10px 30px 0 rgba(95,186,233,0.4); + -webkit-border-radius: 5px 5px 5px 5px; + border-radius: 5px 5px 5px 5px; + margin: 5px 20px 40px 20px; + -webkit-transition: all 0.3s ease-in-out; + -moz-transition: all 0.3s ease-in-out; + -ms-transition: all 0.3s ease-in-out; + -o-transition: all 0.3s ease-in-out; + transition: all 0.3s ease-in-out; +} + +input[type=button]:hover, input[type=submit]:hover, input[type=reset]:hover { + background-color: #39ace7; +} + +input[type=button]:active, input[type=submit]:active, input[type=reset]:active { + -moz-transform: scale(0.95); + -webkit-transform: scale(0.95); + -o-transform: scale(0.95); + -ms-transform: scale(0.95); + transform: scale(0.95); +} + +input[type=text], input[type=password] { + background-color: #f6f6f6; + border: none; + color: #0d0d0d; + padding: 15px 32px; + text-align: center; + text-decoration: none; + display: inline-block; + font-size: 16px; + margin: 5px; + width: 85%; + border: 2px solid #f6f6f6; + -webkit-transition: all 0.5s ease-in-out; + -moz-transition: all 0.5s ease-in-out; + -ms-transition: all 0.5s ease-in-out; + -o-transition: all 0.5s ease-in-out; + transition: all 0.5s ease-in-out; + -webkit-border-radius: 5px 5px 5px 5px; + border-radius: 5px 5px 5px 5px; +} + +input[type=text]:focus { + background-color: #fff; + border-bottom: 2px solid #5fbae9; +} + +input[type=text]:placeholder { + color: #cccccc; +} + +/* ANIMATIONS */ + +/* Simple CSS3 Fade-in-down Animation */ +.fadeInDown { + -webkit-animation-name: fadeInDown; + animation-name: fadeInDown; + -webkit-animation-duration: 1s; + animation-duration: 1s; + -webkit-animation-fill-mode: both; + animation-fill-mode: both; +} + +@-webkit-keyframes fadeInDown { + 0% { + opacity: 0; + -webkit-transform: translate3d(0, -100%, 0); + transform: translate3d(0, -100%, 0); + } + 100% { + opacity: 1; + -webkit-transform: none; + transform: none; + } +} + +@keyframes fadeInDown { + 0% { + opacity: 0; + -webkit-transform: translate3d(0, -100%, 0); + transform: translate3d(0, -100%, 0); + } + 100% { + opacity: 1; + -webkit-transform: none; + transform: none; + } +} + +/* Simple CSS3 Fade-in Animation */ +@-webkit-keyframes fadeIn { from { opacity:0; } to { opacity:1; } } +@-moz-keyframes fadeIn { from { opacity:0; } to { opacity:1; } } +@keyframes fadeIn { from { opacity:0; } to { opacity:1; } } + +.fadeIn { + opacity:0; + -webkit-animation:fadeIn ease-in 1; + -moz-animation:fadeIn ease-in 1; + animation:fadeIn ease-in 1; + + -webkit-animation-fill-mode:forwards; + -moz-animation-fill-mode:forwards; + animation-fill-mode:forwards; + + -webkit-animation-duration:1s; + -moz-animation-duration:1s; + animation-duration:1s; +} + +.fadeIn.first { + -webkit-animation-delay: 0.4s; + -moz-animation-delay: 0.4s; + animation-delay: 0.4s; +} + +.fadeIn.second { + -webkit-animation-delay: 0.6s; + -moz-animation-delay: 0.6s; + animation-delay: 0.6s; +} + +.fadeIn.third { + -webkit-animation-delay: 0.8s; + -moz-animation-delay: 0.8s; + animation-delay: 0.8s; +} + +.fadeIn.fourth { + -webkit-animation-delay: 1s; + -moz-animation-delay: 1s; + animation-delay: 1s; +} + +/* Simple CSS3 Fade-in Animation */ +.underlineHover:after { + display: block; + left: 0; + bottom: -10px; + width: 0; + height: 2px; + background-color: #56baed; + content: ""; + transition: width 0.2s; +} + +.underlineHover:hover { + color: #0d0d0d; +} + +.underlineHover:hover:after{ + width: 100%; +} + + + +/* OTHERS */ + +*:focus { + outline: none; +} + +#icon { + width:60%; +}