Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Frontend #2500

Merged
merged 1 commit into from
Oct 8, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -193,3 +193,4 @@ test/sleep.json
/.superduperdb
/output/
/deploy/testenv/requirements.txt
/superduper/rest/superdupertmp
1 change: 1 addition & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -39,6 +39,7 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
- Updated CONTRIBUTING.md
- Add README.md files for the plugins.
- Add templates to project
- Add frontend to project

#### New Features & Functionality

Expand Down
3 changes: 2 additions & 1 deletion MANIFEST.in
Original file line number Diff line number Diff line change
@@ -1 +1,2 @@
recursive-include templates *.zip
recursive-include templates *
recursive-include superduper/rest/out *
3 changes: 2 additions & 1 deletion pyproject.toml
Original file line number Diff line number Diff line change
Expand Up @@ -46,9 +46,10 @@ dependencies = [
"numpy>=1.24.3",
"overrides>=7",
"tenacity>=8.1.0,<=8.2.3",
"PyYAML>=6.0.0",
"packaging",
"prettytable",
"python-dotenv",
"PyYAML>=6.0.0",
"uvicorn>=0.24.0",
"fastapi>=0.103.2",
"ruamel.yaml>=0.18",
Expand Down
21 changes: 19 additions & 2 deletions superduper/cli/main.py
Original file line number Diff line number Diff line change
Expand Up @@ -19,8 +19,25 @@ def apply(name: str, variables: str | None = None):

@command(help='Start rest server and user interface')
def start(port: int = 8000, host: str = 'localhost'):
"""Start the rest server and user interface."""
...
"""Start the rest server and user interface.

:param port: Port to run the server on.
:param host: Host to run the server on.
"""
from superduper.rest.base import SuperDuperApp
from superduper.rest.build import build_frontend, build_rest_app

app = SuperDuperApp('rest', port=port)

if host == 'localhost':
# host frontend and server together
build_rest_app(app)
app.add_default_endpoints()
else:
logging.warn('Frontend pointing to remote server!')

build_frontend(app, port=port, host=host)
app.start()


@command(help='Apply a template or application to a `superduper` deployment')
Expand Down
8 changes: 6 additions & 2 deletions superduper/rest/base.py
Original file line number Diff line number Diff line change
Expand Up @@ -138,8 +138,9 @@ def add_default_endpoints(self):
- /health: Health check endpoint
- /handshake/config: Handshake endpoint
"""
logging.info(f"Adding default endpoints to '{self.service}' app")

@self.router.get('/health')
@self.add('/health', method='get')
def health():
return {'status': 200}

Expand All @@ -152,7 +153,10 @@ def print_routes(self):

# Add rows to the table
for route in self._app.routes:
table.add_row([route.path, ", ".join(route.methods), route.name])
try:
table.add_row([route.path, ", ".join(route.methods), route.name])
except AttributeError:
logging.warn(f"Route {route} has no name")

logging.info(f"Routes for '{self.service}' app: \n{table}")

Expand Down
76 changes: 76 additions & 0 deletions superduper/rest/build.py
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
import hashlib
import shutil
import typing as t

import magic
Expand Down Expand Up @@ -132,3 +133,78 @@ def db_execute(
if 'score' in result[0][0]:
result = sorted(result, key=lambda x: -x[0]['score'])
return result


def build_frontend(app: SuperDuperApp, host: str = 'localhost', port: int = 8000):
"""Add the frontend to the FastAPI app.

:param app: app instance SuperDuperApp
:param host: host address
:param port: port number
"""
import os

from fastapi import HTTPException, Request
from fastapi.responses import FileResponse
from fastapi.staticfiles import StaticFiles

ROOT = os.path.dirname(os.path.abspath(__file__))

try:
shutil.rmtree(f"{ROOT}/superdupertmp")
except FileNotFoundError:
pass

shutil.copytree(f"{ROOT}/out", f"{ROOT}/superdupertmp")

DIRECTORY = f"{ROOT}/superdupertmp"

if host != 'localhost' or port != 8000:
for root, _, files in os.walk(DIRECTORY):
for file in files:
if file.endswith('.js'):
with open(os.path.join(root, file), "r") as f:
content = f.read()
content = content.replace("localhost:8000", f"{ host }:{ port }")
with open(os.path.join(root, file), "w") as f:
f.write(content)

app.app.mount("/static", StaticFiles(directory=DIRECTORY), name="static")

@app.app.get("/{path:path}")
async def serve_file(request: Request, path: str):
"""Serve files from the default 'out' directory.

:param request: Request
:param path: path to file
"""
# Special case: if path is 'webui', serve the 'index.html'
# from the 'out' directory
if path == "webui":
webui_index = os.path.join(DIRECTORY, "index.html")
if os.path.exists(webui_index):
return FileResponse(webui_index)
else:
raise HTTPException(
status_code=404, detail="index.html not found for /webui"
)

# Normal case: serve files from the 'out' directory
requested_path = os.path.join(DIRECTORY, path.lstrip("/"))

# If the path is a directory, attempt to serve index.html
if os.path.isdir(requested_path):
index_file = os.path.join(requested_path, "index.html")
if os.path.exists(index_file):
return FileResponse(index_file)

if os.path.exists(requested_path):
return FileResponse(requested_path)

# Try appending .html to the requested path
path_with_html = f"{requested_path}.html"
if os.path.exists(path_with_html):
return FileResponse(path_with_html)

# If file not found, raise a 404 error
raise HTTPException(status_code=404, detail="File not found")
1 change: 1 addition & 0 deletions superduper/rest/out/404.html
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
<!DOCTYPE html><html lang="en"><head><meta charSet="utf-8"/><meta name="viewport" content="width=device-width, initial-scale=1"/><link rel="stylesheet" href="/_next/static/css/3e415ce24b168a6e.css" data-precedence="next"/><link rel="preload" as="script" fetchPriority="low" href="/_next/static/chunks/webpack-22c91002c2a64cc5.js"/><script src="/_next/static/chunks/fd9d1056-4ed9fe495d6d5770.js" async=""></script><script src="/_next/static/chunks/23-826c8670610b69f2.js" async=""></script><script src="/_next/static/chunks/main-app-bd936c06284ec9cf.js" async=""></script><script src="/_next/static/chunks/231-d9cf399ecf8e45f9.js" async=""></script><script src="/_next/static/chunks/app/not-found-d03cfc12dc19fbbb.js" async=""></script><script src="/_next/static/chunks/726-245b5a45db2a1347.js" async=""></script><script src="/_next/static/chunks/app/layout-410275617432b0ca.js" async=""></script><link rel="icon" href="/webui_asset/favicon.ico"/><title>Superduper Enterprise</title><meta name="description" content="Bring AI to your database"/><meta property="og:image" content="http://webui.playground.superduper.io/webui_asset/background-5.png"/><title>Superduper Enterprise</title><meta name="description" content="Bring AI to your database"/><meta property="og:title" content="Superduper Enterprise"/><meta property="og:description" content="Bring AI to your database"/><meta property="og:image" content="http://webui.playground.superduper.io/webui_asset/background-5.png"/><meta name="twitter:card" content="summary_large_image"/><meta name="twitter:title" content="Superduper Enterprise"/><meta name="twitter:description" content="Bring AI to your database"/><meta name="twitter:image" content="http://webui.playground.superduper.io/webui_asset/background-5.png"/><link rel="icon" href="/favicon.ico" type="image/x-icon" sizes="48x48"/><link rel="icon" href="/icon.ico?1bfbb5e575db8bf0" type="image/x-icon" sizes="48x48"/><script src="/_next/static/chunks/polyfills-78c92fac7aa8fdd8.js" noModule=""></script></head><body style="font-family:Inter, sans-serif"><div class="flex flex-col items-center justify-center min-h-[400px] space-y-4"><div class="text-center space-y-2"><h1 class="text-4xl font-extrabold tracking-tighter sm:text-6xl">404</h1><p class="text-gray-500 dark:text-gray-400">Page Not Found</p></div><a class="inline-flex h-10 items-center rounded-md border border-gray-200 bg-white px-8 text-sm font-medium shadow-sm transition-colors hover:bg-gray-100 hover:text-gray-900 focus-visible:outline-none focus-visible:ring-1 focus-visible:ring-gray-950 dark:border-gray-800 dark:bg-gray-950 dark:hover:bg-gray-800 dark:hover:text-gray-50 dark:focus-visible:ring-gray-300" href="/webui">Go back home</a></div><style>#nprogress{pointer-events:none}#nprogress .bar{background:#0BDA51;position:fixed;z-index:1600;top: 0;left:0;width:100%;height:3px}#nprogress .peg{display:block;position:absolute;right:0;width:100px;height:100%;box-shadow:0 0 10px #0BDA51,0 0 5px #0BDA51;opacity:1;-webkit-transform:rotate(3deg) translate(0px,-4px);-ms-transform:rotate(3deg) translate(0px,-4px);transform:rotate(3deg) translate(0px,-4px)}#nprogress .spinner{display:block;position:fixed;z-index:1600;top: 15px;right:15px}#nprogress .spinner-icon{width:18px;height:18px;box-sizing:border-box;border:2px solid transparent;border-top-color:#0BDA51;border-left-color:#0BDA51;border-radius:50%;-webkit-animation:nprogress-spinner 400ms linear infinite;animation:nprogress-spinner 400ms linear infinite}.nprogress-custom-parent{overflow:hidden;position:relative}.nprogress-custom-parent #nprogress .bar,.nprogress-custom-parent #nprogress .spinner{position:absolute}@-webkit-keyframes nprogress-spinner{0%{-webkit-transform:rotate(0deg)}100%{-webkit-transform:rotate(360deg)}}@keyframes nprogress-spinner{0%{transform:rotate(0deg)}100%{transform:rotate(360deg)}}</style><div style="position:fixed;z-index:9999;top:16px;left:16px;right:16px;bottom:16px;pointer-events:none"></div><script src="/_next/static/chunks/webpack-22c91002c2a64cc5.js" async=""></script><script>(self.__next_f=self.__next_f||[]).push([0]);self.__next_f.push([2,null])</script><script>self.__next_f.push([1,"1:HL[\"/_next/static/css/3e415ce24b168a6e.css\",\"style\"]\n"])</script><script>self.__next_f.push([1,"2:I[5751,[],\"\"]\n4:I[231,[\"231\",\"static/chunks/231-d9cf399ecf8e45f9.js\",\"160\",\"static/chunks/app/not-found-d03cfc12dc19fbbb.js\"],\"\"]\n5:I[9275,[],\"\"]\n6:I[1343,[],\"\"]\n7:I[9593,[\"726\",\"static/chunks/726-245b5a45db2a1347.js\",\"185\",\"static/chunks/app/layout-410275617432b0ca.js\"],\"\"]\n8:I[8726,[\"726\",\"static/chunks/726-245b5a45db2a1347.js\",\"185\",\"static/chunks/app/layout-410275617432b0ca.js\"],\"Toaster\"]\na:I[6130,[],\"\"]\nb:[]\n"])</script><script>self.__next_f.push([1,"0:[[[\"$\",\"link\",\"0\",{\"rel\":\"stylesheet\",\"href\":\"/_next/static/css/3e415ce24b168a6e.css\",\"precedence\":\"next\",\"crossOrigin\":\"$undefined\"}]],[\"$\",\"$L2\",null,{\"buildId\":\"u7kQQ-ZckL3eMsoMMqmR4\",\"assetPrefix\":\"\",\"initialCanonicalUrl\":\"/_not-found\",\"initialTree\":[\"\",{\"children\":[\"/_not-found\",{\"children\":[\"__PAGE__\",{}]}]},\"$undefined\",\"$undefined\",true],\"initialSeedData\":[\"\",{\"children\":[\"/_not-found\",{\"children\":[\"__PAGE__\",{},[[\"$L3\",[\"$\",\"div\",null,{\"className\":\"flex flex-col items-center justify-center min-h-[400px] space-y-4\",\"children\":[[\"$\",\"div\",null,{\"className\":\"text-center space-y-2\",\"children\":[[\"$\",\"h1\",null,{\"className\":\"text-4xl font-extrabold tracking-tighter sm:text-6xl\",\"children\":\"404\"}],[\"$\",\"p\",null,{\"className\":\"text-gray-500 dark:text-gray-400\",\"children\":\"Page Not Found\"}]]}],[\"$\",\"$L4\",null,{\"className\":\"inline-flex h-10 items-center rounded-md border border-gray-200 bg-white px-8 text-sm font-medium shadow-sm transition-colors hover:bg-gray-100 hover:text-gray-900 focus-visible:outline-none focus-visible:ring-1 focus-visible:ring-gray-950 dark:border-gray-800 dark:bg-gray-950 dark:hover:bg-gray-800 dark:hover:text-gray-50 dark:focus-visible:ring-gray-300\",\"href\":\"/webui\",\"children\":\"Go back home\"}]]}]],null],null]},[\"$\",\"$L5\",null,{\"parallelRouterKey\":\"children\",\"segmentPath\":[\"children\",\"/_not-found\",\"children\"],\"error\":\"$undefined\",\"errorStyles\":\"$undefined\",\"errorScripts\":\"$undefined\",\"template\":[\"$\",\"$L6\",null,{}],\"templateStyles\":\"$undefined\",\"templateScripts\":\"$undefined\",\"notFound\":\"$undefined\",\"notFoundStyles\":\"$undefined\",\"styles\":null}],null]},[[\"$\",\"html\",null,{\"lang\":\"en\",\"children\":[[\"$\",\"head\",null,{\"children\":[[\"$\",\"link\",null,{\"rel\":\"icon\",\"href\":\"/webui_asset/favicon.ico\"}],[\"$\",\"title\",null,{\"children\":\"Superduper Enterprise\"}],[\"$\",\"meta\",null,{\"name\":\"description\",\"content\":\"Bring AI to your database\"}],[\"$\",\"meta\",null,{\"property\":\"og:image\",\"content\":\"http://webui.playground.superduper.io/webui_asset/background-5.png\"}]]}],[\"$\",\"body\",null,{\"style\":{\"fontFamily\":\"Inter, sans-serif\"},\"children\":[[\"$\",\"$L5\",null,{\"parallelRouterKey\":\"children\",\"segmentPath\":[\"children\"],\"error\":\"$undefined\",\"errorStyles\":\"$undefined\",\"errorScripts\":\"$undefined\",\"template\":[\"$\",\"$L6\",null,{}],\"templateStyles\":\"$undefined\",\"templateScripts\":\"$undefined\",\"notFound\":[\"$\",\"div\",null,{\"className\":\"flex flex-col items-center justify-center min-h-[400px] space-y-4\",\"children\":[[\"$\",\"div\",null,{\"className\":\"text-center space-y-2\",\"children\":[[\"$\",\"h1\",null,{\"className\":\"text-4xl font-extrabold tracking-tighter sm:text-6xl\",\"children\":\"404\"}],[\"$\",\"p\",null,{\"className\":\"text-gray-500 dark:text-gray-400\",\"children\":\"Page Not Found\"}]]}],[\"$\",\"$L4\",null,{\"className\":\"inline-flex h-10 items-center rounded-md border border-gray-200 bg-white px-8 text-sm font-medium shadow-sm transition-colors hover:bg-gray-100 hover:text-gray-900 focus-visible:outline-none focus-visible:ring-1 focus-visible:ring-gray-950 dark:border-gray-800 dark:bg-gray-950 dark:hover:bg-gray-800 dark:hover:text-gray-50 dark:focus-visible:ring-gray-300\",\"href\":\"/webui\",\"children\":\"Go back home\"}]]}],\"notFoundStyles\":[],\"styles\":null}],[\"$\",\"$L7\",null,{\"color\":\"#0BDA51\",\"initialPosition\":0.08,\"crawlSpeed\":200,\"height\":3,\"crawl\":true,\"showSpinner\":true,\"easing\":\"ease\",\"speed\":200,\"shadow\":\"0 0 10px #0BDA51,0 0 5px #0BDA51\",\"template\":\"\u003cdiv class=\\\"bar\\\" role=\\\"bar\\\"\u003e\u003cdiv class=\\\"peg\\\"\u003e\u003c/div\u003e\u003c/div\u003e \u003cdiv class=\\\"spinner\\\" role=\\\"spinner\\\"\u003e\u003cdiv class=\\\"spinner-icon\\\"\u003e\u003c/div\u003e\u003c/div\u003e\",\"zIndex\":1600,\"showAtBottom\":false}],[\"$\",\"$L8\",null,{}]]}]]}],null],null],\"couldBeIntercepted\":false,\"initialHead\":[false,\"$L9\"],\"globalErrorComponent\":\"$a\",\"missingSlots\":\"$Wb\"}]]\n"])</script><script>self.__next_f.push([1,"9:[[\"$\",\"meta\",\"0\",{\"name\":\"viewport\",\"content\":\"width=device-width, initial-scale=1\"}],[\"$\",\"meta\",\"1\",{\"charSet\":\"utf-8\"}],[\"$\",\"title\",\"2\",{\"children\":\"Superduper Enterprise\"}],[\"$\",\"meta\",\"3\",{\"name\":\"description\",\"content\":\"Bring AI to your database\"}],[\"$\",\"meta\",\"4\",{\"property\":\"og:title\",\"content\":\"Superduper Enterprise\"}],[\"$\",\"meta\",\"5\",{\"property\":\"og:description\",\"content\":\"Bring AI to your database\"}],[\"$\",\"meta\",\"6\",{\"property\":\"og:image\",\"content\":\"http://webui.playground.superduper.io/webui_asset/background-5.png\"}],[\"$\",\"meta\",\"7\",{\"name\":\"twitter:card\",\"content\":\"summary_large_image\"}],[\"$\",\"meta\",\"8\",{\"name\":\"twitter:title\",\"content\":\"Superduper Enterprise\"}],[\"$\",\"meta\",\"9\",{\"name\":\"twitter:description\",\"content\":\"Bring AI to your database\"}],[\"$\",\"meta\",\"10\",{\"name\":\"twitter:image\",\"content\":\"http://webui.playground.superduper.io/webui_asset/background-5.png\"}],[\"$\",\"link\",\"11\",{\"rel\":\"icon\",\"href\":\"/favicon.ico\",\"type\":\"image/x-icon\",\"sizes\":\"48x48\"}],[\"$\",\"link\",\"12\",{\"rel\":\"icon\",\"href\":\"/icon.ico?1bfbb5e575db8bf0\",\"type\":\"image/x-icon\",\"sizes\":\"48x48\"}]]\n3:null\n"])</script></body></html>
Loading
Loading