services/ui_backend_service/frontend.py (44 lines of code) (raw):
import os
import glob
from aiohttp import web
dirname = os.path.dirname(os.path.realpath(__file__))
static_ui_path = os.path.join(dirname, "ui")
METAFLOW_SERVICE = os.environ.get("METAFLOW_SERVICE", "/")
METAFLOW_HEAD = os.environ.get("METAFLOW_HEAD", None)
METAFLOW_BODY_BEFORE = os.environ.get("METAFLOW_BODY_BEFORE", None)
METAFLOW_BODY_AFTER = os.environ.get("METAFLOW_BODY_AFTER", None)
class Frontend(object):
"""
Provides routes for the static UI webpage.
Require this as the last Api, as it is a catch-all route.
"""
def __init__(self, app):
app.router.add_static('/static',
path=os.path.join(static_ui_path, "static"),
name='static')
# serve the root static files separately.
static_files = glob.glob(os.path.join(static_ui_path, "*.*"))
for filepath in static_files:
filename = filepath[len(static_ui_path) + 1:]
app.router.add_route(
'GET', f'/{filename}', self.serve_file(filename))
# catch-all route that unfortunately messes with root static file serving.
# Refreshing SPA pages won't work without the tail.
app.router.add_route('GET', '/{tail:.*}', self.serve_index_html)
def serve_file(self, filename: str):
"Generator for single static file serving handlers"
async def filehandler(request):
return web.FileResponse(os.path.join(static_ui_path, filename))
return filehandler
async def serve_index_html(self, request):
"Serve index.html by injecting `METAFLOW_SERVICE` variable to define API base url."
try:
with open(os.path.join(static_ui_path, "index.html")) as f:
content = f.read() \
.replace("</head>",
"<script>window.METAFLOW_SERVICE=\"{METAFLOW_SERVICE}\";</script></head>".format(METAFLOW_SERVICE=METAFLOW_SERVICE))
if METAFLOW_HEAD:
content = content.replace("</head>", "{METAFLOW_HEAD}</head>"
.format(METAFLOW_HEAD=METAFLOW_HEAD))
if METAFLOW_BODY_BEFORE:
content = content.replace("<body>", "<body>{METAFLOW_BODY_BEFORE}"
.format(METAFLOW_BODY_BEFORE=METAFLOW_BODY_BEFORE))
if METAFLOW_BODY_AFTER:
content = content.replace("</body>", "{METAFLOW_BODY_AFTER}</body>"
.format(METAFLOW_BODY_AFTER=METAFLOW_BODY_AFTER))
return web.Response(text=content, content_type='text/html')
except Exception as err:
return web.Response(text=str(err), status=500, content_type='text/plain')