Repository: feast-dev/feast

Issue:
feast ui not working behind proxy
## Expected Behavior 
Feast UI should be able to load up behind proxy

## Current Behavior
Feast UI not loaded behind proxy

## Steps to reproduce

### Specifications

- Version: 0.23.0
- Platform: Linux
- Subsystem:

## Possible Solution
Allow user to pass root_path when initialize FastAPI. 

E.g.
```
from fastapi import FastAPI, Request

app = FastAPI(root_path="/api/v1")


@app.get("/app")
def read_main(request: Request):
    return {"message": "Hello World", "root_path": request.scope.get("root_path")}
```


Discussion:
would you mind contributing a fix for this? Thanks!
This issue has been automatically marked as stale because it has not had recent activity. It will be closed if no further activity occurs. Thank you for your contributions.


Resolving patch:
diff --git a/sdk/python/feast/cli.py b/sdk/python/feast/cli.py
--- a/sdk/python/feast/cli.py
+++ b/sdk/python/feast/cli.py
@@ -151,12 +151,24 @@ def version():
     "--registry_ttl_sec",
     "-r",
     help="Number of seconds after which the registry is refreshed",
-    type=int,
+    type=click.INT,
     default=5,
     show_default=True,
 )
+@click.option(
+    "--root_path",
+    help="Provide root path to make the UI working behind proxy",
+    type=click.STRING,
+    default="",
+)
 @click.pass_context
-def ui(ctx: click.Context, host: str, port: int, registry_ttl_sec: int):
+def ui(
+    ctx: click.Context,
+    host: str,
+    port: int,
+    registry_ttl_sec: int,
+    root_path: Optional[str] = "",
+):
     """
     Shows the Feast UI over the current directory
     """
@@ -170,6 +182,7 @@ def ui(ctx: click.Context, host: str, port: int, registry_ttl_sec: int):
         port=port,
         get_registry_dump=registry_dump,
         registry_ttl_sec=registry_ttl_sec,
+        root_path=root_path,
     )
 
 
diff --git a/sdk/python/feast/feature_store.py b/sdk/python/feast/feature_store.py
--- a/sdk/python/feast/feature_store.py
+++ b/sdk/python/feast/feature_store.py
@@ -2319,7 +2319,12 @@ def get_feature_server_endpoint(self) -> Optional[str]:
 
     @log_exceptions_and_usage
     def serve_ui(
-        self, host: str, port: int, get_registry_dump: Callable, registry_ttl_sec: int
+        self,
+        host: str,
+        port: int,
+        get_registry_dump: Callable,
+        registry_ttl_sec: int,
+        root_path: Optional[str] = "",
     ) -> None:
         """Start the UI server locally"""
         if flags_helper.is_test():
@@ -2335,6 +2340,7 @@ def serve_ui(
             get_registry_dump=get_registry_dump,
             project_id=self.config.project,
             registry_ttl_sec=registry_ttl_sec,
+            root_path=root_path,
         )
 
     @log_exceptions_and_usage
diff --git a/sdk/python/feast/ui_server.py b/sdk/python/feast/ui_server.py
--- a/sdk/python/feast/ui_server.py
+++ b/sdk/python/feast/ui_server.py
@@ -101,6 +101,14 @@ def start_server(
     get_registry_dump: Callable,
     project_id: str,
     registry_ttl_sec: int,
+    root_path: Optional[str] = "",
 ):
-    app = get_app(store, get_registry_dump, project_id, registry_ttl_sec, host, port)
-    uvicorn.run(app, host=host, port=port)
+    app = get_app(
+        store,
+        get_registry_dump,
+        project_id,
+        registry_ttl_sec,
+        host,
+        port,
+    )
+    uvicorn.run(app, host=host, port=port, root_path=root_path)


Test patch: