|
- import os
- import cherrypy
- import logging
- from datetime import datetime, timedelta
- from nodepupper.nodeops import NodeOps, NObject, NClass, NClassAttachment
- from jinja2 import Environment, FileSystemLoader, select_autoescape
- from nodepupper.common import pwhash
- import math
- from urllib.parse import urlparse
-
-
- APPROOT = os.path.abspath(os.path.join(os.path.dirname(__file__), "../"))
-
-
- def auth():
- """
- Return the currently authorized username (per request) or None
- """
- return cherrypy.session.get('authed', None)
-
-
- def require_auth(func):
- """
- Decorator: raise 403 unless session is authed
- """
- def wrapped(*args, **kwargs):
- if not auth():
- raise cherrypy.HTTPError(403)
- return func(*args, **kwargs)
- return wrapped
-
-
- def slugify(words):
- return ''.join(letter for letter in '-'.join(words.lower().split())
- if ('a' <= letter <= 'z') or ('0' <= letter <= '9') or letter == '-')
-
-
- class AppWeb(object):
- def __init__(self, nodedb, template_dir):
- self.nodes = nodedb
- self.tpl = Environment(loader=FileSystemLoader(template_dir),
- autoescape=select_autoescape(['html', 'xml']))
- self.tpl.filters.update(basename=os.path.basename,
- ceil=math.ceil,
- statusstr=lambda x: str(x).split(".")[-1])
- self.node = NodesWeb(self)
- self.classes = ClassWeb(self)
-
- def render(self, template, **kwargs):
- """
- Render a template
- """
- return self.tpl.get_template(template).render(**kwargs, **self.get_default_vars())
-
- def get_default_vars(self):
- """
- Return a dict containing variables expected to be on every page
- """
- with self.nodes.db.transaction() as c:
- ret = {
- "classes": c.root.classes,
- # "all_albums": [],
- "path": cherrypy.request.path_info,
- "auth": True or auth()
- }
- return ret
-
- @cherrypy.expose
- def node_edit(self, node=None, op=None, body=None, fqdn=None):
- print(op, body, fqdn)
- if op in ("Edit", "Create") and body and fqdn:
- with self.nodes.db.transaction() as c:
- obj = c.root.nodes[fqdn] if fqdn in c.root.nodes else NObject(fqdn, body)
- obj.body = body
- c.root.nodes[fqdn] = obj
- raise cherrypy.HTTPRedirect("node/{}".format(fqdn), 302)
- with self.nodes.db.transaction() as c:
- yield self.render("node_edit.html", node=c.root.nodes.get(node, None))
-
- @cherrypy.expose
- def index(self):
- """
- """
- with self.nodes.db.transaction() as c:
- yield self.render("nodes.html", nodes=c.root.nodes.values())
- # raise cherrypy.HTTPRedirect('feed', 302)
-
- @cherrypy.expose
- def login(self):
- """
- /login - enable super features by logging into the app
- """
- cherrypy.session['authed'] = cherrypy.request.login
- dest = "/feed" if "Referer" not in cherrypy.request.headers \
- else urlparse(cherrypy.request.headers["Referer"]).path
- raise cherrypy.HTTPRedirect(dest, 302)
-
- @cherrypy.expose
- def logout(self):
- """
- /logout
- """
- cherrypy.session.clear()
- dest = "/feed" if "Referer" not in cherrypy.request.headers \
- else urlparse(cherrypy.request.headers["Referer"]).path
- raise cherrypy.HTTPRedirect(dest, 302)
-
- @cherrypy.expose
- def error(self, status, message, traceback, version):
- yield self.render("error.html", status=status, message=message, traceback=traceback)
-
-
- @cherrypy.popargs("node")
- class NodesWeb(object):
- def __init__(self, root):
- self.root = root
- self.nodes = root.nodes
- self.render = root.render
-
- @cherrypy.expose
- def index(self, node):
- with self.nodes.db.transaction() as c:
- yield self.render("node.html", node=c.root.nodes[node])
-
-
- @cherrypy.popargs("cls")
- class ClassWeb(object):
- def __init__(self, root):
- self.root = root
- self.nodes = root.nodes
- self.render = root.render
-
- @cherrypy.expose
- def index(self, cls):
- # with self.nodes.db.transaction() as c:
- yield self.render("classes.html")
-
- @cherrypy.expose
- def op(self, cls, op=None, name=None):
- # with self.nodes.db.transaction() as c:
- yield self.render("classes.html")
-
-
- def main():
- import argparse
- import signal
-
- parser = argparse.ArgumentParser(description="Photod photo server")
-
- parser.add_argument('-p', '--port', default=8080, type=int, help="tcp port to listen on")
- # parser.add_argument('-l', '--library', default="./library", help="library path")
- # parser.add_argument('-c', '--cache', default="./cache", help="cache path")
- parser.add_argument('-s', '--database', default="./pupper.db", help="path to persistent sqlite database")
- parser.add_argument('--debug', action="store_true", help="enable development options")
-
- args = parser.parse_args()
-
- logging.basicConfig(level=logging.INFO if args.debug else logging.WARNING,
- format="%(asctime)-15s %(levelname)-8s %(filename)s:%(lineno)d %(message)s")
-
- library = NodeOps(args.database)
-
- tpl_dir = os.path.join(APPROOT, "templates") if not args.debug else "templates"
-
- web = AppWeb(library, tpl_dir)
-
- def validate_password(realm, username, password):
- s = library.session()
- if s.query(User).filter(User.name == username, User.password == pwhash(password)).first():
- return True
- return False
-
- cherrypy.tree.mount(web, '/', {'/': {'tools.trailing_slash.on': False,
- 'error_page.403': web.error,
- 'error_page.404': web.error},
- '/static': {"tools.staticdir.on": True,
- "tools.staticdir.dir": os.path.join(APPROOT, "styles/dist")
- if not args.debug else os.path.abspath("styles/dist")},
- '/login': {'tools.auth_basic.on': True,
- 'tools.auth_basic.realm': 'webapp',
- 'tools.auth_basic.checkpassword': validate_password}})
-
- cherrypy.config.update({
- 'tools.sessions.on': True,
- 'tools.sessions.locking': 'explicit',
- 'tools.sessions.timeout': 525600,
- 'request.show_tracebacks': True,
- 'server.socket_port': args.port,
- 'server.thread_pool': 25,
- 'server.socket_host': '0.0.0.0',
- 'server.show_tracebacks': True,
- 'log.screen': False,
- 'engine.autoreload.on': args.debug
- })
-
- def signal_handler(signum, stack):
- logging.critical('Got sig {}, exiting...'.format(signum))
- cherrypy.engine.exit()
-
- signal.signal(signal.SIGINT, signal_handler)
- signal.signal(signal.SIGTERM, signal_handler)
-
- try:
- cherrypy.engine.start()
- cherrypy.engine.block()
- finally:
- logging.info("API has shut down")
- cherrypy.engine.exit()
-
-
- if __name__ == '__main__':
- main()
|