prompt: We are currently solving the following issue within our repository. Here is the issue text:
  --- BEGIN ISSUE ---
  feast ui does not work on proxy subpath
  ## Expected Behavior 
  
  Feast UI should work when it is served behind a proxy, on a subpath e.g. `/feast-ui`
  
  ## Current Behavior
  
  Parts of the feast UI works behind a subpath, but not entirely (nothing is displayed, just the feast logo with a "404" text - refer to screenshot). No requests in the network tab of the web browser are hitting 404.
  
  ![image](https://user-images.githubusercontent.com/12453748/216761133-a493bfa9-f752-4d23-9fc9-afc7cf4eb08b.png)
  
  ## Steps to reproduce
  
  Serve feast UI as you would e.g. `feature_store.serve_ui()`, optionally passing in the `root_path` parameter (it does not help).
  
  Set up an nginx pod with the following configuration (i.e. the nginx pod should have `/etc/nginx/conf.d/default.conf` with the following contents - `dummy_project` is the project name, and `http://feast-ui-service:8080` is where the feast UI can be accessed from your nginx pod / container):
  
  ```
  server {
    listen 80 default_server;
  
    location = /feast-ui/ {
        rewrite (.*) /feast-ui/p/dummy_project permanent;
    }
  
    location /feast-ui/ {
        proxy_pass http://feast-ui-service:8080/;
    }
  
    location / {
        proxy_pass http://feast-ui-service:8080/;
    }
  }
  ```
  
  This configuration works on localhost when nginx can listen on the root path `/`. However, note that the URL after all the redirects is wrong (it does not have the prefix).
  
  - The first block is required to force a redirect to the `/p/{project_name}`. Without this, the page will display 404 as above.
  - The second block is required to strip away `/feast-ui` so the UI app does not receive that path that it is not aware of
  - The third block is a trick to make this setup work in a local environment, because the app itself will redirect the user back to `/p/dummy_project` (without the prefix), which we then proxy into the feast UI app. However, in an actual environment, this setup does not work, because when the url does not contain the `/feast-ui` prefix, the ingress will not route it to the nginx pod, so the nginx pod cannot proxy the connection to the right place.
  
  Ideally, if the feast ui app is capable of being served on a subpath, only the second `location` block should be required in the nginx configuration. The first and third `location` blocks are workarounds.
  
  ### Specifications
  
  - Version: 0.29.0
  
  ## Possible Solution
  
  The app should redirect to relative and not absolute paths
  
  --- END ISSUE ---
  
  Below are some code segments, each from a relevant file. One or more of these files may contain bugs.
  --- BEGIN FILES ---
  Path: `sdk/python/feast/ui_server.py`
  Content:
  ```
  1 import json
  2 import threading
  3 from typing import Callable, Optional
  4 
  5 import pkg_resources
  6 import uvicorn
  7 from fastapi import FastAPI, Response
  8 from fastapi.middleware.cors import CORSMiddleware
  9 from fastapi.staticfiles import StaticFiles
  10 
  11 import feast
  12 
  13 
  14 def get_app(
  15     store: "feast.FeatureStore",
  16     get_registry_dump: Callable,
  17     project_id: str,
  18     registry_ttl_secs: int,
  19     host: str,
  20     port: int,
  21 ):
  22     app = FastAPI()
  23 
  24     app.add_middleware(
  25         CORSMiddleware,
  26         allow_origins=["*"],
  27         allow_credentials=True,
  28         allow_methods=["*"],
  29         allow_headers=["*"],
  30     )
  31 
  32     # Asynchronously refresh registry, notifying shutdown and canceling the active timer if the app is shutting down
  33     registry_proto = None
  34     shutting_down = False
  35     active_timer: Optional[threading.Timer] = None
  36 
  37     def async_refresh():
  38         store.refresh_registry()
  39         nonlocal registry_proto
  40         registry_proto = store.registry.proto()
  41         if shutting_down:
  42             return
  43         nonlocal active_timer
  44         active_timer = threading.Timer(registry_ttl_secs, async_refresh)
  45         active_timer.start()
  46 
  47     @app.on_event("shutdown")
  48     def shutdown_event():
  49         nonlocal shutting_down
  50         shutting_down = True
  51         if active_timer:
  52             active_timer.cancel()
  53 
  54     async_refresh()
  55 
  56     ui_dir = pkg_resources.resource_filename(__name__, "ui/build/")
  57     # Initialize with the projects-list.json file
  58     with open(ui_dir + "projects-list.json", mode="w") as f:
  59         projects_dict = {
  60             "projects": [
  61                 {
  62                     "name": "Project",
  63                     "description": "Test project",
  64                     "id": project_id,
  65                     "registryPath": "/registry",
  66                 }
  67             ]
  68         }
  69         f.write(json.dumps(projects_dict))
  70 
  71     @app.get("/registry")
  72     def read_registry():
  73         return Response(
  74             content=registry_proto.SerializeToString(),
  75             media_type="application/octet-stream",
  76         )
  77 
  78     # For all other paths (such as paths that would otherwise be handled by react router), pass to React
  79     @app.api_route("/p/{path_name:path}", methods=["GET"])
  80     def catch_all():
  81         filename = ui_dir + "index.html"
  82 
  83         with open(filename) as f:
  84             content = f.read()
  85 
  86         return Response(content, media_type="text/html")
  87 
  88     app.mount(
  89         "/",
  90         StaticFiles(directory=ui_dir, html=True),
  91         name="site",
  92     )
  93 
  94     return app
  95 
  96 
  97 def start_server(
  98     store: "feast.FeatureStore",
  99     host: str,
  100     port: int,
  101     get_registry_dump: Callable,
  102     project_id: str,
  103     registry_ttl_sec: int,
  104     root_path: str = "",
  105 ):
  106     app = get_app(
  107         store,
  108         get_registry_dump,
  109         project_id,
  110         registry_ttl_sec,
  111         host,
  112         port,
  113     )
  114     assert root_path is not None
  115     uvicorn.run(app, host=host, port=port, root_path=root_path)
  116
  ```
  --- END FILES ---
  
  Please first localize the bug based on the issue statement, and then generate a patch according to the `git diff` format fenced by three backticks.
  
  Here is an example:
  
  ```diff
  diff --git a/examples/server_async.py b/examples/server_async.py
  --- a/examples/server_async.py
  +++ b/examples/server_async.py
  @@ -313,4 +313,4 @@
   
   
   if __name__ == "__main__":
  -    asyncio.run(run_async_server("."), debug=True)
  +    asyncio.run(run_async_server(), debug=True)
  diff --git a/examples/server_sync.py b/examples/server_sync.py
  --- a/examples/server_sync.py
  +++ b/examples/server_sync.py
  @@ -313,5 +313,5 @@
   
   
   if __name__ == "__main__":
  -    server = run_sync_server(".")
  +    server = run_sync_server()
       server.shutdown()
  
  ```
  
golden_diff: 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
  @@ -13,11 +13,9 @@
   
   def get_app(
       store: "feast.FeatureStore",
  -    get_registry_dump: Callable,
       project_id: str,
       registry_ttl_secs: int,
  -    host: str,
  -    port: int,
  +    root_path: str = "",
   ):
       app = FastAPI()
   
  @@ -62,7 +60,7 @@
                       "name": "Project",
                       "description": "Test project",
                       "id": project_id,
  -                    "registryPath": "/registry",
  +                    "registryPath": f"{root_path}/registry",
                   }
               ]
           }
  @@ -105,11 +103,8 @@
   ):
       app = get_app(
           store,
  -        get_registry_dump,
           project_id,
           registry_ttl_sec,
  -        host,
  -        port,
  +        root_path,
       )
  -    assert root_path is not None
  -    uvicorn.run(app, host=host, port=port, root_path=root_path)
  +    uvicorn.run(app, host=host, port=port)
  
