@nocobase/client-v2 2.2.0-beta.1 → 2.2.0-beta.10

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 (247) hide show
  1. package/es/BaseApplication.d.ts +3 -0
  2. package/es/PluginSettingsManager.d.ts +33 -0
  3. package/es/RouteRepository.d.ts +29 -6
  4. package/es/RouterManager.d.ts +15 -0
  5. package/es/authRedirect.d.ts +1 -0
  6. package/es/collection-field-interface/CollectionFieldInterface.d.ts +1 -0
  7. package/es/collection-field-interface/CollectionFieldInterfaceManager.d.ts +1 -0
  8. package/es/components/form/JsonTextArea.d.ts +2 -1
  9. package/es/components/form/VariableJsonTextArea.d.ts +19 -0
  10. package/es/components/form/index.d.ts +1 -0
  11. package/es/components/index.d.ts +1 -0
  12. package/es/entry-actions/EntryActionManager.d.ts +24 -0
  13. package/es/entry-actions/index.d.ts +9 -0
  14. package/es/flow/actions/afterSuccess.d.ts +8 -1
  15. package/es/flow/actions/index.d.ts +1 -1
  16. package/es/flow/admin-shell/BaseLayoutModel.d.ts +6 -0
  17. package/es/flow/admin-shell/BaseLayoutRouteCoordinator.d.ts +7 -0
  18. package/es/flow/admin-shell/admin-layout/AdminLayoutEntryGuard.d.ts +2 -0
  19. package/es/flow/admin-shell/admin-layout/AdminLayoutMenuFlowUtils.d.ts +2 -1
  20. package/es/flow/admin-shell/admin-layout/AdminLayoutMenuModels.d.ts +2 -0
  21. package/es/flow/admin-shell/admin-layout/AdminLayoutMenuUtils.d.ts +3 -2
  22. package/es/flow/admin-shell/admin-layout/AdminLayoutSlotModels.d.ts +5 -0
  23. package/es/flow/admin-shell/admin-layout/AppListRender.d.ts +2 -1
  24. package/es/flow/admin-shell/admin-layout/AppSwitcherActionPanelModel.d.ts +24 -0
  25. package/es/flow/admin-shell/admin-layout/index.d.ts +1 -0
  26. package/es/flow/admin-shell/admin-layout/resolveAdminRouteRuntimeTarget.d.ts +6 -0
  27. package/es/flow/admin-shell/admin-layout/useApplications.d.ts +7 -2
  28. package/es/flow/components/FieldAssignRulesEditor.d.ts +3 -1
  29. package/es/flow/models/base/ActionModelCore.d.ts +2 -0
  30. package/es/flow/models/base/GridModel.d.ts +1 -1
  31. package/es/flow/models/blocks/filter-form/FilterFormBlockModel.d.ts +5 -0
  32. package/es/flow/models/blocks/form/FormBlockModel.d.ts +4 -0
  33. package/es/flow/models/blocks/form/QuickEditFormModel.d.ts +17 -2
  34. package/es/flow/models/blocks/form/submitHandler.d.ts +1 -1
  35. package/es/flow/models/blocks/form/value-runtime/rules.d.ts +21 -0
  36. package/es/flow/models/blocks/form/value-runtime/runtime.d.ts +30 -0
  37. package/es/flow/models/blocks/form/value-runtime/types.d.ts +2 -2
  38. package/es/flow/models/blocks/js-block/JSBlock.d.ts +1 -0
  39. package/es/flow/models/blocks/table/TableActionsColumnModel.d.ts +1 -0
  40. package/es/flow/models/blocks/table/TableBlockModel.d.ts +1 -0
  41. package/es/flow/models/blocks/table/dragSort/dragSortHooks.d.ts +1 -1
  42. package/es/flow/models/blocks/table/dragSort/dragSortSettings.d.ts +2 -1
  43. package/es/flow/models/blocks/table/dragSort/dragSortUtils.d.ts +6 -4
  44. package/es/flow/models/fields/AssociationFieldModel/RecordSelectFieldModel.d.ts +2 -0
  45. package/es/flow/models/fields/AssociationFieldModel/SubTableFieldModel/SubTableColumnModel.d.ts +6 -0
  46. package/es/flow/resolveViewParamsToViewList.d.ts +1 -1
  47. package/es/flow/routeTransientInputArgs.d.ts +14 -0
  48. package/es/flow/utils/dataScopeRowSnapshot.d.ts +59 -0
  49. package/es/flow/utils/dateTimeDisplayProps.d.ts +49 -0
  50. package/es/flow/utils/formValueDeps.d.ts +32 -0
  51. package/es/flow-compat/FieldValidation.d.ts +2 -1
  52. package/es/index.d.ts +4 -1
  53. package/es/index.mjs +305 -157
  54. package/es/utils/markdownSanitize.d.ts +11 -0
  55. package/lib/index.js +305 -157
  56. package/package.json +7 -7
  57. package/src/BaseApplication.tsx +6 -0
  58. package/src/PluginSettingsManager.ts +53 -0
  59. package/src/RouteRepository.ts +151 -24
  60. package/src/RouterManager.tsx +142 -1
  61. package/src/__tests__/PluginSettingsManager.test.ts +13 -0
  62. package/src/__tests__/RouteRepository.test.ts +239 -0
  63. package/src/__tests__/RouterManager.test.ts +125 -0
  64. package/src/__tests__/authRedirect.test.ts +17 -0
  65. package/src/__tests__/exports.test.ts +16 -0
  66. package/src/__tests__/nocobase-buildin-plugin-auth.test.tsx +199 -0
  67. package/src/__tests__/plugin-manager.test.tsx +44 -2
  68. package/src/__tests__/settings-center.test.tsx +40 -1
  69. package/src/authRedirect.ts +38 -0
  70. package/src/collection-field-interface/CollectionFieldInterface.ts +1 -0
  71. package/src/collection-field-interface/CollectionFieldInterfaceManager.ts +1 -0
  72. package/src/collection-manager/field-configure.ts +1 -1
  73. package/src/collection-manager/interfaces/id.ts +1 -1
  74. package/src/collection-manager/interfaces/m2m.tsx +2 -2
  75. package/src/collection-manager/interfaces/m2o.tsx +2 -2
  76. package/src/collection-manager/interfaces/nanoid.ts +1 -1
  77. package/src/collection-manager/interfaces/o2m.tsx +2 -2
  78. package/src/collection-manager/interfaces/obo.tsx +2 -2
  79. package/src/collection-manager/interfaces/oho.tsx +2 -2
  80. package/src/collection-manager/interfaces/properties/index.ts +2 -2
  81. package/src/collection-manager/interfaces/uuid.ts +1 -1
  82. package/src/components/AppComponents.tsx +19 -3
  83. package/src/components/form/JsonTextArea.tsx +21 -11
  84. package/src/components/form/VariableJsonTextArea.tsx +175 -0
  85. package/src/components/form/__tests__/JsonTextArea.test.tsx +43 -0
  86. package/src/components/form/__tests__/VariableJsonTextArea.test.tsx +86 -0
  87. package/src/components/form/index.tsx +1 -0
  88. package/src/components/index.ts +1 -0
  89. package/src/entry-actions/EntryActionManager.ts +76 -0
  90. package/src/entry-actions/index.ts +10 -0
  91. package/src/flow/FlowPage.tsx +38 -3
  92. package/src/flow/__tests__/FlowPage.test.tsx +201 -27
  93. package/src/flow/__tests__/FlowRoute.test.tsx +1088 -105
  94. package/src/flow/__tests__/getKey.test.ts +7 -0
  95. package/src/flow/__tests__/resolveViewParamsToViewList.test.ts +34 -0
  96. package/src/flow/actions/__tests__/afterSuccess.test.ts +73 -0
  97. package/src/flow/actions/__tests__/dataScopeFilter.test.ts +120 -0
  98. package/src/flow/actions/__tests__/dataScopeFormValueClear.test.ts +1022 -0
  99. package/src/flow/actions/__tests__/fieldLinkageRules.scopeDepth.test.ts +150 -1
  100. package/src/flow/actions/__tests__/linkageAssignField.legacy.test.ts +135 -0
  101. package/src/flow/actions/__tests__/linkageRules.hiddenSync.restore.test.ts +19 -5
  102. package/src/flow/actions/__tests__/linkageRules.subFormSetFieldProps.test.ts +92 -0
  103. package/src/flow/actions/__tests__/openView.defineProps.route.test.tsx +164 -0
  104. package/src/flow/actions/__tests__/subFormFieldLinkageRules.inputArgs.test.ts +2 -2
  105. package/src/flow/actions/afterSuccess.tsx +142 -3
  106. package/src/flow/actions/customVariable.tsx +1 -2
  107. package/src/flow/actions/dataScopeFilter.ts +20 -1
  108. package/src/flow/actions/dateTimeFormat.tsx +42 -28
  109. package/src/flow/actions/index.ts +1 -1
  110. package/src/flow/actions/linkageRules.tsx +123 -35
  111. package/src/flow/actions/linkageRulesFormValueRefresh.ts +22 -130
  112. package/src/flow/actions/openView.tsx +59 -5
  113. package/src/flow/actions/runjs.tsx +2 -14
  114. package/src/flow/actions/validation.tsx +62 -30
  115. package/src/flow/admin-shell/BaseLayoutModel.tsx +149 -3
  116. package/src/flow/admin-shell/BaseLayoutRouteCoordinator.ts +187 -42
  117. package/src/flow/admin-shell/__tests__/AdminLayoutRouteCoordinator.test.ts +1020 -110
  118. package/src/flow/admin-shell/admin-layout/AdminLayoutComponent.tsx +74 -13
  119. package/src/flow/admin-shell/admin-layout/AdminLayoutEntryGuard.test.tsx +486 -0
  120. package/src/flow/admin-shell/admin-layout/AdminLayoutEntryGuard.tsx +64 -8
  121. package/src/flow/admin-shell/admin-layout/AdminLayoutMenuFlowUtils.ts +5 -3
  122. package/src/flow/admin-shell/admin-layout/AdminLayoutMenuModels.tsx +61 -9
  123. package/src/flow/admin-shell/admin-layout/AdminLayoutMenuUtils.tsx +71 -8
  124. package/src/flow/admin-shell/admin-layout/AdminLayoutModel.tsx +1 -1
  125. package/src/flow/admin-shell/admin-layout/AdminLayoutSlotModels.tsx +23 -9
  126. package/src/flow/admin-shell/admin-layout/AppListRender.tsx +13 -2
  127. package/src/flow/admin-shell/admin-layout/AppSwitcherActionPanelModel.tsx +289 -0
  128. package/src/flow/admin-shell/admin-layout/HelpLite.tsx +1 -3
  129. package/src/flow/admin-shell/admin-layout/__tests__/AdminLayoutComponent.test.tsx +188 -0
  130. package/src/flow/admin-shell/admin-layout/__tests__/AdminLayoutMenuModels.test.ts +99 -0
  131. package/src/flow/admin-shell/admin-layout/__tests__/AdminLayoutModel.test.tsx +518 -2
  132. package/src/flow/admin-shell/admin-layout/__tests__/TopbarActionsBar.test.tsx +214 -2
  133. package/src/flow/admin-shell/admin-layout/index.ts +1 -0
  134. package/src/flow/admin-shell/admin-layout/resolveAdminRouteRuntimeTarget.test.ts +20 -0
  135. package/src/flow/admin-shell/admin-layout/resolveAdminRouteRuntimeTarget.ts +33 -5
  136. package/src/flow/admin-shell/admin-layout/useApplications.tsx +81 -12
  137. package/src/flow/common/Markdown/Display.tsx +14 -3
  138. package/src/flow/common/Markdown/Edit.tsx +19 -7
  139. package/src/flow/components/DefaultValue.tsx +1 -2
  140. package/src/flow/components/DynamicFlowsIcon.tsx +447 -126
  141. package/src/flow/components/FieldAssignRulesEditor.tsx +128 -21
  142. package/src/flow/components/FieldAssignValueInput.tsx +15 -11
  143. package/src/flow/components/FlowRoute.tsx +186 -25
  144. package/src/flow/components/__tests__/DynamicFlowsIcon.test.tsx +148 -2
  145. package/src/flow/components/__tests__/FieldAssignRulesEditor.test.tsx +508 -4
  146. package/src/flow/components/__tests__/FieldAssignValueInput.context.test.tsx +134 -0
  147. package/src/flow/components/__tests__/fieldAssignOptions.test.ts +151 -3
  148. package/src/flow/components/code-editor/__tests__/useCodeRunner.test.tsx +2 -17
  149. package/src/flow/components/code-editor/hooks/useCodeRunner.ts +2 -14
  150. package/src/flow/components/code-editor/runjsDiagnostics.ts +8 -45
  151. package/src/flow/components/fieldAssignOptions.ts +155 -27
  152. package/src/flow/flows/editMarkdownFlow.tsx +1 -1
  153. package/src/flow/getViewDiffAndUpdateHidden.tsx +1 -0
  154. package/src/flow/index.ts +1 -1
  155. package/src/flow/internal/components/Markdown/util.ts +2 -1
  156. package/src/flow/internal/utils/operatorSchemaHelper.ts +15 -1
  157. package/src/flow/models/actions/JSActionModel.tsx +2 -7
  158. package/src/flow/models/actions/JSCollectionActionModel.tsx +2 -7
  159. package/src/flow/models/actions/JSItemActionModel.tsx +2 -7
  160. package/src/flow/models/actions/JSRecordActionModel.tsx +2 -7
  161. package/src/flow/models/base/ActionModel.tsx +21 -8
  162. package/src/flow/models/base/ActionModelCore.tsx +11 -4
  163. package/src/flow/models/base/GridModel.tsx +38 -7
  164. package/src/flow/models/base/PageModel/PageModel.tsx +4 -1
  165. package/src/flow/models/base/PageModel/__tests__/PageModel.test.ts +28 -4
  166. package/src/flow/models/base/__tests__/ActionModel.test.ts +83 -0
  167. package/src/flow/models/base/__tests__/ActionModelCore.render.test.tsx +37 -0
  168. package/src/flow/models/base/__tests__/GridModel.dragSnapshotContainer.test.ts +239 -1
  169. package/src/flow/models/base/__tests__/transformRowsToSingleColumn.test.ts +48 -0
  170. package/src/flow/models/blocks/filter-form/FilterFormBlockModel.tsx +32 -3
  171. package/src/flow/models/blocks/filter-form/FilterFormItemModel.tsx +27 -12
  172. package/src/flow/models/blocks/filter-form/FilterFormJSActionModel.tsx +2 -7
  173. package/src/flow/models/blocks/filter-form/__tests__/FilterFormItemModel.defineChildren.test.ts +36 -0
  174. package/src/flow/models/blocks/filter-form/__tests__/defaultValues.wiring.test.ts +111 -0
  175. package/src/flow/models/blocks/filter-form/fields/FilterFormCustomFieldModel.tsx +1 -1
  176. package/src/flow/models/blocks/filter-manager/flow-actions/__tests__/customizeFilterRender.test.tsx +56 -1
  177. package/src/flow/models/blocks/form/EditFormModel.tsx +1 -0
  178. package/src/flow/models/blocks/form/FormActionModel.tsx +1 -1
  179. package/src/flow/models/blocks/form/FormBlockModel.tsx +7 -0
  180. package/src/flow/models/blocks/form/JSFormActionModel.tsx +2 -7
  181. package/src/flow/models/blocks/form/QuickEditFormModel.tsx +189 -36
  182. package/src/flow/models/blocks/form/__tests__/FormBlockModel.test.tsx +17 -0
  183. package/src/flow/models/blocks/form/__tests__/QuickEditFormModel.quickEdit.test.ts +350 -2
  184. package/src/flow/models/blocks/form/__tests__/submitHandler.test.ts +54 -1
  185. package/src/flow/models/blocks/form/submitHandler.ts +17 -3
  186. package/src/flow/models/blocks/form/value-runtime/__tests__/runtime.test.ts +940 -75
  187. package/src/flow/models/blocks/form/value-runtime/rules.ts +445 -13
  188. package/src/flow/models/blocks/form/value-runtime/runtime.ts +348 -13
  189. package/src/flow/models/blocks/form/value-runtime/types.ts +2 -2
  190. package/src/flow/models/blocks/js-block/JSBlock.tsx +225 -9
  191. package/src/flow/models/blocks/js-block/__tests__/JSBlockModel.test.tsx +150 -0
  192. package/src/flow/models/blocks/table/JSColumnModel.tsx +1 -9
  193. package/src/flow/models/blocks/table/TableActionsColumnModel.tsx +36 -13
  194. package/src/flow/models/blocks/table/TableBlockModel.tsx +38 -17
  195. package/src/flow/models/blocks/table/TableColumnModel.tsx +42 -5
  196. package/src/flow/models/blocks/table/__tests__/TableActionsColumnModel.test.tsx +111 -1
  197. package/src/flow/models/blocks/table/__tests__/TableBlockModel.dragSort.test.ts +127 -0
  198. package/src/flow/models/blocks/table/__tests__/TableBlockModel.mobileSettingsButtons.test.tsx +78 -0
  199. package/src/flow/models/blocks/table/__tests__/TableBlockModel.rowSelection.test.tsx +1 -0
  200. package/src/flow/models/blocks/table/__tests__/TableColumnModel.test.tsx +210 -1
  201. package/src/flow/models/blocks/table/dragSort/dragSortHooks.tsx +28 -17
  202. package/src/flow/models/blocks/table/dragSort/dragSortSettings.ts +13 -6
  203. package/src/flow/models/blocks/table/dragSort/dragSortUtils.ts +15 -2
  204. package/src/flow/models/fields/AssociationFieldModel/AssociationFieldModel.tsx +1 -1
  205. package/src/flow/models/fields/AssociationFieldModel/PopupSubTableFieldModel/PopupSubTableFieldModel.tsx +30 -6
  206. package/src/flow/models/fields/AssociationFieldModel/RecordSelectFieldModel.tsx +61 -17
  207. package/src/flow/models/fields/AssociationFieldModel/SubTableFieldModel/SubTableColumnModel.tsx +141 -19
  208. package/src/flow/models/fields/AssociationFieldModel/SubTableFieldModel/SubTableField.tsx +13 -1
  209. package/src/flow/models/fields/AssociationFieldModel/SubTableFieldModel/__tests__/SubTableColumnModel.rowRecord.test.ts +197 -0
  210. package/src/flow/models/fields/AssociationFieldModel/SubTableFieldModel/__tests__/SubTableFieldModel.reset.test.ts +180 -0
  211. package/src/flow/models/fields/AssociationFieldModel/SubTableFieldModel/index.tsx +6 -4
  212. package/src/flow/models/fields/AssociationFieldModel/__tests__/AssociationFieldModel.updateAssociation.test.ts +72 -0
  213. package/src/flow/models/fields/AssociationFieldModel/__tests__/RecordPickerFieldModel.itemContext.test.ts +23 -0
  214. package/src/flow/models/fields/ClickableFieldModel.tsx +5 -0
  215. package/src/flow/models/fields/DisplayDateTimeFieldModel.tsx +29 -1
  216. package/src/flow/models/fields/InputFieldModel.tsx +40 -2
  217. package/src/flow/models/fields/JSEditableFieldModel.tsx +2 -11
  218. package/src/flow/models/fields/JSFieldModel.tsx +2 -7
  219. package/src/flow/models/fields/JSItemModel.tsx +2 -14
  220. package/src/flow/models/fields/SelectFieldModel.tsx +6 -4
  221. package/src/flow/models/fields/TextareaFieldModel.tsx +72 -3
  222. package/src/flow/models/fields/__tests__/DisplayDateTimeFieldModel.test.tsx +96 -0
  223. package/src/flow/models/fields/__tests__/InputFieldModel.test.tsx +117 -1
  224. package/src/flow/models/fields/__tests__/SelectFieldModel.test.tsx +43 -0
  225. package/src/flow/models/fields/__tests__/TextareaFieldModel.test.tsx +131 -0
  226. package/src/flow/models/fields/mobile-components/MobileLazySelect.tsx +20 -10
  227. package/src/flow/models/fields/mobile-components/MobileSelect.tsx +24 -12
  228. package/src/flow/models/topbar/TopbarActionModel.tsx +168 -10
  229. package/src/flow/resolveViewParamsToViewList.tsx +11 -3
  230. package/src/flow/routeTransientInputArgs.ts +27 -0
  231. package/src/flow/utils/__tests__/dateTimeFormat.test.ts +300 -0
  232. package/src/flow/utils/dataScopeFormValueClear.ts +218 -81
  233. package/src/flow/utils/dataScopeRowSnapshot.ts +616 -0
  234. package/src/flow/utils/dateTimeDisplayProps.ts +135 -0
  235. package/src/flow/utils/formValueDeps.ts +170 -0
  236. package/src/flow-compat/FieldValidation.tsx +122 -60
  237. package/src/flow-compat/Popover.tsx +43 -4
  238. package/src/flow-compat/__tests__/Popover.test.tsx +34 -0
  239. package/src/index.ts +10 -1
  240. package/src/layout-manager/__tests__/LayoutRoute.test.tsx +41 -1
  241. package/src/nocobase-buildin-plugin/index.tsx +51 -15
  242. package/src/settings-center/AdminSettingsLayout.tsx +10 -2
  243. package/src/settings-center/SystemSettingsPage.tsx +1 -2
  244. package/src/settings-center/plugin-manager/PluginCard.tsx +2 -2
  245. package/src/settings-center/plugin-manager/index.tsx +3 -0
  246. package/src/settings-center/utils.tsx +0 -6
  247. package/src/utils/markdownSanitize.ts +88 -0
