120 lines
3.8 KiB
Python
120 lines
3.8 KiB
Python
# coding: utf-8
|
|
"""
|
|
This module contains the definition of the Bottle web app.
|
|
"""
|
|
from __future__ import (
|
|
absolute_import, division, print_function, unicode_literals
|
|
)
|
|
|
|
import functools
|
|
import json
|
|
import logging
|
|
import os
|
|
|
|
import bottle
|
|
import canister
|
|
|
|
from flatisfy import database
|
|
from flatisfy.tools import DateAwareJSONEncoder
|
|
from flatisfy.web.routes import api as api_routes
|
|
from flatisfy.web.configplugin import ConfigPlugin
|
|
from flatisfy.web.dbplugin import DatabasePlugin
|
|
|
|
|
|
class QuietWSGIRefServer(bottle.WSGIRefServer):
|
|
"""
|
|
Quiet implementation of Bottle built-in WSGIRefServer, as `Canister` is
|
|
handling the logging through standard Python logging.
|
|
"""
|
|
# pylint: disable=locally-disabled,too-few-public-methods
|
|
quiet = True
|
|
|
|
|
|
def _serve_static_file(filename):
|
|
"""
|
|
Helper function to serve static file.
|
|
"""
|
|
return bottle.static_file(
|
|
filename,
|
|
root=os.path.join(
|
|
os.path.dirname(os.path.realpath(__file__)),
|
|
"static"
|
|
)
|
|
)
|
|
|
|
def get_app(config):
|
|
"""
|
|
Get a Bottle app instance with all the routes set-up.
|
|
|
|
:return: The built bottle app.
|
|
"""
|
|
get_session = database.init_db(config["database"], config["search_index"])
|
|
|
|
app = bottle.Bottle()
|
|
app.install(DatabasePlugin(get_session))
|
|
app.install(ConfigPlugin(config))
|
|
app.config.setdefault("canister.log_level", logging.root.level)
|
|
app.config.setdefault("canister.log_path", None)
|
|
app.config.setdefault("canister.debug", False)
|
|
app.install(canister.Canister())
|
|
# Use DateAwareJSONEncoder to dump JSON strings
|
|
# From http://stackoverflow.com/questions/21282040/bottle-framework-how-to-return-datetime-in-json-response#comment55718456_21282666. pylint: disable=locally-disabled,line-too-long
|
|
app.install(
|
|
bottle.JSONPlugin(
|
|
json_dumps=functools.partial(json.dumps, cls=DateAwareJSONEncoder)
|
|
)
|
|
)
|
|
|
|
# Enable CORS
|
|
@app.hook('after_request')
|
|
def enable_cors():
|
|
"""
|
|
Add CORS headers at each request.
|
|
"""
|
|
# The str() call is required as we import unicode_literal and WSGI
|
|
# headers list should have plain str type.
|
|
bottle.response.headers[str('Access-Control-Allow-Origin')] = str('*')
|
|
bottle.response.headers[str('Access-Control-Allow-Methods')] = str(
|
|
'PUT, GET, POST, DELETE, OPTIONS, PATCH'
|
|
)
|
|
bottle.response.headers[str('Access-Control-Allow-Headers')] = str(
|
|
'Origin, Accept, Content-Type, X-Requested-With, X-CSRF-Token'
|
|
)
|
|
|
|
# API v1 routes
|
|
app.route("/api/v1", ["GET", "OPTIONS"], api_routes.index_v1)
|
|
|
|
app.route("/api/v1/time_to_places", ["GET", "OPTIONS"],
|
|
api_routes.time_to_places_v1)
|
|
|
|
app.route("/api/v1/flats", ["GET", "OPTIONS"], api_routes.flats_v1)
|
|
app.route("/api/v1/flats/:flat_id", ["GET", "OPTIONS"], api_routes.flat_v1)
|
|
app.route("/api/v1/flats/:flat_id", ["PATCH", "OPTIONS"],
|
|
api_routes.update_flat_v1)
|
|
|
|
app.route("/api/v1/ics/visits.ics", ["GET", "OPTIONS"],
|
|
api_routes.ics_feed_v1)
|
|
|
|
app.route("/api/v1/search", "POST", api_routes.search_v1)
|
|
|
|
app.route("/api/v1/opendata", "GET", api_routes.opendata_index_v1)
|
|
app.route("/api/v1/opendata/postal_codes", "GET",
|
|
api_routes.opendata_postal_codes_v1)
|
|
|
|
# Index
|
|
app.route("/", "GET", lambda: _serve_static_file("index.html"))
|
|
|
|
# Static files
|
|
app.route("/favicon.ico", "GET",
|
|
lambda: _serve_static_file("favicon.ico"))
|
|
app.route(
|
|
"/assets/<filename:path>", "GET",
|
|
lambda filename: _serve_static_file("/assets/{}".format(filename))
|
|
)
|
|
app.route(
|
|
"/img/<filename:path>", "GET",
|
|
lambda filename: _serve_static_file("/img/{}".format(filename))
|
|
)
|
|
|
|
return app
|