verification_info: {"golden_diff": "diff --git a/sdk/python/feast/ui_server.py b/sdk/python/feast/ui_server.py\n--- a/sdk/python/feast/ui_server.py\n+++ b/sdk/python/feast/ui_server.py\n@@ -13,11 +13,9 @@\n \n def get_app(\n     store: \"feast.FeatureStore\",\n-    get_registry_dump: Callable,\n     project_id: str,\n     registry_ttl_secs: int,\n-    host: str,\n-    port: int,\n+    root_path: str = \"\",\n ):\n     app = FastAPI()\n \n@@ -62,7 +60,7 @@\n                     \"name\": \"Project\",\n                     \"description\": \"Test project\",\n                     \"id\": project_id,\n-                    \"registryPath\": \"/registry\",\n+                    \"registryPath\": f\"{root_path}/registry\",\n                 }\n             ]\n         }\n@@ -105,11 +103,8 @@\n ):\n     app = get_app(\n         store,\n-        get_registry_dump,\n         project_id,\n         registry_ttl_sec,\n-        host,\n-        port,\n+        root_path,\n     )\n-    assert root_path is not None\n-    uvicorn.run(app, host=host, port=port, root_path=root_path)\n+    uvicorn.run(app, host=host, port=port)\n", "issue": "feast ui does not work on proxy subpath\n## Expected Behavior \r\n\r\nFeast UI should work when it is served behind a proxy, on a subpath e.g. `/feast-ui`\r\n\r\n## Current Behavior\r\n\r\nParts of the feast UI works behind a subpath, but not entirely (nothing is displayed, just the feast logo with a \"404\" text - refer to screenshot). No requests in the network tab of the web browser are hitting 404.\r\n\r\n![image](https://user-images.githubusercontent.com/12453748/216761133-a493bfa9-f752-4d23-9fc9-afc7cf4eb08b.png)\r\n\r\n## Steps to reproduce\r\n\r\nServe feast UI as you would e.g. `feature_store.serve_ui()`, optionally passing in the `root_path` parameter (it does not help).\r\n\r\nSet up an nginx pod with the following configuration (i.e. the nginx pod should have `/etc/nginx/conf.d/default.conf` with the following contents - `dummy_project` is the project name, and `http://feast-ui-service:8080` is where the feast UI can be accessed from your nginx pod / container):\r\n\r\n```\r\nserver {\r\n  listen 80 default_server;\r\n\r\n  location = /feast-ui/ {\r\n      rewrite (.*) /feast-ui/p/dummy_project permanent;\r\n  }\r\n\r\n  location /feast-ui/ {\r\n      proxy_pass http://feast-ui-service:8080/;\r\n  }\r\n\r\n  location / {\r\n      proxy_pass http://feast-ui-service:8080/;\r\n  }\r\n}\r\n```\r\n\r\nThis configuration works on localhost when nginx can listen on the root path `/`. However, note that the URL after all the redirects is wrong (it does not have the prefix).\r\n\r\n- The first block is required to force a redirect to the `/p/{project_name}`. Without this, the page will display 404 as above.\r\n- The second block is required to strip away `/feast-ui` so the UI app does not receive that path that it is not aware of\r\n- The third block is a trick to make this setup work in a local environment, because the app itself will redirect the user back to `/p/dummy_project` (without the prefix), which we then proxy into the feast UI app. However, in an actual environment, this setup does not work, because when the url does not contain the `/feast-ui` prefix, the ingress will not route it to the nginx pod, so the nginx pod cannot proxy the connection to the right place.\r\n\r\nIdeally, if the feast ui app is capable of being served on a subpath, only the second `location` block should be required in the nginx configuration. The first and third `location` blocks are workarounds.\r\n\r\n### Specifications\r\n\r\n- Version: 0.29.0\r\n\r\n## Possible Solution\r\n\r\nThe app should redirect to relative and not absolute paths\n", "before_files": [{"content": "import json\nimport threading\nfrom typing import Callable, Optional\n\nimport pkg_resources\nimport uvicorn\nfrom fastapi import FastAPI, Response\nfrom fastapi.middleware.cors import CORSMiddleware\nfrom fastapi.staticfiles import StaticFiles\n\nimport feast\n\n\ndef get_app(\n    store: \"feast.FeatureStore\",\n    get_registry_dump: Callable,\n    project_id: str,\n    registry_ttl_secs: int,\n    host: str,\n    port: int,\n):\n    app = FastAPI()\n\n    app.add_middleware(\n        CORSMiddleware,\n        allow_origins=[\"*\"],\n        allow_credentials=True,\n        allow_methods=[\"*\"],\n        allow_headers=[\"*\"],\n    )\n\n    # Asynchronously refresh registry, notifying shutdown and canceling the active timer if the app is shutting down\n    registry_proto = None\n    shutting_down = False\n    active_timer: Optional[threading.Timer] = None\n\n    def async_refresh():\n        store.refresh_registry()\n        nonlocal registry_proto\n        registry_proto = store.registry.proto()\n        if shutting_down:\n            return\n        nonlocal active_timer\n        active_timer = threading.Timer(registry_ttl_secs, async_refresh)\n        active_timer.start()\n\n    @app.on_event(\"shutdown\")\n    def shutdown_event():\n        nonlocal shutting_down\n        shutting_down = True\n        if active_timer:\n            active_timer.cancel()\n\n    async_refresh()\n\n    ui_dir = pkg_resources.resource_filename(__name__, \"ui/build/\")\n    # Initialize with the projects-list.json file\n    with open(ui_dir + \"projects-list.json\", mode=\"w\") as f:\n        projects_dict = {\n            \"projects\": [\n                {\n                    \"name\": \"Project\",\n                    \"description\": \"Test project\",\n                    \"id\": project_id,\n                    \"registryPath\": \"/registry\",\n                }\n            ]\n        }\n        f.write(json.dumps(projects_dict))\n\n    @app.get(\"/registry\")\n    def read_registry():\n        return Response(\n            content=registry_proto.SerializeToString(),\n            media_type=\"application/octet-stream\",\n        )\n\n    # For all other paths (such as paths that would otherwise be handled by react router), pass to React\n    @app.api_route(\"/p/{path_name:path}\", methods=[\"GET\"])\n    def catch_all():\n        filename = ui_dir + \"index.html\"\n\n        with open(filename) as f:\n            content = f.read()\n\n        return Response(content, media_type=\"text/html\")\n\n    app.mount(\n        \"/\",\n        StaticFiles(directory=ui_dir, html=True),\n        name=\"site\",\n    )\n\n    return app\n\n\ndef start_server(\n    store: \"feast.FeatureStore\",\n    host: str,\n    port: int,\n    get_registry_dump: Callable,\n    project_id: str,\n    registry_ttl_sec: int,\n    root_path: str = \"\",\n):\n    app = get_app(\n        store,\n        get_registry_dump,\n        project_id,\n        registry_ttl_sec,\n        host,\n        port,\n    )\n    assert root_path is not None\n    uvicorn.run(app, host=host, port=port, root_path=root_path)\n", "path": "sdk/python/feast/ui_server.py"}], "after_files": [{"content": "import json\nimport threading\nfrom typing import Callable, Optional\n\nimport pkg_resources\nimport uvicorn\nfrom fastapi import FastAPI, Response\nfrom fastapi.middleware.cors import CORSMiddleware\nfrom fastapi.staticfiles import StaticFiles\n\nimport feast\n\n\ndef get_app(\n    store: \"feast.FeatureStore\",\n    project_id: str,\n    registry_ttl_secs: int,\n    root_path: str = \"\",\n):\n    app = FastAPI()\n\n    app.add_middleware(\n        CORSMiddleware,\n        allow_origins=[\"*\"],\n        allow_credentials=True,\n        allow_methods=[\"*\"],\n        allow_headers=[\"*\"],\n    )\n\n    # Asynchronously refresh registry, notifying shutdown and canceling the active timer if the app is shutting down\n    registry_proto = None\n    shutting_down = False\n    active_timer: Optional[threading.Timer] = None\n\n    def async_refresh():\n        store.refresh_registry()\n        nonlocal registry_proto\n        registry_proto = store.registry.proto()\n        if shutting_down:\n            return\n        nonlocal active_timer\n        active_timer = threading.Timer(registry_ttl_secs, async_refresh)\n        active_timer.start()\n\n    @app.on_event(\"shutdown\")\n    def shutdown_event():\n        nonlocal shutting_down\n        shutting_down = True\n        if active_timer:\n            active_timer.cancel()\n\n    async_refresh()\n\n    ui_dir = pkg_resources.resource_filename(__name__, \"ui/build/\")\n    # Initialize with the projects-list.json file\n    with open(ui_dir + \"projects-list.json\", mode=\"w\") as f:\n        projects_dict = {\n            \"projects\": [\n                {\n                    \"name\": \"Project\",\n                    \"description\": \"Test project\",\n                    \"id\": project_id,\n                    \"registryPath\": f\"{root_path}/registry\",\n                }\n            ]\n        }\n        f.write(json.dumps(projects_dict))\n\n    @app.get(\"/registry\")\n    def read_registry():\n        return Response(\n            content=registry_proto.SerializeToString(),\n            media_type=\"application/octet-stream\",\n        )\n\n    # For all other paths (such as paths that would otherwise be handled by react router), pass to React\n    @app.api_route(\"/p/{path_name:path}\", methods=[\"GET\"])\n    def catch_all():\n        filename = ui_dir + \"index.html\"\n\n        with open(filename) as f:\n            content = f.read()\n\n        return Response(content, media_type=\"text/html\")\n\n    app.mount(\n        \"/\",\n        StaticFiles(directory=ui_dir, html=True),\n        name=\"site\",\n    )\n\n    return app\n\n\ndef start_server(\n    store: \"feast.FeatureStore\",\n    host: str,\n    port: int,\n    get_registry_dump: Callable,\n    project_id: str,\n    registry_ttl_sec: int,\n    root_path: str = \"\",\n):\n    app = get_app(\n        store,\n        project_id,\n        registry_ttl_sec,\n        root_path,\n    )\n    uvicorn.run(app, host=host, port=port)\n", "path": "sdk/python/feast/ui_server.py"}]}