progress
This commit is contained in:
parent
3ae7b05b9b
commit
691d54ee3e
|
@ -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
|
||||||
|
|
4
Makefile
4
Makefile
|
@ -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
|
||||||
|
|
||||||
|
|
|
@ -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)
|
||||||
|
|
|
@ -0,0 +1,4 @@
|
||||||
|
% rebase('base.tpl', refresh=None, title='tvid administration')
|
||||||
|
|
||||||
|
|
||||||
|
<p><small>Powered by tvid v{{version}}</small></p>
|
|
@ -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>
|
|
@ -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>
|
|
||||||
|
|
|
@ -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>
|
||||||
|
|
|
@ -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>
|
|
@ -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>
|
|
||||||
|
|
246
views/style.tpl
246
views/style.tpl
|
@ -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%;
|
||||||
|
}
|
||||||
|
|
Loading…
Reference in New Issue