Fix paths and URLs generated by API, and add note about config for proxying
This commit is contained in:
parent
9a13631097
commit
8dec4c8262
|
@ -8,7 +8,12 @@ from os.path import dirname, join, normpath, abspath, exists, isfile
|
||||||
from datetime import datetime, date
|
from datetime import datetime, date
|
||||||
|
|
||||||
from sanic import Sanic, Blueprint
|
from sanic import Sanic, Blueprint
|
||||||
from sanic.response import text, json as json_response, file as file_response
|
from sanic.response import (
|
||||||
|
text,
|
||||||
|
json as json_response,
|
||||||
|
file as file_response,
|
||||||
|
html as html_response,
|
||||||
|
)
|
||||||
from sanic.exceptions import Unauthorized, NotFound
|
from sanic.exceptions import Unauthorized, NotFound
|
||||||
from sanic_session import Session, InMemorySessionInterface
|
from sanic_session import Session, InMemorySessionInterface
|
||||||
|
|
||||||
|
@ -96,6 +101,43 @@ async def app_disconnect_db(app, loop):
|
||||||
await app.ctx._db_engine_ctx.__aexit__(None, None, None)
|
await app.ctx._db_engine_ctx.__aexit__(None, None, None)
|
||||||
|
|
||||||
|
|
||||||
|
def remove_right(l, r):
|
||||||
|
if l.endswith(r):
|
||||||
|
return l[: -len(r)]
|
||||||
|
return l
|
||||||
|
|
||||||
|
|
||||||
|
@app.middleware("request")
|
||||||
|
async def inject_urls(req):
|
||||||
|
if req.app.config.FRONTEND_HTTPS:
|
||||||
|
req.ctx.frontend_scheme = "https"
|
||||||
|
elif req.app.config.FRONTEND_URL:
|
||||||
|
req.ctx.frontend_scheme = (
|
||||||
|
"http" if req.app.config.FRONTEND_URL.startswith("http://") else "https"
|
||||||
|
)
|
||||||
|
else:
|
||||||
|
req.ctx.frontend_scheme = req.scheme
|
||||||
|
|
||||||
|
req.ctx.api_scheme = req.ctx.frontend_scheme # just use the same for now
|
||||||
|
req.ctx.api_base_path = remove_right(req.server_path, req.path)
|
||||||
|
req.ctx.api_url = f"{req.ctx.frontend_scheme}://{req.host}{req.ctx.api_base_path}"
|
||||||
|
|
||||||
|
if req.app.config.FRONTEND_URL:
|
||||||
|
req.ctx.frontend_base_path = "/" + urlparse(
|
||||||
|
req.app.config.FRONTEND_URL
|
||||||
|
).path.strip("/")
|
||||||
|
req.ctx.frontend_url = req.app.config.FRONTEND_URL.rstrip("/")
|
||||||
|
elif app.config.FRONTEND_DIR:
|
||||||
|
req.ctx.frontend_base_path = req.ctx.api_base_path
|
||||||
|
req.ctx.frontend_url = req.ctx.api_url
|
||||||
|
|
||||||
|
else:
|
||||||
|
req.ctx.frontend_base_path = "/"
|
||||||
|
req.ctx.frontend_url = (
|
||||||
|
f"{req.ctx.frontend_scheme}://{req.host}{req.ctx.frontend_base_path}"
|
||||||
|
)
|
||||||
|
|
||||||
|
|
||||||
@app.middleware("request")
|
@app.middleware("request")
|
||||||
async def inject_session(req):
|
async def inject_session(req):
|
||||||
req.ctx._session_ctx = make_session()
|
req.ctx._session_ctx = make_session()
|
||||||
|
@ -156,16 +198,16 @@ if INDEX_HTML and exists(INDEX_HTML):
|
||||||
|
|
||||||
@app.get("/config.json")
|
@app.get("/config.json")
|
||||||
def get_frontend_config(req):
|
def get_frontend_config(req):
|
||||||
base_path = req.server_path.replace("config.json", "")
|
|
||||||
scheme = "https" if req.app.config.FRONTEND_HTTPS else req.scheme
|
|
||||||
result = {
|
result = {
|
||||||
|
"basename": req.ctx.frontend_base_path,
|
||||||
**req.app.config.FRONTEND_CONFIG,
|
**req.app.config.FRONTEND_CONFIG,
|
||||||
"apiUrl": f"{scheme}://{req.host}{base_path}api",
|
"apiUrl": f"{req.ctx.api_url}/api",
|
||||||
"loginUrl": f"{scheme}://{req.host}{base_path}login",
|
"loginUrl": f"{req.ctx.api_url}/login",
|
||||||
"obsMapSource": {
|
"obsMapSource": {
|
||||||
"type": "vector",
|
"type": "vector",
|
||||||
"tiles": [
|
"tiles": [
|
||||||
req.app.url_for("tiles", zoom="000", x="111", y="222.pbf")
|
req.ctx.api_url
|
||||||
|
+ req.app.url_for("tiles", zoom="000", x="111", y="222.pbf")
|
||||||
.replace("000", "{z}")
|
.replace("000", "{z}")
|
||||||
.replace("111", "{x}")
|
.replace("111", "{x}")
|
||||||
.replace("222", "{y}")
|
.replace("222", "{y}")
|
||||||
|
@ -177,14 +219,21 @@ if INDEX_HTML and exists(INDEX_HTML):
|
||||||
|
|
||||||
return json_response(result)
|
return json_response(result)
|
||||||
|
|
||||||
|
with open(INDEX_HTML, "rt") as f:
|
||||||
|
index_file_contents = f.read()
|
||||||
|
|
||||||
@app.get("/<path:path>")
|
@app.get("/<path:path>")
|
||||||
def get_frontend_static(req, path):
|
def get_frontend_static(req, path):
|
||||||
|
print("++++++++++++++++++++++++++++++++++++++++++++++++", path)
|
||||||
if path.startswith("api/"):
|
if path.startswith("api/"):
|
||||||
raise NotFound()
|
raise NotFound()
|
||||||
|
|
||||||
file = join(app.config.FRONTEND_DIR, path)
|
file = join(app.config.FRONTEND_DIR, path)
|
||||||
if not exists(file) or not path or not isfile(file):
|
if not exists(file) or not path or not isfile(file):
|
||||||
file = INDEX_HTML
|
return html_response(
|
||||||
|
index_file_contents.replace("__BASE_HREF__", req.ctx.frontend_url + "/")
|
||||||
|
)
|
||||||
|
|
||||||
return file_response(file)
|
return file_response(file)
|
||||||
|
|
||||||
|
|
||||||
|
|
|
@ -38,13 +38,12 @@ async def login(req, next: str = None):
|
||||||
session["state"] = rndstr()
|
session["state"] = rndstr()
|
||||||
session["nonce"] = rndstr()
|
session["nonce"] = rndstr()
|
||||||
session["next"] = next
|
session["next"] = next
|
||||||
scheme = 'https' if req.app.config.FRONTEND_HTTPS else req.scheme
|
|
||||||
args = {
|
args = {
|
||||||
"client_id": client.client_id,
|
"client_id": client.client_id,
|
||||||
"response_type": "code",
|
"response_type": "code",
|
||||||
"scope": ["openid"],
|
"scope": ["openid"],
|
||||||
"nonce": session["nonce"],
|
"nonce": session["nonce"],
|
||||||
"redirect_uri": scheme + "://" + req.host + "/login/redirect",
|
"redirect_uri": req.ctx.frontend_url + "/login/redirect",
|
||||||
"state": session["state"],
|
"state": session["state"],
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -81,7 +80,9 @@ async def login_redirect(req):
|
||||||
email = userinfo.get("email")
|
email = userinfo.get("email")
|
||||||
|
|
||||||
if email is None:
|
if email is None:
|
||||||
raise ValueError("user has no email set, please configure keycloak to require emails")
|
raise ValueError(
|
||||||
|
"user has no email set, please configure keycloak to require emails"
|
||||||
|
)
|
||||||
|
|
||||||
user = (await req.ctx.db.execute(select(User).where(User.sub == sub))).scalar()
|
user = (await req.ctx.db.execute(select(User).where(User.sub == sub))).scalar()
|
||||||
|
|
||||||
|
|
|
@ -78,6 +78,14 @@ Then edit `config/config.py` to your heart's content (and matching the
|
||||||
configuration of the keycloak). Do not forget to generate a secure secret
|
configuration of the keycloak). Do not forget to generate a secure secret
|
||||||
string.
|
string.
|
||||||
|
|
||||||
|
Also set `PROXIES_COUNT = 1` in your config, even if that option is not
|
||||||
|
included in the example file. Read the [sanic
|
||||||
|
docs](https://sanic.readthedocs.io/en/v20.12.3/sanic/config.html) for why this
|
||||||
|
needs to be done. If your reverse proxy supports it, you can also use a
|
||||||
|
forwarded secret to secure your proxy target from spoofing. This is not
|
||||||
|
required if your application server does not listen on a public interface, but
|
||||||
|
it is recommended anyway, if possible.
|
||||||
|
|
||||||
### Build container and run them
|
### Build container and run them
|
||||||
|
|
||||||
```bash
|
```bash
|
||||||
|
|
Loading…
Reference in a new issue