2019-03-28 13:45:03 +00:00
|
|
|
|
#!/usr/bin/env python
|
|
|
|
|
# -*- mode: python; indent-tabs-mode: nil; -*- coding: utf-8 -*-
|
|
|
|
|
|
|
|
|
|
"""
|
|
|
|
|
CherryPyApp.py
|
|
|
|
|
|
|
|
|
|
Copyright 2009-2014 by Marcello Perathoner
|
|
|
|
|
|
|
|
|
|
Distributable under the GNU General Public License Version 3 or newer.
|
|
|
|
|
|
|
|
|
|
The Project Gutenberg Catalog App Server.
|
|
|
|
|
|
|
|
|
|
Config and route setup.
|
|
|
|
|
|
|
|
|
|
"""
|
|
|
|
|
|
|
|
|
|
from __future__ import unicode_literals
|
|
|
|
|
|
|
|
|
|
import logging
|
|
|
|
|
import logging.handlers # rotating file handler
|
|
|
|
|
import os
|
|
|
|
|
import time
|
|
|
|
|
import traceback
|
|
|
|
|
|
|
|
|
|
import cherrypy
|
|
|
|
|
from cherrypy.process import plugins
|
|
|
|
|
|
|
|
|
|
from libgutenberg import GutenbergDatabase
|
2019-07-26 18:22:09 +00:00
|
|
|
|
import i18n_tool
|
2019-03-28 13:45:03 +00:00
|
|
|
|
|
2019-06-19 19:03:41 +00:00
|
|
|
|
# this import causes ConnectionPool.ConnectionPool to become the cherrypy connection pool
|
2019-03-28 13:45:03 +00:00
|
|
|
|
import ConnectionPool
|
2019-06-19 19:03:41 +00:00
|
|
|
|
|
2019-03-28 13:45:03 +00:00
|
|
|
|
import Page
|
|
|
|
|
import StartPage
|
|
|
|
|
import SuggestionsPage
|
|
|
|
|
from SearchPage import BookSearchPage, AuthorSearchPage, SubjectSearchPage, BookshelfSearchPage, \
|
|
|
|
|
AuthorPage, SubjectPage, BookshelfPage, AlsoDownloadedPage
|
|
|
|
|
from BibrecPage import BibrecPage
|
|
|
|
|
import CoverPages
|
|
|
|
|
import QRCodePage
|
2019-07-29 18:31:45 +00:00
|
|
|
|
import diagnostics
|
2019-03-28 13:45:03 +00:00
|
|
|
|
import Sitemap
|
|
|
|
|
import Formatters
|
2019-08-01 20:20:18 +00:00
|
|
|
|
from errors import ErrorPage
|
2019-03-28 13:45:03 +00:00
|
|
|
|
|
|
|
|
|
import Timer
|
|
|
|
|
|
|
|
|
|
plugins.Timer = Timer.TimerPlugin
|
2019-04-29 17:29:56 +00:00
|
|
|
|
install_dir = os.path.dirname(os.path.abspath(__file__))
|
2019-03-28 13:45:03 +00:00
|
|
|
|
|
2019-04-20 18:34:53 +00:00
|
|
|
|
CHERRYPY_CONFIG = os.path.join(install_dir, 'CherryPy.conf')
|
2019-04-24 17:35:56 +00:00
|
|
|
|
LOCAL_CONFIG = [os.path.expanduser('~/.autocat3'), '/etc/autocat3.conf']
|
2019-03-28 13:45:03 +00:00
|
|
|
|
|
2019-08-01 20:20:18 +00:00
|
|
|
|
def error_page_404(status, message, traceback, version):
|
2019-08-01 23:40:23 +00:00
|
|
|
|
resp = ErrorPage(status, message).index()
|
|
|
|
|
|
|
|
|
|
# signal that we needn't save the session
|
|
|
|
|
cherrypy.session.loaded = False
|
|
|
|
|
return resp
|
2019-08-01 20:20:18 +00:00
|
|
|
|
|
|
|
|
|
|
2019-04-29 17:29:56 +00:00
|
|
|
|
class MyRoutesDispatcher(cherrypy.dispatch.RoutesDispatcher):
|
2019-03-28 13:45:03 +00:00
|
|
|
|
""" Dispatcher that tells us the matched route.
|
|
|
|
|
|
|
|
|
|
CherryPy makes it hard for us by forgetting the matched route object.
|
|
|
|
|
Here we add a 'route_name' parameter, that will tell us the route's name.
|
|
|
|
|
|
|
|
|
|
"""
|
|
|
|
|
|
2019-04-29 17:29:56 +00:00
|
|
|
|
def connect(self, name, route, controller, **kwargs):
|
2019-03-28 13:45:03 +00:00
|
|
|
|
""" Add a 'route_name' parameter that will tell us the matched route. """
|
|
|
|
|
kwargs['route_name'] = name
|
2019-04-29 17:29:56 +00:00
|
|
|
|
kwargs.setdefault('action', 'index')
|
|
|
|
|
cherrypy.dispatch.RoutesDispatcher.connect(self, name, route, controller, **kwargs)
|
2019-03-28 13:45:03 +00:00
|
|
|
|
|
|
|
|
|
|
2019-04-29 17:29:56 +00:00
|
|
|
|
def main():
|
2019-03-28 13:45:03 +00:00
|
|
|
|
""" Main function. """
|
|
|
|
|
|
|
|
|
|
# default config
|
2019-04-29 17:29:56 +00:00
|
|
|
|
cherrypy.config.update({
|
2019-03-28 13:45:03 +00:00
|
|
|
|
'uid': 0,
|
|
|
|
|
'gid': 0,
|
|
|
|
|
'server_name': 'localhost',
|
2019-04-29 17:29:56 +00:00
|
|
|
|
'genshi.template_dir': os.path.join(install_dir, 'templates'),
|
2019-03-28 13:45:03 +00:00
|
|
|
|
'daemonize': False,
|
|
|
|
|
'pidfile': None,
|
|
|
|
|
'host': 'localhost',
|
|
|
|
|
'host_mobile': 'localhost',
|
|
|
|
|
'file_host': 'localhost',
|
|
|
|
|
})
|
|
|
|
|
|
2019-04-29 17:00:26 +00:00
|
|
|
|
cherrypy.config.update(CHERRYPY_CONFIG)
|
2019-04-29 17:29:56 +00:00
|
|
|
|
|
2019-04-29 17:00:26 +00:00
|
|
|
|
extra_config = ''
|
|
|
|
|
for config_filename in LOCAL_CONFIG:
|
|
|
|
|
try:
|
|
|
|
|
cherrypy.config.update(config_filename)
|
|
|
|
|
extra_config = config_filename
|
|
|
|
|
break
|
|
|
|
|
except IOError:
|
|
|
|
|
pass
|
|
|
|
|
|
2019-03-28 13:45:03 +00:00
|
|
|
|
# Rotating Logs
|
2019-04-30 19:36:46 +00:00
|
|
|
|
# CherryPy will already open log files if present in config
|
2019-04-30 19:52:43 +00:00
|
|
|
|
error_file = access_file = ''
|
2019-04-30 19:36:46 +00:00
|
|
|
|
# read the logger file locations from config file.
|
|
|
|
|
if not cherrypy.log.error_file:
|
|
|
|
|
error_file = cherrypy.config.get('logger.error_file', '')
|
|
|
|
|
if not cherrypy.log.access_file:
|
|
|
|
|
access_file = cherrypy.config.get('logger.access_file', '')
|
2019-03-28 13:45:03 +00:00
|
|
|
|
|
2019-05-17 22:30:30 +00:00
|
|
|
|
# disable log file handlers
|
|
|
|
|
cherrypy.log.error_file = ""
|
|
|
|
|
cherrypy.log.access_file = ""
|
|
|
|
|
|
2019-04-29 17:00:26 +00:00
|
|
|
|
# set up python logging
|
2019-04-29 17:29:56 +00:00
|
|
|
|
max_bytes = getattr(cherrypy.log, "rot_max_bytes", 100 * 1024 * 1024)
|
|
|
|
|
backup_count = getattr(cherrypy.log, "rot_backup_count", 2)
|
|
|
|
|
|
2019-04-30 19:36:46 +00:00
|
|
|
|
if error_file:
|
|
|
|
|
h = logging.handlers.RotatingFileHandler(error_file, 'a', max_bytes, backup_count, 'utf-8')
|
|
|
|
|
h.setLevel(logging.INFO)
|
|
|
|
|
h.setFormatter(cherrypy._cplogging.logfmt)
|
|
|
|
|
cherrypy.log.error_log.addHandler(h)
|
|
|
|
|
|
|
|
|
|
if access_file:
|
|
|
|
|
h = logging.handlers.RotatingFileHandler(access_file, 'a', max_bytes, backup_count, 'utf-8')
|
|
|
|
|
h.setLevel(logging.INFO)
|
|
|
|
|
h.setFormatter(cherrypy._cplogging.logfmt)
|
|
|
|
|
cherrypy.log.access_log.addHandler(h)
|
2019-04-29 17:29:56 +00:00
|
|
|
|
|
2019-04-24 17:35:56 +00:00
|
|
|
|
|
2019-03-28 13:45:03 +00:00
|
|
|
|
|
|
|
|
|
if not cherrypy.config['daemonize']:
|
2019-04-29 17:29:56 +00:00
|
|
|
|
ch = logging.StreamHandler()
|
|
|
|
|
ch.setLevel(logging.DEBUG)
|
|
|
|
|
ch.setFormatter(cherrypy._cplogging.logfmt)
|
|
|
|
|
cherrypy.log.error_log.addHandler(ch)
|
2019-03-28 13:45:03 +00:00
|
|
|
|
|
|
|
|
|
# continue app init
|
|
|
|
|
#
|
|
|
|
|
|
2019-04-29 17:29:56 +00:00
|
|
|
|
cherrypy.log('*' * 80, context='ENGINE', severity=logging.INFO)
|
|
|
|
|
cherrypy.log("Using config file '%s'." % CHERRYPY_CONFIG,
|
|
|
|
|
context='ENGINE', severity=logging.INFO)
|
2019-04-29 17:00:26 +00:00
|
|
|
|
if extra_config:
|
|
|
|
|
cherrypy.log('extra_config: %s' % extra_config, context='ENGINE', severity=logging.INFO)
|
2019-03-28 13:45:03 +00:00
|
|
|
|
|
|
|
|
|
# after cherrypy.config is parsed
|
2019-04-29 17:29:56 +00:00
|
|
|
|
Formatters.init()
|
|
|
|
|
cherrypy.log("Continuing App Init", context='ENGINE', severity=logging.INFO)
|
2019-03-28 13:45:03 +00:00
|
|
|
|
|
2019-04-29 17:29:56 +00:00
|
|
|
|
cherrypy.log("Continuing App Init", context='ENGINE', severity=logging.INFO)
|
|
|
|
|
cherrypy.tools.I18nTool = i18n_tool.I18nTool()
|
2019-03-28 13:45:03 +00:00
|
|
|
|
|
2019-04-29 17:29:56 +00:00
|
|
|
|
cherrypy.log("Continuing App Init", context='ENGINE', severity=logging.INFO)
|
2019-03-28 13:45:03 +00:00
|
|
|
|
|
|
|
|
|
# Used to bust the cache on js and css files. This should be the
|
|
|
|
|
# files' mtime, but the files are not stored on the app server.
|
|
|
|
|
# This is a `good enough´ replacement though.
|
2019-04-29 17:29:56 +00:00
|
|
|
|
t = str(int(time.time()))
|
2019-03-28 13:45:03 +00:00
|
|
|
|
cherrypy.config['css_mtime'] = t
|
|
|
|
|
cherrypy.config['js_mtime'] = t
|
|
|
|
|
|
|
|
|
|
cherrypy.config['all_hosts'] = (
|
|
|
|
|
cherrypy.config['host'], cherrypy.config['host_mobile'], cherrypy.config['file_host'])
|
2019-08-01 20:20:18 +00:00
|
|
|
|
|
|
|
|
|
cherrypy.config.update({'error_page.404': error_page_404})
|
2019-03-28 13:45:03 +00:00
|
|
|
|
|
2019-04-29 17:29:56 +00:00
|
|
|
|
if hasattr(cherrypy.engine, 'signal_handler'):
|
|
|
|
|
cherrypy.engine.signal_handler.subscribe()
|
2019-03-28 13:45:03 +00:00
|
|
|
|
|
2019-04-05 21:33:43 +00:00
|
|
|
|
GutenbergDatabase.options.update(cherrypy.config)
|
2019-04-29 17:29:56 +00:00
|
|
|
|
cherrypy.engine.pool = plugins.ConnectionPool(
|
|
|
|
|
cherrypy.engine, params=GutenbergDatabase.get_connection_params(cherrypy.config))
|
|
|
|
|
cherrypy.engine.pool.subscribe()
|
2019-03-28 13:45:03 +00:00
|
|
|
|
|
2019-04-29 17:29:56 +00:00
|
|
|
|
plugins.Timer(cherrypy.engine).subscribe()
|
2019-03-28 13:45:03 +00:00
|
|
|
|
|
2019-04-29 17:29:56 +00:00
|
|
|
|
cherrypy.log("Daemonizing", context='ENGINE', severity=logging.INFO)
|
2019-03-28 13:45:03 +00:00
|
|
|
|
|
|
|
|
|
if cherrypy.config['daemonize']:
|
2019-04-29 17:29:56 +00:00
|
|
|
|
plugins.Daemonizer(cherrypy.engine).subscribe()
|
2019-03-28 13:45:03 +00:00
|
|
|
|
|
|
|
|
|
uid = cherrypy.config['uid']
|
|
|
|
|
gid = cherrypy.config['gid']
|
|
|
|
|
if uid > 0 or gid > 0:
|
2019-04-29 17:29:56 +00:00
|
|
|
|
plugins.DropPrivileges(cherrypy.engine, uid=uid, gid=gid, umask=0o22).subscribe()
|
2019-03-28 13:45:03 +00:00
|
|
|
|
|
|
|
|
|
if cherrypy.config['pidfile']:
|
2019-04-29 17:29:56 +00:00
|
|
|
|
pid = plugins.PIDFile(cherrypy.engine, cherrypy.config['pidfile'])
|
|
|
|
|
# Write pidfile after privileges are dropped(prio == 77)
|
2019-03-28 13:45:03 +00:00
|
|
|
|
# or we will not be able to remove it.
|
2019-04-29 17:29:56 +00:00
|
|
|
|
cherrypy.engine.subscribe('start', pid.start, 78)
|
|
|
|
|
cherrypy.engine.subscribe('exit', pid.exit, 78)
|
2019-03-28 13:45:03 +00:00
|
|
|
|
|
|
|
|
|
|
2019-04-29 17:29:56 +00:00
|
|
|
|
cherrypy.log("Setting up routes", context='ENGINE', severity=logging.INFO)
|
2019-03-28 13:45:03 +00:00
|
|
|
|
|
|
|
|
|
# setup 'routes' dispatcher
|
|
|
|
|
#
|
2019-04-29 17:29:56 +00:00
|
|
|
|
# d = cherrypy.dispatch.RoutesDispatcher(full_result=True)
|
|
|
|
|
d = MyRoutesDispatcher(full_result=True)
|
2019-03-28 13:45:03 +00:00
|
|
|
|
cherrypy.routes_mapper = d.mapper
|
|
|
|
|
|
2019-04-29 17:29:56 +00:00
|
|
|
|
def check_id(environ, result):
|
2019-03-28 13:45:03 +00:00
|
|
|
|
""" Check if id is a valid number. """
|
|
|
|
|
try:
|
2019-04-29 17:29:56 +00:00
|
|
|
|
return str(int(result['id'])) == result['id']
|
2019-03-28 13:45:03 +00:00
|
|
|
|
except:
|
|
|
|
|
return False
|
|
|
|
|
|
2019-04-29 17:29:56 +00:00
|
|
|
|
d.connect('start', r'/ebooks{.format}/',
|
|
|
|
|
controller=StartPage.Start())
|
2019-03-28 13:45:03 +00:00
|
|
|
|
|
2019-04-29 17:29:56 +00:00
|
|
|
|
d.connect('suggest', r'/ebooks/suggest{.format}/',
|
|
|
|
|
controller=SuggestionsPage.Suggestions())
|
2019-03-28 13:45:03 +00:00
|
|
|
|
|
|
|
|
|
# search pages
|
|
|
|
|
|
2019-04-29 17:29:56 +00:00
|
|
|
|
d.connect('search', r'/ebooks/search{.format}/',
|
|
|
|
|
controller=BookSearchPage())
|
2019-03-28 13:45:03 +00:00
|
|
|
|
|
2019-04-29 17:29:56 +00:00
|
|
|
|
d.connect('author_search', r'/ebooks/authors/search{.format}/',
|
|
|
|
|
controller=AuthorSearchPage())
|
2019-03-28 13:45:03 +00:00
|
|
|
|
|
2019-04-29 17:29:56 +00:00
|
|
|
|
d.connect('subject_search', r'/ebooks/subjects/search{.format}/',
|
|
|
|
|
controller=SubjectSearchPage())
|
2019-03-28 13:45:03 +00:00
|
|
|
|
|
2019-04-29 17:29:56 +00:00
|
|
|
|
d.connect('bookshelf_search', r'/ebooks/bookshelves/search{.format}/',
|
|
|
|
|
controller=BookshelfSearchPage())
|
2019-03-28 13:45:03 +00:00
|
|
|
|
|
|
|
|
|
# 'id' pages
|
|
|
|
|
|
2019-04-29 17:29:56 +00:00
|
|
|
|
d.connect('author', r'/ebooks/author/{id:\d+}{.format}',
|
|
|
|
|
controller=AuthorPage(), conditions=dict(function=check_id))
|
2019-03-28 13:45:03 +00:00
|
|
|
|
|
2019-04-29 17:29:56 +00:00
|
|
|
|
d.connect('subject', r'/ebooks/subject/{id:\d+}{.format}',
|
|
|
|
|
controller=SubjectPage(), conditions=dict(function=check_id))
|
2019-03-28 13:45:03 +00:00
|
|
|
|
|
2019-04-29 17:29:56 +00:00
|
|
|
|
d.connect('bookshelf', r'/ebooks/bookshelf/{id:\d+}{.format}',
|
|
|
|
|
controller=BookshelfPage(), conditions=dict(function=check_id))
|
2019-03-28 13:45:03 +00:00
|
|
|
|
|
2019-04-29 17:29:56 +00:00
|
|
|
|
d.connect('also', r'/ebooks/{id:\d+}/also/{.format}',
|
|
|
|
|
controller=AlsoDownloadedPage(), conditions=dict(function=check_id))
|
2019-03-28 13:45:03 +00:00
|
|
|
|
|
|
|
|
|
# bibrec pages
|
|
|
|
|
|
2019-04-29 17:29:56 +00:00
|
|
|
|
d.connect('download', r'/ebooks/{id:\d+}/download{.format}',
|
|
|
|
|
controller=Page.NullPage(), _static=True)
|
2019-03-28 13:45:03 +00:00
|
|
|
|
|
2019-04-29 17:29:56 +00:00
|
|
|
|
d.connect('bibrec', r'/ebooks/{id:\d+}{.format}',
|
|
|
|
|
controller=BibrecPage(), conditions=dict(function=check_id))
|
2019-03-28 13:45:03 +00:00
|
|
|
|
|
|
|
|
|
|
|
|
|
|
# legacy compatibility with /ebooks/123.bibrec
|
2019-04-29 17:29:56 +00:00
|
|
|
|
d.connect('bibrec2', r'/ebooks/{id:\d+}.bibrec{.format}',
|
|
|
|
|
controller=BibrecPage(), conditions=dict(function=check_id))
|
2019-03-28 13:45:03 +00:00
|
|
|
|
|
2019-04-29 17:29:56 +00:00
|
|
|
|
d.connect('cover', r'/covers/{size:small|medium}/{order:latest|popular}/{count}',
|
|
|
|
|
controller=CoverPages.CoverPages())
|
2019-03-28 13:45:03 +00:00
|
|
|
|
|
2019-04-29 17:29:56 +00:00
|
|
|
|
d.connect('qrcode', r'/qrcode/',
|
|
|
|
|
controller=QRCodePage.QRCodePage())
|
2019-03-28 13:45:03 +00:00
|
|
|
|
|
2019-04-29 17:29:56 +00:00
|
|
|
|
d.connect('iplimit', r'/iplimit/',
|
|
|
|
|
controller=Page.NullPage())
|
2019-03-28 13:45:03 +00:00
|
|
|
|
|
2019-07-29 18:31:45 +00:00
|
|
|
|
d.connect('diagnostics', r'/diagnostics/',
|
|
|
|
|
controller=diagnostics.DiagnosticsPage())
|
|
|
|
|
|
2019-04-29 17:29:56 +00:00
|
|
|
|
d.connect('stats', r'/stats/',
|
|
|
|
|
controller=Page.NullPage(), _static=True)
|
2019-03-28 13:45:03 +00:00
|
|
|
|
|
2019-04-29 17:29:56 +00:00
|
|
|
|
d.connect('honeypot_send', r'/ebooks/send/megaupload/{id:\d+}.{filetype}',
|
|
|
|
|
controller=Page.NullPage(), _static=True)
|
2019-03-28 13:45:03 +00:00
|
|
|
|
|
|
|
|
|
# /w/captcha/question/ so varnish will cache it
|
2019-04-29 17:29:56 +00:00
|
|
|
|
d.connect('captcha.question', r'/w/captcha/question/',
|
|
|
|
|
controller=Page.GoHomePage())
|
2019-03-28 13:45:03 +00:00
|
|
|
|
|
2019-04-29 17:29:56 +00:00
|
|
|
|
d.connect('captcha.answer', r'/w/captcha/answer/',
|
|
|
|
|
controller=Page.GoHomePage())
|
2019-03-28 13:45:03 +00:00
|
|
|
|
|
|
|
|
|
# sitemap protocol access control requires us to place sitemaps in /ebooks/
|
2019-04-29 17:29:56 +00:00
|
|
|
|
d.connect('sitemap', r'/ebooks/sitemaps/',
|
|
|
|
|
controller=Sitemap.SitemapIndex())
|
2019-03-28 13:45:03 +00:00
|
|
|
|
|
2019-04-29 17:29:56 +00:00
|
|
|
|
d.connect('sitemap_index', r'/ebooks/sitemaps/{page:\d+}',
|
|
|
|
|
controller=Sitemap.Sitemap())
|
2019-03-28 13:45:03 +00:00
|
|
|
|
|
|
|
|
|
if 'dropbox_client_id' in cherrypy.config:
|
|
|
|
|
import Dropbox
|
2019-04-29 17:29:56 +00:00
|
|
|
|
dropbox = Dropbox.Dropbox()
|
|
|
|
|
cherrypy.log("Dropbox Client Id: %s" % cherrypy.config['dropbox_client_id'],
|
|
|
|
|
context='ENGINE', severity=logging.INFO)
|
|
|
|
|
d.connect('dropbox_send', r'/ebooks/send/dropbox/{id:\d+}.{filetype}',
|
|
|
|
|
controller=dropbox, conditions=dict(function=check_id))
|
|
|
|
|
d.connect('dropbox_callback', r'/ebooks/send/dropbox/',
|
|
|
|
|
controller=dropbox)
|
2019-03-28 13:45:03 +00:00
|
|
|
|
|
|
|
|
|
if 'gdrive_client_id' in cherrypy.config:
|
|
|
|
|
import GDrive
|
2019-04-29 17:29:56 +00:00
|
|
|
|
gdrive = GDrive.GDrive()
|
|
|
|
|
cherrypy.log("GDrive Client Id: %s" % cherrypy.config['gdrive_client_id'],
|
|
|
|
|
context='ENGINE', severity=logging.INFO)
|
|
|
|
|
d.connect('gdrive_send', r'/ebooks/send/gdrive/{id:\d+}.{filetype}',
|
|
|
|
|
controller=gdrive, conditions=dict(function=check_id))
|
|
|
|
|
d.connect('gdrive_callback', r'/ebooks/send/gdrive/',
|
|
|
|
|
controller=gdrive)
|
2019-03-28 13:45:03 +00:00
|
|
|
|
|
|
|
|
|
if 'msdrive_client_id' in cherrypy.config:
|
|
|
|
|
import MSDrive
|
2019-04-29 17:29:56 +00:00
|
|
|
|
msdrive = MSDrive.MSDrive()
|
|
|
|
|
cherrypy.log("MSDrive Client Id: %s" % cherrypy.config['msdrive_client_id'],
|
|
|
|
|
context='ENGINE', severity=logging.INFO)
|
|
|
|
|
d.connect('msdrive_send', r'/ebooks/send/msdrive/{id:\d+}.{filetype}',
|
|
|
|
|
controller=msdrive, conditions=dict(function=check_id))
|
|
|
|
|
d.connect('msdrive_callback', r'/ebooks/send/msdrive/',
|
|
|
|
|
controller=msdrive)
|
2019-03-28 13:45:03 +00:00
|
|
|
|
|
|
|
|
|
# start http server
|
|
|
|
|
#
|
|
|
|
|
|
2019-04-29 17:29:56 +00:00
|
|
|
|
cherrypy.log("Mounting root", context='ENGINE', severity=logging.INFO)
|
2019-03-28 13:45:03 +00:00
|
|
|
|
|
2019-04-29 17:29:56 +00:00
|
|
|
|
app = cherrypy.tree.mount(root=None, config=CHERRYPY_CONFIG)
|
2019-03-28 13:45:03 +00:00
|
|
|
|
|
2019-04-29 17:29:56 +00:00
|
|
|
|
app.merge({'/': {'request.dispatch': d}})
|
2019-03-28 13:45:03 +00:00
|
|
|
|
return app
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
if __name__ == '__main__':
|
2019-04-29 17:29:56 +00:00
|
|
|
|
main()
|
|
|
|
|
cherrypy.engine.start()
|
|
|
|
|
cherrypy.engine.block()
|