From ef48a7138f1f71d44fcc093bbc3709bbacb7becc Mon Sep 17 00:00:00 2001 From: Iisakki Rotko Date: Wed, 20 Mar 2024 09:32:23 +0100 Subject: [PATCH] feat: add support for redirects --- solara/server/server.py | 1 + solara/server/starlette.py | 5 +- solara/website/pages/__init__.py | 195 +++++++++++++++++++++++++++++++ 3 files changed, 200 insertions(+), 1 deletion(-) diff --git a/solara/server/server.py b/solara/server/server.py index e5706178b..6fd61b916 100644 --- a/solara/server/server.py +++ b/solara/server/server.py @@ -35,6 +35,7 @@ ipywidgets_major = int(ipywidgets.__version__.split(".")[0]) cache_memory = solara.cache.Memory(max_items=128) vue3 = ipyvue.__version__.startswith("3") +_redirects: Dict[str, str] = {} # first look at the project directory, then the builtin solara directory diff --git a/solara/server/starlette.py b/solara/server/starlette.py index fdd27cd55..a8e7a026d 100644 --- a/solara/server/starlette.py +++ b/solara/server/starlette.py @@ -40,7 +40,7 @@ from starlette.middleware.authentication import AuthenticationMiddleware from starlette.middleware.gzip import GZipMiddleware from starlette.requests import HTTPConnection, Request -from starlette.responses import HTMLResponse, JSONResponse, Response +from starlette.responses import HTMLResponse, JSONResponse, RedirectResponse, Response from starlette.routing import Mount, Route, WebSocketRoute from starlette.staticfiles import StaticFiles from starlette.types import Receive, Scope, Send @@ -315,6 +315,9 @@ async def root(request: Request, fullpath: str = ""): request_path = request.url.path if request_path.startswith(root_path): request_path = request_path[len(root_path) :] + if request_path in server._redirects.keys(): + return RedirectResponse(server._redirects[request_path]) + content = server.read_root(request_path, root_path) if content is None: if settings.oauth.private and not request.user.is_authenticated: diff --git a/solara/website/pages/__init__.py b/solara/website/pages/__init__.py index 5a4129f3d..678ce1a9b 100644 --- a/solara/website/pages/__init__.py +++ b/solara/website/pages/__init__.py @@ -3,6 +3,7 @@ import solara from solara.alias import rv from solara.components.title import Title +from solara.server import server from ..components import Header, Hero from ..components.mailchimp import MailChimp @@ -12,6 +13,200 @@ route_order = ["/", "showcase", "documentation", "apps", "contact", "changelog"] +server._redirects = { + "/docs": "/documentation/getting_started/introduction", + "/docs/installing": "/documentation/getting_started/installing", + "/docs/quickstart": "/documentation/getting_started", + "/docs/tutorial": "/documentation/getting_started/tutorials", + "/docs/tutorial/data-science": "/documentation/getting_started/tutorials/data-science", + "/docs/tutorial/web-app": "/documentation/getting_started/tutorials/web-app", + "/docs/tutorial/ipywidgets": "/documentation/getting_started/tutorials/ipywidgets", + "/docs/tutorial/streamlit": "/documentation/getting_started/tutorials/streamlit", + "/docs/tutorial/dash": "/documentation/getting_started/tutorials/dash", + "/docs/tutorial/jupyter-dashboard-part1": "/documentation/getting_started/tutorials/jupyter-dashboard-part1", + "/docs/fundamentals": "/documentation/getting_started/fundamentals", + "/docs/fundamentals/components": "/documentation/getting_started/fundamentals/components", + "/docs/fundamentals/state-management": "/documentation/getting_started/fundamentals/state-management", + "/docs/howto": "/documentation/advanced/howto", + "/docs/howto/contribute": "/documentation/advanced/development/contribute", + "/docs/howto/multipage": "/documentation/advanced/howto/multipage", + "/docs/howto/layout": "/documentation/advanced/howto/layout", + "/docs/howto/testing": "/documentation/advanced/howto/testing", + "/docs/howto/debugging": "/documentation/advanced/howto/debugging", + "/docs/howto/embed": "/documentation/advanced/howto/embed", + "/docs/howto/ipywidget-libraries": "/documentation/advanced/howto/ipywidget-libraries", + "/docs/reference": "/documentation/getting_started/reference", + "/docs/reference/static-files": "/documentation/getting_started/reference/static-files", + "/docs/reference/asset-files": "/documentation/getting_started/reference/asset-files", + "/docs/reference/static-site-generation": "/documentation/getting_started/reference/static-site-generation", + "/docs/reference/search": "/documentation/getting_started/reference/search", + "/docs/reference/reloading": "/documentation/getting_started/reference/reloading", + "/docs/reference/notebook-support": "/documentation/getting_started/reference/notebook-support", + "/docs/reference/caching": "/documentation/getting_started/reference/caching", + "/docs/understanding": "/documentation/advanced/understanding", + "/docs/understanding/ipywidgets": "/documentation/advanced/understanding/ipywidgets", + "/docs/understanding/ipyvuetify": "/documentation/advanced/understanding/ipyvuetify", + "/docs/understanding/reacton": "/documentation/advanced/understanding/reacton", + "/docs/understanding/reacton-basics": "/documentation/advanced/understanding/reacton-basics", + "/docs/understanding/anatomy": "/documentation/advanced/understanding/anatomy", + "/docs/understanding/rules-of-hooks": "/documentation/advanced/understanding/rules-of-hooks", + "/docs/understanding/containers": "/documentation/advanced/understanding/containers", + "/docs/understanding/solara": "/documentation/advanced/understanding/solara", + "/docs/understanding/routing": "/documentation/advanced/understanding/routing", + "/docs/understanding/solara-server": "/documentation/advanced/understanding/solara-server", + "/docs/understanding/voila": "/documentation/advanced/understanding/voila", + "/docs/deploying": "/documentation/getting_started/deploying", + "/docs/deploying/self-hosted": "/documentation/getting_started/deploying/self-hosted", + "/docs/deploying/cloud-hosted": "/documentation/getting_started/deploying/cloud-hosted", + "/docs/enterprise": "/documentation/advanced/enterprise", + "/docs/enterprise/oauth": "/documentation/advanced/enterprise/oauth", + "/docs/development": "/documentation/advanced/development/setup", + "/docs/troubleshoot": "/documentation/getting_started/troubleshoot", + "/docs/changelog": "/changelog", + "/docs/contact": "/contact", + "/docs/faq": "/documentation/faq", + "/docs/lab": "/documentation/getting_started/lab", + "/api": "/documentation/api", + "/api/altair": "/documentation/components/viz/altair", + "/api/app_bar": "/documentation/components/layout/app_bar", + "/api/app_bar_title": "/documentation/components/layout/app_bar_title", + "/api/app_layout": "/documentation/components/layout/app_layout", + "/api/avatar": "/documentation/components/enterprise/avatar", + "/api/avatar_menu": "/documentation/components/enterprise/avatar_menu", + "/api/card": "/documentation/components/layout/card", + "/api/card_actions": "/documentation/components/layout/card_actions", + "/api/column": "/documentation/components/layout/column", + "/api/columns": "/documentation/components/layout/columns", + "/api/columns_responsive": "/documentation/components/layout/columns_responsive", + "/api/cross_filter_dataframe": "/documentation/api/cross_filter/cross_filter_dataframe", + "/api/cross_filter_report": "/documentation/api/cross_filter/cross_filter_report", + "/api/cross_filter_select": "/documentation/api/cross_filter/cross_filter_select", + "/api/cross_filter_slider": "/documentation/api/cross_filter/cross_filter_slider", + "/api/generate_routes": "/documentation/api/routing/generate_routes", + "/api/generate_routes_directory": "/documentation/api/routing/generate_routes_directory", + "/api/resolve_path": "/documentation/api/routing/resolve_path", + "/api/resolve_path/kiwi": "/documentation/api/routing/resolve_path/kiwi", + "/api/resolve_path/banana": "/documentation/api/routing/resolve_path/banana", + "/api/resolve_path/apple": "/documentation/api/routing/resolve_path/apple", + "/api/link": "/documentation/components/advanced/link", + "/api/link/kiwi": "/documentation/components/advanced/link/kiwi", + "/api/link/banana": "/documentation/components/advanced/link/banana", + "/api/link/apple": "/documentation/components/advanced/link/apple", + "/api/use_route": "/documentation/api/routing/use_route", + "/api/use_route/fruit": "/documentation/api/routing/use_route/fruit", + "/api/use_route/fruit/kiwi": "/documentation/api/routing/use_route/fruit/kiwi", + "/api/use_route/fruit/banana": "/documentation/api/routing/use_route/fruit/banana", + "/api/use_route/fruit/apple": "/documentation/api/routing/use_route/fruit/apple", + "/api/use_router": "/documentation/api/routing/use_router", + "/api/use_cross_filter": "/documentation/api/hooks/use_cross_filter", + "/api/use_dark_effective": "/documentation/api/hooks/use_dark_effective", + "/api/use_effect": "/documentation/api/hooks/use_effect", + "/api/use_exception": "/documentation/api/hooks/use_exception", + "/api/use_memo": "/documentation/api/hooks/use_memo", + "/api/use_previous": "/documentation/api/hooks/use_previous", + "/api/use_reactive": "/documentation/api/hooks/use_reactive", + "/api/use_state": "/documentation/api/hooks/use_state", + "/api/use_state_or_update": "/documentation/api/hooks/use_state_or_update", + "/api/use_task": "/documentation/components/lab/use_task", + "/api/use_thread": "/documentation/api/hooks/use_thread", + "/api/use_trait_observe": "/documentation/api/hooks/use_trait_observe", + "/examples/fullscreen": "/documentation/examples/fullscreen", + "/examples/fullscreen/authorization": "/documentation/examples/fullscreen/authorization", + "/examples/fullscreen/layout_demo": "/documentation/examples/fullscreen/layout_demo", + "/examples/fullscreen/multipage": "/documentation/examples/fullscreen/multipage", + "/examples/fullscreen/scatter": "/documentation/examples/fullscreen/scatter", + "/examples/fullscreen/scrolling": "/documentation/examples/fullscreen/scrolling", + "/examples/fullscreen/tutorial_streamlit": "/documentation/examples/fullscreen/tutorial_streamlit", + "/api/route": "/documentation/api/routing/route", + "/api/route/kiwi": "/documentation/api/routing/route/kiwi", + "/api/route/banana": "/documentation/api/routing/route/banana", + "/api/route/apple": "/documentation/api/routing/route/apple", + "/examples/libraries": "/documentation/examples/libraries", + "/examples/libraries/altair": "/documentation/examples/libraries/altair", + "/examples/libraries/bqplot": "/documentation/examples/libraries/bqplot", + "/examples/libraries/ipyleaflet": "/documentation/examples/libraries/ipyleaflet", + "/examples/libraries/ipyleaflet_advanced": "/documentation/examples/libraries/ipyleaflet_advanced", + "/examples/utilities": "/documentation/examples/utilities", + "/examples/utilities/calculator": "/documentation/examples/utilities/calculator", + "/examples/utilities/countdown_timer": "/documentation/examples/utilities/countdown_timer", + "/examples/utilities/todo": "/documentation/examples/utilities/todo", + "/examples/visualization": "/documentation/examples/visualization", + "/examples/visualization/annotator": "/documentation/examples/visualization/annotator", + "/examples/visualization/linked_views": "/documentation/examples/visualization/linked_views", + "/examples/visualization/plotly": "/documentation/examples/visualization/plotly", + "/examples/general": "/documentation/examples/general", + "/examples/general/custom_storage": "/documentation/examples/general/custom_storage", + "/examples/general/deploy_model": "/documentation/examples/general/deploy_model", + "/examples/general/live_update": "/documentation/examples/general/live_update", + "/examples/general/login_oauth": "/documentation/examples/general/login_oauth", + "/examples/general/pokemon_search": "/documentation/examples/general/pokemon_search", + "/examples/general/vue_component": "/documentation/examples/general/vue_component", + "/examples": "/documentation/examples", + "/examples/ai": "/documentation/examples/ai", + "/examples/ai/chatbot": "/documentation/examples/ai/chatbot", + "/examples/ai/tokenizer": "/documentation/examples/ai/tokenizer", + "/examples/basics": "/documentation/examples/basics", + "/examples/basics/sine": "/documentation/examples/basics/sine", + "/api/get_kernel_id": "/documentation/api/utilities/get_kernel_id", + "/api/get_session_id": "/documentation/api/utilities/get_session_id", + "/api/on_kernel_start": "/documentation/api/utilities/on_kernel_start", + "/api/component_vue": "/documentation/api/utilities/component_vue", + "/api/plotly": "/documentation/components/viz/plotly", + "/api/plotly_express": "/documentation/components/viz/plotly_express", + "/api/tab": "/documentation/components/lab/tab", + "/api/tabs": "/documentation/components/lab/tabs", + "/api/task": "/documentation/components/lab/task", + "/api/computed": "/documentation/api/utilities/computed", + "/api/markdown": "/documentation/components/output/markdown", + "/api/markdown_editor": "/documentation/components/output/markdown_editor", + "/api/matplotlib": "/documentation/components/viz/matplotlib", + "/api/echarts": "/documentation/components/viz/echarts", + "/api/theming": "/documentation/components/lab/theming", + "/api/griddraggable": "/documentation/components/layout/griddraggable", + "/api/gridfixed": "/documentation/components/layout/gridfixed", + "/api/html": "/documentation/components/output/html", + "/api/file_download": "/documentation/components/output/file_download", + "/api/hbox": "/documentation/components/layout/hbox", + "/api/vbox": "/documentation/components/layout/vbox", + "/api/sidebar": "/documentation/components/layout/sidebar", + "/api/row": "/documentation/components/layout/row", + "/api/error": "/documentation/components/status/error", + "/api/info": "/documentation/components/status/info", + "/api/progress": "/documentation/components/status/progress", + "/api/spinner": "/documentation/components/status/spinner", + "/api/success": "/documentation/components/status/success", + "/api/warning": "/documentation/components/status/warning", + "/api/image": "/documentation/components/output/image", + "/api/sql_code": "/documentation/components/output/sql_code", + "/api/tooltip": "/documentation/components/output/tooltip", + "/api/chat": "/documentation/components/lab/chat", + "/api/confirmation_dialog": "/documentation/components/lab/confirmation_dialog", + "/api/cookies_headers": "/documentation/components/lab/cookies_headers", + "/api/input_date": "/documentation/components/lab/input_date", + "/api/button": "/documentation/components/input/button", + "/api/checkbox": "/documentation/components/input/checkbox", + "/api/file_browser": "/documentation/components/input/file_browser", + "/api/file_drop": "/documentation/components/input/file_drop", + "/api/meta": "/documentation/components/advanced/meta", + "/api/style": "/documentation/components/advanced/style", + "/api/menu": "/documentation/components/lab/menu", + "/api/head": "/documentation/components/page/head", + "/api/input": "/documentation/components/input/input", + "/api/select": "/documentation/components/input/select", + "/api/slider": "/documentation/components/input/slider", + "/api/switch": "/documentation/components/input/switch", + "/api/togglebuttons": "/documentation/components/input/togglebuttons", + "/api/dataframe": "/documentation/components/data/dataframe", + "/api/pivot_table": "/documentation/components/data/pivot_table", + "/api/display": "/documentation/api/utilities/display", + "/api/memoize": "/documentation/api/utilities/memoize", + "/api/reactive": "/documentation/api/utilities/reactive", + "/api/widget": "/documentation/api/utilities/widget", + "/api/default_layout": "/documentation/components/layout", + "/api/title": "/documentation/components/page/title", +} + + @solara.component def Page(): solara.Markdown("should not see me")