fix(ui): stop logout on method-level UNAUTHORIZED, align TTS route guard (#502)

ws-client.ts treated any UNAUTHORIZED WebSocket response as session
invalidation, triggering full logout. This caused clicking the TTS tab
to log users out because config.get requires owner role while the route
only required admin.

- Remove UNAUTHORIZED from onAuthFailure trigger in handleResponse;
  only TENANT_ACCESS_REVOKED forces logout now
- Change TTS route guard from RequireAdmin to RequireOwner (matches
  backend requireOwner on config.get/config.patch)
- Gate TTS sidebar item behind isOwner so non-owners don't see it

Closes #501
This commit is contained in:
Kai (Tam Nhu) Tran
2026-03-27 13:43:25 +07:00
committed by GitHub
parent 9e74974129
commit 4dafd70c83
3 changed files with 8 additions and 3 deletions
+4 -1
View File
@@ -282,7 +282,10 @@ export class WsClient {
pending.resolve(frame.payload);
} else {
const err = frame.error as ErrorShape;
if (err.code === "UNAUTHORIZED" || err.code === "TENANT_ACCESS_REVOKED") {
// Only force logout on tenant revocation (session-level invalidation).
// UNAUTHORIZED from a method call means "insufficient permission for this action",
// not "session expired" — let the caller handle it via the rejected promise.
if (err.code === "TENANT_ACCESS_REVOKED") {
this.onAuthFailure?.();
}
pending.reject(
+3 -1
View File
@@ -101,7 +101,9 @@ export function Sidebar({ collapsed, onNavItemClick }: SidebarProps) {
<SidebarItem to={ROUTES.SKILLS} icon={Zap} label={t("nav.skills")} collapsed={collapsed} />
<SidebarItem to={ROUTES.BUILTIN_TOOLS} icon={Package} label={t("nav.builtinTools")} collapsed={collapsed} />
<SidebarItem to={ROUTES.MCP} icon={Plug} label={t("nav.mcpServers")} collapsed={collapsed} />
<SidebarItem to={ROUTES.TTS} icon={Volume2} label={t("nav.tts")} collapsed={collapsed} />
{isOwner && (
<SidebarItem to={ROUTES.TTS} icon={Volume2} label={t("nav.tts")} collapsed={collapsed} />
)}
<SidebarItem to={ROUTES.CRON} icon={Clock} label={t("nav.cron")} collapsed={collapsed} />
</SidebarGroup>
+1 -1
View File
@@ -173,7 +173,7 @@ export function AppRoutes() {
<Route path={ROUTES.LOGS} element={<RequireAdmin><LogsPage /></RequireAdmin>} />
<Route path={ROUTES.BUILTIN_TOOLS} element={<RequireAdmin><BuiltinToolsPage /></RequireAdmin>} />
<Route path={ROUTES.MCP} element={<RequireAdmin><MCPPage /></RequireAdmin>} />
<Route path={ROUTES.TTS} element={<RequireAdmin><TtsPage /></RequireAdmin>} />
<Route path={ROUTES.TTS} element={<RequireCrossTenant><TtsPage /></RequireCrossTenant>} />
<Route path={ROUTES.STORAGE} element={<RequireAdmin><StoragePage /></RequireAdmin>} />
<Route path={ROUTES.PACKAGES} element={<RequireAdmin><PackagesPage /></RequireAdmin>} />
<Route path={ROUTES.TENANTS} element={<RequireCrossTenant><TenantsAdminPage /></RequireCrossTenant>} />