Mercurial > piecrust2
comparison foodtruck/web.py @ 772:3885421c29a3
admin: Make the whole FoodTruck site into a blueprint.
This makes it possible to use an app factory, which makes it easier to write
unit tests.
author | Ludovic Chabant <ludovic@chabant.com> |
---|---|
date | Sun, 03 Jul 2016 07:54:54 -0700 |
parents | e67da1f7293b |
children | ba0a6bd5e913 |
comparison
equal
deleted
inserted
replaced
771:673979a5d548 | 772:3885421c29a3 |
---|---|
1 import os | |
2 import os.path | 1 import os.path |
3 import time | |
4 import logging | 2 import logging |
5 from flask import Flask, g, request, render_template | 3 from flask import Flask |
6 from werkzeug import SharedDataMiddleware | 4 from werkzeug import SharedDataMiddleware |
7 from .configuration import ( | 5 from .blueprint import foodtruck_bp, login_manager, bcrypt_ext |
8 FoodTruckConfigNotFoundError, get_foodtruck_config) | |
9 from .sites import FoodTruckSites, InvalidSiteError | |
10 | 6 |
11 | 7 |
12 logger = logging.getLogger(__name__) | 8 logger = logging.getLogger(__name__) |
13 | 9 |
14 app = Flask(__name__) | |
15 app.config.from_object('foodtruck.settings') | |
16 app.config.from_envvar('FOODTRUCK_SETTINGS', silent=True) | |
17 | 10 |
18 admin_root = app.config.get('FOODTRUCK_ROOT', os.getcwd()) | 11 def create_foodtruck_app(extra_settings=None): |
19 config_path = os.path.join(admin_root, 'app.cfg') | 12 app = Flask(__name__) |
13 app.config.from_object('foodtruck.settings') | |
14 app.config.from_envvar('FOODTRUCK_SETTINGS', silent=True) | |
15 if extra_settings: | |
16 app.config.from_object(extra_settings) | |
20 | 17 |
21 # If we're being run as the `chef admin run` command, from inside a PieCrust | 18 admin_root = app.config.setdefault('FOODTRUCK_ROOT', os.getcwd()) |
22 # website, do a few things differently. | 19 config_path = os.path.join(admin_root, 'app.cfg') |
23 _procedural_config = None | |
24 | 20 |
25 if (app.config.get('FOODTRUCK_CMDLINE_MODE', False) and | 21 # If we're being run as the `chef admin run` command, from inside a PieCrust |
26 os.path.isfile(os.path.join(admin_root, 'config.yml'))): | 22 # website, do a few things differently. |
27 app.secret_key = os.urandom(22) | 23 app.config['FOODTRUCK_PROCEDURAL_CONFIG'] = None |
28 app.config['LOGIN_DISABLED'] = True | 24 if (app.config.get('FOODTRUCK_CMDLINE_MODE', False) and |
29 _procedural_config = { | 25 os.path.isfile(os.path.join(admin_root, 'config.yml'))): |
30 'sites': { | 26 app.secret_key = os.urandom(22) |
31 'local': admin_root} | 27 app.config['LOGIN_DISABLED'] = True |
32 } | 28 app.config['FOODTRUCK_PROCEDURAL_CONFIG'] = { |
29 'sites': { | |
30 'local': admin_root} | |
31 } | |
33 | 32 |
34 # Add a special route for the `.well-known` directory. | 33 # Add a special route for the `.well-known` directory. |
35 app.wsgi_app = SharedDataMiddleware( | 34 app.wsgi_app = SharedDataMiddleware( |
36 app.wsgi_app, | 35 app.wsgi_app, |
37 {'/.well-known': os.path.join(admin_root, '.well-known')}) | 36 {'/.well-known': os.path.join(admin_root, '.well-known')}) |
38 | 37 |
39 if os.path.isfile(config_path): | 38 if os.path.isfile(config_path): |
40 app.config.from_pyfile(config_path) | 39 app.config.from_pyfile(config_path) |
41 | 40 |
42 if app.config['DEBUG']: | 41 if app.config['DEBUG']: |
43 l = logging.getLogger() | 42 l = logging.getLogger() |
44 l.setLevel(logging.DEBUG) | 43 l.setLevel(logging.DEBUG) |
44 else: | |
45 @app.errorhandler(FoodTruckConfigNotFoundError) | |
46 def _on_config_missing(ex): | |
47 return render_template('install.html') | |
45 | 48 |
46 logger.debug("Using FoodTruck admin root: %s" % admin_root) | 49 @app.errorhandler(InvalidSiteError) |
50 def _on_invalid_site(ex): | |
51 data = {'error': "The was an error with your configuration file: %s" % | |
52 str(ex)} | |
53 return render_template('error.html', **data) | |
47 | 54 |
55 _missing_secret_key = False | |
56 if not app.secret_key: | |
57 # If there's no secret key, create a temp one but mark the app as not | |
58 # correctly installed so it shows the installation information page. | |
59 app.secret_key = 'temp-key' | |
60 _missing_secret_key = True | |
48 | 61 |
49 def after_this_request(f): | 62 # Register extensions and blueprints. |
50 if not hasattr(g, 'after_request_callbacks'): | 63 login_manager.init_app(app) |
51 g.after_request_callbacks = [] | 64 bcrypt_ext.init_app(app) |
52 g.after_request_callbacks.append(f) | 65 app.register_blueprint(foodtruck_bp) |
53 return f | |
54 | 66 |
67 logger.debug("Created FoodTruck app with admin root: %s" % admin_root) | |
55 | 68 |
56 class LazySomething(object): | 69 return app |
57 def __init__(self, factory): | |
58 self._factory = factory | |
59 self._something = None | |
60 | 70 |
61 def __getattr__(self, name): | |
62 if self._something is not None: | |
63 return getattr(self._something, name) | |
64 | |
65 self._something = self._factory() | |
66 return getattr(self._something, name) | |
67 | |
68 | |
69 @app.before_request | |
70 def _setup_foodtruck_globals(): | |
71 def _get_config(): | |
72 return get_foodtruck_config(admin_root, _procedural_config) | |
73 | |
74 def _get_sites(): | |
75 names = g.config.get('sites') | |
76 if not names or not isinstance(names, dict): | |
77 raise InvalidSiteError( | |
78 "No sites are defined in the configuration file.") | |
79 | |
80 current = request.cookies.get('foodtruck_site_name') | |
81 if current is not None and current not in names: | |
82 current = None | |
83 if current is None: | |
84 current = next(iter(names.keys())) | |
85 s = FoodTruckSites(g.config, current) | |
86 return s | |
87 | |
88 def _get_current_site(): | |
89 return g.sites.get() | |
90 | |
91 g.config = LazySomething(_get_config) | |
92 g.sites = LazySomething(_get_sites) | |
93 g.site = LazySomething(_get_current_site) | |
94 | |
95 | |
96 @app.after_request | |
97 def _call_after_request_callbacks(response): | |
98 for callback in getattr(g, 'after_request_callbacks', ()): | |
99 callback(response) | |
100 return response | |
101 | |
102 | |
103 if not app.config['DEBUG']: | |
104 logger.debug("Registering exception handlers.") | |
105 | |
106 @app.errorhandler(FoodTruckConfigNotFoundError) | |
107 def _on_config_missing(ex): | |
108 return render_template('install.html') | |
109 | |
110 @app.errorhandler(InvalidSiteError) | |
111 def _on_invalid_site(ex): | |
112 data = {'error': "The was an error with your configuration file: %s" % | |
113 str(ex)} | |
114 return render_template('error.html', **data) | |
115 | |
116 | |
117 @app.errorhandler | |
118 def _on_error(ex): | |
119 logging.exception(ex) | |
120 | |
121 | |
122 _missing_secret_key = False | |
123 | |
124 if not app.secret_key: | |
125 # If there's no secret key, create a temp one but mark the app as not | |
126 # correctly installed so it shows the installation information page. | |
127 app.secret_key = 'temp-key' | |
128 _missing_secret_key = True | |
129 | |
130 | |
131 from flask.ext.login import LoginManager, UserMixin | |
132 | |
133 | |
134 class User(UserMixin): | |
135 def __init__(self, uid, pwd): | |
136 self.id = uid | |
137 self.password = pwd | |
138 | |
139 | |
140 def load_user(user_id): | |
141 admin_id = g.config.get('security/username') | |
142 if admin_id == user_id: | |
143 admin_pwd = g.config.get('security/password') | |
144 return User(admin_id, admin_pwd) | |
145 return None | |
146 | |
147 | |
148 login_manager = LoginManager() | |
149 login_manager.init_app(app) | |
150 login_manager.login_view = 'login' | |
151 login_manager.user_loader(load_user) | |
152 | |
153 if _missing_secret_key: | |
154 def _handler(): | |
155 raise FoodTruckConfigNotFoundError() | |
156 | |
157 logger.debug("No secret key found, disabling website.") | |
158 login_manager.unauthorized_handler(_handler) | |
159 login_manager.login_view = None | |
160 | |
161 | |
162 from foodtruck.bcryptfallback import Bcrypt | |
163 if (getattr(Bcrypt, 'is_fallback_bcrypt', None) is True and | |
164 not app.config.get('FOODTRUCK_CMDLINE_MODE', False)): | |
165 raise Exception( | |
166 "You're running FoodTruck outside of `chef`, and will need to " | |
167 "install Flask-Bcrypt to get more proper security.") | |
168 app.bcrypt = Bcrypt(app) | |
169 | |
170 | |
171 @app.template_filter('iso8601') | |
172 def timestamp_to_iso8601(t): | |
173 t = time.localtime(t) | |
174 return time.strftime('%Y-%m-%dT%H:%M:%SZ', t) | |
175 | |
176 @app.template_filter('datetime') | |
177 def timestamp_to_datetime(t, fmt=None): | |
178 fmt = fmt or '%x' | |
179 t = time.localtime(t) | |
180 return time.strftime(fmt, t) | |
181 | |
182 | |
183 import foodtruck.views.create # NOQA | |
184 import foodtruck.views.dashboard # NOQA | |
185 import foodtruck.views.edit # NOQA | |
186 import foodtruck.views.menu # NOQA | |
187 import foodtruck.views.preview # NOQA | |
188 import foodtruck.views.publish # NOQA | |
189 import foodtruck.views.sources # NOQA | |
190 |