Archived
feat(org): 成员目录 —— Keycloak 读写面穿透、治理台成员/组织/权限三页、姓在前 personName、Staff.kind
后端:`KeycloakDirectory` 端口(users / groups / roles 读写;写直接穿透 Keycloak —— 这里建的人立刻能登录、 删的人立刻消失,停用与收权之后 logout 踢掉会话)、`keycloak-directory.ts` + `keycloak-admin-http.ts` 适配器、 org-directory 契约与 /api/gov 路由、`sync-roster` CLI(ops 路由够不着时整册照过来), 容器按 provisioning 服务账号有无决定注不注入(没配就如实说不可用)。 `personName` 姓在前:汉字直拼「柴浩」、否则留一个空格「Admin Amy」,token 与名册同步两条路同一把尺; `staffKindOf` 按 Keycloak 固定前缀 service-account- 认服务账号,员工那一屏不摆它(`Staff.kind` 不落库,按账号名现判)。 前端:员工那一屏重做成成员 / 组织 / 权限三页签(member-page 下钻),页签栏右端一句话说清这一格管什么。 契约变更:contracts org.ts / api/http.ts / api/org-directory.ts;api-report 已刷新;org CONTRACT.md 与 ADR-012 同步。 2026-08-26 做的,一直留在工作树没入库。 Co-Authored-By: Claude Fable 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_0167qWjeyBwvfdGovd7FWbCu
This commit is contained in:
@@ -16,8 +16,9 @@ import { createEventService, createEventStore } from '@modules/event';
|
||||
import type { LedgerService } from '@modules/ledger';
|
||||
import type { LoopEngineService } from '@modules/loop-engine';
|
||||
import { createDraftStore, createLedgerService, createLedgerStore, DRAFT_DDL } from '@modules/ledger';
|
||||
import type { InFlightWorkProbe, OrgService, RosterSource, TokenVerifier } from '@modules/org';
|
||||
import type { InFlightWorkProbe, KeycloakDirectory, OrgService, RosterSource, TokenVerifier } from '@modules/org';
|
||||
import {
|
||||
createKeycloakDirectory,
|
||||
createKeycloakRoster,
|
||||
createKeycloakTokenVerifier,
|
||||
createOrgService,
|
||||
@@ -185,6 +186,7 @@ const sfeelEvaluator = (expression: string, scope: Readonly<Record<string, unkno
|
||||
export type ContainerOverrides = {
|
||||
readonly tokenVerifier?: TokenVerifier;
|
||||
readonly roster?: RosterSource;
|
||||
readonly directory?: KeycloakDirectory;
|
||||
};
|
||||
|
||||
export async function createContainer(config: AppConfig, overrides: ContainerOverrides = {}): Promise<Container> {
|
||||
@@ -256,9 +258,22 @@ export async function createContainer(config: AppConfig, overrides: ContainerOve
|
||||
secret: keycloak.provisioning.secret,
|
||||
}));
|
||||
|
||||
/* 成员目录与名册同步同一枚 provisioning 服务账号 —— 没配就都不注入,界面如实说不可用 */
|
||||
const directory: KeycloakDirectory | undefined =
|
||||
overrides.directory ??
|
||||
(keycloak?.provisioning === undefined
|
||||
? undefined
|
||||
: createKeycloakDirectory({
|
||||
url: keycloak.url,
|
||||
realm: keycloak.realm,
|
||||
clientId: keycloak.provisioning.clientId,
|
||||
secret: keycloak.provisioning.secret,
|
||||
}));
|
||||
|
||||
const org = createOrgService({
|
||||
verifier,
|
||||
...(roster === undefined ? {} : { roster }),
|
||||
...(directory === undefined ? {} : { directory }),
|
||||
positions: createPositionRepository(db),
|
||||
staff: createStaffRepository(db),
|
||||
roleAccess: createRoleAccessRepository(db),
|
||||
|
||||
Reference in New Issue
Block a user