@@ -15,7 +15,12 @@ import type { Application } from '../Application';
15
15
  import { getCurrentV2RedirectPath, getDefaultV2AdminRedirectPath } from '../authRedirect';
16
16
  import { AppNotFound } from '../components';
17
17
  import { PluginFlowEngine } from '../flow';
18
- import { ADMIN_LAYOUT_MODEL_UID, AdminLayoutMenuItemModel, AdminLayoutModel } from '../flow/admin-shell/admin-layout';
18
+ import {
19
+ ADMIN_LAYOUT_MODEL_UID,
20
+ AdminLayoutMenuItemModel,
21
+ AdminLayoutModel,
22
+ AppSwitcherActionPanelModel,
23
+ } from '../flow/admin-shell/admin-layout';
19
24
  import { useApp } from '../hooks/useApp';
20
25
  import { Plugin } from '../Plugin';
21
26
  import { AdminSettingsLayoutModel } from '../settings-center';
@@ -28,6 +33,13 @@ export type CurrentUserState = {
28
33
  loading: boolean;
29
34
  };
30
35
 
36
+ type CurrentUserAuthStatus = 'unknown' | 'authenticated' | 'unauthenticated' | 'redirecting';
37
+
38
+ type CurrentUserInternalState = CurrentUserState & {
39
+ authStatus: CurrentUserAuthStatus;
40
+ error?: Error | null;
41
+ };
42
+
31
43
  export type CurrentRoleOption = {
32
44
  name: string;
33
45
  title: string;
@@ -151,7 +163,7 @@ const DataSourceBootstrapProvider: FC = ({ children }) => {
151
163
  }
152
164
  };
