URL restructure, 404 page, trailing slash normalization, layout fixes
- Rename /reactive-islands/ → /reactive/, /reference/ → /hypermedia/reference/, /examples/ → /hypermedia/examples/ across all .sx and .py files - Add 404 error page (not-found.sx) working on both server refresh and client-side SX navigation via orchestration.sx error response handling - Add trailing slash redirect (GET only, excludes /api/, /static/, /internal/) - Remove blue sky-500 header bar from SX docs layout (conditional on header-rows) - Fix 405 on API endpoints from trailing slash redirect hitting POST/PUT/DELETE - Fix client-side 404: orchestration.sx now swaps error response content instead of silently dropping it - Add new plan files and home page component Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
This commit is contained in:
56
sx/app.py
56
sx/app.py
@@ -95,6 +95,8 @@ def create_app() -> "Quart":
|
||||
),
|
||||
}
|
||||
|
||||
app.url_map.strict_slashes = False
|
||||
|
||||
from sxc.pages import setup_sx_pages
|
||||
setup_sx_pages()
|
||||
|
||||
@@ -104,6 +106,60 @@ def create_app() -> "Quart":
|
||||
from shared.sx.pages import auto_mount_pages
|
||||
auto_mount_pages(app, "sx")
|
||||
|
||||
@app.before_request
|
||||
async def trailing_slash_redirect():
|
||||
from quart import request, redirect
|
||||
path = request.path
|
||||
if (path != "/"
|
||||
and not path.endswith("/")
|
||||
and request.method == "GET"
|
||||
and not path.startswith(("/static/", "/internal/", "/auth/"))
|
||||
and "/api/" not in path
|
||||
and "." not in path.rsplit("/", 1)[-1]):
|
||||
qs = request.query_string.decode()
|
||||
target = path + "/" + ("?" + qs if qs else "")
|
||||
return redirect(target, 301)
|
||||
|
||||
@app.errorhandler(404)
|
||||
async def sx_not_found(e):
|
||||
from quart import request, make_response
|
||||
from shared.browser.app.utils.htmx import is_htmx_request
|
||||
from shared.sx.jinja_bridge import get_component_env, _get_request_context
|
||||
from shared.sx.async_eval import async_eval_slot_to_sx
|
||||
from shared.sx.types import Symbol, Keyword
|
||||
from shared.sx.helpers import full_page_sx, oob_page_sx, sx_response
|
||||
from shared.sx.pages import get_page_helpers
|
||||
from shared.sx.page import get_template_context
|
||||
|
||||
path = request.path
|
||||
content_ast = [
|
||||
Symbol("~sx-doc"), Keyword("path"), path,
|
||||
[Symbol("~not-found-content"), Keyword("path"), path],
|
||||
]
|
||||
|
||||
env = dict(get_component_env())
|
||||
env.update(get_page_helpers("sx"))
|
||||
ctx = _get_request_context()
|
||||
|
||||
try:
|
||||
content_sx = await async_eval_slot_to_sx(content_ast, env, ctx)
|
||||
except Exception:
|
||||
from shared.browser.app.errors import _sx_error_page
|
||||
html = _sx_error_page("404", "NOT FOUND",
|
||||
image="/static/errors/404.gif")
|
||||
return await make_response(html, 404)
|
||||
|
||||
if is_htmx_request():
|
||||
return sx_response(
|
||||
await oob_page_sx(content=content_sx),
|
||||
status=404,
|
||||
)
|
||||
else:
|
||||
tctx = await get_template_context()
|
||||
html = await full_page_sx(tctx, header_rows="",
|
||||
content=content_sx)
|
||||
return await make_response(html, 404)
|
||||
|
||||
return app
|
||||
|
||||
|
||||
|
||||
Reference in New Issue
Block a user