Conversation
Remove the server-rendered UI that nothing reaches in the shipping topology:
15 app Jinja routers, the three shared form/AJAX routers, the login/logout/
homepage shell handlers, CSRF form validation, flash messages, cookie-based
session authentication, the per-app authenticated static mounts, and the
98 SSR templates plus their static assets. The report PDF template and its
logo are carved out and keep rendering off disk.
The six exception handlers now return JSON unconditionally; the auth-provider
handler in particular no longer resolves url_for("login"), which would raise
NoMatchFound once that route is gone.
get_current_user is Bearer-only and raises HTTPUnauthorizedException before
oauth2_scheme can surface a bare Starlette error. The duplicated auth aliases
collapse onto the Api* pair, and RequireBearerForUnsafeMethods is hoisted to
the /api router.
Also removes the legacy SEP.SESSION setting and the MessagesSettings class,
with data migrations on all three tracks clearing their orphaned override
rows and narrowing the setting_class vocabulary.
yyyyyyyan
requested review from
a team,
marcuscruz-percona,
maxbube,
nachodd and
peter-o-addo
as code owners
August 8, 2026 08:33
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Summary
SEP-1687 — the terminal ticket of Wave 3 (SEP-950). Deletes the legacy Jinja server-rendered UI, leaving the backend headless.
React parity was certified in SEP-1684, the SPA has been served by default since SEP-1685, and PMM's nginx forwards only
/api,/sep_app,/files,/stream-logsand/execution-events— so nothing in the shipping topology reaches the deleted surface.Removed
app/sep/apps/*/routes.py), theDeprecatedJinja2Routeshim, and thejinja_routerfield/wiring on the app framework/inventory-api,/stop-task,/periodic)app/sep/main.py:GET /login,POST /login,POST /logout,GET /validate_csrf,IsCsrfValidated,CSRFMiddleware,csrf_exempt) and the flash-message middlewareget_current_useris now Bearer-only/staticmounts,StaticMount, andAuthenticatedStaticFilesstatic/tree — carving outtemplates/report/result_pdf.html.j2andstatic/img/percona-logo.png, both read off disk by the report-PDF rendererto_form()/_to_form()/to_form_field()path and the form-element DSL behind it (app/sep/snippets/forms.py)static/Changed
auth_provider_exception_handlerno longer resolvesurl_for("login"), which would raiseNoMatchFoundonce that route is gone and turn every upstream auth failure into an unhandled 500.Api*pair;IsApiAuthenticated = Depends(get_current_user).RequireBearerForUnsafeMethodsis hoisted to the/apirouter so every mutating route inherits it.IsCsrfValidateddropped fromPOST /api/apps/topology/collect, closing a live defect: a caller presenting both a Bearer token and a stale session cookie previously fell intoawait request.form()on a JSON body and got a 400./static/snippetsmount to the Bearer-authenticatedGET /api/apps/snippets/snippet/download, via a newuseSnippetDownloadhook.Breaking changes
Three, all in
changelog.d/SEP-1687.breaking.md:/static/*is gone. A browser client must sendAuthorization: Bearer; a session cookie alone now yields401where it previously redirected with303.SEP.SESSIONis removed — five keys (SESSION__COOKIE_NAME,__MAX_AGE,__SAMESITE,__SECURE,__PATH) disappear fromGET /api/sep/admin/settings.SESSION_REFRESH(the SPA'srefreshTokencookie) is unaffected.MessagesSettingsis removed with the middleware it configured —MESSAGES_SETTINGSis dropped from thesettingoverride.setting_classvocabulary.Three Alembic data migrations clear the orphaned override rows and narrow the CHECK constraint: one on
sep(SESSION + MESSAGES rows), one each ontasksandinventory(MESSAGES rows). All three were applied, downgraded and re-applied locally;alembic checkreports no drift on any track.Scope taken beyond the plan
MessagesSettingsremoval was not in the plan. Deleting the messages middleware orphaned a registered settings-override class (proxy map, admin settings API,SettingClassEnum). Surfaced mid-implementation; full removal including the enum member was chosen, which is what pulls in thetasksandinventorymigrations.SEP-1373docstring cross-reference inapp/sep/apps/backup_mongo/deps.py, and two redundantresponse_model=kwargs inapp/sep/apps/topology/api_routes.py.app/sep/inventory.pygained an explicitmodel_rebuild()loop.Service/Schemareference each other's children before those classes exist, so Pydantic defers the build; a deleted Jinja route signature was incidentally forcing the rebuild. Resolving it in the defining module fixesSystemFactsService, which cannot resolveSchemafrom its own namespace.HTTPRedirectExceptiondeleted fromapp/core/exceptions.py— its last consumer (LoginRedirectException) went with this change. The plan asked for this check explicitly.Plan deviations
Three places where the approved plan was wrong, verified against the code:
SessionOptionsmust stay. The plan said to delete the class with theSESSIONfield, butSESSION_REFRESH: SessionOptionsis retained by the same criterion. Only the field is removed.legacy_checksums_create_to_formtrap is inverted. It claims the symbol is live viadeps.py:237insidebuild_checksums_payload(thepayload_builder=). Line 237 was in fact insidebuild_checksums_task_payload— aForm()-body Jinja handler with zero consumers — while the livebuild_checksums_payloadnever calls it. Both are deleted; checksums task creation goes throughbuild_checksums_payload, unaffected.app/sep/connectivity.pyneeded more than the message call removed.get_check_connectivity_flag,check_and_warn_connectivityandmaybe_check_connectivityall lost their last consumers with the Jinja routers; stripping only themessages.warning(...)call would have left a "warn" helper that never warns and nothing calls.Tested
alembic checkclean onsep,tasks,inventory; theSESSIONdeletion SQL escapes the LIKE wildcard soSESSION_REFRESH__*rows survive (covered by a comment at the SQL)./apisurface is exactlyartifacts,files,stream-logs,execution-events,health,docs— no/static,/legacy,/login,/logout,/.tests/app/sep/apps/test_import_smoke.pyimports all 15 app packages and asserts none re-exports arouter— guards the__init__.pytrap that would have crashed startup.tests/app/sep/routes/test_shared_route_auth.pypins 401 (not 303) on/files,/stream-logs,/execution-eventsfor a credential-less and a cookie-only caller.TestJsonExceptionHandlersintests/app/sep/test_main.pycovers the 404/422/HTTPException-with-headers/BaseAuthProviderExceptionpaths and asserts the deleted shell routes 404.@sep/api(102),@sep/snippets(72) and the vite-proxy suite pass;pnpm -r type-checkclean.tests/app/sep/{api,routes,sync,snippets,apps/*},tests/app/core/settings_override,test_main.py,test_deps.py,test_connectivity.py.Not verified in one pass: the full suite. It exceeds the local time budget, so it was exercised directory-by-directory rather than as a single run. CI's
pythonjob is skipped whileqa in progressis set (label-gateblocks it by design), so the first end-to-end signal arrives when QA flips the label — worth a full run before merge.Acceptance criteria
17 of 18 ticked. The one left unchecked is "The legacy session-cookie setting and its options class are removed" — the criterion is internally contradictory: it also retains
SESSION_REFRESH, which is typed bySessionOptions. Everything else in that criterion (the breaking admin-API change, the five removed keys, thesep-track data migration,SESSION_REFRESHunaffected) is satisfied.Checklist
changelog.d/