tvid is a redirection manager for televisions, lobby displays, status dashboards, and the like, so that you can set all of the displays in your organization to one URL and manage them via the web.
You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

189 lines
5.9 KiB

#!/usr/bin/env python3
# this is a quick hack, to be improved later. trying to do the simplest
# thing that will possibly work.
## # TODO
## * put page content into templates/static files
## * clean up FIXME
import bottle
from bottle import route, run, request, response, redirect
from bottle import HTTPError, template
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
from datetime import datetime, timedelta
VERSION = "1.0.0"
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)
ADMIN_PSK = os.environ.get("ADMIN_PSK", "hunter2")
# sorry for global
SQLBASE = declarative_base()
# FIXME make this skip letters not in base58
def randomUpper(count):
return "".join(random.choice(string.ascii_uppercase) for _ in range(count))
# FIXME maybe make the IDs longer
def genRandomTVID():
tvid = str(randomUpper(3) + "-" + randomUpper(3))
return tvid
def serve():
app = bottle.Bottle()
# pull in models
from .db import TV
engine = create_engine(DATABASE_URL, echo=False)
plugin = sqlalchemy.Plugin(
# here we cookie them if they have no cookie, then redirect them to the
# cookie'd value (whether preexisting or new).
def indexpage():
c = request.get_cookie("displayid")
if c:
# redirect
return redirect("/tv/" + c)
newid = genRandomTVID()
response.set_cookie("displayid", newid)
return redirect("/tv/" + newid)
def stylesheet():
response.content_type = "text/css"
return template("style")
# here we check to see if they have a redirect URL in the db. if they do
# we send them there. if they don't, we display their ID really big,
# reloading the page once per hour.
def tvpage(db, displayid=None):
# FIXME check for cookie, this is broken
if displayid is None:
return template("nocookie")
# check db for tv id
tv = db.query(TV).filter_by(displayid=displayid).first()
if tv:
tv.lastSeen =
return redirect(
return template("displayid", id=displayid, version=VERSION)
# otherwise, just show their display ID bigly and keep them
# bouncing periodically until some admin configures them
# a target:
# update lastseen here:
newtv = TV(displayid=displayid,
return template("displayid", id=displayid, version=VERSION)
def displayeditform(db, displayid=None):
c = request.get_cookie("psk")
if not c:
return redirect("/login")
if c != ADMIN_PSK:
return redirect("/logout")
if not displayid:
return redirect("/admin")
tv = db.query(TV).filter_by(displayid=displayid).first()
if tv is None:
return redirect("/admin")
return template("displayeditform", tv=tv, version=VERSION)"/admin/edit")
def displayedithandler(db):
# FIXME SECURITY csrf issue
c = request.get_cookie("psk")
if not c:
return redirect("/login")
if c != ADMIN_PSK:
return redirect("/logout")
displayid = request.forms.get("displayid")
tv = db.query(TV).filter_by(displayid=displayid).first()
if tv is None:
return redirect("/admin")
# FIXME make sure this is a valid URL = request.forms.get("target")
tv.memo = request.forms.get("formmemo")
return redirect("/admin")
# here we display the administration list of TVs if logged in
# if logged out then redirect to /login
# FIXME make this use sessions instead of just storing PSK in a cookie
def adminpage(db):
c = request.get_cookie("psk")
if not c:
return redirect("/login")
if c != ADMIN_PSK:
return redirect("/logout")
# first, cleanup db of old entries:
week_ago = - timedelta(days=7)
db.query(TV).filter(TV.lastSeen < week_ago).delete()
tvs = db.query(TV).order_by(TV.lastSeen.desc())
response.headers["Cache-Control"] = "no-cache"
return template("adminpanel", tvs=tvs, version=VERSION)
# here we ask for a password:
def loginform():
msg = request.GET.msg
return template("loginform", version=VERSION, msg=msg)"/checklogin")
def checklogin():
attemptedPass = request.forms.get("password")
if not attemptedPass:
return redirect(
"/login?msg=" + urllib.parse.quote_plus(u"Incorrect password.")
if attemptedPass != ADMIN_PSK:
return redirect(
"/login?msg=" + urllib.parse.quote_plus(u"Incorrect password.")
# password is right, cookie them:
response.set_cookie("psk", attemptedPass)
return redirect("/admin")
def logout():
response.set_cookie("psk", "")
return redirect("/login")"", port=PORT, debug=DEBUG)