@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.
Files changed (124) hide show
  1. package/dist/chunks/{App-BF96TQ_D.js → App-CfQx7jWq.js} +4 -4
  2. package/dist/chunks/{App-BF96TQ_D.js.map → App-CfQx7jWq.js.map} +1 -1
  3. package/dist/chunks/{AppModal-CLkMJ-96.js → AppModal-CVoUC2t3.js} +2 -2
  4. package/dist/chunks/{AppModal-CLkMJ-96.js.map → AppModal-CVoUC2t3.js.map} +1 -1
  5. package/dist/chunks/{ArcBaseAdmin--xQxd4tg.js → ArcBaseAdmin-DQyddbFC.js} +2 -2
  6. package/dist/chunks/{ArcBaseAdmin--xQxd4tg.js.map → ArcBaseAdmin-DQyddbFC.js.map} +1 -1
  7. package/dist/chunks/{DepartmentsPage-D6YVegPU.js → DepartmentsPage-CQx53QGE.js} +2 -2
  8. package/dist/chunks/{DepartmentsPage-D6YVegPU.js.map → DepartmentsPage-CQx53QGE.js.map} +1 -1
  9. package/dist/chunks/{DictsPage-uV3Uukwc.js → DictsPage-BGnamkWk.js} +2 -2
  10. package/dist/chunks/{DictsPage-uV3Uukwc.js.map → DictsPage-BGnamkWk.js.map} +1 -1
  11. package/dist/chunks/EntityContextRuntime-C9q95WDf.js +1 -0
  12. package/dist/chunks/{EntityContextRuntime-DbjyzCtY.js → EntityContextRuntime-CbzeMNXR.js} +2 -2
  13. package/dist/chunks/{EntityContextRuntime-DbjyzCtY.js.map → EntityContextRuntime-CbzeMNXR.js.map} +1 -1
  14. package/dist/chunks/{ExportButton-e2ybxuHb.js → ExportButton-sMPAV2hd.js} +2 -2
  15. package/dist/chunks/{ExportButton-e2ybxuHb.js.map → ExportButton-sMPAV2hd.js.map} +1 -1
  16. package/dist/chunks/{FileViewerPreviewPanel-BhI3pTi8.js → FileViewerPreviewPanel-Rti7pUhU.js} +3 -3
  17. package/dist/chunks/{FileViewerPreviewPanel-BhI3pTi8.js.map → FileViewerPreviewPanel-Rti7pUhU.js.map} +1 -1
  18. package/dist/chunks/{FilesPage-BI35mStl.js → FilesPage-D_iYsUwl.js} +3 -3
  19. package/dist/chunks/{FilesPage-BI35mStl.js.map → FilesPage-D_iYsUwl.js.map} +1 -1
  20. package/dist/chunks/IdentitySecurityPage-rDYX5ksX.js +9 -0
  21. package/dist/chunks/IdentitySecurityPage-rDYX5ksX.js.map +1 -0
  22. package/dist/chunks/{LogStatsScaffold-m0vVUXPh.js → LogStatsScaffold-CMIz4z0a.js} +2 -2
  23. package/dist/chunks/{LogStatsScaffold-m0vVUXPh.js.map → LogStatsScaffold-CMIz4z0a.js.map} +1 -1
  24. package/dist/chunks/{LoginLogsPage-BzBUR4Oo.js → LoginLogsPage-Bv5pPJb2.js} +2 -2
  25. package/dist/chunks/{LoginLogsPage-BzBUR4Oo.js.map → LoginLogsPage-Bv5pPJb2.js.map} +1 -1
  26. package/dist/chunks/{LoginLogsTable-BweUEjQX.js → LoginLogsTable-DYcTx2oR.js} +2 -2
  27. package/dist/chunks/{LoginLogsTable-BweUEjQX.js.map → LoginLogsTable-DYcTx2oR.js.map} +1 -1
  28. package/dist/chunks/{MarkdownPreviewPanel-C1OKSLmh.js → MarkdownPreviewPanel-lqs6AsoN.js} +2 -2
  29. package/dist/chunks/{MarkdownPreviewPanel-C1OKSLmh.js.map → MarkdownPreviewPanel-lqs6AsoN.js.map} +1 -1
  30. package/dist/chunks/{MasterDetailLayout-KVizcSNO.js → MasterDetailLayout-B5uqvh98.js} +2 -2
  31. package/dist/chunks/{MasterDetailLayout-KVizcSNO.js.map → MasterDetailLayout-B5uqvh98.js.map} +1 -1
  32. package/dist/chunks/{McpSettingsPage-Dhf0CbDN.js → McpSettingsPage-Cdej9Evi.js} +2 -2
  33. package/dist/chunks/{McpSettingsPage-Dhf0CbDN.js.map → McpSettingsPage-Cdej9Evi.js.map} +1 -1
  34. package/dist/chunks/{MemberAssignmentSheet-TYpnyA0z.js → MemberAssignmentSheet-AKvLbbYW.js} +2 -2
  35. package/dist/chunks/{MemberAssignmentSheet-TYpnyA0z.js.map → MemberAssignmentSheet-AKvLbbYW.js.map} +1 -1
  36. package/dist/chunks/{MenuCommandPalette-BcvpUfK7.js → MenuCommandPalette-ia0pSIFg.js} +2 -2
  37. package/dist/chunks/{MenuCommandPalette-BcvpUfK7.js.map → MenuCommandPalette-ia0pSIFg.js.map} +1 -1
  38. package/dist/chunks/{MenusPage-Dd7OLQYT.js → MenusPage-Bh55bvKH.js} +2 -2
  39. package/dist/chunks/{MenusPage-Dd7OLQYT.js.map → MenusPage-Bh55bvKH.js.map} +1 -1
  40. package/dist/chunks/{MonacoPreviewPanel-BW1j3ZTF.js → MonacoPreviewPanel-CT_sjf7Z.js} +2 -2
  41. package/dist/chunks/{MonacoPreviewPanel-BW1j3ZTF.js.map → MonacoPreviewPanel-CT_sjf7Z.js.map} +1 -1
  42. package/dist/chunks/{NavListPanel-Iz1RCkVw.js → NavListPanel-DMIUMh7q.js} +2 -2
  43. package/dist/chunks/{NavListPanel-Iz1RCkVw.js.map → NavListPanel-DMIUMh7q.js.map} +1 -1
  44. package/dist/chunks/{OperationLogsPage-Dt22tU4I.js → OperationLogsPage-7XVfmhxP.js} +2 -2
  45. package/dist/chunks/{OperationLogsPage-Dt22tU4I.js.map → OperationLogsPage-7XVfmhxP.js.map} +1 -1
  46. package/dist/chunks/{OperationLogsTable-B23Ry-2v.js → OperationLogsTable-gp-WZxiF.js} +3 -3
  47. package/dist/chunks/{OperationLogsTable-B23Ry-2v.js.map → OperationLogsTable-gp-WZxiF.js.map} +1 -1
  48. package/dist/chunks/{PDFPreviewPanel-kBPz5dQr.js → PDFPreviewPanel-CyfhyWav.js} +3 -3
  49. package/dist/chunks/{PDFPreviewPanel-kBPz5dQr.js.map → PDFPreviewPanel-CyfhyWav.js.map} +1 -1
  50. package/dist/chunks/{PositionsPage-BIKYQDB5.js → PositionsPage-Bwua1vNV.js} +2 -2
  51. package/dist/chunks/{PositionsPage-BIKYQDB5.js.map → PositionsPage-Bwua1vNV.js.map} +1 -1
  52. package/dist/chunks/{PreferencesSheetBody-D276rrp4.js → PreferencesSheetBody-C80D3dOM.js} +2 -2
  53. package/dist/chunks/{PreferencesSheetBody-D276rrp4.js.map → PreferencesSheetBody-C80D3dOM.js.map} +1 -1
  54. package/dist/chunks/{ProfilePage-DLftC6KZ.js → ProfilePage-DeTw-59a.js} +2 -2
  55. package/dist/chunks/{ProfilePage-DLftC6KZ.js.map → ProfilePage-DeTw-59a.js.map} +1 -1
  56. package/dist/chunks/{ProjectInfoPage-DkuBLC8t.js → ProjectInfoPage-Bqn3gfXa.js} +2 -2
  57. package/dist/chunks/{ProjectInfoPage-DkuBLC8t.js.map → ProjectInfoPage-Bqn3gfXa.js.map} +1 -1
  58. package/dist/chunks/{RelationPanel-B4BeO0Bz.js → RelationPanel-Bio-ig-l.js} +2 -2
  59. package/dist/chunks/{RelationPanel-B4BeO0Bz.js.map → RelationPanel-Bio-ig-l.js.map} +1 -1
  60. package/dist/chunks/{RolesPage-BhltU2Qm.js → RolesPage-fwUSs2HR.js} +2 -2
  61. package/dist/chunks/{RolesPage-BhltU2Qm.js.map → RolesPage-fwUSs2HR.js.map} +1 -1
  62. package/dist/chunks/{SessionConflictModal-3R8jdYkI.js → SessionConflictModal-Dw4opoNP.js} +2 -2
  63. package/dist/chunks/{SessionConflictModal-3R8jdYkI.js.map → SessionConflictModal-Dw4opoNP.js.map} +1 -1
  64. package/dist/chunks/{SettingModuleDetail-Dppk9An1.js → SettingModuleDetail-I9CSJ-fh.js} +2 -2
  65. package/dist/chunks/{SettingModuleDetail-Dppk9An1.js.map → SettingModuleDetail-I9CSJ-fh.js.map} +1 -1
  66. package/dist/chunks/{SettingsPage-DMzeDrnc.js → SettingsPage-Cy5-q-P6.js} +2 -2
  67. package/dist/chunks/{SettingsPage-DMzeDrnc.js.map → SettingsPage-Cy5-q-P6.js.map} +1 -1
  68. package/dist/chunks/{StatCard-C0xSZyiS.js → StatCard-BROG4NSp.js} +2 -2
  69. package/dist/chunks/{StatCard-C0xSZyiS.js.map → StatCard-BROG4NSp.js.map} +1 -1
  70. package/dist/chunks/{UserGroupsPage-DTJdszHl.js → UserGroupsPage-CsTBkUPy.js} +2 -2
  71. package/dist/chunks/{UserGroupsPage-DTJdszHl.js.map → UserGroupsPage-CsTBkUPy.js.map} +1 -1
  72. package/dist/chunks/{UserLogsPanels-z4UhQtgu.js → UserLogsPanels-3u8Z93YU.js} +2 -2
  73. package/dist/chunks/{UserLogsPanels-z4UhQtgu.js.map → UserLogsPanels-3u8Z93YU.js.map} +1 -1
  74. package/dist/chunks/{UserTransferSelect-BJNUXMhl.js → UserTransferSelect-ChLj4EkS.js} +2 -2
  75. package/dist/chunks/{UserTransferSelect-BJNUXMhl.js.map → UserTransferSelect-ChLj4EkS.js.map} +1 -1
  76. package/dist/chunks/{UsersPage--p68X2Yr.js → UsersPage-CfNnQJi4.js} +2 -2
  77. package/dist/chunks/{UsersPage--p68X2Yr.js.map → UsersPage-CfNnQJi4.js.map} +1 -1
  78. package/dist/chunks/{browser-BKLM0ThC-3Xar5rZw.js → browser-BKLM0ThC-D0TgzKTC.js} +1 -1
  79. package/dist/chunks/{code-P0HJS_q1.js → code-BkkeN2fd.js} +1 -1
  80. package/dist/chunks/{direct-engine-BYEscsGg.js → direct-engine-m32m22nx.js} +1 -1
  81. package/dist/chunks/{html-B__1Vr_C.js → html-BGNWR_Bp.js} +1 -1
  82. package/dist/chunks/{line-DzLRZVEq.js → line-CsTeuEcK.js} +1 -1
  83. package/dist/chunks/{lrc-kf-TilFB.js → lrc-D6gjnGZ5.js} +1 -1
  84. package/dist/chunks/{markdown-xIo-vnKK.js → markdown-BglUe9j8.js} +1 -1
  85. package/dist/chunks/{patch-BEaX-xYV.js → patch-C97Yl3jr.js} +1 -1
  86. package/dist/chunks/{worker-engine-FCdIQLzN.js → worker-engine-B7_3qQVS.js} +1 -1
  87. package/dist/core/hooks/queries/settings.d.ts +6 -0
  88. package/dist/core/hooks/queries/settings.d.ts.map +1 -1
  89. package/dist/core/hooks/queries/settings.js +34 -25
  90. package/dist/core/hooks/queries/settings.js.map +1 -1
  91. package/dist/core/hooks/useAuth.d.ts +2 -0
  92. package/dist/core/hooks/useAuth.d.ts.map +1 -1
  93. package/dist/core/hooks/useAuth.js.map +1 -1
  94. package/dist/core/index.js +7 -7
  95. package/dist/core/lib/go-auth-api.d.ts +1 -1
  96. package/dist/core/lib/go-auth-api.d.ts.map +1 -1
  97. package/dist/core/lib/go-auth-api.js +12 -8
  98. package/dist/core/lib/go-auth-api.js.map +1 -1
  99. package/dist/core/lib/native-operations.d.ts +85 -4
  100. package/dist/core/lib/native-operations.d.ts.map +1 -1
  101. package/dist/core/lib/native-operations.js +1 -0
  102. package/dist/core/lib/native-operations.js.map +1 -1
  103. package/dist/core/providers/GoAuthProvider.d.ts.map +1 -1
  104. package/dist/core/providers/GoAuthProvider.js +191 -142
  105. package/dist/core/providers/GoAuthProvider.js.map +1 -1
  106. package/dist/core/session-renewal.d.ts +15 -0
  107. package/dist/core/session-renewal.d.ts.map +1 -0
  108. package/dist/core/session-renewal.js +95 -0
  109. package/dist/core/session-renewal.js.map +1 -0
  110. package/dist/core/session-transition.d.ts +2 -0
  111. package/dist/core/session-transition.d.ts.map +1 -1
  112. package/dist/core/session-transition.js +20 -7
  113. package/dist/core/session-transition.js.map +1 -1
  114. package/dist/core/session.d.ts +4 -0
  115. package/dist/core/session.d.ts.map +1 -1
  116. package/dist/core/session.js +88 -61
  117. package/dist/core/session.js.map +1 -1
  118. package/dist/index.js +1 -1
  119. package/dist/{src-C8_bzBi4.js → src-DAkFn9SG.js} +2 -2
  120. package/dist/{src-C8_bzBi4.js.map → src-DAkFn9SG.js.map} +1 -1
  121. package/package.json +9 -9
  122. package/dist/chunks/EntityContextRuntime-BOJySc1D.js +0 -1
  123. package/dist/chunks/IdentitySecurityPage-DW5ybVL4.js +0 -9
  124. 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"]}