diff --git a/README.rst b/README.rst index 8bcc648..155302a 100644 --- a/README.rst +++ b/README.rst @@ -48,7 +48,12 @@ To deploy it, I'm using gunicorn and supervisord:: Don't forget to set the right permission for your files ! Also, create a `settings.py` file with the appropriate values if you need to -use a different database for instance. +use a different database for instance. You can also set `APPLICATION_ROOT` if +you want to prefix your URLs to serve ihatemonney in the *folder* of a domain, +e.g: + + APPLICATION_ROOT='/budget' + How about the REST API? ======================= diff --git a/budget/default_settings.py b/budget/default_settings.py index 394ab00..c80c783 100644 --- a/budget/default_settings.py +++ b/budget/default_settings.py @@ -4,6 +4,7 @@ SQLACHEMY_ECHO = DEBUG SECRET_KEY = "tralala" MAIL_DEFAULT_SENDER = ("Budget manager", "budget@notmyidea.org") +APPLICATION_ROOT = '/' try: from settings import * diff --git a/budget/run.py b/budget/run.py index 1a65022..bdb8f46 100644 --- a/budget/run.py +++ b/budget/run.py @@ -6,21 +6,29 @@ from raven.contrib.flask import Sentry from web import main, db, mail from api import api - +from utils import PrefixedWSGI app = Flask(__name__) -app.config.from_object("default_settings") -# Deprecations -if 'DEFAULT_MAIL_SENDER' in app.config: - # Since flask-mail 0.8 - warnings.warn( - "DEFAULT_MAIL_SENDER is deprecated in favor of MAIL_DEFAULT_SENDER" - +" and will be removed in further version", - UserWarning - ) - if not 'MAIL_DEFAULT_SENDER' in app.config: - app.config['MAIL_DEFAULT_SENDER'] = DEFAULT_MAIL_SENDER + +def configure(): + """ A way to (re)configure the app, specially reset the settings + """ + app.config.from_object("default_settings") + app.wsgi_app = PrefixedWSGI(app) + + # Deprecations + if 'DEFAULT_MAIL_SENDER' in app.config: + # Since flask-mail 0.8 + warnings.warn( + "DEFAULT_MAIL_SENDER is deprecated in favor of MAIL_DEFAULT_SENDER" + +" and will be removed in further version", + UserWarning + ) + if not 'MAIL_DEFAULT_SENDER' in app.config: + app.config['MAIL_DEFAULT_SENDER'] = DEFAULT_MAIL_SENDER + +configure() app.register_blueprint(main) diff --git a/budget/tests.py b/budget/tests.py index 1fb9fe4..fd30c63 100644 --- a/budget/tests.py +++ b/budget/tests.py @@ -821,5 +821,20 @@ class APITestCase(TestCase): self.assertStatus(404, req) +class ServerTestCase(APITestCase): + def setUp(self): + run.configure() + super(ServerTestCase, self).setUp() + + def test_unprefixed(self): + req = self.app.get("/foo/") + self.assertStatus(303, req) + + def test_prefixed(self): + run.app.config['APPLICATION_ROOT'] = '/foo' + req = self.app.get("/foo/") + self.assertStatus(200, req) + + if __name__ == "__main__": unittest.main() diff --git a/budget/utils.py b/budget/utils.py index 60337fb..7717aaa 100644 --- a/budget/utils.py +++ b/budget/utils.py @@ -32,3 +32,34 @@ class Redirect303(HTTPException, RoutingException): def get_response(self, environ): return redirect(self.new_url, 303) + + +class PrefixedWSGI(object): + ''' + Wrap the application in this middleware and configure the + front-end server to add these headers, to let you quietly bind + this to a URL other than / and to an HTTP scheme that is + different than what is used locally. + + It relies on "APPLICATION_ROOT" app setting. + + Inspired from http://flask.pocoo.org/snippets/35/ + + :param app: the WSGI application + ''' + def __init__(self, app): + self.app = app + self.wsgi_app = app.wsgi_app + + def __call__(self, environ, start_response): + script_name = self.app.config['APPLICATION_ROOT'] + if script_name: + environ['SCRIPT_NAME'] = script_name + path_info = environ['PATH_INFO'] + if path_info.startswith(script_name): + environ['PATH_INFO'] = path_info[len(script_name):] + + scheme = environ.get('HTTP_X_SCHEME', '') + if scheme: + environ['wsgi.url_scheme'] = scheme + return self.wsgi_app(environ, start_response)