diffido/diffido.py

180 lines
6.1 KiB
Python
Raw Normal View History

2018-01-15 21:58:10 +01:00
#!/usr/bin/env python3
# -*- coding: utf-8 -*-
2018-01-16 20:54:56 +01:00
import os
2018-01-18 22:46:48 +01:00
import json
2018-01-16 20:54:56 +01:00
import logging
2018-01-15 21:58:10 +01:00
from tornado.ioloop import IOLoop
2018-01-16 20:54:56 +01:00
# from lxml.html.diff import htmldiff
2018-01-15 21:58:10 +01:00
from apscheduler.schedulers.tornado import TornadoScheduler
from apscheduler.jobstores.sqlalchemy import SQLAlchemyJobStore
2018-01-16 20:54:56 +01:00
import tornado.httpserver
import tornado.ioloop
import tornado.options
from tornado.options import define, options
import tornado.web
from tornado import gen, escape
2018-01-15 21:58:10 +01:00
CONF_DIR = ''
2018-01-16 20:56:46 +01:00
JOBS_STORE = 'sqlite:///storage/jobs.db'
2018-01-16 20:54:56 +01:00
API_VERSION = '1.0'
2018-01-18 22:46:48 +01:00
SCHEDULES_FILE = 'conf/schedules.json'
2018-01-16 20:54:56 +01:00
class DiffidoBaseException(Exception):
"""Base class for diffido custom exceptions.
:param message: text message
:type message: str
:param status: numeric http status code
:type status: int"""
def __init__(self, message, status=400):
super(DiffidoBaseException, self).__init__(message)
self.message = message
self.status = status
class BaseHandler(tornado.web.RequestHandler):
"""Base class for request handlers."""
# Cache currently connected users.
_users_cache = {}
# set of documents we're managing (a collection in MongoDB or a table in a SQL database)
document = None
collection = None
# A property to access the first value of each argument.
arguments = property(lambda self: dict([(k, v[0].decode('utf-8'))
for k, v in self.request.arguments.items()]))
@property
def clean_body(self):
"""Return a clean dictionary from a JSON body, suitable for a query on MongoDB.
:returns: a clean copy of the body arguments
:rtype: dict"""
return escape.json_decode(self.request.body or '{}')
def write_error(self, status_code, **kwargs):
"""Default error handler."""
if isinstance(kwargs.get('exc_info', (None, None))[1], DiffidoBaseException):
exc = kwargs['exc_info'][1]
status_code = exc.status
message = exc.message
else:
message = 'internal error'
self.build_error(message, status=status_code)
def is_api(self):
"""Return True if the path is from an API call."""
return self.request.path.startswith('/v%s' % API_VERSION)
def initialize(self, **kwargs):
"""Add every passed (key, value) as attributes of the instance."""
for key, value in kwargs.items():
setattr(self, key, value)
def build_error(self, message='', status=400):
"""Build and write an error message.
:param message: textual message
:type message: str
:param status: HTTP status code
:type status: int
"""
self.set_status(status)
self.write({'error': True, 'message': message})
2018-01-18 22:46:48 +01:00
class SchedulesHandler(BaseHandler):
def read_schedules(self):
if not os.path.isfile(SCHEDULES_FILE):
return {}
with open(SCHEDULES_FILE, 'r') as fd:
return json.loads(fd.read())
def write_schedules(self, schedules):
with open(SCHEDULES_FILE, 'w') as fd:
fd.write(json.dumps(schedules, indent=2))
@gen.coroutine
def get(self, id_=None, *args, **kwargs):
schedules = self.read_schedules()
self.write(schedules)
2018-01-16 22:24:30 +01:00
class TemplateHandler(BaseHandler):
2018-01-16 20:54:56 +01:00
"""Handler for the / path."""
app_path = os.path.join(os.path.dirname(__file__), "dist")
@gen.coroutine
def get(self, *args, **kwargs):
2018-01-16 22:24:30 +01:00
page = 'index.html'
if args and args[0]:
page = args[0].strip('/')
2018-01-17 22:54:06 +01:00
arguments = self.arguments
self.render(page, **arguments)
2018-01-15 21:58:10 +01:00
2018-01-16 23:10:03 +01:00
def run_scheduled(id_=None, *args, **kwargs):
print('RUNNING %d' % id_)
def run():
print('runno!')
2018-01-15 21:58:10 +01:00
def serve():
jobstores = {'default': SQLAlchemyJobStore(url=JOBS_STORE)}
scheduler = TornadoScheduler(jobstores=jobstores)
scheduler.start()
2018-01-17 22:54:06 +01:00
#scheduler.remove_job('run')
2018-01-16 23:10:03 +01:00
#scheduler.add_job(run, 'interval', minutes=1)
2018-01-16 20:54:56 +01:00
2018-01-18 22:46:48 +01:00
define('port', default=3210, help='run on the given port', type=int)
define('address', default='', help='bind the server at the given address', type=str)
define('ssl_cert', default=os.path.join(os.path.dirname(__file__), 'ssl', 'diffido_cert.pem'),
help='specify the SSL certificate to use for secure connections')
define('ssl_key', default=os.path.join(os.path.dirname(__file__), 'ssl', 'diffido_key.pem'),
help='specify the SSL private key to use for secure connections')
define('debug', default=False, help='run in debug mode')
define('config', help='read configuration file',
2018-01-16 20:54:56 +01:00
callback=lambda path: tornado.options.parse_config_file(path, final=False))
tornado.options.parse_command_line()
logger = logging.getLogger()
logger.setLevel(logging.INFO)
if options.debug:
logger.setLevel(logging.DEBUG)
ssl_options = {}
if os.path.isfile(options.ssl_key) and os.path.isfile(options.ssl_cert):
ssl_options = dict(certfile=options.ssl_cert, keyfile=options.ssl_key)
2018-01-18 22:46:48 +01:00
init_params = dict(listen_port=options.port, logger=logger, ssl_options=ssl_options,
scheduler=scheduler)
2018-01-16 20:54:56 +01:00
2018-01-18 22:46:48 +01:00
_schedules_path = r'schedules/?(?P<id_>\d+)?'
2018-01-16 20:54:56 +01:00
application = tornado.web.Application([
2018-01-18 22:46:48 +01:00
('/api/%s' % _schedules_path, SchedulesHandler, init_params),
(r'/api/v%s/%s' % (API_VERSION, _schedules_path), SchedulesHandler, init_params),
(r'/?(.*)', TemplateHandler, init_params),
2018-01-16 20:54:56 +01:00
],
2018-01-18 22:46:48 +01:00
static_path=os.path.join(os.path.dirname(__file__), 'dist/static'),
2018-01-16 22:24:30 +01:00
template_path=os.path.join(os.path.dirname(__file__), 'dist/'),
2018-01-16 20:54:56 +01:00
debug=options.debug)
http_server = tornado.httpserver.HTTPServer(application, ssl_options=ssl_options or None)
logger.info('Start serving on %s://%s:%d', 'https' if ssl_options else 'http',
options.address if options.address else '127.0.0.1',
options.port)
http_server.listen(options.port, options.address)
2018-01-15 21:58:10 +01:00
try:
IOLoop.instance().start()
except (KeyboardInterrupt, SystemExit):
pass
if __name__ == '__main__':
serve()