This commit is contained in:
Jeffrey Paul 2020-03-10 12:33:57 -07:00
parent 3ae7b05b9b
commit 691d54ee3e
10 changed files with 361 additions and 44 deletions

View File

@ -59,9 +59,9 @@ ENV PATH $PYENV_ROOT/bin:$PATH
## Python ## Python
#######################################################################33 #######################################################################33
RUN pyenv install $PYTHON_VERSION && \ RUN pyenv install $PYTHON_VERSION && \
pyenv global $PYTHON_VERISON && \ pyenv global $PYTHON_VERISON
pip3 install pipenv
RUN ls $PYENV_ROOT/bin/
#######################################################################33 #######################################################################33
## Install Deps ## Install Deps
#######################################################################33 #######################################################################33

View File

@ -1,4 +1,6 @@
export PYTHONPATH =: $(PWD) export DOCKER_HOST := ssh://datavi.be
export PYTHONPATH := $(PWD)
export SQLITE_FILENAME := ./db.sqlite
default: docker default: docker

View File

@ -15,6 +15,7 @@ from sqlalchemy.ext.declarative import declarative_base
from bottle.ext import sqlalchemy from bottle.ext import sqlalchemy
from pprint import pprint from pprint import pprint
from sqlalchemy import create_engine from sqlalchemy import create_engine
import urllib.parse
import os import os
import random import random
import string import string
@ -25,7 +26,7 @@ PORT = os.environ.get('PORT', 8080)
DEBUG = os.environ.get('DEBUG', False) DEBUG = os.environ.get('DEBUG', False)
SQLITE_FILENAME = os.environ.get('SQLITE_FILENAME','/data/db.sqlite') SQLITE_FILENAME = os.environ.get('SQLITE_FILENAME','/data/db.sqlite')
DATABASE_URL = os.environ.get('DATABASE_URL','sqlite:///' + SQLITE_FILENAME) 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 # sorry for global
SQLBASE = declarative_base() SQLBASE = declarative_base()
@ -109,22 +110,57 @@ def serve():
# FIXME make this use sessions instead of just storing PSK in a cookie # FIXME make this use sessions instead of just storing PSK in a cookie
# https://bottlepy.org/docs/dev/recipes.html # https://bottlepy.org/docs/dev/recipes.html
@app.get('/admin') @app.get('/admin')
def adminpage(): def adminpage(db):
c = request.get_cookie("adminpw") c = request.get_cookie("psk")
if not c:
# 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():
raise NotImplementedError()
#response.set_cookie("adminpw", whatever)
redirect('/login') 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)
@app.get('/logut') @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()
# 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(): def logout():
response.set_cookie("adminpw", "") response.set_cookie("psk", "")
redirect('/login') redirect('/login')
app.run(host='0.0.0.0', port=PORT, debug=DEBUG) app.run(host='0.0.0.0', port=PORT, debug=DEBUG)

4
views/adminpanel.tpl Normal file
View File

@ -0,0 +1,4 @@
% rebase('base.tpl', refresh=None, title='tvid administration')
<p><small>Powered by tvid v{{version}}</small></p>

16
views/base.tpl Normal file
View File

@ -0,0 +1,16 @@
<!doctype html>
<html lang="en">
<head>
<title>{{ get('title','tvid') }}</title>
<meta charset="utf-8">
% if defined('refresh'):
<meta http-equiv="refresh" content="{{ get('refresh',60) }}">
% end
% include('htmlheader.tpl')
<link rel="stylesheet" href="/style.css" />
</head>
<body>
{{!base}}
</body>
</html>

View File

@ -1,11 +1,5 @@
<html> % rebase('base.tpl', refresh='60')
<head> <div id="main">
<title>tv info page</title>
<meta http-equiv="refresh" content="60">
<link rel="stylesheet" href="/style.css" />
</head>
<body>
<div id="main">
<p> <p>
<i>Display ID:</i> <i>Display ID:</i>
</p> </p>
@ -15,6 +9,4 @@
Oscar.)</small> Oscar.)</small>
<p><small>Powered by tvid v{{version}}</small></p> <p><small>Powered by tvid v{{version}}</small></p>
</div> </div>
</body>
</html>

5
views/htmlheader.tpl Normal file
View File

@ -0,0 +1,5 @@
<link rel="stylesheet" href="https://maxcdn.bootstrapcdn.com/bootstrap/4.0.0/css/bootstrap.min.css" integrity="sha384-Gn5384xqQ1aoWXA+058RXPxPg6fy4IWvTNh0E263XmFcJlSAwiGgFAW/dAiS6JXm" crossorigin="anonymous">
<script src="https://code.jquery.com/jquery-3.2.1.slim.min.js" integrity="sha384-KJ3o2DKtIkvYIK3UENzmM7KCkRr/rE9/Qpg6aAZGJwFDMVNA/GpGFF93hXpG5KkN" crossorigin="anonymous"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/popper.js/1.12.9/umd/popper.min.js" integrity="sha384-ApNbgh9B+Y1QKtv3Rn7W3mgPxhU9K/ScQsAP7hUibX39j7fakFPskvXusvfa0b4Q" crossorigin="anonymous"></script>
<script src="https://maxcdn.bootstrapcdn.com/bootstrap/4.0.0/js/bootstrap.min.js" integrity="sha384-JZR6Spejh4U02d8jOt6vLEHfe/JQGiRRSQQxSfFWpi1MquVdAyjUar5+76PVCmYl" crossorigin="anonymous"></script>

24
views/loginform.tpl Normal file
View File

@ -0,0 +1,24 @@
% rebase('base.tpl', refresh=None, title=None)
% if defined('msg') and msg:
<div class="card card-body bg-light">{{msg}}</div>
% end
<div class="wrapper fadeInDown">
<div id="formContent">
<!-- Tabs Titles -->
<div class="fadeIn first">
<h2>tvid administration</h2>
</div>
<!-- Login Form -->
<form action="/checklogin" method="post">
<input type="password" id="password" class="fadeIn third"
name="password" placeholder="password">
<input type="submit" class="fadeIn fourth" value="Log In">
</form>
</div>
</div>
<p><small>Powered by tvid v{{version}}</small></p>

View File

@ -1,10 +1,2 @@
<html> % rebase('base.tpl', refresh='60')
<head> <h1>Please enable cookies, they're required.</h1>
<title>tv info page</title>
<meta http-equiv="refresh" content="60; url=/">
<link rel="stylesheet" href="/style.css" />
</head>
<body>
<h1>Please enable cookies, they're required.</h1>
</body>
</html>

View File

@ -8,3 +8,249 @@ body {
h1 { h1 {
font-size: 48pt; 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%;
}