# -*- coding: utf-8 -*- import asyncio import os import sys import signal import time from tornado.ioloop import IOLoop import tornado.web from tornado.web import Application from cache import Cache from item.handlers import EpubHandler, ReaderHandler, FileHandler from item.handlers import OMLHandler, UploadHandler from item.handlers import CropHandler from item.icons import IconHandler import db import node.server import oxtornado import downloads import settings import setup import state import tasks import websocket import ui_websocket import update import logging logger = logging.getLogger(__name__) class StaticFileHandler(tornado.web.StaticFileHandler): def get_content_type(self): if self.request.path.split('?')[0].endswith('.mjs'): return 'application/javascript' return super().get_content_type() class MainHandler(OMLHandler): def get(self, path): version = settings.MINOR_VERSION.split('-')[0] if version == 'git': version = int(time.mktime(time.gmtime())) path = os.path.join(settings.static_path, 'html', 'oml.html') with open(path) as fd: content = fd.read() content = content.replace('.js?1', '.js?%s' % version) if state.update: content = content.replace('oml.js', 'oml.update.js') self.set_header('Content-Type', 'text/html') self.set_header('Content-Length', str(len(content))) self.set_header('Cache-Control', 'no-cache, no-store, must-revalidate') self.set_header('Pragma', 'no-cache') self.set_header('Expires', '0') self.write(content) def log_request(handler): if settings.DEBUG_HTTP: if handler.get_status() < 400: log_method = logger.info elif handler.get_status() < 500: log_method = logger.warning else: log_method = logger.error request_time = 1000.0 * handler.request.request_time() log_method("%d %s %.2fms", handler.get_status(), handler._request_summary(), request_time) async def shutdown(): state.shutdown = True if state.tor: state.tor._shutdown = True if state.nodes: logger.debug('shutdown nodes') await state.nodes.join() if state.downloads: logger.debug('shutdown downloads') state.downloads.join() logger.debug('shutdown http_server') if state.tasks: logger.debug('shutdown tasks') state.tasks.join() if state.update: logger.debug('shutdown updates') state.update.join() if state.node: state.node.stop() if state.tor: logger.debug('shutdown tor') state.tor.shutdown() for peer in state.peers: state.peers[peer].join() if state.PID and os.path.exists(state.PID): logger.debug('remove %s', state.PID) os.unlink(state.PID) def run(): PID = sys.argv[2] if len(sys.argv) > 2 else None if len(sys.argv) > 3 and sys.argv[2] == 'debug': PID = sys.argv[3] debug = True else: debug = False log_format = '%(asctime)s:%(levelname)s:%(name)s:%(message)s' if debug: logging.basicConfig(level=logging.DEBUG, format=log_format) else: logging.basicConfig(level=logging.DEBUG, handlers=[logging.FileHandler(settings.log_path, 'w', 'utf-8')], format=log_format) options = { 'debug': False, 'log_function': log_request, 'gzip': True } common_handlers = [ (r'/(favicon.ico)', StaticFileHandler, {'path': settings.static_path}), (r'/static/oxjs/(.*)', StaticFileHandler, {'path': os.path.join(settings.top_dir, 'oxjs')}), (r'/static/cbr.js/(.*)', StaticFileHandler, {'path': os.path.join(settings.top_dir, 'reader', 'cbr.js')}), (r'/static/epub.js/(.*)', StaticFileHandler, {'path': os.path.join(settings.top_dir, 'reader', 'epub.js')}), (r'/static/pdf.js/(.*)', StaticFileHandler, {'path': os.path.join(settings.top_dir, 'reader', 'pdf.js')}), (r'/static/txt.js/(.*)', StaticFileHandler, {'path': os.path.join(settings.top_dir, 'reader', 'txt.js')}), (r'/static/(.*)', StaticFileHandler, {'path': settings.static_path}), (r'/(.*)/epub/(.*)', EpubHandler), (r'/(.*?)/reader/', ReaderHandler), (r'/(.*?)/cbr/', FileHandler), (r'/(.*?)/pdf/', FileHandler), (r'/(.*?)/txt/', FileHandler), (r'/(.*?)/get/', FileHandler, { 'attachment': True }), (r'/(.*)/2048p(\d*),(\d*),(\d*),(\d*),(\d*).jpg', CropHandler), (r'/(.*)/(cover|preview)(\d*).jpg', IconHandler), ] handlers = common_handlers + [ (r'/api/upload/', UploadHandler, dict(context=db.session)), (r'/api/', oxtornado.ApiHandler, dict(context=db.session)), (r'/ui_socket', ui_websocket.Handler), (r'/ws', websocket.Handler), (r"(.*)", MainHandler), ] public_handlers = common_handlers + [ (r'/api/', oxtornado.ApiHandler, dict(context=db.session, public=True)), (r'/ws', websocket.Handler, dict(public=True)), (r"(.*)", MainHandler), ] setup.create_db() http_server = Application(handlers, **options) max_buffer_size = 2*1024*1024*1024 http_server.listen(settings.server['port'], settings.server['address'], max_buffer_size=max_buffer_size) # public server if settings.preferences.get('enableReadOnlyService'): public_port = settings.server.get('public_port') public_address = settings.server['public_address'] if public_port: public_server = Application(public_handlers, **options) public_server.listen(public_port, public_address) if PID: with open(PID, 'w') as pid: pid.write('%s' % os.getpid()) state.update = update.update_available() state.PID = PID state.http_server = http_server state.main = IOLoop.instance() state.cache = Cache(ttl=60) def start_node(): import nodes import tor import bandwidth state.bandwidth = bandwidth.Bandwidth() state.tor = tor.Tor() state.nodes = nodes.Nodes() state.node = node.server.start() def publish(): if not state.tor.is_online(): state.main.call_later(10, publish) else: nodes.publish_node() state.main.add_callback(publish) state.main.call_later(10, lambda: state.tasks.queue('scanimport')) if not state.update: state.downloads = downloads.Downloads() state.tasks = tasks.Tasks() state.main.add_callback(start_node) else: state.update = update.Update() if ':' in settings.server['address']: host = '[%s]' % settings.server['address'] elif not settings.server['address']: host = '127.0.0.1' else: host = settings.server['address'] url = 'http://%s:%s/' % (host, settings.server['port']) print('open browser at %s' % url) logger.debug('Starting OML %s at %s', settings.VERSION, url) signal.signal(signal.SIGTERM, lambda _, __: sys.exit(0)) try: state.main.start() except: print('shutting down...') asyncio.run(shutdown())