diff --git a/.flake8 b/.flake8 new file mode 100644 index 0000000..6c8cf32 --- /dev/null +++ b/.flake8 @@ -0,0 +1,4 @@ +[flake8] +max-line-length = 120 +exclude = tests +max-complexity = 10 diff --git a/.gitignore b/.gitignore index a56ccf1..c4d4ba0 100644 --- a/.gitignore +++ b/.gitignore @@ -92,3 +92,4 @@ sacredboard/Scripts/ \.idea/ node_modules +*~ \ No newline at end of file diff --git a/sacredboard/app/webapi/proxy.py b/sacredboard/app/webapi/proxy.py new file mode 100644 index 0000000..44a91a9 --- /dev/null +++ b/sacredboard/app/webapi/proxy.py @@ -0,0 +1,36 @@ +""" +Reverse proxy support for Sacredboard. + +http://blog.macuyiko.com/post/2016/fixing-flask-url_for-when-behind-mod_proxy.html +""" + + +class ReverseProxied(object): + """ + Allow to use a reverse proxy. + + http://blog.macuyiko.com/post/2016/fixing-flask-url_for-when-behind-mod_proxy.html + """ + + def __init__(self, app, script_name=None, scheme=None, server=None): + """Create a new wrapper for Flask.""" + self.app = app + self.script_name = script_name + self.scheme = scheme + self.server = server + + def __call__(self, environ, start_response): + """Set environment for Flask.""" + script_name = environ.get('HTTP_X_SCRIPT_NAME', '') or self.script_name + 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', '') or self.scheme + if scheme: + environ['wsgi.url_scheme'] = scheme + server = environ.get('HTTP_X_FORWARDED_SERVER', '') or self.server + if server: + environ['HTTP_HOST'] = server + return self.app(environ, start_response) diff --git a/sacredboard/bootstrap.py b/sacredboard/bootstrap.py index bf6803e..38c5a02 100755 --- a/sacredboard/bootstrap.py +++ b/sacredboard/bootstrap.py @@ -15,6 +15,7 @@ from sacredboard.app.data.filestorage import FileStorage from sacredboard.app.data.mongodb import PyMongoDataAccess from sacredboard.app.webapi import routes, metrics +from sacredboard.app.webapi.proxy import ReverseProxied locale.setlocale(locale.LC_ALL, '') app = Flask(__name__) @@ -41,11 +42,15 @@ "File Storage observer. (experimental)") @click.option("--no-browser", is_flag=True, default=False, help="Do not open web browser automatically.") +@click.option("--sub-url", default="/", + help="Run the app on a sub-url. Example '-sub_url /sacredboard/' " + "maps localhost:5000/ -> localhost:5000/sacredboard/. " + "Useful with http proxy.") @click.option("--debug", is_flag=True, default=False, help="Run the application in Flask debug mode " "(for development).") @click.version_option() -def run(debug, no_browser, m, mu, mc, f): +def run(debug, no_browser, m, mu, mc, f, sub_url): """ Sacredboard. @@ -93,6 +98,8 @@ def run(debug, no_browser, m, mu, mc, f): app.config['DEBUG'] = debug app.debug = debug + if sub_url is not "/": + app.wsgi_app = ReverseProxied(app.wsgi_app, script_name=sub_url) jinja_filters.setup_filters(app) routes.setup_routes(app) metrics.initialize(app) diff --git a/sacredboard/static/scripts/runs/filters.js b/sacredboard/static/scripts/runs/filters.js index 6086bd6..295ec45 100644 --- a/sacredboard/static/scripts/runs/filters.js +++ b/sacredboard/static/scripts/runs/filters.js @@ -24,7 +24,7 @@ define(["knockout", "jquery", "text!runs/filters.html", "runs/filters/queryFilte Example: - @method + @function */ "initialize": function () { ko.components.register("query-filter", { diff --git a/sacredboard/static/scripts/runs/metricsViewer/ProxiedMetric.js b/sacredboard/static/scripts/runs/metricsViewer/ProxiedMetric.js index 2e277b8..f8da953 100644 --- a/sacredboard/static/scripts/runs/metricsViewer/ProxiedMetric.js +++ b/sacredboard/static/scripts/runs/metricsViewer/ProxiedMetric.js @@ -62,7 +62,7 @@ define(["runs/Metric", "knockout", "jquery"], function (Metric, ko, $) { } var self = this; this._fetchingInProgress = true; - $.getJSON("/api/run/" + this._runId + "/metric/" + this._metricId, + $.getJSON("api/run/" + this._runId + "/metric/" + this._metricId, function (data) { self._values(data["values"]); self._steps(data["steps"]); diff --git a/sacredboard/static/scripts/runs/runTable.js b/sacredboard/static/scripts/runs/runTable.js index a811095..49bc3d6 100644 --- a/sacredboard/static/scripts/runs/runTable.js +++ b/sacredboard/static/scripts/runs/runTable.js @@ -71,7 +71,7 @@ define(["bootstrap", "datatable", "datatables-bootstrap", "runs/detailView", "jq * and additional URL parameters to be passed to the backend. */ ajax: { - url: "/api/run", + url: "api/run", data: function (request) { request.queryFilter = JSON.stringify(createRunTable.queryFilter); @@ -132,7 +132,7 @@ define(["bootstrap", "datatable", "datatables-bootstrap", "runs/detailView", "jq var id = row.data().id; var loadDetailData = function () { $.ajax({ - url: "/api/run/" + id + url: "api/run/" + id }).done(function (data) { if (data.data[0].id != row.data().id) { /* Before this ajax function was called, diff --git a/tox.ini b/tox.ini index b686bda..c79ccf3 100644 --- a/tox.ini +++ b/tox.ini @@ -13,7 +13,7 @@ commands = {envpython} setup.py test --addopts -v deps = flake8 commands = - flake8 sacredboard --max-complexity 10 --exclude tests + flake8 sacredboard [testenv:pydocstyle] deps =