view foodtruck/web.py @ 706:e67da1f7293b

admin: Add support for `.well-known` folder.
author Ludovic Chabant <ludovic@chabant.com>
date Sat, 14 May 2016 18:18:54 -0700
parents 65706804e1de
children 3885421c29a3
line wrap: on
line source

import os
import os.path
import time
import logging
from flask import Flask, g, request, render_template
from werkzeug import SharedDataMiddleware
from .configuration import (
        FoodTruckConfigNotFoundError, get_foodtruck_config)
from .sites import FoodTruckSites, InvalidSiteError


logger = logging.getLogger(__name__)

app = Flask(__name__)
app.config.from_object('foodtruck.settings')
app.config.from_envvar('FOODTRUCK_SETTINGS', silent=True)

admin_root = app.config.get('FOODTRUCK_ROOT', os.getcwd())
config_path = os.path.join(admin_root, 'app.cfg')

# If we're being run as the `chef admin run` command, from inside a PieCrust
# website, do a few things differently.
_procedural_config = None

if (app.config.get('FOODTRUCK_CMDLINE_MODE', False) and
        os.path.isfile(os.path.join(admin_root, 'config.yml'))):
    app.secret_key = os.urandom(22)
    app.config['LOGIN_DISABLED'] = True
    _procedural_config = {
            'sites': {
                'local': admin_root}
            }

# Add a special route for the `.well-known` directory.
app.wsgi_app = SharedDataMiddleware(
        app.wsgi_app,
        {'/.well-known': os.path.join(admin_root, '.well-known')})

if os.path.isfile(config_path):
    app.config.from_pyfile(config_path)

if app.config['DEBUG']:
    l = logging.getLogger()
    l.setLevel(logging.DEBUG)

logger.debug("Using FoodTruck admin root: %s" % admin_root)


def after_this_request(f):
    if not hasattr(g, 'after_request_callbacks'):
        g.after_request_callbacks = []
    g.after_request_callbacks.append(f)
    return f


class LazySomething(object):
    def __init__(self, factory):
        self._factory = factory
        self._something = None

    def __getattr__(self, name):
        if self._something is not None:
            return getattr(self._something, name)

        self._something = self._factory()
        return getattr(self._something, name)


@app.before_request
def _setup_foodtruck_globals():
    def _get_config():
        return get_foodtruck_config(admin_root, _procedural_config)

    def _get_sites():
        names = g.config.get('sites')
        if not names or not isinstance(names, dict):
            raise InvalidSiteError(
                    "No sites are defined in the configuration file.")

        current = request.cookies.get('foodtruck_site_name')
        if current is not None and current not in names:
            current = None
        if current is None:
            current = next(iter(names.keys()))
        s = FoodTruckSites(g.config, current)
        return s

    def _get_current_site():
        return g.sites.get()

    g.config = LazySomething(_get_config)
    g.sites = LazySomething(_get_sites)
    g.site = LazySomething(_get_current_site)


@app.after_request
def _call_after_request_callbacks(response):
    for callback in getattr(g, 'after_request_callbacks', ()):
        callback(response)
    return response


if not app.config['DEBUG']:
    logger.debug("Registering exception handlers.")

    @app.errorhandler(FoodTruckConfigNotFoundError)
    def _on_config_missing(ex):
        return render_template('install.html')

    @app.errorhandler(InvalidSiteError)
    def _on_invalid_site(ex):
        data = {'error': "The was an error with your configuration file: %s" %
                str(ex)}
        return render_template('error.html', **data)


@app.errorhandler
def _on_error(ex):
    logging.exception(ex)


_missing_secret_key = False

if not app.secret_key:
    # If there's no secret key, create a temp one but mark the app as not
    # correctly installed so it shows the installation information page.
    app.secret_key = 'temp-key'
    _missing_secret_key = True


from flask.ext.login import LoginManager, UserMixin


class User(UserMixin):
    def __init__(self, uid, pwd):
        self.id = uid
        self.password = pwd


def load_user(user_id):
    admin_id = g.config.get('security/username')
    if admin_id == user_id:
        admin_pwd = g.config.get('security/password')
        return User(admin_id, admin_pwd)
    return None


login_manager = LoginManager()
login_manager.init_app(app)
login_manager.login_view = 'login'
login_manager.user_loader(load_user)

if _missing_secret_key:
    def _handler():
        raise FoodTruckConfigNotFoundError()

    logger.debug("No secret key found, disabling website.")
    login_manager.unauthorized_handler(_handler)
    login_manager.login_view = None


from foodtruck.bcryptfallback import Bcrypt
if (getattr(Bcrypt, 'is_fallback_bcrypt', None) is True and
        not app.config.get('FOODTRUCK_CMDLINE_MODE', False)):
    raise Exception(
            "You're running FoodTruck outside of `chef`, and will need to "
            "install Flask-Bcrypt to get more proper security.")
app.bcrypt = Bcrypt(app)


@app.template_filter('iso8601')
def timestamp_to_iso8601(t):
    t = time.localtime(t)
    return time.strftime('%Y-%m-%dT%H:%M:%SZ', t)

@app.template_filter('datetime')
def timestamp_to_datetime(t, fmt=None):
    fmt = fmt or '%x'
    t = time.localtime(t)
    return time.strftime(fmt, t)


import foodtruck.views.create  # NOQA
import foodtruck.views.dashboard  # NOQA
import foodtruck.views.edit  # NOQA
import foodtruck.views.menu  # NOQA
import foodtruck.views.preview  # NOQA
import foodtruck.views.publish  # NOQA
import foodtruck.views.sources  # NOQA