153
165
 
154
- void run();
166
+ run();
155
167
 
156
168
  return () => {
157
169
  mounted = false;
@@ -173,21 +185,33 @@ const CurrentUserProvider: FC = ({ children }) => {
173
185
  const app = useApp<Application>();
174
186
  const location = useLocation();
175
187
  const navigate = useNavigate();
176
- const [state, setState] = useState<CurrentUserState>({ loading: true });
188
+ const [state, setState] = useState<CurrentUserInternalState>({ loading: true, authStatus: 'unknown' });
177
189
  const locationRef = useRef(location);
178
190
  locationRef.current = location;
179
191
  const authCheckRouteState = getCurrentUserAuthCheckRouteState(app, location.pathname);
192
+ const shouldBlockAuthRequiredRoute = authCheckRouteState === 'required' && state.authStatus !== 'authenticated';
193
+ const contextValue = useMemo<CurrentUserState>(
194
+ () => ({
195
+ data: state.data,
196
+ loading: state.loading,
197
+ }),
198
+ [state.data, state.loading],
199
+ );
180
200
 
181
201
  useEffect(() => {
182
202
  let mounted = true;
183
203
 
184
204
  if (authCheckRouteState !== 'required') {
185
205
  // 认证页等免鉴权路由不应再执行 `/auth:check`,否则未登录时会重复鉴权并触发重定向抖动。
186
- setState({ loading: false });
206
+ setState({ loading: false, authStatus: 'unauthenticated', error: null });
187
207
  return;
188
208
  }
189
209
 
190
- setState((previous) => (previous.loading ? previous : { ...previous, loading: true }));
210
+ setState((previous) =>
211
+ previous.authStatus === 'authenticated'
212
+ ? previous
213
+ : { data: previous.data, loading: true, authStatus: 'unknown', error: null },
214
+ );
191
215
 
192
216
  const run = async () => {
193
217
  try {
@@ -204,11 +228,12 @@ const CurrentUserProvider: FC = ({ children }) => {
204
228
  const user = res?.data?.data;
205
229
  // 服务端通过 `{ code: 302, redirect }` 通知客户端先去某个中间页(例如 2FA 验证页)。这类响应没有 user.id,但也不能视为未登录——否则会和处理 302 的全局响应拦截器 (例如 plugin-two-factor-authentication 注册的那一个)竞态,而 `window.location.replace` 会覆盖更早发出的 `window.location.href`,把用户错误地弹回登录页。让响应拦截器接管跳转。
206
230
  if (user?.code === 302) {
207
- setState({ loading: false });
231
+ setState({ loading: false, authStatus: 'redirecting', error: null });
208
232
  return;
209
233
  }
210
234
  if (user?.id == null) {
211
235
  // 用 react-router navigate (虚拟跳转)而不是 location.replace, 这样如果有其他响应拦截器已经发起了 window.location.href 整页跳转(例如 2FA 插件接收到服务端 302 重定向), 真实跳转可以胜出 navigate, 不会被这里的 signin 重定向覆盖。
236
+ setState({ loading: true, authStatus: 'unauthenticated', error: null });
212
237
  navigate(`/signin?redirect=${encodeURIComponent(getCurrentV2RedirectPath(app, locationRef.current))}`, {
213
238
  replace: true,
214
239
  });
@@ -230,21 +255,28 @@ const CurrentUserProvider: FC = ({ children }) => {
230
255
  setState({
231
256
  data: res?.data,
232
257
  loading: false,
258
+ authStatus: 'authenticated',
259
+ error: null,
233
260
  });
234
- } catch (error: any) {
261
+ } catch (error: unknown) {
235
262
  if (!mounted) {
236
263
  return;
237
264
  }
238
265
 
239
- const isAuthError = error?.response?.status === 401 || error?.status === 401;
266
+ const errorLike = error as { response?: { status?: number }; status?: number };
267
+ const isAuthError = errorLike?.response?.status === 401 || errorLike?.status === 401;
240
268
  if (isAuthError) {
269
+ setState({ loading: true, authStatus: 'unauthenticated', error: null });
241
270
  navigate(`/signin?redirect=${encodeURIComponent(getCurrentV2RedirectPath(app, locationRef.current))}`, {
242
271
  replace: true,
243
272
  });
244
273
  return;
245
274
  }
246
- setState({ loading: false });
247
- throw error;
275
+ setState({
276
+ loading: false,
277
+ authStatus: 'unknown',
278
+ error: error instanceof Error ? error : new Error(String(error)),
279
+ });
248
280
  }
249
281
  };
250
282
 
@@ -255,11 +287,15 @@ const CurrentUserProvider: FC = ({ children }) => {
255
287
  };
256
288
  }, [app, authCheckRouteState, navigate]);
257
289
 
258
- if (state.loading) {
290
+ if (state.error) {
291
+ throw state.error;
292
+ }
293
+
294
+ if (state.loading || shouldBlockAuthRequiredRoute) {
259
295
  return app.renderComponent('AppSpin');
260
296
  }
261
297
 
262
- return <CurrentUserContext.Provider value={state}>{children}</CurrentUserContext.Provider>;
298
+ return <CurrentUserContext.Provider value={contextValue}>{children}</CurrentUserContext.Provider>;
263
299
  };
264
300
 
265
301
  const RootRedirect: FC = () => {
@@ -289,11 +325,13 @@ export class NocoBaseBuildInPlugin extends Plugin<any, Application> {
289
325
  this.addComponents();
290
326
  this.addRoutes();
291
327
 
292
- this.app.use(DataSourceBootstrapProvider);
328
+ // Auth-required routes must finish auth check or redirect before data source metadata requests can start.
293
329
  this.app.use(CurrentUserProvider);
330
+ this.app.use(DataSourceBootstrapProvider);
294
331
  this.app.flowEngine.registerModels({
295
332
  AdminLayoutModel,
296
333
  AdminLayoutMenuItemModel,
334
+ AppSwitcherActionPanelModel,
297
335
  AdminSettingsLayoutModel,
298
336
  });
299
337
  this.app.layoutManager.registerLayout({
@@ -323,7 +361,6 @@ export class NocoBaseBuildInPlugin extends Plugin<any, Application> {
323
361
  title: this.app.i18n.t('System settings'),
324
362
  icon: 'SettingOutlined',
325
363
  aclSnippet: 'pm.system-settings.system-settings',
326
- sort: -100,
327
364
  });
328
365
  this.app.pluginSettingsManager.addPageTabItem({
329
366
  menuKey: 'system-settings',
@@ -331,7 +368,6 @@ export class NocoBaseBuildInPlugin extends Plugin<any, Application> {
331
368
  title: this.app.i18n.t('System settings'),
332
369
  componentLoader: () => import('../settings-center/SystemSettingsPage'),
333
370
  aclSnippet: 'pm.system-settings.system-settings',
334
- sort: -100,
335
371
  });
336
372
  // Parent menu for security-related plugin settings (password policy, locked users, etc.). Registered here in the buildin plugin so any pro plugin can attach page tabs to `menuKey: 'security'` without each one re-registering the same parent.
337
373
  this.app.pluginSettingsManager.addMenuItem({
@@ -209,6 +209,9 @@ export const InternalAdminSettingsLayout = () => {
209
209
  style={{
210
210
  background: token.colorBgContainer,
211
211
  borderInlineEnd: `${token.lineWidth}px solid ${token.colorBorderSecondary}`,
212
+ minHeight: 0,
213
+ overflowY: 'auto',
214
+ overflowX: 'hidden',
212
215
  }}
213
216
  >
214
217
  <Menu
@@ -239,11 +242,12 @@ export const InternalAdminSettingsLayout = () => {
239
242
  <Layout.Content
240
243
  style={{
241
244
  background: token.colorBgLayout,
245
+ flex: 1,
242
246
  display: 'flex',
243
247
  flexDirection: 'column',
244
248
  minWidth: 0,
245
- overflowY: 'auto',
246
- overflowX: 'hidden',
249
+ minHeight: 0,
250
+ overflow: 'hidden',
247
251
  }}
248
252
  >
249
253
  <PageHeader
@@ -273,6 +277,10 @@ export const InternalAdminSettingsLayout = () => {
273
277
  />
274
278
  <div
275
279
  style={{
280
+ flex: 1,
281
+ minHeight: 0,
282
+ boxSizing: 'border-box',
283
+ overflow: 'auto',
276
284
  padding: token.paddingLG,
277
285
  }}
278
286
  >
@@ -221,7 +221,6 @@ export const SystemSettingsPage = () => {
221
221
  return (
222
222
  <div
223
223
  style={{
224
- minHeight: '100%',
225
224
  background: token.colorBgContainer,
226
225
  borderRadius: token.borderRadiusLG,
227
226
  padding: token.paddingLG,
@@ -240,7 +239,7 @@ export const SystemSettingsPage = () => {
240
239
  <Form.Item
241
240
  name="raw_title"
242
241
  label={t('System title')}
243
- rules={[{ required: true, message: t('Please enter') + t('System title') }]}
242
+ rules={[{ required: true, message: t('Please enter') + ' ' + t('System title') }]}
244
243
  >
245
244
  <Input.TextArea autoSize={{ minRows: 2, maxRows: 4 }} />
246
245
  </Form.Item>
@@ -116,7 +116,7 @@ export const PluginCard: FC<PluginCardProps> = ({ data }) => {
116
116
  });
117
117
 
118
118
  const openSettings = useMemoizedFn(() => {
119
- navigate(app.pluginSettingsManager.getRoutePath(name));
119
+ navigate(app.pluginSettingsManager.getPluginSettingsRoutePath(name));
120
120
  });
121
121
 
122
122
  const cardClassName = useMemo(
@@ -180,7 +180,7 @@ export const PluginCard: FC<PluginCardProps> = ({ data }) => {
180
180
  <ReadOutlined /> {t('Docs')}
181
181
  </a>
182
182
  )}
183
- {enabled && app.pluginSettingsManager.has(name) && (
183
+ {enabled && app.pluginSettingsManager.hasPluginSettings(name) && (
184
184
  <a
185
185
  onClick={(e) => {
186
186
  e.stopPropagation();
@@ -86,6 +86,9 @@ export const PluginManagerPage: React.FC = () => {
86
86
  async () => {
87
87
  const response = await app.apiClient.request({
88
88
  url: 'pm:list',
89
+ params: {
90
+ v2: true,
91
+ },
89
92
  skipNotify: true,
90
93
  });
91
94
  return Array.isArray(response?.data?.data) ? response.data.data : [];
@@ -85,12 +85,6 @@ export function filterVisibleSettings(settings: readonly PluginSettingsPageType[
85
85
  */
86
86
  export function sortTopLevelSettings(settings: PluginSettingsPageType[] = []) {
87
87
  return [...settings].sort((a, b) => {
88
- if (a.name === SYSTEM_SETTINGS_SETTING_NAME && b.name !== SYSTEM_SETTINGS_SETTING_NAME) {
89
- return -1;
90
- }
91
- if (b.name === SYSTEM_SETTINGS_SETTING_NAME && a.name !== SYSTEM_SETTINGS_SETTING_NAME) {
92
- return 1;
93
- }
94
88
  if ((a.sort || 0) !== (b.sort || 0)) {
95
89
  return (a.sort || 0) - (b.sort || 0);
96
90
  }
@@ -0,0 +1,88 @@
1
+ /**
2
+ * This file is part of the NocoBase (R) project.
3
+ * Copyright (c) 2020-2024 NocoBase Co., Ltd.
4
+ * Authors: NocoBase Team.
5
+ *
6
+ * This project is dual-licensed under AGPL-3.0 and NocoBase Commercial License.
7
+ * For more information, please refer to: https://www.nocobase.com/agreement.
8
+ */
9
+
10
+ export function stripMarkdownIframeTags(markdown: string) {
11
+ if (!markdown) {
12
+ return markdown;
13
+ }
14
+
15
+ let result = '';
16
+ let cursor = 0;
17
+
18
+ while (cursor < markdown.length) {
19
+ const iframeStart = markdown.slice(cursor).search(/<iframe\b/i);
20
+ if (iframeStart === -1) {
21
+ result += markdown.slice(cursor);
22
+ break;
23
+ }
24
+
25
+ const start = cursor + iframeStart;
26
+ result += markdown.slice(cursor, start);
27
+
28
+ const openingEnd = findTagEnd(markdown, start);
29
+ if (openingEnd === -1) {
30
+ break;
31
+ }
32
+
33
+ const openingTag = markdown.slice(start, openingEnd + 1);
34
+ if (/\/\s*>$/.test(openingTag)) {
35
+ cursor = openingEnd + 1;
36
+ continue;
37
+ }
38
+
39
+ const closingStart = markdown.slice(openingEnd + 1).search(/<\/iframe\s*>/i);
40
+ if (closingStart === -1) {
41
+ break;
42
+ }
43
+
44
+ cursor =
45
+ openingEnd + 1 + closingStart + markdown.slice(openingEnd + 1 + closingStart).match(/^<\/iframe\s*>/i)[0].length;
46
+ }
47
+
48
+ return result;
49
+ }
50
+
51
+ export function stripMarkdownIframes(html: string) {
52
+ if (!html || typeof DOMParser === 'undefined') {
53
+ return html;
54
+ }
55
+
56
+ const doc = new DOMParser().parseFromString(html, 'text/html');
57
+ doc.querySelectorAll('iframe').forEach((iframe) => iframe.remove());
58
+ return doc.body.innerHTML;
59
+ }
60
+
61
+ export function removeMarkdownIframes(container?: ParentNode | null) {
62
+ container?.querySelectorAll('iframe').forEach((iframe) => iframe.remove());
63
+ }
64
+
65
+ function findTagEnd(html: string, start: number) {
66
+ let quote: string | undefined;
67
+
68
+ for (let index = start; index < html.length; index += 1) {
69
+ const char = html[index];
70
+ if (quote) {
71
+ if (char === quote) {
72
+ quote = undefined;
73
+ }
74
+ continue;
75
+ }
76
+
77
+ if (char === '"' || char === "'") {
78
+ quote = char;
79
+ continue;
80
+ }
81
+
82
+ if (char === '>') {
83
+ return index;
84
+ }
85
+ }
86
+
87
+ return -1;
88
+ }