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:
+72
-2
@@ -5,8 +5,27 @@
|
||||
* 本模块管三件事:①验签后的人是谁(名册即时更新);②他能进哪几个端(角色→端);③岗位与工作组。
|
||||
* 口令 / 会话 / 岗位授权(客体三类 × 动作三种)已退役。
|
||||
*/
|
||||
import type { InFlightWork, Position, PositionId, Principal, RoleAccess, RosterSyncOutcome, Staff, StaffId, Surface, WorkGroup, WorkGroupBinding } from '@contracts';
|
||||
export type { InFlightWork, Position, Principal, RoleAccess, RosterSyncOutcome, Staff, Surface, WorkGroup, WorkGroupBinding };
|
||||
import type { InFlightWork, OrgGroup, OrgRole, OrgUser, OrgUserDetail, Position, PositionId, Principal, RoleAccess, RosterSyncOutcome, Staff, StaffId, Surface, WorkGroup, WorkGroupBinding } from '@contracts';
|
||||
export type { InFlightWork, OrgGroup, OrgRole, OrgUser, OrgUserDetail, Position, Principal, RoleAccess, RosterSyncOutcome, Staff, Surface, WorkGroup, WorkGroupBinding };
|
||||
/**
|
||||
* 目录写面的两个入参 —— 住在 contract.ts 而不是 port.ts:它们是**服务出口的形状**
|
||||
* (路由照它填),port 只是恰好也要用。反过来放会让 port ↔ contract 成环。
|
||||
*/
|
||||
export type CreateDirectoryUser = {
|
||||
readonly username: string;
|
||||
readonly firstName?: string;
|
||||
readonly lastName?: string;
|
||||
readonly email?: string;
|
||||
/** 临时口令;不给就建一个还登不进来的壳 */
|
||||
readonly password?: string;
|
||||
/** 一并授的 realm 角色 */
|
||||
readonly roles?: readonly string[];
|
||||
};
|
||||
export type DirectoryUserPatch = {
|
||||
readonly firstName?: string;
|
||||
readonly lastName?: string;
|
||||
readonly email?: string;
|
||||
};
|
||||
/** 员工连同其岗位的读模型 —— 治理台员工管理列表用 */
|
||||
export type StaffWithPosition = Staff & {
|
||||
readonly position: Position;
|
||||
@@ -47,6 +66,24 @@ export declare class AccountDisabledError extends Error {
|
||||
export declare class AccessLockoutError extends Error {
|
||||
constructor(message?: string);
|
||||
}
|
||||
/**
|
||||
* 目录整个不可用 —— 没配 provisioning 服务账号,或那个账号缺 realm-management 角色。
|
||||
* **重试无益**:路由译成 502 并原样带出 Keycloak 的话,界面提示找管理员补权限(手册 §14.6)。
|
||||
*/
|
||||
export declare class DirectoryUnavailableError extends Error {
|
||||
/** true = 配错了(补权限);false = 暂时够不着(可退避重试)—— 两句话,人的下一步不同 */
|
||||
readonly misconfigured: boolean;
|
||||
constructor(message: string, misconfigured: boolean);
|
||||
}
|
||||
/**
|
||||
* 写面对着一个服务账号 —— 拒。它是 Keycloak 给 client 自动建的身份(见 `staffKindSchema`),
|
||||
* 在这三个页签里**根本不该出现**;能对着它下手,只能是有人手输了 sub。
|
||||
* 停掉它的后果不显眼却很重:试跑与 e2e 第二天全趴下。
|
||||
*/
|
||||
export declare class ServiceAccountProtectedError extends Error {
|
||||
readonly account: string;
|
||||
constructor(account: string);
|
||||
}
|
||||
export type OrgService = {
|
||||
/** 验签 → 名册即时更新 → 角色→端。抛 TokenRejectedError(401)/ AccountDisabledError(403) */
|
||||
authenticate(token: string): Promise<Principal>;
|
||||
@@ -75,6 +112,39 @@ export type OrgService = {
|
||||
deleteRoleAccess(role: string): Promise<void>;
|
||||
/** 能进该端的活跃员工 —— 治理台通知的收件人 */
|
||||
staffWithEnd(surface: Surface): Promise<StaffWithPosition[]>;
|
||||
/**
|
||||
* 目录**没配 provisioning 服务账号就整个不可用** —— 一律抛 `DirectoryUnavailableError`
|
||||
* (路由译成 502 + 原话)。不给「读得了写不了」这种半开状态:那样人会以为是自己点错了。
|
||||
* 服务账号在这一层就剔干净;写面对着服务账号的 sub 一律抛 `ServiceAccountProtectedError`。
|
||||
*/
|
||||
directoryUsers(query: {
|
||||
q?: string;
|
||||
includeDisabled?: boolean;
|
||||
}): Promise<{
|
||||
users: OrgUser[];
|
||||
}>;
|
||||
directoryUser(sub: string): Promise<OrgUserDetail>;
|
||||
createDirectoryUser(input: CreateDirectoryUser): Promise<OrgUser>;
|
||||
patchDirectoryUser(sub: string, patch: DirectoryUserPatch): Promise<OrgUser>;
|
||||
/** 停用顺手踢会话 —— 界面写了「已停用」,那枚 token 就不该还能用到过期 */
|
||||
setDirectoryUserEnabled(sub: string, enabled: boolean): Promise<OrgUser>;
|
||||
resetDirectoryUserPassword(sub: string, password: string): Promise<void>;
|
||||
deleteDirectoryUser(sub: string): Promise<void>;
|
||||
directoryRoles(): Promise<{
|
||||
roles: OrgRole[];
|
||||
}>;
|
||||
/** 授 / 收一个 realm 角色;收权顺手踢会话。抛 AccessLockoutError(409):收完没人进得了运营中心 */
|
||||
setDirectoryUserRole(sub: string, role: string, granted: boolean): Promise<OrgUser>;
|
||||
directoryGroups(): Promise<{
|
||||
groups: OrgGroup[];
|
||||
}>;
|
||||
directoryGroupMembers(groupId: string): Promise<{
|
||||
members: OrgUser[];
|
||||
}>;
|
||||
createDirectoryGroup(name: string, parentId?: string): Promise<OrgGroup>;
|
||||
renameDirectoryGroup(groupId: string, name: string): Promise<OrgGroup>;
|
||||
deleteDirectoryGroup(groupId: string): Promise<void>;
|
||||
setDirectoryUserGroup(sub: string, groupId: string, member: boolean): Promise<void>;
|
||||
/** 建组 / 改组;id 留空即新建 */
|
||||
saveWorkGroup(input: {
|
||||
id?: string;
|
||||
|
||||
+7
-3
@@ -1,18 +1,22 @@
|
||||
/**
|
||||
* module: org —— 唯一出口。只做 re-export,不写逻辑(编码规范 §10.1)。
|
||||
*/
|
||||
export type { Identity, InFlightWork, OrgService, Position, Principal, RoleAccess, RosterSyncOutcome, Staff, StaffWithPosition, Surface, WorkGroup, WorkGroupBinding, } from './contract.ts';
|
||||
export { AccessLockoutError, AccountDisabledError, PositionNotFoundError, StaffNotFoundError, TokenRejectedError, } from './contract.ts';
|
||||
export type { Clock, IdGenerator, IdentityPatch, InFlightWorkProbe, PositionRepository, RoleAccessRepository, RosterSource, RosterUser, StaffRecord, StaffRepository, TokenVerifier, VerifiedClaims, WorkGroupRepository, } from './port.ts';
|
||||
export type { CreateDirectoryUser, DirectoryUserPatch, Identity, InFlightWork, OrgGroup, OrgRole, OrgService, OrgUser, OrgUserDetail, Position, Principal, RoleAccess, RosterSyncOutcome, Staff, StaffWithPosition, Surface, WorkGroup, WorkGroupBinding, } from './contract.ts';
|
||||
export { AccessLockoutError, AccountDisabledError, DirectoryUnavailableError, PositionNotFoundError, ServiceAccountProtectedError, StaffNotFoundError, TokenRejectedError, } from './contract.ts';
|
||||
export type { Clock, DirectoryGroup, DirectoryRole, DirectoryUser, IdGenerator, IdentityPatch, InFlightWorkProbe, KeycloakDirectory, PositionRepository, RoleAccessRepository, RosterSource, RosterUser, StaffRecord, StaffRepository, TokenVerifier, VerifiedClaims, WorkGroupRepository, } from './port.ts';
|
||||
export type { OrgServiceDeps } from './internal/usecase/org-service.ts';
|
||||
export { createOrgService } from './internal/usecase/org-service.ts';
|
||||
export { identityOf, isNoiseRole, normalizeRoles } from './internal/domain/claims.ts';
|
||||
export { assertNotLockedOut, endsOf, normalizeEnds } from './internal/domain/ends.ts';
|
||||
export { displayNameOf, filterOrgUsers, isServiceAccount, sortOrgUsers, toOrgGroups, toOrgRoles, toOrgUser, } from './internal/domain/directory.ts';
|
||||
export { buildInFlightWork } from './internal/domain/in-flight.ts';
|
||||
export { DEFAULT_ROLE_ACCESS, NONE_POSITION_CODE, POSITION_SEED, TEST_POSITION_CODE } from './internal/domain/seed-data.ts';
|
||||
export { createKeycloakTokenVerifier, explainJoseError } from './internal/adapter/keycloak-token-verifier.ts';
|
||||
export type { KeycloakVerifierOptions } from './internal/adapter/keycloak-token-verifier.ts';
|
||||
export { createKeycloakRoster } from './internal/adapter/keycloak-admin-roster.ts';
|
||||
export type { KeycloakRosterOptions } from './internal/adapter/keycloak-admin-roster.ts';
|
||||
export { createKeycloakDirectory } from './internal/adapter/keycloak-directory.ts';
|
||||
export { createAdminHttp, KeycloakMisconfiguredError, KeycloakRejectedError, KeycloakUnavailableError } from './internal/adapter/keycloak-admin-http.ts';
|
||||
export type { AdminHttp, KeycloakAdminOptions } from './internal/adapter/keycloak-admin-http.ts';
|
||||
export { createPositionRepository, createRoleAccessRepository, createStaffRepository, createWorkGroupRepository, } from './internal/adapter/org-repository.ts';
|
||||
export { positionTable, roleAccessTable, staffTable, workGroupTable } from './internal/adapter/org-schema.ts';
|
||||
|
||||
+72
-3
@@ -3,7 +3,7 @@
|
||||
* adapter 实现之,usecase 依赖之,app 装配之。
|
||||
*/
|
||||
import type { Position, PositionId, RoleAccess, Staff, StaffId, WorkGroup } from '@contracts';
|
||||
import type { Identity } from './contract.ts';
|
||||
import type { CreateDirectoryUser, DirectoryUserPatch, Identity } from './contract.ts';
|
||||
export type Clock = {
|
||||
now(): Date;
|
||||
};
|
||||
@@ -11,7 +11,12 @@ export type Clock = {
|
||||
export type VerifiedClaims = {
|
||||
readonly sub: string;
|
||||
readonly preferred_username: string;
|
||||
/** Keycloak 拼好的 `firstName lastName` —— 西式顺序,只在拆不开时才用它 */
|
||||
readonly name?: string;
|
||||
/** 名(firstName) */
|
||||
readonly given_name?: string;
|
||||
/** 姓(lastName)—— 我们按「姓在前」自己拼,见 `personName` */
|
||||
readonly family_name?: string;
|
||||
readonly email?: string;
|
||||
readonly realm_access?: {
|
||||
readonly roles?: readonly string[];
|
||||
@@ -28,6 +33,65 @@ export type RosterUser = Identity & {
|
||||
export type RosterSource = {
|
||||
listUsers(): Promise<readonly RosterUser[]>;
|
||||
};
|
||||
/**
|
||||
* Keycloak 那一侧的一个用户 —— **原样的形状**,不掺本地字段。
|
||||
* 与 `RosterUser` 的区别:名册只要「同步要比对的那几列」,目录要摆给人看的全部。
|
||||
*/
|
||||
export type DirectoryUser = {
|
||||
readonly sub: string;
|
||||
readonly username: string;
|
||||
readonly firstName?: string;
|
||||
readonly lastName?: string;
|
||||
readonly email?: string;
|
||||
readonly enabled: boolean;
|
||||
readonly emailVerified: boolean;
|
||||
/** realm 角色(家具角色已滤) */
|
||||
readonly roles: readonly string[];
|
||||
};
|
||||
/** 组树里的一行 —— 平表,层级看 `parentId` / `depth`(契约同款,理由见 org-directory.ts) */
|
||||
export type DirectoryGroup = {
|
||||
readonly id: string;
|
||||
readonly name: string;
|
||||
readonly path: string;
|
||||
readonly parentId?: string;
|
||||
readonly depth: number;
|
||||
readonly memberCount: number;
|
||||
};
|
||||
export type DirectoryRole = {
|
||||
readonly name: string;
|
||||
readonly description?: string;
|
||||
readonly composite: boolean;
|
||||
};
|
||||
/**
|
||||
* 成员目录 —— app 未配 provisioning 服务账号时不注入(与 `RosterSource` 同款)。
|
||||
* 读是投影,写**直接穿透**到 Keycloak:这里建的人立刻能登录,这里删的人从 Keycloak 消失。
|
||||
*/
|
||||
export type KeycloakDirectory = {
|
||||
listUsers(): Promise<readonly DirectoryUser[]>;
|
||||
getUser(sub: string): Promise<DirectoryUser & {
|
||||
readonly groups: ReadonlyArray<{
|
||||
id: string;
|
||||
path: string;
|
||||
}>;
|
||||
}>;
|
||||
createUser(input: CreateDirectoryUser): Promise<string>;
|
||||
updateUser(sub: string, patch: DirectoryUserPatch): Promise<void>;
|
||||
setEnabled(sub: string, enabled: boolean): Promise<void>;
|
||||
resetPassword(sub: string, password: string): Promise<void>;
|
||||
deleteUser(sub: string): Promise<void>;
|
||||
/** 踢掉此人全部会话 —— 停用与收权之后必调,否则旧 token 到期前照样通行 */
|
||||
logout(sub: string): Promise<void>;
|
||||
listRoles(): Promise<readonly DirectoryRole[]>;
|
||||
setRole(sub: string, role: string, granted: boolean): Promise<void>;
|
||||
listGroups(): Promise<readonly DirectoryGroup[]>;
|
||||
groupMembers(groupId: string): Promise<readonly DirectoryUser[]>;
|
||||
/** 组的直属成员用户名 —— 组树只要个数,别为了数数把每个人的角色都拉一遍 */
|
||||
groupMemberUsernames(groupId: string): Promise<readonly string[]>;
|
||||
createGroup(name: string, parentId?: string): Promise<DirectoryGroup>;
|
||||
renameGroup(groupId: string, name: string): Promise<DirectoryGroup>;
|
||||
deleteGroup(groupId: string): Promise<void>;
|
||||
setUserGroup(sub: string, groupId: string, member: boolean): Promise<void>;
|
||||
};
|
||||
/** 名册即时更新 / 同步要改的那几列 */
|
||||
export type IdentityPatch = {
|
||||
readonly account: string;
|
||||
@@ -45,7 +109,12 @@ export type PositionRepository = {
|
||||
}): Promise<Position>;
|
||||
};
|
||||
/** 员工行 —— 契约 Staff 之外还带两枚时刻,只给用例判「要不要再写一次」,不出模块 */
|
||||
export type StaffRecord = Staff & {
|
||||
/**
|
||||
* 库里真存着的那几列。`Staff.kind` 不在其中 —— 它由账号名现判(见 `staffKindSchema`),
|
||||
* 存一列就会与账号名分叉,而分叉之后没人看得出以哪份为准。
|
||||
*/
|
||||
export type StoredStaff = Omit<Staff, 'kind'>;
|
||||
export type StaffRecord = StoredStaff & {
|
||||
readonly lastSeenAt?: Date;
|
||||
readonly syncedAt?: Date;
|
||||
};
|
||||
@@ -55,7 +124,7 @@ export type StaffRepository = {
|
||||
/** 按账号找 —— `account` 有唯一索引,建档前得先问一句「这个名字被谁占着」(同名让位) */
|
||||
findByAccount(account: string): Promise<StaffRecord | undefined>;
|
||||
listByPosition(positionId: PositionId): Promise<StaffRecord[]>;
|
||||
insert(staff: Staff, marks: {
|
||||
insert(staff: StoredStaff, marks: {
|
||||
readonly lastSeenAt?: Date;
|
||||
readonly syncedAt?: Date;
|
||||
}): Promise<StaffRecord>;
|
||||
|
||||
@@ -19,6 +19,10 @@ export declare const listStaffRoute: {
|
||||
disabled: "disabled";
|
||||
}>>;
|
||||
roles: z.ZodDefault<z.ZodArray<z.ZodString>>;
|
||||
kind: z.ZodDefault<z.ZodEnum<{
|
||||
person: "person";
|
||||
service: "service";
|
||||
}>>;
|
||||
position: z.ZodObject<{
|
||||
id: z.core.$ZodBranded<z.ZodString, "PositionId", "out">;
|
||||
code: z.ZodString;
|
||||
@@ -55,6 +59,10 @@ export declare const createStaffRoute: {
|
||||
disabled: "disabled";
|
||||
}>>;
|
||||
roles: z.ZodDefault<z.ZodArray<z.ZodString>>;
|
||||
kind: z.ZodDefault<z.ZodEnum<{
|
||||
person: "person";
|
||||
service: "service";
|
||||
}>>;
|
||||
}, z.core.$strip>;
|
||||
};
|
||||
/** @deprecated 随 ADR-012 退役,后端回 410;改用 staffInFlightRoute */
|
||||
@@ -266,6 +274,10 @@ export declare const setStaffPositionRoute: {
|
||||
disabled: "disabled";
|
||||
}>>;
|
||||
roles: z.ZodDefault<z.ZodArray<z.ZodString>>;
|
||||
kind: z.ZodDefault<z.ZodEnum<{
|
||||
person: "person";
|
||||
service: "service";
|
||||
}>>;
|
||||
position: z.ZodObject<{
|
||||
id: z.core.$ZodBranded<z.ZodString, "PositionId", "out">;
|
||||
code: z.ZodString;
|
||||
|
||||
+1
-1
@@ -41,7 +41,7 @@ export type RouteContract<TParams extends z.ZodTypeAny = z.ZodTypeAny, TQuery ex
|
||||
export type RouteOutput<R extends RouteContract> = z.infer<R['output']>;
|
||||
export type RouteBody<R extends RouteContract> = R['body'] extends z.ZodTypeAny ? z.infer<R['body']> : never;
|
||||
/** 结构化错误:前端按 code 分辨,不解析文案。`gone` = 410,路由已随统一鉴权中心退役 */
|
||||
export declare const API_ERROR_CODES: readonly ["unauthenticated", "forbidden", "not-found", "validation-failed", "conflict", "precondition-failed", "internal", "gone"];
|
||||
export declare const API_ERROR_CODES: readonly ["unauthenticated", "forbidden", "not-found", "validation-failed", "conflict", "precondition-failed", "internal", "gone", "keycloak-misconfigured", "keycloak-unavailable"];
|
||||
export type ApiErrorCode = (typeof API_ERROR_CODES)[number];
|
||||
export type ApiError = {
|
||||
readonly code: ApiErrorCode;
|
||||
|
||||
@@ -1,5 +1,6 @@
|
||||
export * from './http.ts';
|
||||
export * from './workbench.ts';
|
||||
export * from './governance.ts';
|
||||
export * from './org-directory.ts';
|
||||
export * from './ingest.ts';
|
||||
export * from './mail.ts';
|
||||
|
||||
@@ -0,0 +1,701 @@
|
||||
/**
|
||||
* 成员与组织(ADR-012 · 运营中心「员工」那一屏的三个页签)。
|
||||
*
|
||||
* **真源是 Keycloak**:读接口是它的投影,写接口**直接穿透**到 Admin API ——
|
||||
* 这里建的人立刻能登录、这里停的人立刻登不上、这里删的人从 Keycloak 消失。
|
||||
* 没有「先存本地草稿再同步」这回事,**也没有撤销**;界面上凡写必二次确认。
|
||||
*
|
||||
* 与本地名册的关系:`sys.staff` 是这份目录的投影 + 一列本地岗位(见 org/CONTRACT.md)。
|
||||
* 涉及身份的写(停用 / 删除 / 授收角色)之后,本地那一行当场跟手 —— 两屏不许说两句话。
|
||||
*
|
||||
* 三处口径与 Keycloak 的形状有关,写在这儿免得每条接口重复:
|
||||
* ① **服务账号一律不可见、不可写**:目录读剔除 `service-account-*`(它是身份不是人,
|
||||
* 见 `staffKindSchema`);写面对着服务账号的 sub 一律 403。
|
||||
* ② **组树是平表**:一行一个组,靠 `parentId` / `depth` 表达层级(按 `path` 升序已排好)。
|
||||
* 契约里不放递归结构 —— 递归 schema 到了声明文件那一层只会变成一坨 any。
|
||||
* ③ **列表无分页**:realm 不大,一次拉全;Keycloak 那一侧的 100 条上限由适配器分页兜住。
|
||||
*/
|
||||
import { z } from 'zod';
|
||||
export declare const orgUserSchema: z.ZodObject<{
|
||||
sub: z.ZodString;
|
||||
username: z.ZodString;
|
||||
displayName: z.ZodString;
|
||||
email: z.ZodOptional<z.ZodString>;
|
||||
enabled: z.ZodBoolean;
|
||||
roles: z.ZodArray<z.ZodString>;
|
||||
ends: z.ZodArray<z.ZodEnum<{
|
||||
design: "design";
|
||||
ops: "ops";
|
||||
workbench: "workbench";
|
||||
}>>;
|
||||
lastSeenAt: z.ZodOptional<z.ZodString>;
|
||||
positionCode: z.ZodOptional<z.ZodString>;
|
||||
}, z.core.$strip>;
|
||||
export type OrgUser = z.infer<typeof orgUserSchema>;
|
||||
export declare const orgUserDetailSchema: z.ZodObject<{
|
||||
sub: z.ZodString;
|
||||
username: z.ZodString;
|
||||
displayName: z.ZodString;
|
||||
email: z.ZodOptional<z.ZodString>;
|
||||
enabled: z.ZodBoolean;
|
||||
roles: z.ZodArray<z.ZodString>;
|
||||
ends: z.ZodArray<z.ZodEnum<{
|
||||
design: "design";
|
||||
ops: "ops";
|
||||
workbench: "workbench";
|
||||
}>>;
|
||||
lastSeenAt: z.ZodOptional<z.ZodString>;
|
||||
positionCode: z.ZodOptional<z.ZodString>;
|
||||
firstName: z.ZodOptional<z.ZodString>;
|
||||
lastName: z.ZodOptional<z.ZodString>;
|
||||
emailVerified: z.ZodBoolean;
|
||||
groups: z.ZodArray<z.ZodObject<{
|
||||
id: z.ZodString;
|
||||
path: z.ZodString;
|
||||
}, z.core.$strip>>;
|
||||
}, z.core.$strip>;
|
||||
export type OrgUserDetail = z.infer<typeof orgUserDetailSchema>;
|
||||
export declare const listOrgUsersRoute: {
|
||||
readonly method: "GET";
|
||||
readonly path: "/api/gov/org/users";
|
||||
readonly access: "ops";
|
||||
readonly output: z.ZodObject<{
|
||||
users: z.ZodArray<z.ZodObject<{
|
||||
sub: z.ZodString;
|
||||
username: z.ZodString;
|
||||
displayName: z.ZodString;
|
||||
email: z.ZodOptional<z.ZodString>;
|
||||
enabled: z.ZodBoolean;
|
||||
roles: z.ZodArray<z.ZodString>;
|
||||
ends: z.ZodArray<z.ZodEnum<{
|
||||
design: "design";
|
||||
ops: "ops";
|
||||
workbench: "workbench";
|
||||
}>>;
|
||||
lastSeenAt: z.ZodOptional<z.ZodString>;
|
||||
positionCode: z.ZodOptional<z.ZodString>;
|
||||
}, z.core.$strip>>;
|
||||
}, z.core.$strip>;
|
||||
};
|
||||
export declare const getOrgUserRoute: {
|
||||
readonly method: "GET";
|
||||
readonly path: "/api/gov/org/users/:sub";
|
||||
readonly access: "ops";
|
||||
readonly params: z.ZodObject<{
|
||||
sub: z.ZodString;
|
||||
}, z.core.$strip>;
|
||||
readonly output: z.ZodObject<{
|
||||
sub: z.ZodString;
|
||||
username: z.ZodString;
|
||||
displayName: z.ZodString;
|
||||
email: z.ZodOptional<z.ZodString>;
|
||||
enabled: z.ZodBoolean;
|
||||
roles: z.ZodArray<z.ZodString>;
|
||||
ends: z.ZodArray<z.ZodEnum<{
|
||||
design: "design";
|
||||
ops: "ops";
|
||||
workbench: "workbench";
|
||||
}>>;
|
||||
lastSeenAt: z.ZodOptional<z.ZodString>;
|
||||
positionCode: z.ZodOptional<z.ZodString>;
|
||||
firstName: z.ZodOptional<z.ZodString>;
|
||||
lastName: z.ZodOptional<z.ZodString>;
|
||||
emailVerified: z.ZodBoolean;
|
||||
groups: z.ZodArray<z.ZodObject<{
|
||||
id: z.ZodString;
|
||||
path: z.ZodString;
|
||||
}, z.core.$strip>>;
|
||||
}, z.core.$strip>;
|
||||
};
|
||||
/**
|
||||
* 新建成员 —— 建完立刻能登录。口令是**临时**的:本人首登必须改
|
||||
* (`temporary:true`,手册 §14.2);我们不存它,也不回显。
|
||||
*/
|
||||
export declare const createOrgUserRoute: {
|
||||
readonly method: "POST";
|
||||
readonly path: "/api/gov/org/users";
|
||||
readonly access: "ops";
|
||||
readonly body: z.ZodObject<{
|
||||
username: z.ZodString;
|
||||
firstName: z.ZodOptional<z.ZodString>;
|
||||
lastName: z.ZodOptional<z.ZodString>;
|
||||
email: z.ZodOptional<z.ZodString>;
|
||||
password: z.ZodOptional<z.ZodString>;
|
||||
roles: z.ZodOptional<z.ZodArray<z.ZodString>>;
|
||||
}, z.core.$strip>;
|
||||
readonly output: z.ZodObject<{
|
||||
sub: z.ZodString;
|
||||
username: z.ZodString;
|
||||
displayName: z.ZodString;
|
||||
email: z.ZodOptional<z.ZodString>;
|
||||
enabled: z.ZodBoolean;
|
||||
roles: z.ZodArray<z.ZodString>;
|
||||
ends: z.ZodArray<z.ZodEnum<{
|
||||
design: "design";
|
||||
ops: "ops";
|
||||
workbench: "workbench";
|
||||
}>>;
|
||||
lastSeenAt: z.ZodOptional<z.ZodString>;
|
||||
positionCode: z.ZodOptional<z.ZodString>;
|
||||
}, z.core.$strip>;
|
||||
};
|
||||
/** 改资料(姓名 / 邮箱)—— 用户名建后不可改,不在这里 */
|
||||
export declare const patchOrgUserRoute: {
|
||||
readonly method: "PATCH";
|
||||
readonly path: "/api/gov/org/users/:sub";
|
||||
readonly access: "ops";
|
||||
readonly params: z.ZodObject<{
|
||||
sub: z.ZodString;
|
||||
}, z.core.$strip>;
|
||||
readonly body: z.ZodObject<{
|
||||
firstName: z.ZodOptional<z.ZodString>;
|
||||
lastName: z.ZodOptional<z.ZodString>;
|
||||
email: z.ZodOptional<z.ZodString>;
|
||||
}, z.core.$strip>;
|
||||
readonly output: z.ZodObject<{
|
||||
sub: z.ZodString;
|
||||
username: z.ZodString;
|
||||
displayName: z.ZodString;
|
||||
email: z.ZodOptional<z.ZodString>;
|
||||
enabled: z.ZodBoolean;
|
||||
roles: z.ZodArray<z.ZodString>;
|
||||
ends: z.ZodArray<z.ZodEnum<{
|
||||
design: "design";
|
||||
ops: "ops";
|
||||
workbench: "workbench";
|
||||
}>>;
|
||||
lastSeenAt: z.ZodOptional<z.ZodString>;
|
||||
positionCode: z.ZodOptional<z.ZodString>;
|
||||
}, z.core.$strip>;
|
||||
};
|
||||
/**
|
||||
* 启用 / 停用。停用**立刻踢掉该用户全部会话** —— 不然他攥着的 token
|
||||
* 还能用到过期为止,而「已停用」这三个字在界面上已经写出去了(界面不撒谎)。
|
||||
*/
|
||||
export declare const setOrgUserEnabledRoute: {
|
||||
readonly method: "POST";
|
||||
readonly path: "/api/gov/org/users/:sub/enabled";
|
||||
readonly access: "ops";
|
||||
readonly params: z.ZodObject<{
|
||||
sub: z.ZodString;
|
||||
}, z.core.$strip>;
|
||||
readonly body: z.ZodObject<{
|
||||
enabled: z.ZodBoolean;
|
||||
}, z.core.$strip>;
|
||||
readonly output: z.ZodObject<{
|
||||
sub: z.ZodString;
|
||||
username: z.ZodString;
|
||||
displayName: z.ZodString;
|
||||
email: z.ZodOptional<z.ZodString>;
|
||||
enabled: z.ZodBoolean;
|
||||
roles: z.ZodArray<z.ZodString>;
|
||||
ends: z.ZodArray<z.ZodEnum<{
|
||||
design: "design";
|
||||
ops: "ops";
|
||||
workbench: "workbench";
|
||||
}>>;
|
||||
lastSeenAt: z.ZodOptional<z.ZodString>;
|
||||
positionCode: z.ZodOptional<z.ZodString>;
|
||||
}, z.core.$strip>;
|
||||
};
|
||||
/** 重置口令 —— 一律临时口令,本人首登必须改;服务端不回显、不留痕 */
|
||||
export declare const resetOrgUserPasswordRoute: {
|
||||
readonly method: "POST";
|
||||
readonly path: "/api/gov/org/users/:sub/reset-password";
|
||||
readonly access: "ops";
|
||||
readonly params: z.ZodObject<{
|
||||
sub: z.ZodString;
|
||||
}, z.core.$strip>;
|
||||
readonly body: z.ZodObject<{
|
||||
password: z.ZodString;
|
||||
}, z.core.$strip>;
|
||||
readonly output: z.ZodObject<{
|
||||
ok: z.ZodLiteral<true>;
|
||||
}, z.core.$strip>;
|
||||
};
|
||||
/** 删人 —— 从 Keycloak 消失,不可撤销;本地名册那一行同批停用(留痕与在途还挂着他) */
|
||||
export declare const deleteOrgUserRoute: {
|
||||
readonly method: "DELETE";
|
||||
readonly path: "/api/gov/org/users/:sub";
|
||||
readonly access: "ops";
|
||||
readonly params: z.ZodObject<{
|
||||
sub: z.ZodString;
|
||||
}, z.core.$strip>;
|
||||
readonly output: z.ZodObject<{
|
||||
ok: z.ZodLiteral<true>;
|
||||
}, z.core.$strip>;
|
||||
};
|
||||
export declare const orgRoleSchema: z.ZodObject<{
|
||||
name: z.ZodString;
|
||||
description: z.ZodOptional<z.ZodString>;
|
||||
composite: z.ZodBoolean;
|
||||
ends: z.ZodArray<z.ZodEnum<{
|
||||
design: "design";
|
||||
ops: "ops";
|
||||
workbench: "workbench";
|
||||
}>>;
|
||||
holders: z.ZodNumber;
|
||||
}, z.core.$strip>;
|
||||
export type OrgRole = z.infer<typeof orgRoleSchema>;
|
||||
export declare const listOrgRolesRoute: {
|
||||
readonly method: "GET";
|
||||
readonly path: "/api/gov/org/roles";
|
||||
readonly access: "ops";
|
||||
readonly output: z.ZodObject<{
|
||||
roles: z.ZodArray<z.ZodObject<{
|
||||
name: z.ZodString;
|
||||
description: z.ZodOptional<z.ZodString>;
|
||||
composite: z.ZodBoolean;
|
||||
ends: z.ZodArray<z.ZodEnum<{
|
||||
design: "design";
|
||||
ops: "ops";
|
||||
workbench: "workbench";
|
||||
}>>;
|
||||
holders: z.ZodNumber;
|
||||
}, z.core.$strip>>;
|
||||
}, z.core.$strip>;
|
||||
};
|
||||
/**
|
||||
* 授 / 收一个 realm 角色。收权**立刻踢掉该用户全部会话**(手册 §17.3)——
|
||||
* 否则他手上那枚 token 还带着旧角色,收权要等到 token 过期才生效。
|
||||
* 不能收自己的最后一条进运营中心的路(409)—— 收完没人进得来,这扇门就锁死了。
|
||||
*/
|
||||
export declare const setOrgUserRoleRoute: {
|
||||
readonly method: "POST";
|
||||
readonly path: "/api/gov/org/users/:sub/roles/:role";
|
||||
readonly access: "ops";
|
||||
readonly params: z.ZodObject<{
|
||||
sub: z.ZodString;
|
||||
role: z.ZodString;
|
||||
}, z.core.$strip>;
|
||||
readonly body: z.ZodObject<{
|
||||
granted: z.ZodBoolean;
|
||||
}, z.core.$strip>;
|
||||
readonly output: z.ZodObject<{
|
||||
sub: z.ZodString;
|
||||
username: z.ZodString;
|
||||
displayName: z.ZodString;
|
||||
email: z.ZodOptional<z.ZodString>;
|
||||
enabled: z.ZodBoolean;
|
||||
roles: z.ZodArray<z.ZodString>;
|
||||
ends: z.ZodArray<z.ZodEnum<{
|
||||
design: "design";
|
||||
ops: "ops";
|
||||
workbench: "workbench";
|
||||
}>>;
|
||||
lastSeenAt: z.ZodOptional<z.ZodString>;
|
||||
positionCode: z.ZodOptional<z.ZodString>;
|
||||
}, z.core.$strip>;
|
||||
};
|
||||
export declare const orgGroupSchema: z.ZodObject<{
|
||||
id: z.ZodString;
|
||||
name: z.ZodString;
|
||||
path: z.ZodString;
|
||||
parentId: z.ZodOptional<z.ZodString>;
|
||||
depth: z.ZodNumber;
|
||||
memberCount: z.ZodNumber;
|
||||
}, z.core.$strip>;
|
||||
export type OrgGroup = z.infer<typeof orgGroupSchema>;
|
||||
/** 组树平表,按 `path` 升序 —— 层级看 `parentId` / `depth` */
|
||||
export declare const listOrgGroupsRoute: {
|
||||
readonly method: "GET";
|
||||
readonly path: "/api/gov/org/groups";
|
||||
readonly access: "ops";
|
||||
readonly output: z.ZodObject<{
|
||||
groups: z.ZodArray<z.ZodObject<{
|
||||
id: z.ZodString;
|
||||
name: z.ZodString;
|
||||
path: z.ZodString;
|
||||
parentId: z.ZodOptional<z.ZodString>;
|
||||
depth: z.ZodNumber;
|
||||
memberCount: z.ZodNumber;
|
||||
}, z.core.$strip>>;
|
||||
}, z.core.$strip>;
|
||||
};
|
||||
/** 组的直属成员(含被停用的人 —— 这一屏管的是归属,不是能不能登录) */
|
||||
export declare const listOrgGroupMembersRoute: {
|
||||
readonly method: "GET";
|
||||
readonly path: "/api/gov/org/groups/:id/members";
|
||||
readonly access: "ops";
|
||||
readonly params: z.ZodObject<{
|
||||
id: z.ZodString;
|
||||
}, z.core.$strip>;
|
||||
readonly output: z.ZodObject<{
|
||||
members: z.ZodArray<z.ZodObject<{
|
||||
sub: z.ZodString;
|
||||
username: z.ZodString;
|
||||
displayName: z.ZodString;
|
||||
email: z.ZodOptional<z.ZodString>;
|
||||
enabled: z.ZodBoolean;
|
||||
roles: z.ZodArray<z.ZodString>;
|
||||
ends: z.ZodArray<z.ZodEnum<{
|
||||
design: "design";
|
||||
ops: "ops";
|
||||
workbench: "workbench";
|
||||
}>>;
|
||||
lastSeenAt: z.ZodOptional<z.ZodString>;
|
||||
positionCode: z.ZodOptional<z.ZodString>;
|
||||
}, z.core.$strip>>;
|
||||
}, z.core.$strip>;
|
||||
};
|
||||
/** 建组:`parentId` 给了就是子组,不给就是顶层组。同级不得重名(Keycloak 那一侧拦,409) */
|
||||
export declare const createOrgGroupRoute: {
|
||||
readonly method: "POST";
|
||||
readonly path: "/api/gov/org/groups";
|
||||
readonly access: "ops";
|
||||
readonly body: z.ZodObject<{
|
||||
name: z.ZodString;
|
||||
parentId: z.ZodOptional<z.ZodString>;
|
||||
}, z.core.$strip>;
|
||||
readonly output: z.ZodObject<{
|
||||
id: z.ZodString;
|
||||
name: z.ZodString;
|
||||
path: z.ZodString;
|
||||
parentId: z.ZodOptional<z.ZodString>;
|
||||
depth: z.ZodNumber;
|
||||
memberCount: z.ZodNumber;
|
||||
}, z.core.$strip>;
|
||||
};
|
||||
export declare const renameOrgGroupRoute: {
|
||||
readonly method: "PATCH";
|
||||
readonly path: "/api/gov/org/groups/:id";
|
||||
readonly access: "ops";
|
||||
readonly params: z.ZodObject<{
|
||||
id: z.ZodString;
|
||||
}, z.core.$strip>;
|
||||
readonly body: z.ZodObject<{
|
||||
name: z.ZodString;
|
||||
}, z.core.$strip>;
|
||||
readonly output: z.ZodObject<{
|
||||
id: z.ZodString;
|
||||
name: z.ZodString;
|
||||
path: z.ZodString;
|
||||
parentId: z.ZodOptional<z.ZodString>;
|
||||
depth: z.ZodNumber;
|
||||
memberCount: z.ZodNumber;
|
||||
}, z.core.$strip>;
|
||||
};
|
||||
/** 删组 —— 连同子组一起没(Keycloak 语义);人不会被删,只是不再属于这个组 */
|
||||
export declare const deleteOrgGroupRoute: {
|
||||
readonly method: "DELETE";
|
||||
readonly path: "/api/gov/org/groups/:id";
|
||||
readonly access: "ops";
|
||||
readonly params: z.ZodObject<{
|
||||
id: z.ZodString;
|
||||
}, z.core.$strip>;
|
||||
readonly output: z.ZodObject<{
|
||||
ok: z.ZodLiteral<true>;
|
||||
}, z.core.$strip>;
|
||||
};
|
||||
/** 成员进 / 出组 */
|
||||
export declare const setOrgUserGroupRoute: {
|
||||
readonly method: "POST";
|
||||
readonly path: "/api/gov/org/users/:sub/groups/:groupId";
|
||||
readonly access: "ops";
|
||||
readonly params: z.ZodObject<{
|
||||
sub: z.ZodString;
|
||||
groupId: z.ZodString;
|
||||
}, z.core.$strip>;
|
||||
readonly body: z.ZodObject<{
|
||||
member: z.ZodBoolean;
|
||||
}, z.core.$strip>;
|
||||
readonly output: z.ZodObject<{
|
||||
ok: z.ZodLiteral<true>;
|
||||
}, z.core.$strip>;
|
||||
};
|
||||
/**
|
||||
* 这一屏的全部路由 —— 归属一处列全,测试拿它逐条断言。
|
||||
* 逐个 import 去断言的写法,新增一条只要忘了加进名单就悄悄漏过去了。
|
||||
*/
|
||||
export declare const ORG_DIRECTORY_ROUTES: readonly [{
|
||||
readonly method: "GET";
|
||||
readonly path: "/api/gov/org/users";
|
||||
readonly access: "ops";
|
||||
readonly output: z.ZodObject<{
|
||||
users: z.ZodArray<z.ZodObject<{
|
||||
sub: z.ZodString;
|
||||
username: z.ZodString;
|
||||
displayName: z.ZodString;
|
||||
email: z.ZodOptional<z.ZodString>;
|
||||
enabled: z.ZodBoolean;
|
||||
roles: z.ZodArray<z.ZodString>;
|
||||
ends: z.ZodArray<z.ZodEnum<{
|
||||
design: "design";
|
||||
ops: "ops";
|
||||
workbench: "workbench";
|
||||
}>>;
|
||||
lastSeenAt: z.ZodOptional<z.ZodString>;
|
||||
positionCode: z.ZodOptional<z.ZodString>;
|
||||
}, z.core.$strip>>;
|
||||
}, z.core.$strip>;
|
||||
}, {
|
||||
readonly method: "GET";
|
||||
readonly path: "/api/gov/org/users/:sub";
|
||||
readonly access: "ops";
|
||||
readonly params: z.ZodObject<{
|
||||
sub: z.ZodString;
|
||||
}, z.core.$strip>;
|
||||
readonly output: z.ZodObject<{
|
||||
sub: z.ZodString;
|
||||
username: z.ZodString;
|
||||
displayName: z.ZodString;
|
||||
email: z.ZodOptional<z.ZodString>;
|
||||
enabled: z.ZodBoolean;
|
||||
roles: z.ZodArray<z.ZodString>;
|
||||
ends: z.ZodArray<z.ZodEnum<{
|
||||
design: "design";
|
||||
ops: "ops";
|
||||
workbench: "workbench";
|
||||
}>>;
|
||||
lastSeenAt: z.ZodOptional<z.ZodString>;
|
||||
positionCode: z.ZodOptional<z.ZodString>;
|
||||
firstName: z.ZodOptional<z.ZodString>;
|
||||
lastName: z.ZodOptional<z.ZodString>;
|
||||
emailVerified: z.ZodBoolean;
|
||||
groups: z.ZodArray<z.ZodObject<{
|
||||
id: z.ZodString;
|
||||
path: z.ZodString;
|
||||
}, z.core.$strip>>;
|
||||
}, z.core.$strip>;
|
||||
}, {
|
||||
readonly method: "POST";
|
||||
readonly path: "/api/gov/org/users";
|
||||
readonly access: "ops";
|
||||
readonly body: z.ZodObject<{
|
||||
username: z.ZodString;
|
||||
firstName: z.ZodOptional<z.ZodString>;
|
||||
lastName: z.ZodOptional<z.ZodString>;
|
||||
email: z.ZodOptional<z.ZodString>;
|
||||
password: z.ZodOptional<z.ZodString>;
|
||||
roles: z.ZodOptional<z.ZodArray<z.ZodString>>;
|
||||
}, z.core.$strip>;
|
||||
readonly output: z.ZodObject<{
|
||||
sub: z.ZodString;
|
||||
username: z.ZodString;
|
||||
displayName: z.ZodString;
|
||||
email: z.ZodOptional<z.ZodString>;
|
||||
enabled: z.ZodBoolean;
|
||||
roles: z.ZodArray<z.ZodString>;
|
||||
ends: z.ZodArray<z.ZodEnum<{
|
||||
design: "design";
|
||||
ops: "ops";
|
||||
workbench: "workbench";
|
||||
}>>;
|
||||
lastSeenAt: z.ZodOptional<z.ZodString>;
|
||||
positionCode: z.ZodOptional<z.ZodString>;
|
||||
}, z.core.$strip>;
|
||||
}, {
|
||||
readonly method: "PATCH";
|
||||
readonly path: "/api/gov/org/users/:sub";
|
||||
readonly access: "ops";
|
||||
readonly params: z.ZodObject<{
|
||||
sub: z.ZodString;
|
||||
}, z.core.$strip>;
|
||||
readonly body: z.ZodObject<{
|
||||
firstName: z.ZodOptional<z.ZodString>;
|
||||
lastName: z.ZodOptional<z.ZodString>;
|
||||
email: z.ZodOptional<z.ZodString>;
|
||||
}, z.core.$strip>;
|
||||
readonly output: z.ZodObject<{
|
||||
sub: z.ZodString;
|
||||
username: z.ZodString;
|
||||
displayName: z.ZodString;
|
||||
email: z.ZodOptional<z.ZodString>;
|
||||
enabled: z.ZodBoolean;
|
||||
roles: z.ZodArray<z.ZodString>;
|
||||
ends: z.ZodArray<z.ZodEnum<{
|
||||
design: "design";
|
||||
ops: "ops";
|
||||
workbench: "workbench";
|
||||
}>>;
|
||||
lastSeenAt: z.ZodOptional<z.ZodString>;
|
||||
positionCode: z.ZodOptional<z.ZodString>;
|
||||
}, z.core.$strip>;
|
||||
}, {
|
||||
readonly method: "POST";
|
||||
readonly path: "/api/gov/org/users/:sub/enabled";
|
||||
readonly access: "ops";
|
||||
readonly params: z.ZodObject<{
|
||||
sub: z.ZodString;
|
||||
}, z.core.$strip>;
|
||||
readonly body: z.ZodObject<{
|
||||
enabled: z.ZodBoolean;
|
||||
}, z.core.$strip>;
|
||||
readonly output: z.ZodObject<{
|
||||
sub: z.ZodString;
|
||||
username: z.ZodString;
|
||||
displayName: z.ZodString;
|
||||
email: z.ZodOptional<z.ZodString>;
|
||||
enabled: z.ZodBoolean;
|
||||
roles: z.ZodArray<z.ZodString>;
|
||||
ends: z.ZodArray<z.ZodEnum<{
|
||||
design: "design";
|
||||
ops: "ops";
|
||||
workbench: "workbench";
|
||||
}>>;
|
||||
lastSeenAt: z.ZodOptional<z.ZodString>;
|
||||
positionCode: z.ZodOptional<z.ZodString>;
|
||||
}, z.core.$strip>;
|
||||
}, {
|
||||
readonly method: "POST";
|
||||
readonly path: "/api/gov/org/users/:sub/reset-password";
|
||||
readonly access: "ops";
|
||||
readonly params: z.ZodObject<{
|
||||
sub: z.ZodString;
|
||||
}, z.core.$strip>;
|
||||
readonly body: z.ZodObject<{
|
||||
password: z.ZodString;
|
||||
}, z.core.$strip>;
|
||||
readonly output: z.ZodObject<{
|
||||
ok: z.ZodLiteral<true>;
|
||||
}, z.core.$strip>;
|
||||
}, {
|
||||
readonly method: "DELETE";
|
||||
readonly path: "/api/gov/org/users/:sub";
|
||||
readonly access: "ops";
|
||||
readonly params: z.ZodObject<{
|
||||
sub: z.ZodString;
|
||||
}, z.core.$strip>;
|
||||
readonly output: z.ZodObject<{
|
||||
ok: z.ZodLiteral<true>;
|
||||
}, z.core.$strip>;
|
||||
}, {
|
||||
readonly method: "GET";
|
||||
readonly path: "/api/gov/org/roles";
|
||||
readonly access: "ops";
|
||||
readonly output: z.ZodObject<{
|
||||
roles: z.ZodArray<z.ZodObject<{
|
||||
name: z.ZodString;
|
||||
description: z.ZodOptional<z.ZodString>;
|
||||
composite: z.ZodBoolean;
|
||||
ends: z.ZodArray<z.ZodEnum<{
|
||||
design: "design";
|
||||
ops: "ops";
|
||||
workbench: "workbench";
|
||||
}>>;
|
||||
holders: z.ZodNumber;
|
||||
}, z.core.$strip>>;
|
||||
}, z.core.$strip>;
|
||||
}, {
|
||||
readonly method: "POST";
|
||||
readonly path: "/api/gov/org/users/:sub/roles/:role";
|
||||
readonly access: "ops";
|
||||
readonly params: z.ZodObject<{
|
||||
sub: z.ZodString;
|
||||
role: z.ZodString;
|
||||
}, z.core.$strip>;
|
||||
readonly body: z.ZodObject<{
|
||||
granted: z.ZodBoolean;
|
||||
}, z.core.$strip>;
|
||||
readonly output: z.ZodObject<{
|
||||
sub: z.ZodString;
|
||||
username: z.ZodString;
|
||||
displayName: z.ZodString;
|
||||
email: z.ZodOptional<z.ZodString>;
|
||||
enabled: z.ZodBoolean;
|
||||
roles: z.ZodArray<z.ZodString>;
|
||||
ends: z.ZodArray<z.ZodEnum<{
|
||||
design: "design";
|
||||
ops: "ops";
|
||||
workbench: "workbench";
|
||||
}>>;
|
||||
lastSeenAt: z.ZodOptional<z.ZodString>;
|
||||
positionCode: z.ZodOptional<z.ZodString>;
|
||||
}, z.core.$strip>;
|
||||
}, {
|
||||
readonly method: "GET";
|
||||
readonly path: "/api/gov/org/groups";
|
||||
readonly access: "ops";
|
||||
readonly output: z.ZodObject<{
|
||||
groups: z.ZodArray<z.ZodObject<{
|
||||
id: z.ZodString;
|
||||
name: z.ZodString;
|
||||
path: z.ZodString;
|
||||
parentId: z.ZodOptional<z.ZodString>;
|
||||
depth: z.ZodNumber;
|
||||
memberCount: z.ZodNumber;
|
||||
}, z.core.$strip>>;
|
||||
}, z.core.$strip>;
|
||||
}, {
|
||||
readonly method: "GET";
|
||||
readonly path: "/api/gov/org/groups/:id/members";
|
||||
readonly access: "ops";
|
||||
readonly params: z.ZodObject<{
|
||||
id: z.ZodString;
|
||||
}, z.core.$strip>;
|
||||
readonly output: z.ZodObject<{
|
||||
members: z.ZodArray<z.ZodObject<{
|
||||
sub: z.ZodString;
|
||||
username: z.ZodString;
|
||||
displayName: z.ZodString;
|
||||
email: z.ZodOptional<z.ZodString>;
|
||||
enabled: z.ZodBoolean;
|
||||
roles: z.ZodArray<z.ZodString>;
|
||||
ends: z.ZodArray<z.ZodEnum<{
|
||||
design: "design";
|
||||
ops: "ops";
|
||||
workbench: "workbench";
|
||||
}>>;
|
||||
lastSeenAt: z.ZodOptional<z.ZodString>;
|
||||
positionCode: z.ZodOptional<z.ZodString>;
|
||||
}, z.core.$strip>>;
|
||||
}, z.core.$strip>;
|
||||
}, {
|
||||
readonly method: "POST";
|
||||
readonly path: "/api/gov/org/groups";
|
||||
readonly access: "ops";
|
||||
readonly body: z.ZodObject<{
|
||||
name: z.ZodString;
|
||||
parentId: z.ZodOptional<z.ZodString>;
|
||||
}, z.core.$strip>;
|
||||
readonly output: z.ZodObject<{
|
||||
id: z.ZodString;
|
||||
name: z.ZodString;
|
||||
path: z.ZodString;
|
||||
parentId: z.ZodOptional<z.ZodString>;
|
||||
depth: z.ZodNumber;
|
||||
memberCount: z.ZodNumber;
|
||||
}, z.core.$strip>;
|
||||
}, {
|
||||
readonly method: "PATCH";
|
||||
readonly path: "/api/gov/org/groups/:id";
|
||||
readonly access: "ops";
|
||||
readonly params: z.ZodObject<{
|
||||
id: z.ZodString;
|
||||
}, z.core.$strip>;
|
||||
readonly body: z.ZodObject<{
|
||||
name: z.ZodString;
|
||||
}, z.core.$strip>;
|
||||
readonly output: z.ZodObject<{
|
||||
id: z.ZodString;
|
||||
name: z.ZodString;
|
||||
path: z.ZodString;
|
||||
parentId: z.ZodOptional<z.ZodString>;
|
||||
depth: z.ZodNumber;
|
||||
memberCount: z.ZodNumber;
|
||||
}, z.core.$strip>;
|
||||
}, {
|
||||
readonly method: "DELETE";
|
||||
readonly path: "/api/gov/org/groups/:id";
|
||||
readonly access: "ops";
|
||||
readonly params: z.ZodObject<{
|
||||
id: z.ZodString;
|
||||
}, z.core.$strip>;
|
||||
readonly output: z.ZodObject<{
|
||||
ok: z.ZodLiteral<true>;
|
||||
}, z.core.$strip>;
|
||||
}, {
|
||||
readonly method: "POST";
|
||||
readonly path: "/api/gov/org/users/:sub/groups/:groupId";
|
||||
readonly access: "ops";
|
||||
readonly params: z.ZodObject<{
|
||||
sub: z.ZodString;
|
||||
groupId: z.ZodString;
|
||||
}, z.core.$strip>;
|
||||
readonly body: z.ZodObject<{
|
||||
member: z.ZodBoolean;
|
||||
}, z.core.$strip>;
|
||||
readonly output: z.ZodObject<{
|
||||
ok: z.ZodLiteral<true>;
|
||||
}, z.core.$strip>;
|
||||
}];
|
||||
@@ -20,6 +20,10 @@ export declare const sessionRoute: {
|
||||
disabled: "disabled";
|
||||
}>>;
|
||||
roles: z.ZodDefault<z.ZodArray<z.ZodString>>;
|
||||
kind: z.ZodDefault<z.ZodEnum<{
|
||||
person: "person";
|
||||
service: "service";
|
||||
}>>;
|
||||
}, z.core.$strip>;
|
||||
position: z.ZodObject<{
|
||||
id: z.core.$ZodBranded<z.ZodString, "PositionId", "out">;
|
||||
@@ -80,6 +84,10 @@ export declare const loginRoute: {
|
||||
disabled: "disabled";
|
||||
}>>;
|
||||
roles: z.ZodDefault<z.ZodArray<z.ZodString>>;
|
||||
kind: z.ZodDefault<z.ZodEnum<{
|
||||
person: "person";
|
||||
service: "service";
|
||||
}>>;
|
||||
}, z.core.$strip>;
|
||||
position: z.ZodObject<{
|
||||
id: z.core.$ZodBranded<z.ZodString, "PositionId", "out">;
|
||||
@@ -158,6 +166,10 @@ export declare const meRoute: {
|
||||
disabled: "disabled";
|
||||
}>>;
|
||||
roles: z.ZodDefault<z.ZodArray<z.ZodString>>;
|
||||
kind: z.ZodDefault<z.ZodEnum<{
|
||||
person: "person";
|
||||
service: "service";
|
||||
}>>;
|
||||
}, z.core.$strip>;
|
||||
position: z.ZodObject<{
|
||||
id: z.core.$ZodBranded<z.ZodString, "PositionId", "out">;
|
||||
@@ -891,6 +903,7 @@ export declare const taskDetailSchema: z.ZodObject<{
|
||||
cols: z.ZodOptional<z.ZodUnion<readonly [z.ZodLiteral<1>, z.ZodLiteral<2>]>>;
|
||||
}, z.core.$strip>>;
|
||||
submitLabel: z.ZodOptional<z.ZodString>;
|
||||
gateLedgers: z.ZodOptional<z.ZodArray<z.ZodString>>;
|
||||
gate: z.ZodOptional<z.ZodObject<{
|
||||
ready: z.ZodBoolean;
|
||||
why: z.ZodString;
|
||||
@@ -1475,6 +1488,7 @@ export declare const taskDetailSchema: z.ZodObject<{
|
||||
cols: z.ZodOptional<z.ZodUnion<readonly [z.ZodLiteral<1>, z.ZodLiteral<2>]>>;
|
||||
}, z.core.$strip>>;
|
||||
submitLabel: z.ZodOptional<z.ZodString>;
|
||||
gateLedgers: z.ZodOptional<z.ZodArray<z.ZodString>>;
|
||||
gate: z.ZodOptional<z.ZodObject<{
|
||||
ready: z.ZodBoolean;
|
||||
why: z.ZodString;
|
||||
@@ -2796,6 +2810,7 @@ export declare const taskDetailRoute: {
|
||||
cols: z.ZodOptional<z.ZodUnion<readonly [z.ZodLiteral<1>, z.ZodLiteral<2>]>>;
|
||||
}, z.core.$strip>>;
|
||||
submitLabel: z.ZodOptional<z.ZodString>;
|
||||
gateLedgers: z.ZodOptional<z.ZodArray<z.ZodString>>;
|
||||
gate: z.ZodOptional<z.ZodObject<{
|
||||
ready: z.ZodBoolean;
|
||||
why: z.ZodString;
|
||||
@@ -3380,6 +3395,7 @@ export declare const taskDetailRoute: {
|
||||
cols: z.ZodOptional<z.ZodUnion<readonly [z.ZodLiteral<1>, z.ZodLiteral<2>]>>;
|
||||
}, z.core.$strip>>;
|
||||
submitLabel: z.ZodOptional<z.ZodString>;
|
||||
gateLedgers: z.ZodOptional<z.ZodArray<z.ZodString>>;
|
||||
gate: z.ZodOptional<z.ZodObject<{
|
||||
ready: z.ZodBoolean;
|
||||
why: z.ZodString;
|
||||
|
||||
+28
@@ -19,6 +19,22 @@ export declare const staffStateSchema: z.ZodEnum<{
|
||||
disabled: "disabled";
|
||||
}>;
|
||||
export type StaffState = z.infer<typeof staffStateSchema>;
|
||||
/**
|
||||
* 人,还是服务账号。
|
||||
*
|
||||
* Keycloak 给每个开了 `serviceAccountsEnabled` 的 client 自动建一个用户,用户名恒为
|
||||
* `service-account-<clientId>` —— 那是 Keycloak 自己的命名,不是我们的约定;它也不出现在
|
||||
* 管理台的 Users 列表、Admin API 的 `GET /users` 里(名册同步的下限保护②为此存在)。
|
||||
*
|
||||
* 它是**真身份**(试跑与 e2e 就用它跑单,铁规矩 6),但**不是人**:
|
||||
* 员工管理那一屏不摆它,试跑台的负责人下拉照旧摆。
|
||||
* 这条判断归后端 —— 界面不该去猜账号名长什么样。
|
||||
*/
|
||||
export declare const staffKindSchema: z.ZodEnum<{
|
||||
person: "person";
|
||||
service: "service";
|
||||
}>;
|
||||
export type StaffKind = z.infer<typeof staffKindSchema>;
|
||||
export declare const staffSchema: z.ZodObject<{
|
||||
id: z.core.$ZodBranded<z.ZodString, "StaffId", "out">;
|
||||
account: z.ZodString;
|
||||
@@ -30,6 +46,10 @@ export declare const staffSchema: z.ZodObject<{
|
||||
disabled: "disabled";
|
||||
}>>;
|
||||
roles: z.ZodDefault<z.ZodArray<z.ZodString>>;
|
||||
kind: z.ZodDefault<z.ZodEnum<{
|
||||
person: "person";
|
||||
service: "service";
|
||||
}>>;
|
||||
}, z.core.$strip>;
|
||||
export type Staff = z.infer<typeof staffSchema>;
|
||||
export declare const principalSchema: z.ZodObject<{
|
||||
@@ -44,6 +64,10 @@ export declare const principalSchema: z.ZodObject<{
|
||||
disabled: "disabled";
|
||||
}>>;
|
||||
roles: z.ZodDefault<z.ZodArray<z.ZodString>>;
|
||||
kind: z.ZodDefault<z.ZodEnum<{
|
||||
person: "person";
|
||||
service: "service";
|
||||
}>>;
|
||||
}, z.core.$strip>;
|
||||
position: z.ZodObject<{
|
||||
id: z.core.$ZodBranded<z.ZodString, "PositionId", "out">;
|
||||
@@ -226,6 +250,10 @@ export declare const sessionSchema: z.ZodObject<{
|
||||
disabled: "disabled";
|
||||
}>>;
|
||||
roles: z.ZodDefault<z.ZodArray<z.ZodString>>;
|
||||
kind: z.ZodDefault<z.ZodEnum<{
|
||||
person: "person";
|
||||
service: "service";
|
||||
}>>;
|
||||
}, z.core.$strip>;
|
||||
position: z.ZodObject<{
|
||||
id: z.core.$ZodBranded<z.ZodString, "PositionId", "out">;
|
||||
|
||||
Reference in New Issue
Block a user