@fudanda/admin-antd 2.20261107.1-beta.1 → 2.20261109.1-beta.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/chunks/{App-BF96TQ_D.js → App-CfQx7jWq.js} +4 -4
- package/dist/chunks/{App-BF96TQ_D.js.map → App-CfQx7jWq.js.map} +1 -1
- package/dist/chunks/{AppModal-CLkMJ-96.js → AppModal-CVoUC2t3.js} +2 -2
- package/dist/chunks/{AppModal-CLkMJ-96.js.map → AppModal-CVoUC2t3.js.map} +1 -1
- package/dist/chunks/{ArcBaseAdmin--xQxd4tg.js → ArcBaseAdmin-DQyddbFC.js} +2 -2
- package/dist/chunks/{ArcBaseAdmin--xQxd4tg.js.map → ArcBaseAdmin-DQyddbFC.js.map} +1 -1
- package/dist/chunks/{DepartmentsPage-D6YVegPU.js → DepartmentsPage-CQx53QGE.js} +2 -2
- package/dist/chunks/{DepartmentsPage-D6YVegPU.js.map → DepartmentsPage-CQx53QGE.js.map} +1 -1
- package/dist/chunks/{DictsPage-uV3Uukwc.js → DictsPage-BGnamkWk.js} +2 -2
- package/dist/chunks/{DictsPage-uV3Uukwc.js.map → DictsPage-BGnamkWk.js.map} +1 -1
- package/dist/chunks/EntityContextRuntime-C9q95WDf.js +1 -0
- package/dist/chunks/{EntityContextRuntime-DbjyzCtY.js → EntityContextRuntime-CbzeMNXR.js} +2 -2
- package/dist/chunks/{EntityContextRuntime-DbjyzCtY.js.map → EntityContextRuntime-CbzeMNXR.js.map} +1 -1
- package/dist/chunks/{ExportButton-e2ybxuHb.js → ExportButton-sMPAV2hd.js} +2 -2
- package/dist/chunks/{ExportButton-e2ybxuHb.js.map → ExportButton-sMPAV2hd.js.map} +1 -1
- package/dist/chunks/{FileViewerPreviewPanel-BhI3pTi8.js → FileViewerPreviewPanel-Rti7pUhU.js} +3 -3
- package/dist/chunks/{FileViewerPreviewPanel-BhI3pTi8.js.map → FileViewerPreviewPanel-Rti7pUhU.js.map} +1 -1
- package/dist/chunks/{FilesPage-BI35mStl.js → FilesPage-D_iYsUwl.js} +3 -3
- package/dist/chunks/{FilesPage-BI35mStl.js.map → FilesPage-D_iYsUwl.js.map} +1 -1
- package/dist/chunks/IdentitySecurityPage-rDYX5ksX.js +9 -0
- package/dist/chunks/IdentitySecurityPage-rDYX5ksX.js.map +1 -0
- package/dist/chunks/{LogStatsScaffold-m0vVUXPh.js → LogStatsScaffold-CMIz4z0a.js} +2 -2
- package/dist/chunks/{LogStatsScaffold-m0vVUXPh.js.map → LogStatsScaffold-CMIz4z0a.js.map} +1 -1
- package/dist/chunks/{LoginLogsPage-BzBUR4Oo.js → LoginLogsPage-Bv5pPJb2.js} +2 -2
- package/dist/chunks/{LoginLogsPage-BzBUR4Oo.js.map → LoginLogsPage-Bv5pPJb2.js.map} +1 -1
- package/dist/chunks/{LoginLogsTable-BweUEjQX.js → LoginLogsTable-DYcTx2oR.js} +2 -2
- package/dist/chunks/{LoginLogsTable-BweUEjQX.js.map → LoginLogsTable-DYcTx2oR.js.map} +1 -1
- package/dist/chunks/{MarkdownPreviewPanel-C1OKSLmh.js → MarkdownPreviewPanel-lqs6AsoN.js} +2 -2
- package/dist/chunks/{MarkdownPreviewPanel-C1OKSLmh.js.map → MarkdownPreviewPanel-lqs6AsoN.js.map} +1 -1
- package/dist/chunks/{MasterDetailLayout-KVizcSNO.js → MasterDetailLayout-B5uqvh98.js} +2 -2
- package/dist/chunks/{MasterDetailLayout-KVizcSNO.js.map → MasterDetailLayout-B5uqvh98.js.map} +1 -1
- package/dist/chunks/{McpSettingsPage-Dhf0CbDN.js → McpSettingsPage-Cdej9Evi.js} +2 -2
- package/dist/chunks/{McpSettingsPage-Dhf0CbDN.js.map → McpSettingsPage-Cdej9Evi.js.map} +1 -1
- package/dist/chunks/{MemberAssignmentSheet-TYpnyA0z.js → MemberAssignmentSheet-AKvLbbYW.js} +2 -2
- package/dist/chunks/{MemberAssignmentSheet-TYpnyA0z.js.map → MemberAssignmentSheet-AKvLbbYW.js.map} +1 -1
- package/dist/chunks/{MenuCommandPalette-BcvpUfK7.js → MenuCommandPalette-ia0pSIFg.js} +2 -2
- package/dist/chunks/{MenuCommandPalette-BcvpUfK7.js.map → MenuCommandPalette-ia0pSIFg.js.map} +1 -1
- package/dist/chunks/{MenusPage-Dd7OLQYT.js → MenusPage-Bh55bvKH.js} +2 -2
- package/dist/chunks/{MenusPage-Dd7OLQYT.js.map → MenusPage-Bh55bvKH.js.map} +1 -1
- package/dist/chunks/{MonacoPreviewPanel-BW1j3ZTF.js → MonacoPreviewPanel-CT_sjf7Z.js} +2 -2
- package/dist/chunks/{MonacoPreviewPanel-BW1j3ZTF.js.map → MonacoPreviewPanel-CT_sjf7Z.js.map} +1 -1
- package/dist/chunks/{NavListPanel-Iz1RCkVw.js → NavListPanel-DMIUMh7q.js} +2 -2
- package/dist/chunks/{NavListPanel-Iz1RCkVw.js.map → NavListPanel-DMIUMh7q.js.map} +1 -1
- package/dist/chunks/{OperationLogsPage-Dt22tU4I.js → OperationLogsPage-7XVfmhxP.js} +2 -2
- package/dist/chunks/{OperationLogsPage-Dt22tU4I.js.map → OperationLogsPage-7XVfmhxP.js.map} +1 -1
- package/dist/chunks/{OperationLogsTable-B23Ry-2v.js → OperationLogsTable-gp-WZxiF.js} +3 -3
- package/dist/chunks/{OperationLogsTable-B23Ry-2v.js.map → OperationLogsTable-gp-WZxiF.js.map} +1 -1
- package/dist/chunks/{PDFPreviewPanel-kBPz5dQr.js → PDFPreviewPanel-CyfhyWav.js} +3 -3
- package/dist/chunks/{PDFPreviewPanel-kBPz5dQr.js.map → PDFPreviewPanel-CyfhyWav.js.map} +1 -1
- package/dist/chunks/{PositionsPage-BIKYQDB5.js → PositionsPage-Bwua1vNV.js} +2 -2
- package/dist/chunks/{PositionsPage-BIKYQDB5.js.map → PositionsPage-Bwua1vNV.js.map} +1 -1
- package/dist/chunks/{PreferencesSheetBody-D276rrp4.js → PreferencesSheetBody-C80D3dOM.js} +2 -2
- package/dist/chunks/{PreferencesSheetBody-D276rrp4.js.map → PreferencesSheetBody-C80D3dOM.js.map} +1 -1
- package/dist/chunks/{ProfilePage-DLftC6KZ.js → ProfilePage-DeTw-59a.js} +2 -2
- package/dist/chunks/{ProfilePage-DLftC6KZ.js.map → ProfilePage-DeTw-59a.js.map} +1 -1
- package/dist/chunks/{ProjectInfoPage-DkuBLC8t.js → ProjectInfoPage-Bqn3gfXa.js} +2 -2
- package/dist/chunks/{ProjectInfoPage-DkuBLC8t.js.map → ProjectInfoPage-Bqn3gfXa.js.map} +1 -1
- package/dist/chunks/{RelationPanel-B4BeO0Bz.js → RelationPanel-Bio-ig-l.js} +2 -2
- package/dist/chunks/{RelationPanel-B4BeO0Bz.js.map → RelationPanel-Bio-ig-l.js.map} +1 -1
- package/dist/chunks/{RolesPage-BhltU2Qm.js → RolesPage-fwUSs2HR.js} +2 -2
- package/dist/chunks/{RolesPage-BhltU2Qm.js.map → RolesPage-fwUSs2HR.js.map} +1 -1
- package/dist/chunks/{SessionConflictModal-3R8jdYkI.js → SessionConflictModal-Dw4opoNP.js} +2 -2
- package/dist/chunks/{SessionConflictModal-3R8jdYkI.js.map → SessionConflictModal-Dw4opoNP.js.map} +1 -1
- package/dist/chunks/{SettingModuleDetail-Dppk9An1.js → SettingModuleDetail-I9CSJ-fh.js} +2 -2
- package/dist/chunks/{SettingModuleDetail-Dppk9An1.js.map → SettingModuleDetail-I9CSJ-fh.js.map} +1 -1
- package/dist/chunks/{SettingsPage-DMzeDrnc.js → SettingsPage-Cy5-q-P6.js} +2 -2
- package/dist/chunks/{SettingsPage-DMzeDrnc.js.map → SettingsPage-Cy5-q-P6.js.map} +1 -1
- package/dist/chunks/{StatCard-C0xSZyiS.js → StatCard-BROG4NSp.js} +2 -2
- package/dist/chunks/{StatCard-C0xSZyiS.js.map → StatCard-BROG4NSp.js.map} +1 -1
- package/dist/chunks/{UserGroupsPage-DTJdszHl.js → UserGroupsPage-CsTBkUPy.js} +2 -2
- package/dist/chunks/{UserGroupsPage-DTJdszHl.js.map → UserGroupsPage-CsTBkUPy.js.map} +1 -1
- package/dist/chunks/{UserLogsPanels-z4UhQtgu.js → UserLogsPanels-3u8Z93YU.js} +2 -2
- package/dist/chunks/{UserLogsPanels-z4UhQtgu.js.map → UserLogsPanels-3u8Z93YU.js.map} +1 -1
- package/dist/chunks/{UserTransferSelect-BJNUXMhl.js → UserTransferSelect-ChLj4EkS.js} +2 -2
- package/dist/chunks/{UserTransferSelect-BJNUXMhl.js.map → UserTransferSelect-ChLj4EkS.js.map} +1 -1
- package/dist/chunks/{UsersPage--p68X2Yr.js → UsersPage-CfNnQJi4.js} +2 -2
- package/dist/chunks/{UsersPage--p68X2Yr.js.map → UsersPage-CfNnQJi4.js.map} +1 -1
- package/dist/chunks/{browser-BKLM0ThC-3Xar5rZw.js → browser-BKLM0ThC-D0TgzKTC.js} +1 -1
- package/dist/chunks/{code-P0HJS_q1.js → code-BkkeN2fd.js} +1 -1
- package/dist/chunks/{direct-engine-BYEscsGg.js → direct-engine-m32m22nx.js} +1 -1
- package/dist/chunks/{html-B__1Vr_C.js → html-BGNWR_Bp.js} +1 -1
- package/dist/chunks/{line-DzLRZVEq.js → line-CsTeuEcK.js} +1 -1
- package/dist/chunks/{lrc-kf-TilFB.js → lrc-D6gjnGZ5.js} +1 -1
- package/dist/chunks/{markdown-xIo-vnKK.js → markdown-BglUe9j8.js} +1 -1
- package/dist/chunks/{patch-BEaX-xYV.js → patch-C97Yl3jr.js} +1 -1
- package/dist/chunks/{worker-engine-FCdIQLzN.js → worker-engine-B7_3qQVS.js} +1 -1
- package/dist/core/hooks/queries/settings.d.ts +6 -0
- package/dist/core/hooks/queries/settings.d.ts.map +1 -1
- package/dist/core/hooks/queries/settings.js +34 -25
- package/dist/core/hooks/queries/settings.js.map +1 -1
- package/dist/core/hooks/useAuth.d.ts +2 -0
- package/dist/core/hooks/useAuth.d.ts.map +1 -1
- package/dist/core/hooks/useAuth.js.map +1 -1
- package/dist/core/index.js +7 -7
- package/dist/core/lib/go-auth-api.d.ts +1 -1
- package/dist/core/lib/go-auth-api.d.ts.map +1 -1
- package/dist/core/lib/go-auth-api.js +12 -8
- package/dist/core/lib/go-auth-api.js.map +1 -1
- package/dist/core/lib/native-operations.d.ts +85 -4
- package/dist/core/lib/native-operations.d.ts.map +1 -1
- package/dist/core/lib/native-operations.js +1 -0
- package/dist/core/lib/native-operations.js.map +1 -1
- package/dist/core/providers/GoAuthProvider.d.ts.map +1 -1
- package/dist/core/providers/GoAuthProvider.js +191 -142
- package/dist/core/providers/GoAuthProvider.js.map +1 -1
- package/dist/core/session-renewal.d.ts +15 -0
- package/dist/core/session-renewal.d.ts.map +1 -0
- package/dist/core/session-renewal.js +95 -0
- package/dist/core/session-renewal.js.map +1 -0
- package/dist/core/session-transition.d.ts +2 -0
- package/dist/core/session-transition.d.ts.map +1 -1
- package/dist/core/session-transition.js +20 -7
- package/dist/core/session-transition.js.map +1 -1
- package/dist/core/session.d.ts +4 -0
- package/dist/core/session.d.ts.map +1 -1
- package/dist/core/session.js +88 -61
- package/dist/core/session.js.map +1 -1
- package/dist/index.js +1 -1
- package/dist/{src-C8_bzBi4.js → src-DAkFn9SG.js} +2 -2
- package/dist/{src-C8_bzBi4.js.map → src-DAkFn9SG.js.map} +1 -1
- package/package.json +9 -9
- package/dist/chunks/EntityContextRuntime-BOJySc1D.js +0 -1
- package/dist/chunks/IdentitySecurityPage-DW5ybVL4.js +0 -9
- package/dist/chunks/IdentitySecurityPage-DW5ybVL4.js.map +0 -1
|
@@ -1,9 +0,0 @@
|
|
|
1
|
-
import{n as e}from"./rolldown-runtime-Bruge11p.js";import{H as t,Ht as n,M as r,R as i,T as a,V as o,Vt as s,b as c,bt as l,f as u,m as d,xt as f}from"./clipboard-dpqHdCyy.js";import{i as p}from"./feedback-B1UL2ETz.js";import{n as m,t as h}from"./MasterDetailLayout-KVizcSNO.js";import{i as g,s as _}from"./toolbar-controls-CrM8hKZg.js";import{n as v,t as ee}from"./info-CMEL1G7n.js";import{n as y,t as b}from"./monitor-DjHEaSak.js";import{n as x,t as S}from"./save-CSrPWJxC.js";import{n as C,t as te}from"./shield-Cf8xmgtJ.js";import{n as ne,r as re}from"./NavListPanel-Iz1RCkVw.js";import{i as ie,n as ae,r as w,t as T}from"./useSectionSelection-DBggfIoz.js";import{useEffect as E,useRef as D,useState as O}from"react";import{Fragment as k,jsx as A,jsxs as j}from"react/jsx-runtime";import{GoaError as M}from"@fudanda/client";import{usePermission as N}from"@fudanda/admin-antd/core/hooks/usePermission";import{useSaveSettings as P,useSettings as F}from"@fudanda/admin-antd/core/hooks/queries/settings";import{SESSION_CONCURRENCY_SCOPES as I,SESSION_CONCURRENCY_SCOPE_LABELS as L,SESSION_EXCEED_ACTIONS as R,SESSION_EXCEED_ACTION_LABELS as z}from"@fudanda/client/identity";import{formatSessionPolicyHint as B}from"@fudanda/client/settings";import{ApiIdentitySecuritySettingsSchema as V}from"@fudanda/client/api/settings";import{foundationSettingDefaults as H}from"@fudanda/client/settings/foundation";var U,W;function G(){return(G=e((()=>{
|
|
2
|
-
/**
|
|
3
|
-
* @license lucide-react v1.67.0 - ISC
|
|
4
|
-
*
|
|
5
|
-
* This source code is licensed under the ISC license.
|
|
6
|
-
* See the LICENSE file in the root directory of this source tree.
|
|
7
|
-
*/
|
|
8
|
-
f(),U={name:`key`,size:24,node:[[`path`,{d:`m2 21 9.6-9.6`,key:`9l79m3`}],[`path`,{d:`m7.5 15.5 2.3 2.3a1 1 0 0 1 0 1.4l-2.1 2.1a1 1 0 0 1-1.4 0L4 19`,key:`fw8biw`}],[`circle`,{cx:`15.5`,cy:`7.5`,r:`5.5`,key:`4wxmhb`}]]},U.node,W=l(U)})))()}function K(e){return e<=0?`unlimited`:e===1?`single`:`limited`}function q(){let e=o(),{values:r}=t(),a=r?.session,c={maxSessions:a?.maxSessions??0,scope:a?.scope??`global`,exceedAction:a?.exceedAction??`kick-oldest`},l=K(c.maxSessions),u=l===`unlimited`,d=B(c),f=t=>{let n=t===`unlimited`?0:t===`single`?1:Math.max(c.maxSessions,J);e.setValue(`session.maxSessions`,n)};return/* @__PURE__ */ j(k,{children:[/* @__PURE__ */ A(i.Slot,{label:`同时在线`,children:/* @__PURE__ */ j(`div`,{className:`session-policy-limit`,children:[/* @__PURE__ */ j(n,{value:l,onChange:e=>f(e.target.value),"aria-label":`同时在线上限`,children:[/* @__PURE__ */ A(s,{value:`unlimited`,children:`不限制`}),/* @__PURE__ */ A(s,{value:`single`,children:`仅一处登录`}),/* @__PURE__ */ A(s,{value:`limited`,children:`最多 N 处`})]}),/* @__PURE__ */ A(i.InputNumber,{field:`session.maxSessions`,noLabel:!0,min:l===`limited`?2:0,max:20,style:{width:120},fieldStyle:{padding:0},fieldClassName:l===`limited`?void 0:`session-policy-limit-hidden`,suffix:`处`,"aria-label":`同时在线上限(处)`})]})}),/* @__PURE__ */ A(i.Select,{field:`session.scope`,label:`统计范围`,disabled:u,style:{width:260},optionList:I.map(e=>({value:e,label:L[e]})),extraText:`按终端分别计算时,网页、移动审批、桌面端各占一份名额`}),/* @__PURE__ */ A(i.RadioGroup,{field:`session.exceedAction`,label:`超限处理`,disabled:u,options:R.map(e=>({value:e,label:z[e]})),extraText:`拒绝新登录时,用户可在登录页选择下线其它设备后继续登录`}),/* @__PURE__ */ A(i.Slot,{label:` `,children:/* @__PURE__ */ j(`div`,{className:`session-policy-preview`,role:`status`,children:[/* @__PURE__ */ A(ee,{size:14,"aria-hidden":!0}),/* @__PURE__ */ A(`span`,{children:d})]})})]})}var J;function Y(){return(Y=e((()=>{r(),v(),J=3})))()}function oe(){let{hasPermission:e}=N(),t=e(`system:identity-security:manage`),n=ae(Z,`password`),r=n.active,o=D(null),[s,l]=O(Q),f=F(`identitySecurity`,t),m=P(`identitySecurity`);E(()=>{f.data&&l(V.parse(f.data.effective))},[f.data]);async function _(){if(!o.current||!f.data)return;let e;try{e=await o.current?.validate()}catch{let e=o.current.getFieldsError().find(({errors:e})=>e.length>0)?.name[0];e===`password`?n.select(`password`):e===`loginChallenge`?n.select(`login-challenge`):e===`session`&&n.select(`session`);return}try{let t=await m.mutateAsync({body:{version:f.data?.version??0,data:e}});l(V.parse(t.effective)),p.success(`身份安全策略已保存`)}catch(e){e instanceof M&&e.status===409&&f.refetch()}}return t?/* @__PURE__ */ A(`div`,{className:`page-container page-container--stretch`,children:/* @__PURE__ */ A(h,{defaultSize:260,minSize:200,maxSize:400,persistKey:`identity-security`,style:{flex:1,minHeight:0,overflow:`hidden`},showDetail:r!==null,onBack:n.back,onResponsiveChange:n.responsiveChange,master:/* @__PURE__ */ A(ne,{title:`安全模块`,rawBody:!0,children:/* @__PURE__ */ A(w,{sections:X,active:r,onSelect:n.select})}),detail:/* @__PURE__ */ j(k,{children:[/* @__PURE__ */ A(h.Header,{extra:/* @__PURE__ */ j(a,{children:[/* @__PURE__ */ A(d,{type:`primary`,icon:/* @__PURE__ */ A(S,{size:14}),loading:m.isPending,disabled:!f.data||f.isFetching,onClick:_,children:`保存`}),/* @__PURE__ */ A(g,{onClick:()=>void f.refetch(),loading:f.isFetching,children:`重载`})]}),children:/* @__PURE__ */ A(`strong`,{children:X.find(e=>e.key===r)?.title??`身份安全`})}),/* @__PURE__ */ j(h.Body,{padding:24,children:[f.error&&/* @__PURE__ */ A(u,{type:`danger`,description:`身份安全策略加载失败,请重载后重试。`}),/* @__PURE__ */ j(i,{initValues:s,getFormApi:e=>{o.current=e},disabled:!f.data||m.isPending,labelPosition:`left`,labelWidth:150,style:{maxWidth:760},children:[/* @__PURE__ */ j(`section`,{hidden:r!==`password`,children:[/* @__PURE__ */ A(i.InputNumber,{field:`password.minLength`,label:`最小长度`,min:6,max:64}),/* @__PURE__ */ A(i.Switch,{field:`password.requireUppercase`,label:`必须包含大写`}),/* @__PURE__ */ A(i.Switch,{field:`password.requireSpecialChar`,label:`必须包含特殊字符`}),/* @__PURE__ */ A(i.Switch,{field:`password.expiryEnabled`,label:`启用密码过期`}),/* @__PURE__ */ A(i.InputNumber,{field:`password.expiryDays`,label:`过期天数`,min:1,max:3650})]}),/* @__PURE__ */ j(`section`,{hidden:r!==`login-challenge`,children:[/* @__PURE__ */ A(i.InputNumber,{field:`loginChallenge.maxAttemptsPerSource`,label:`单来源失败阈值`,min:1,max:1e3,extraText:`同一账号 + 同一 IP 的失败次数,达到后该来源需先过验证码`}),/* @__PURE__ */ A(i.InputNumber,{field:`loginChallenge.sourceLimit`,label:`多来源失败阈值`,min:1,max:100,extraText:`窗口内失败来源 IP 数,达到后该账号所有来源都需验证码(防分布式猜解)`}),/* @__PURE__ */ A(i.InputNumber,{field:`loginChallenge.windowMinutes`,label:`计数窗口(分钟)`,min:1,max:1440})]}),/* @__PURE__ */ A(`section`,{hidden:r!==`session`,children:/* @__PURE__ */ A(q,{})})]},JSON.stringify(s))]})]})})}):/* @__PURE__ */ A(c,{description:`无访问权限`})}var X,Z,Q;function $(){return($=e((()=>{r(),G(),y(),x(),C(),m(),re(),ie(),T(),_(),Y(),X=[{key:`password`,title:`密码策略`,description:`密码强度与过期规则`,icon:W},{key:`login-challenge`,title:`登录失败防护`,description:`失败阈值与验证码防护`,icon:te},{key:`session`,title:`会话并发`,description:`同时在线设备与冲突策略`,icon:b}],Z=X.map(e=>e.key),Q=H.identitySecurity})))()}$();export{oe as default};
|
|
9
|
-
//# sourceMappingURL=IdentitySecurityPage-DW5ybVL4.js.map
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
{"version":3,"file":"IdentitySecurityPage-DW5ybVL4.js","mappings":"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","names":["__iconData","createLucideIcon","modeOf","maxSessions","SessionPolicyFields","formApi","useFormApi","useFormState","draft","values","session","mode","unlimited","preview","formatSessionPolicyHint","changeMode","value","next","DEFAULT_LIMITED","_Fragment","Form","RadioGroup","Radio","SESSION_CONCURRENCY_SCOPES","SESSION_CONCURRENCY_SCOPE_LABELS","SESSION_EXCEED_ACTIONS","SESSION_EXCEED_ACTION_LABELS","Info","IdentitySecurityPage","usePermission","canManagePolicy","hasPermission","navigation","useSectionSelection","SECTION_KEYS","activeSection","useRef","policy","setPolicy","useState","defaultPolicy","policyQuery","useSettings","savePolicyMutation","useSaveSettings","useEffect","identitySecuritySettingsSchema","handleSavePolicy","field","errors","saved","Toast","err","GoaError","MasterDetailLayout","NavListPanel","SectionNavigation","SECTIONS","Space","Button","Save","RefreshButton","section","Banner","api","Empty","Key","Shield","Monitor","foundationSettingDefaults"],"ignoreList":[0],"sources":["../../../../node_modules/lucide-react/dist/esm/icons/key.mjs","../../src/pages/system/identity-security/SessionPolicyFields.tsx","../../src/pages/system/identity-security/IdentitySecurityPage.tsx"],"sourcesContent":[null,"import { Form, RadioGroup, Radio, useFormApi, useFormState } from \"@/ui\";\nimport { Info } from 'lucide-react';\nimport { SESSION_CONCURRENCY_SCOPE_LABELS, SESSION_CONCURRENCY_SCOPES, SESSION_EXCEED_ACTION_LABELS, SESSION_EXCEED_ACTIONS } from '@fudanda/shared/identity';\nimport { formatSessionPolicyHint, type SessionConcurrencyPolicy } from '@fudanda/shared/settings';\ntype LimitMode = 'unlimited' | 'single' | 'limited';\n/** 「最多 N 处」的默认起点:从单处切过来时给一个有意义的 N,避免落在 0 / 1 上又变回别的模式 */\nconst DEFAULT_LIMITED = 3;\nfunction modeOf(maxSessions: number): LimitMode {\n if (maxSessions <= 0)\n return 'unlimited';\n return maxSessions === 1 ? 'single' : 'limited';\n}\n/**\n * 身份安全 → 会话并发。渲染在 <Form> 内:同时在线用单选表达「不限 / 单处 / 最多 N 处」,\n * 落到同一个 session.maxSessions 字段;不限制时范围与超限处理置灰。\n */\nexport function SessionPolicyFields() {\n const formApi = useFormApi();\n const { values } = useFormState<{\n session?: Partial<SessionConcurrencyPolicy>;\n }>();\n const draft = values?.session;\n const session: SessionConcurrencyPolicy = {\n maxSessions: draft?.maxSessions ?? 0,\n scope: draft?.scope ?? 'global',\n exceedAction: draft?.exceedAction ?? 'kick-oldest',\n };\n const mode = modeOf(session.maxSessions);\n const unlimited = mode === 'unlimited';\n const preview = formatSessionPolicyHint(session);\n const changeMode = (next: LimitMode) => {\n const value = next === 'unlimited' ? 0 : next === 'single' ? 1 : Math.max(session.maxSessions, DEFAULT_LIMITED);\n formApi.setValue('session.maxSessions', value);\n };\n return (<>\n <Form.Slot label=\"同时在线\">\n <div className=\"session-policy-limit\">\n <RadioGroup value={mode} onChange={(e) => changeMode(e.target.value as LimitMode)} aria-label=\"同时在线上限\">\n <Radio value=\"unlimited\">不限制</Radio>\n <Radio value=\"single\">仅一处登录</Radio>\n <Radio value=\"limited\">最多 N 处</Radio>\n </RadioGroup>\n {/* 常驻挂载:Semi Field 卸载会连带移除表单值,切回「不限 / 单处」时 maxSessions 必须仍在提交体里;非 N 处模式仅隐藏 */}\n <Form.InputNumber field=\"session.maxSessions\" noLabel min={mode === 'limited' ? 2 : 0} max={20} style={{ width: 120 }} fieldStyle={{ padding: 0 }} fieldClassName={mode === 'limited' ? undefined : 'session-policy-limit-hidden'} suffix=\"处\" aria-label=\"同时在线上限(处)\"/>\n </div>\n </Form.Slot>\n <Form.Select field=\"session.scope\" label=\"统计范围\" disabled={unlimited} style={{ width: 260 }} optionList={SESSION_CONCURRENCY_SCOPES.map((value) => ({ value, label: SESSION_CONCURRENCY_SCOPE_LABELS[value] }))} extraText=\"按终端分别计算时,网页、移动审批、桌面端各占一份名额\"/>\n <Form.RadioGroup field=\"session.exceedAction\" label=\"超限处理\" disabled={unlimited} options={SESSION_EXCEED_ACTIONS.map((value) => ({ value, label: SESSION_EXCEED_ACTION_LABELS[value] }))} extraText=\"拒绝新登录时,用户可在登录页选择下线其它设备后继续登录\"/>\n <Form.Slot label=\" \">\n <div className=\"session-policy-preview\" role=\"status\">\n <Info size={14} aria-hidden/>\n <span>{preview}</span>\n </div>\n </Form.Slot>\n </>);\n}\n","import { useEffect, useRef, useState } from 'react';\nimport { Banner, Button, Empty, Form, Space, Toast } from \"@/ui\";\nimport type { FormApi } from \"@/ui\";\nimport { Key, Monitor, Save, Shield } from 'lucide-react';\nimport { ApiIdentitySecuritySettingsSchema as identitySecuritySettingsSchema, type ApiIdentitySecuritySettings as IdentitySecuritySettings } from '@fudanda/shared/api/settings';\nimport { foundationSettingDefaults } from '@fudanda/shared/settings/foundation';\nimport { MasterDetailLayout } from '@/components/MasterDetailLayout';\nimport { NavListPanel } from '@/components/NavListPanel';\nimport { SectionNavigation } from '@/components/SectionNavigation';\nimport { useSectionSelection } from '@/hooks/useSectionSelection';\n\nimport { usePermission } from \"@fudanda/react-core/hooks/usePermission\";\nimport { useSaveSettings, useSettings } from \"@fudanda/react-core/hooks/queries/settings\";\nimport { GoaError } from '@fudanda/client';\nimport { RefreshButton } from \"@/components/toolbar-controls\";\nimport { SessionPolicyFields } from \"@/pages/system/identity-security/SessionPolicyFields\";\nimport \"@/pages/system/identity-security/IdentitySecurityPage.css\";\nconst SECTIONS = [\n { key: 'password', title: '密码策略', description: '密码强度与过期规则', icon: Key },\n { key: 'login-challenge', title: '登录失败防护', description: '失败阈值与验证码防护', icon: Shield },\n { key: 'session', title: '会话并发', description: '同时在线设备与冲突策略', icon: Monitor },\n] as const;\nconst SECTION_KEYS = SECTIONS.map(section => section.key);\n// 默认值以 shared schema 为唯一真相(通用设置页与服务端同源)\nconst defaultPolicy: IdentitySecuritySettings = foundationSettingDefaults.identitySecurity;\nexport default function IdentitySecurityPage() {\n const { hasPermission } = usePermission();\n const canManagePolicy = hasPermission('system:identity-security:manage');\n const navigation = useSectionSelection(SECTION_KEYS, 'password');\n const activeSection = navigation.active;\n // useEditModal 例外:页面级全局配置表单(身份安全策略),保存后不关闭;表单 key 跟随策略值重挂载\n const formApi = useRef<FormApi | null>(null);\n const [policy, setPolicy] = useState<IdentitySecuritySettings>(defaultPolicy);\n // 页面级全局配置表单(无弹窗、保存后不关闭),不走 useEditModal;策略由运行时设置 identitySecurity 模块承载\n const policyQuery = useSettings('identitySecurity', canManagePolicy);\n const savePolicyMutation = useSaveSettings('identitySecurity');\n useEffect(() => {\n if (policyQuery.data)\n setPolicy(identitySecuritySettingsSchema.parse(policyQuery.data.effective));\n }, [policyQuery.data]);\n async function handleSavePolicy() {\n if (!formApi.current || !policyQuery.data) return;\n let values: IdentitySecuritySettings;\n try {\n values = await formApi.current?.validate() as IdentitySecuritySettings;\n }\n catch {\n const field = formApi.current.getFieldsError().find(({ errors }) => errors.length > 0)?.name[0];\n if (field === 'password') navigation.select('password');\n else if (field === 'loginChallenge') navigation.select('login-challenge');\n else if (field === 'session') navigation.select('session');\n return;\n }\n try {\n const saved = await savePolicyMutation.mutateAsync({ body: { version: policyQuery.data?.version ?? 0, data: values } });\n setPolicy(identitySecuritySettingsSchema.parse(saved.effective));\n Toast.success('身份安全策略已保存');\n }\n catch (err) {\n // 409 = 他人已修改:请求层已提示,重载最新值供比对后再保存\n if (err instanceof GoaError && err.status === 409)\n void policyQuery.refetch();\n }\n }\n if (!canManagePolicy)\n return <Empty description=\"无访问权限\"/>;\n return (<div className=\"page-container page-container--stretch\">\n <MasterDetailLayout defaultSize={260} minSize={200} maxSize={400} persistKey=\"identity-security\"\n style={{ flex: 1, minHeight: 0, overflow: 'hidden' }} showDetail={activeSection !== null}\n onBack={navigation.back} onResponsiveChange={navigation.responsiveChange}\n master={<NavListPanel title=\"安全模块\" rawBody>\n <SectionNavigation sections={SECTIONS} active={activeSection} onSelect={navigation.select} />\n </NavListPanel>}\n detail={<>\n <MasterDetailLayout.Header extra={<Space>\n <Button type=\"primary\" icon={<Save size={14}/>} loading={savePolicyMutation.isPending} disabled={!policyQuery.data || policyQuery.isFetching} onClick={handleSavePolicy}>保存</Button>\n <RefreshButton onClick={() => void policyQuery.refetch()} loading={policyQuery.isFetching}>重载</RefreshButton>\n </Space>}>\n <strong>{SECTIONS.find(section => section.key === activeSection)?.title ?? '身份安全'}</strong>\n </MasterDetailLayout.Header>\n <MasterDetailLayout.Body padding={24}>\n {policyQuery.error && <Banner type=\"danger\" description=\"身份安全策略加载失败,请重载后重试。\" />}\n <Form key={JSON.stringify(policy)} initValues={policy} getFormApi={(api) => { formApi.current = api; }} disabled={!policyQuery.data || savePolicyMutation.isPending} labelPosition=\"left\" labelWidth={150} style={{ maxWidth: 760 }}>\n <section hidden={activeSection !== 'password'}>\n <Form.InputNumber field=\"password.minLength\" label=\"最小长度\" min={6} max={64}/>\n <Form.Switch field=\"password.requireUppercase\" label=\"必须包含大写\"/>\n <Form.Switch field=\"password.requireSpecialChar\" label=\"必须包含特殊字符\"/>\n <Form.Switch field=\"password.expiryEnabled\" label=\"启用密码过期\"/>\n <Form.InputNumber field=\"password.expiryDays\" label=\"过期天数\" min={1} max={3650}/>\n </section>\n\n {/* 登录失败不再锁定账号:达到阈值只要求验证码,真正的用户凭正确密码 + 验证码仍可登录 */}\n <section hidden={activeSection !== 'login-challenge'}>\n <Form.InputNumber field=\"loginChallenge.maxAttemptsPerSource\" label=\"单来源失败阈值\" min={1} max={1000} extraText=\"同一账号 + 同一 IP 的失败次数,达到后该来源需先过验证码\"/>\n <Form.InputNumber field=\"loginChallenge.sourceLimit\" label=\"多来源失败阈值\" min={1} max={100} extraText=\"窗口内失败来源 IP 数,达到后该账号所有来源都需验证码(防分布式猜解)\"/>\n <Form.InputNumber field=\"loginChallenge.windowMinutes\" label=\"计数窗口(分钟)\" min={1} max={1440}/>\n </section>\n <section hidden={activeSection !== 'session'}>\n <SessionPolicyFields />\n </section>\n </Form>\n </MasterDetailLayout.Body>\n </>} />\n </div>);\n}\n"]}
|