#!/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 VERSION = '0.0.1' 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=True) plugin = sqlalchemy.Plugin( engine, # SQLAlchemy engine created with create_engine function. SQLBASE.metadata, # SQLAlchemy metadata, required only if create=True. keyword='db', # Keyword used to inject session database in a route (default 'db'). create=True, # If it is true, execute `metadata.create_all(engine)` when plugin is applied (default False). commit=True, # If it is true, plugin commit changes after route is executed (default True). use_kwargs=False ) app.install(plugin) # here we cookie them if they have no cookie, then redirect them to the # cookie'd value (whether preexisting or new). @app.get('/') def indexpage(): c = request.get_cookie("tvid") if c: # redirect redirect('/tv/' + c) else: newid = genRandomTVID() response.set_cookie("tvid", newid) redirect('/tv/' + newid) @app.get('/style.css') def stylesheet(): 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. @app.get('/tv/') def tvpage(db, displayid=None): # FIXME regex check id to make sure displayid is right format, # return error if not if id is None: return template('nocookie') # check db for tv id tv = db.query(TV).filter_by(displayid=displayid).first() if tv: tv.lastSeen = datetime.now() db.add(tv) if tv.target: redirect(tv.target) else: return template('displayid', id=displayid, version=VERSION) else: # 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,lastSeen=datetime.now()) db.add(newtv) return template('displayid', id=displayid, version=VERSION) # 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 # https://bottlepy.org/docs/dev/recipes.html @app.get('/admin') 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) @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(): response.set_cookie("psk", "") redirect('/login') app.run(host='0.0.0.0', port=PORT, debug=DEBUG)