mirror of
https://github.com/bytedance/deer-flow.git
synced 2026-10-28 06:00:38 +00:00
* feat(authz): admin user-management UI and disabled-account copy (#4063 gap 3) Frontend slice of the account lifecycle backend (#6566): - Settings gains an admin-only Users page (core/auth/admin-users.ts -> GET/PATCH /api/v1/admin/users): email, read-only role, disabled state, and a suspend/restore toggle per row; the Gateway's rejection detail (last-active-admin 409) renders verbatim. The nav entry is hidden for non-admins and the page guards independently; ?settings=users joins the deep-link whitelist (now an exhaustive Record over SettingsSection). - A session that turns out disabled maps account_disabled to a login redirect that states the reason on all three surfaces: the SSR AuthResult tag (consumed exhaustively by the workspace/artifacts/auth layouts), the shared fetcher's 401 interception, and AuthProvider.refreshUser. No next target is preserved - a disabled account cannot sign back in. Login failures surface the localized account_disabled copy when the code is present. - userSchema stays disabled-optional (absent = not disabled); zh/en locales and docs updated in the same change. * fix(authz): address review on admin user UI and disabled copy (#4063 gap 3) Review follow-ups on PR #6641: - Direct /login visits with a suspended session now surface the SSR account_disabled verdict through AuthDisabledContext instead of discarding it (willem-bd): the auth layout wraps pages with the provider and the login form seeds its reason from it — no redirect, so no loop. Regression tests cover both verdict states. - admin-users.ts parses both endpoints against userSchema / z.array(userSchema) (zhfeng): malformed payloads fail the load instead of rendering phantom rows; toggle error copy is scoped to GatewayApiError with a localized fallback for anything else. - The localized account_disabled copy is login-only (zhfeng): sign-up keeps the backend message verbatim so a suspended account's email cannot be probed through the register surface. - Settings page folds the load banner and toggle error into a single alert region (zhfeng); adds coded-envelope 409, malformed-row, and load-failure tests; fetcher.ts documents the 401 body-consumption contract. - prettier across touched files (CI format gate). The admin-users query key keeps user?.id, matching the model-settings-page precedent (converge later per review).
41 lines
1.4 KiB
TypeScript
41 lines
1.4 KiB
TypeScript
import { readFileSync } from "node:fs";
|
|
import path from "node:path";
|
|
|
|
import { describe, expect, it } from "@rstest/core";
|
|
|
|
const FRONTEND_ROOT = path.resolve(__dirname, "../../../..");
|
|
const read = (relativePath: string) =>
|
|
readFileSync(path.join(FRONTEND_ROOT, relativePath), "utf8");
|
|
|
|
describe("interaction-only bundle boundaries", () => {
|
|
it("does not import the settings dialog until its store is open", () => {
|
|
const host = read(
|
|
"src/components/workspace/settings/settings-dialog-host.tsx",
|
|
);
|
|
expect(host).toContain("dynamic(");
|
|
expect(host).toContain("if (!open)");
|
|
expect(host).not.toContain(
|
|
'import { SettingsDialog } from "./settings-dialog"',
|
|
);
|
|
});
|
|
|
|
it("loads each settings page from its active section", () => {
|
|
const dialog = read(
|
|
"src/components/workspace/settings/settings-dialog.tsx",
|
|
);
|
|
expect(dialog.match(/dynamic\(/g)).toHaveLength(9);
|
|
expect(dialog).not.toMatch(
|
|
/import \{ \w+SettingsPage \} from "@\/components\/workspace\/settings\//,
|
|
);
|
|
});
|
|
|
|
it("keeps right-panel implementations behind dynamic imports", () => {
|
|
const chatBox = read("src/components/workspace/chats/chat-box.tsx");
|
|
expect(chatBox).toContain('import dynamic from "next/dynamic"');
|
|
expect(chatBox).not.toMatch(
|
|
/import \{ (?:ArtifactFileDetail|ArtifactFileList|BrowserViewPanel|SidecarPanel)/,
|
|
);
|
|
expect(chatBox.match(/dynamic\(/g)?.length).toBeGreaterThanOrEqual(4);
|
|
});
|
|
});
|