0.1.109 — Inbox unsubscribe works offline + no 3s delay; Casdoor signin_url moved to org-themed page

Two follow-ups to user reports:

"The user should be able to unsubscribe the default course even in offline mode, if user unsubscribe, there should not be 3s delay for remove category."

"Fix the bug for casdoor sso, the final endpoint should be on https://auth.trance-0.com/login/notechondria. If env is wrong, fix that and tell me which var need updates."

1. Inbox row is now removable from the sidebar (offline + no delay)

Before: the Inbox category was hard-blocked in _promptEditCategory — long-press / right-click on the Inbox row showed an "Inbox is the default category. It cannot be renamed or deleted." dialog with only an OK button. There was no path to remove the Inbox at all, so users with a stale or unwanted Inbox row had to wipe local data to get rid of it.

After (in editor_app/lib/core/category_actions.dart): the Inbox dialog now offers a "Remove from sidebar" action alongside Cancel. Tapping it routes through _unsubscribeCategory immediately — no 3-second confirmation delay (the action is fully recoverable: _ensureStarterWorkspace reseeds the Inbox on the next editor launch).

_unsubscribeCategory itself was rewritten to work in any auth state:

  • Cloud + signed in — best-effort widget.client.unsubscribeCourse(token, courseId). A failed call (401 stale session, 403 owned-row, network blip) is logged at warning and does not block the local removal.
  • Cloud + offline / no token — skip the server call entirely, drop the row from _courses locally + _persistLocalCache().
  • Local-only row — drop from _localCourses locally + persistLocalCourses(). No server call.

User intent ("get this off my sidebar") wins. The next online sync may re-surface a cloud row whose subscription wasn't actually dropped server-side; that's acceptable for now and clearer than failing the action when the cloud call fails.

The non-Inbox unsubscribe path (_promptEditCategoryaction == 'unsubscribe') also dropped the _confirmWithDelay(...) second confirm — the user already confirmed by tapping "Unsubscribe" in the edit dialog, and unsubscribe is non-destructive (the category itself stays on the server, only the sidebar row is dropped).

The 3-second delay stays in place for delete since that permanently destroys the row server-side and moves notes to the default category.

2. Casdoor signin_url now points at the org-themed login page

Before: CasdoorConfigApiView returned

{ "signin_url": "https://auth.trance-0.com/login/oauth/authorize" }

The frontend's launchOAuth('casdoor', intent: 'login') then appended OAuth query params and redirected the user to the raw OAuth authorization endpoint — a generic Casdoor consent screen.

After (in backend/creators/api.py, CasdoorConfigApiView.get): signin_url is now derived from the org name —

"signin_url": f"{endpoint}/login/{settings.CASDOOR_ORG_NAME}",

so for CASDOOR_ENDPOINT=https://auth.trance-0.com/ and CASDOOR_ORG_NAME=notechondria, the redirect lands at https://auth.trance-0.com/login/notechondria?client_id=...&... — the branded Casdoor login page for the Notechondria organization. Casdoor accepts the same OAuth params on this URL as on /login/oauth/authorize, so the post-login callback flow is unchanged.

Operator follow-up — Casdoor backend env vars

The user-supplied log shows the production backend at notechondria.trance-0.com is still 404'ing on /api/v1/auth/casdoor/config/. That route was added in 0.1.96; the 404 means the deployed image predates that release. A backend redeploy is required to pick up both the route and this round's signin_url change.

backend/notechondria/settings.py:477-485 reads six env vars. All six must be set in the Northflank service env (or linked Secret Group) for /auth/casdoor/config/ to return {configured: true, ...}:

Env varValue (per sample.northflank.env)
CASDOOR_ENDPOINThttps://auth.trance-0.com/
CASDOOR_CLIENT_ID(Casdoor app's Client ID — d24d31a88e52e81733aa per the sample)
CASDOOR_CLIENT_SECRET(Casdoor app's Client Secret)
CASDOOR_ORG_NAMEnotechondria
CASDOOR_APP_NAMEnotechondria
CASDOOR_CERTIFICATE(Public-key PEM from the Casdoor app's Cert tab; single-line, literal \n in place of newlines)

After the redeploy:

  1. curl -sS https://notechondria.trance-0.com/api/v1/auth/casdoor/config/ should return JSON with configured: true and a signin_url ending in /login/notechondria.
  2. The frontend's boot-time probe will succeed, and clicking "Continue with Casdoor" will redirect to https://auth.trance-0.com/login/notechondria with the appended OAuth params.

If the redeploy is blocked, the Casdoor button still renders (0.1.108 made it always-visible) and clicking it produces a debug-log warning instead of a redirect — non-fatal, just inert until the backend catches up.

Files changed

  • frontend/editor_app/lib/core/category_actions.dart
    • _promptEditCategory: Inbox path now confirms + removes instead of blocking.
    • _unsubscribeCategory: works in any auth state; cloud failure is logged but doesn't block local removal.
    • action == 'unsubscribe' handler: dropped the 3-second _confirmWithDelay call.
  • backend/creators/api.py
    • CasdoorConfigApiView.get: signin_url now uses /login/<org> instead of /login/oauth/authorize.

Verification

  • flutter analyze clean for editor_app (only pre-existing info-level lints remain).
  • Backend file edited only inside CasdoorConfigApiView.get; no migration impact, no URL routing changes.