@nocobase/portal-template-default 1.0.8 → 1.0.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 (262) hide show
  1. package/.env.example +5 -0
  2. package/.github/workflows/publish.yml +106 -0
  3. package/.gitignore +33 -0
  4. package/AGENTS.md +19 -0
  5. package/README.MD +61 -180
  6. package/package.json +3 -5
  7. package/scripts/build-html.mjs +12 -0
  8. package/src/App.css +2 -0
  9. package/src/App.tsx +35 -186
  10. package/src/app/development.tsx +154 -0
  11. package/src/app/extension.ts +5 -0
  12. package/src/app/extensions.tsx +18 -4
  13. package/src/app/routes.tsx +65 -0
  14. package/src/components/access-control/acl-bootstrap.tsx +11 -3
  15. package/src/components/access-control/acl-gate.tsx +7 -6
  16. package/src/components/access-control/can-access.tsx +17 -0
  17. package/src/components/access-control/navigate-to-accessible-resource.tsx +8 -3
  18. package/src/components/app-shell/sidebar.tsx +21 -5
  19. package/src/components/auth/auto-redirect-provider.tsx +2 -1
  20. package/src/extensions/nocobase-acl/README.md +74 -0
  21. package/{registry → src/extensions}/nocobase-acl/components/acl-boundary.tsx +22 -25
  22. package/{registry → src/extensions}/nocobase-acl/components/role-switcher.tsx +26 -23
  23. package/src/extensions/nocobase-acl/demo/acl-preview-provider.tsx +81 -0
  24. package/{registry → src/extensions}/nocobase-acl/demo/boundary-api.tsx +58 -0
  25. package/{registry → src/extensions}/nocobase-acl/demo/index.tsx +243 -105
  26. package/{registry → src/extensions}/nocobase-acl/demo/prompt-generator.tsx +3 -1
  27. package/src/extensions/nocobase-acl/extension.tsx +73 -0
  28. package/{registry → src/extensions}/nocobase-ai/README.md +21 -5
  29. package/{registry → src/extensions}/nocobase-ai/components/chat/chat-composer.tsx +3 -1
  30. package/{registry → src/extensions}/nocobase-ai/components/chat/chat-messages.tsx +38 -3
  31. package/{registry → src/extensions}/nocobase-ai/components/chat/tool-call-card.tsx +1 -9
  32. package/{registry → src/extensions}/nocobase-ai/components/index.ts +5 -0
  33. package/src/extensions/nocobase-ai/components/surfaces/chat-dialog.tsx +17 -0
  34. package/src/extensions/nocobase-ai/components/surfaces/chat-side-panel.tsx +35 -0
  35. package/src/extensions/nocobase-ai/components/surfaces/chat-surface.tsx +108 -0
  36. package/{registry → src/extensions}/nocobase-ai/components/triggers/ai-chat-floating-trigger.tsx +2 -2
  37. package/{registry → src/extensions}/nocobase-ai/demo/configuration-gate.tsx +4 -1
  38. package/{registry → src/extensions}/nocobase-ai/demo/container-showcase.tsx +2 -2
  39. package/{registry → src/extensions}/nocobase-ai/demo/floating.tsx +45 -15
  40. package/{registry → src/extensions}/nocobase-ai/demo/index.tsx +116 -74
  41. package/{registry → src/extensions}/nocobase-ai/demo/prompt-generator.tsx +3 -3
  42. package/{registry → src/extensions}/nocobase-ai/demo/tool-cards.tsx +2 -3
  43. package/src/extensions/nocobase-ai/extension.tsx +160 -0
  44. package/{registry → src/extensions}/nocobase-ai/global-ai-chat.tsx +12 -28
  45. package/{registry → src/extensions}/nocobase-ai/providers/ai-provider.tsx +14 -1
  46. package/{registry → src/extensions}/nocobase-ai/providers/chat-context.tsx +1 -0
  47. package/{registry → src/extensions}/nocobase-ai/providers/chat-provider.tsx +29 -8
  48. package/{registry → src/extensions}/nocobase-ai/providers/use-chat-attachments.ts +6 -1
  49. package/{registry → src/extensions}/nocobase-ai/providers/use-chat-work-context.ts +4 -1
  50. package/{registry → src/extensions}/nocobase-route-surfaces/README.md +2 -1
  51. package/{registry → src/extensions}/nocobase-route-surfaces/demo/index.tsx +33 -7
  52. package/{registry → src/extensions}/nocobase-route-surfaces/demo/scenarios.ts +20 -14
  53. package/{registry → src/extensions}/nocobase-route-surfaces/extension.tsx +55 -52
  54. package/src/{pages/users → extensions/nocobase-users-example}/create.tsx +4 -4
  55. package/src/{pages/users → extensions/nocobase-users-example}/edit.tsx +5 -9
  56. package/src/extensions/nocobase-users-example/extension.tsx +109 -0
  57. package/src/{pages/users → extensions/nocobase-users-example}/form-context.ts +11 -9
  58. package/src/extensions/nocobase-users-example/layout.tsx +16 -0
  59. package/src/{pages/users → extensions/nocobase-users-example}/list.tsx +1 -1
  60. package/src/extensions/nocobase-users-example/locales/en-US.ts +77 -0
  61. package/src/extensions/nocobase-users-example/locales/index.ts +8 -0
  62. package/src/extensions/nocobase-users-example/locales/zh-CN.ts +73 -0
  63. package/src/extensions/nocobase-users-example/optional-ai.ts +67 -0
  64. package/src/{pages/users → extensions/nocobase-users-example}/role-detail.tsx +4 -15
  65. package/src/{pages/users → extensions/nocobase-users-example}/routes.ts +1 -4
  66. package/src/extensions/nocobase-users-example/show.tsx +266 -0
  67. package/src/lib/nocobase/acl/context.ts +32 -0
  68. package/src/lib/nocobase/acl/evaluator.ts +186 -0
  69. package/src/lib/nocobase/acl/hooks.ts +40 -0
  70. package/src/lib/nocobase/acl/index.ts +4 -1
  71. package/src/lib/nocobase/acl/menu.ts +6 -6
  72. package/src/lib/nocobase/acl/record-permissions.ts +37 -7
  73. package/src/lib/nocobase/acl/store-provider.tsx +14 -0
  74. package/src/lib/nocobase/acl/store.ts +92 -86
  75. package/src/lib/nocobase/acl/types.ts +52 -32
  76. package/src/lib/nocobase/auth-session.ts +128 -0
  77. package/src/lib/nocobase/client.ts +48 -50
  78. package/src/locales/en-US.ts +0 -78
  79. package/src/locales/index.ts +2 -7
  80. package/src/locales/zh-CN.ts +0 -74
  81. package/src/providers/access-control.ts +43 -8
  82. package/src/providers/auth-callback.ts +1 -0
  83. package/src/providers/auth.ts +24 -26
  84. package/src/providers/constants.ts +0 -4
  85. package/src/providers/data.ts +14 -26
  86. package/src/providers/runtime-config.ts +40 -0
  87. package/src/vite-env.d.ts +9 -0
  88. package/tests/auth-session-regression.mjs +101 -0
  89. package/tsconfig.json +2 -1
  90. package/vite.config.ts +9 -1
  91. package/registry/nocobase-acl/README.md +0 -19
  92. package/registry/nocobase-acl/extension.tsx +0 -71
  93. package/registry/nocobase-acl/tests/acl-regression.mjs +0 -314
  94. package/registry/nocobase-ai/components/surfaces/chat-dialog.tsx +0 -31
  95. package/registry/nocobase-ai/components/surfaces/chat-side-panel.tsx +0 -74
  96. package/registry/nocobase-ai/extension.tsx +0 -158
  97. package/registry/nocobase-ai/tests/business-report-regression.mjs +0 -70
  98. package/registry/nocobase-ai/tests/form-filler-regression.mjs +0 -129
  99. package/registry/nocobase-ai/tests/frontend-tool-regression.mjs +0 -128
  100. package/registry/nocobase-ai/tests/page-context-regression.mjs +0 -48
  101. package/registry/nocobase-ai/tests/stream-regression.mjs +0 -207
  102. package/registry/nocobase-auth-cas/README.md +0 -6
  103. package/registry/nocobase-auth-cas/cas-sign-in-button.tsx +0 -41
  104. package/registry/nocobase-auth-cas/demo.tsx +0 -20
  105. package/registry/nocobase-auth-cas/extension.tsx +0 -46
  106. package/registry/nocobase-auth-cas/index.ts +0 -1
  107. package/registry/nocobase-auth-cas/use-cas-sign-in.ts +0 -29
  108. package/registry/nocobase-auth-dingtalk/README.md +0 -7
  109. package/registry/nocobase-auth-dingtalk/auto-login-provider.tsx +0 -154
  110. package/registry/nocobase-auth-dingtalk/demo.tsx +0 -23
  111. package/registry/nocobase-auth-dingtalk/dingtalk-sign-in-button.tsx +0 -45
  112. package/registry/nocobase-auth-dingtalk/extension.tsx +0 -55
  113. package/registry/nocobase-auth-dingtalk/index.ts +0 -4
  114. package/registry/nocobase-auth-dingtalk/use-dingtalk-sign-in.ts +0 -64
  115. package/registry/nocobase-auth-ldap/README.md +0 -6
  116. package/registry/nocobase-auth-ldap/demo.tsx +0 -21
  117. package/registry/nocobase-auth-ldap/extension.tsx +0 -46
  118. package/registry/nocobase-auth-ldap/index.ts +0 -1
  119. package/registry/nocobase-auth-ldap/ldap-sign-in-form.tsx +0 -69
  120. package/registry/nocobase-auth-ldap/use-ldap-sign-in.ts +0 -5
  121. package/registry/nocobase-auth-oidc/README.md +0 -11
  122. package/registry/nocobase-auth-oidc/auto-redirect-provider.tsx +0 -13
  123. package/registry/nocobase-auth-oidc/demo.tsx +0 -20
  124. package/registry/nocobase-auth-oidc/extension.tsx +0 -51
  125. package/registry/nocobase-auth-oidc/index.ts +0 -1
  126. package/registry/nocobase-auth-oidc/oidc-sign-in-button.tsx +0 -44
  127. package/registry/nocobase-auth-oidc/use-oidc-sign-in.ts +0 -58
  128. package/registry/nocobase-auth-saml/README.md +0 -6
  129. package/registry/nocobase-auth-saml/auto-redirect-provider.tsx +0 -13
  130. package/registry/nocobase-auth-saml/demo.tsx +0 -20
  131. package/registry/nocobase-auth-saml/extension.tsx +0 -51
  132. package/registry/nocobase-auth-saml/index.ts +0 -1
  133. package/registry/nocobase-auth-saml/saml-sign-in-button.tsx +0 -43
  134. package/registry/nocobase-auth-saml/use-saml-sign-in.ts +0 -51
  135. package/registry/nocobase-auth-sms/README.md +0 -7
  136. package/registry/nocobase-auth-sms/demo.tsx +0 -25
  137. package/registry/nocobase-auth-sms/extension.tsx +0 -46
  138. package/registry/nocobase-auth-sms/index.ts +0 -1
  139. package/registry/nocobase-auth-sms/sms-sign-in-form.tsx +0 -107
  140. package/registry/nocobase-auth-sms/use-sms-sign-in.ts +0 -88
  141. package/registry/nocobase-auth-wecom/README.md +0 -6
  142. package/registry/nocobase-auth-wecom/auto-login-provider.tsx +0 -21
  143. package/registry/nocobase-auth-wecom/demo.tsx +0 -23
  144. package/registry/nocobase-auth-wecom/extension.tsx +0 -49
  145. package/registry/nocobase-auth-wecom/index.ts +0 -1
  146. package/registry/nocobase-auth-wecom/use-wecom-sign-in.ts +0 -62
  147. package/registry/nocobase-auth-wecom/wecom-sign-in-button.tsx +0 -70
  148. package/registry/nocobase-i18n/README.md +0 -44
  149. package/registry/nocobase-i18n/components/index.ts +0 -1
  150. package/registry/nocobase-i18n/components/language-switcher.tsx +0 -138
  151. package/registry/nocobase-i18n/demo/index.tsx +0 -209
  152. package/registry/nocobase-i18n/demo/prompt-generator.tsx +0 -132
  153. package/registry/nocobase-i18n/extension.tsx +0 -45
  154. package/registry/nocobase-i18n/index.ts +0 -7
  155. package/registry/nocobase-i18n/locales/en-US.ts +0 -39
  156. package/registry/nocobase-i18n/locales/index.ts +0 -8
  157. package/registry/nocobase-i18n/locales/zh-CN.ts +0 -35
  158. package/registry/nocobase-i18n/provider.tsx +0 -44
  159. package/registry/nocobase-i18n/server-resources.ts +0 -166
  160. package/registry/nocobase-i18n/tests/i18n-regression.mjs +0 -51
  161. package/registry.config.json +0 -302
  162. package/scripts/registry.mjs +0 -164
  163. package/src/components/access-control/resource-access-guard.tsx +0 -21
  164. package/src/components/access-control/use-acl-runtime.ts +0 -28
  165. package/src/lib/nocobase/acl/action.ts +0 -147
  166. package/src/pages/users/layout.tsx +0 -15
  167. package/src/pages/users/show.tsx +0 -278
  168. /package/{registry → src/extensions}/nocobase-acl/components/index.ts +0 -0
  169. /package/{registry → src/extensions}/nocobase-acl/components/role-options.ts +0 -0
  170. /package/{registry → src/extensions}/nocobase-acl/demo/components.tsx +0 -0
  171. /package/{registry → src/extensions}/nocobase-acl/demo/lazy-route.tsx +0 -0
  172. /package/{registry → src/extensions}/nocobase-acl/demo/role-switcher-prompt-generator.tsx +0 -0
  173. /package/{registry → src/extensions}/nocobase-acl/demo/scenario-section.tsx +0 -0
  174. /package/{registry → src/extensions}/nocobase-acl/index.ts +0 -0
  175. /package/{registry → src/extensions}/nocobase-ai/adapters/react-hook-form.ts +0 -0
  176. /package/{registry → src/extensions}/nocobase-ai/assets/nocobase-ai-chat.svg +0 -0
  177. /package/{registry → src/extensions}/nocobase-ai/components/ai-root-provider.tsx +0 -0
  178. /package/{registry → src/extensions}/nocobase-ai/components/chat/ai-employee-avatar.tsx +0 -0
  179. /package/{registry → src/extensions}/nocobase-ai/components/chat/chat-attachment.tsx +0 -0
  180. /package/{registry → src/extensions}/nocobase-ai/components/chat/chat-compact.tsx +0 -0
  181. /package/{registry → src/extensions}/nocobase-ai/components/chat/chat-empty-state.tsx +0 -0
  182. /package/{registry → src/extensions}/nocobase-ai/components/chat/chat-header.tsx +0 -0
  183. /package/{registry → src/extensions}/nocobase-ai/components/chat/chat-history-dialog.tsx +0 -0
  184. /package/{registry → src/extensions}/nocobase-ai/components/chat/chat-message.tsx +0 -0
  185. /package/{registry → src/extensions}/nocobase-ai/components/chat/chat-window.tsx +0 -0
  186. /package/{registry → src/extensions}/nocobase-ai/components/chat/conversation-list.tsx +0 -0
  187. /package/{registry → src/extensions}/nocobase-ai/components/chat/markdown-message.tsx +0 -0
  188. /package/{registry → src/extensions}/nocobase-ai/components/chat/model-select-options.tsx +0 -0
  189. /package/{registry → src/extensions}/nocobase-ai/components/chat/reasoning-panel.tsx +0 -0
  190. /package/{registry → src/extensions}/nocobase-ai/components/chat/sub-agent-conversation.tsx +0 -0
  191. /package/{registry → src/extensions}/nocobase-ai/components/chat/user-prompt-editor.tsx +0 -0
  192. /package/{registry → src/extensions}/nocobase-ai/components/chat/work-context-chip.tsx +0 -0
  193. /package/{registry → src/extensions}/nocobase-ai/components/page-elements/ai-form.tsx +0 -0
  194. /package/{registry → src/extensions}/nocobase-ai/components/page-elements/page-element-provider.tsx +0 -0
  195. /package/{registry → src/extensions}/nocobase-ai/components/surfaces/chat-inline.tsx +0 -0
  196. /package/{registry → src/extensions}/nocobase-ai/components/surfaces/chat-page.tsx +0 -0
  197. /package/{registry → src/extensions}/nocobase-ai/components/surfaces/chat-side-panel-layout.tsx +0 -0
  198. /package/{registry → src/extensions}/nocobase-ai/components/surfaces/chat-surface-actions.tsx +0 -0
  199. /package/{registry → src/extensions}/nocobase-ai/components/tools/builtin-tool-renderers.tsx +0 -0
  200. /package/{registry → src/extensions}/nocobase-ai/components/tools/business-report-dialog.tsx +0 -0
  201. /package/{registry → src/extensions}/nocobase-ai/components/tools/business-report-renderer.tsx +0 -0
  202. /package/{registry → src/extensions}/nocobase-ai/components/tools/business-report-utils.tsx +0 -0
  203. /package/{registry → src/extensions}/nocobase-ai/components/tools/chart-renderer.tsx +0 -0
  204. /package/{registry → src/extensions}/nocobase-ai/components/tools/echarts-preview.tsx +0 -0
  205. /package/{registry → src/extensions}/nocobase-ai/components/tools/echarts-runtime-advanced.ts +0 -0
  206. /package/{registry → src/extensions}/nocobase-ai/components/tools/echarts-runtime-common.ts +0 -0
  207. /package/{registry → src/extensions}/nocobase-ai/components/tools/echarts-runtime-components.ts +0 -0
  208. /package/{registry → src/extensions}/nocobase-ai/components/tools/echarts-runtime-hierarchy.ts +0 -0
  209. /package/{registry → src/extensions}/nocobase-ai/components/tools/echarts-runtime.ts +0 -0
  210. /package/{registry → src/extensions}/nocobase-ai/components/tools/sub-agent-renderer.tsx +0 -0
  211. /package/{registry → src/extensions}/nocobase-ai/components/tools/suggestions-renderer.tsx +0 -0
  212. /package/{registry → src/extensions}/nocobase-ai/components/tools/tool-renderer-provider.tsx +0 -0
  213. /package/{registry → src/extensions}/nocobase-ai/components/tools/tool-renderer-utils.ts +0 -0
  214. /package/{registry → src/extensions}/nocobase-ai/components/tools/workflow-renderer.tsx +0 -0
  215. /package/{registry → src/extensions}/nocobase-ai/components/triggers/ai-employee-shortcut.tsx +0 -0
  216. /package/{registry → src/extensions}/nocobase-ai/demo/interaction-showcase.tsx +0 -0
  217. /package/{registry → src/extensions}/nocobase-ai/demo/page-context-prompt-generator.tsx +0 -0
  218. /package/{registry → src/extensions}/nocobase-ai/demo/page-context.tsx +0 -0
  219. /package/{registry → src/extensions}/nocobase-ai/demo/page-element-showcase.tsx +0 -0
  220. /package/{registry → src/extensions}/nocobase-ai/demo/prompt-card.tsx +0 -0
  221. /package/{registry → src/extensions}/nocobase-ai/demo/shortcut.tsx +0 -0
  222. /package/{registry → src/extensions}/nocobase-ai/index.ts +0 -0
  223. /package/{registry → src/extensions}/nocobase-ai/providers/avatars.ts +0 -0
  224. /package/{registry → src/extensions}/nocobase-ai/providers/chat-controller.ts +0 -0
  225. /package/{registry → src/extensions}/nocobase-ai/providers/chat-message-utils.ts +0 -0
  226. /package/{registry → src/extensions}/nocobase-ai/providers/chat-reducer.ts +0 -0
  227. /package/{registry → src/extensions}/nocobase-ai/providers/chat-task-utils.ts +0 -0
  228. /package/{registry → src/extensions}/nocobase-ai/providers/chat-transport.ts +0 -0
  229. /package/{registry → src/extensions}/nocobase-ai/providers/form-registry.tsx +0 -0
  230. /package/{registry → src/extensions}/nocobase-ai/providers/frontend-tool-registry.tsx +0 -0
  231. /package/{registry → src/extensions}/nocobase-ai/providers/index.ts +0 -0
  232. /package/{registry → src/extensions}/nocobase-ai/providers/model.ts +0 -0
  233. /package/{registry → src/extensions}/nocobase-ai/providers/page-context.tsx +0 -0
  234. /package/{registry → src/extensions}/nocobase-ai/providers/stream-coalescer.ts +0 -0
  235. /package/{registry → src/extensions}/nocobase-ai/providers/stream-event-utils.ts +0 -0
  236. /package/{registry → src/extensions}/nocobase-ai/providers/stream-parser.ts +0 -0
  237. /package/{registry → src/extensions}/nocobase-ai/providers/sub-agent-stream.ts +0 -0
  238. /package/{registry → src/extensions}/nocobase-ai/providers/types.ts +0 -0
  239. /package/{registry → src/extensions}/nocobase-ai/providers/ui-message-stream.ts +0 -0
  240. /package/{registry → src/extensions}/nocobase-ai/providers/use-automatic-tool-approval.ts +0 -0
  241. /package/{registry → src/extensions}/nocobase-ai/providers/use-chat-message-actions.ts +0 -0
  242. /package/{registry → src/extensions}/nocobase-ai/providers/use-chat-runtime.ts +0 -0
  243. /package/{registry → src/extensions}/nocobase-ai/providers/use-conversation-catalog.ts +0 -0
  244. /package/{registry → src/extensions}/nocobase-ai/providers/use-conversation-history.ts +0 -0
  245. /package/{registry → src/extensions}/nocobase-ai/services/index.ts +0 -0
  246. /package/{registry → src/extensions}/nocobase-ai/services/nocobase-ai-service.ts +0 -0
  247. /package/{registry → src/extensions}/nocobase-ai/services/types.ts +0 -0
  248. /package/{registry → src/extensions}/nocobase-route-surfaces/demo/lazy-route.tsx +0 -0
  249. /package/{registry → src/extensions}/nocobase-route-surfaces/demo/prompt-generator.tsx +0 -0
  250. /package/{registry → src/extensions}/nocobase-route-surfaces/index.ts +0 -0
  251. /package/{registry → src/extensions}/nocobase-route-surfaces/route-dialog.tsx +0 -0
  252. /package/{registry → src/extensions}/nocobase-route-surfaces/route-drawer.tsx +0 -0
  253. /package/{registry → src/extensions}/nocobase-route-surfaces/route-page.tsx +0 -0
  254. /package/{registry → src/extensions}/nocobase-route-surfaces/route-surface-context.ts +0 -0
  255. /package/{registry → src/extensions}/nocobase-route-surfaces/use-refine-unsaved-changes.tsx +0 -0
  256. /package/{registry → src/extensions}/nocobase-route-surfaces/use-route-surface-close.ts +0 -0
  257. /package/{registry → src/extensions}/nocobase-route-surfaces/use-route-surface-state.ts +0 -0
  258. /package/src/{pages/users → extensions/nocobase-users-example}/form-fields.tsx +0 -0
  259. /package/src/{pages/users → extensions/nocobase-users-example}/index.ts +0 -0
  260. /package/src/{pages/users → extensions/nocobase-users-example}/role-badges.tsx +0 -0
  261. /package/src/{pages/users → extensions/nocobase-users-example}/role-utils.ts +0 -0
  262. /package/src/{pages/users → extensions/nocobase-users-example}/types.ts +0 -0
@@ -1,58 +0,0 @@
1
- import { useCallback, useMemo, useState } from "react";
2
- import { useSearchParams } from "react-router";
3
-
4
- import type { Authenticator } from "@/components/auth/types";
5
- import { nocobaseClient } from "@/lib/nocobase/client";
6
-
7
- export function useOidcSignIn(authenticator: Authenticator) {
8
- const [searchParams] = useSearchParams();
9
- const [isPending, setIsPending] = useState(false);
10
- const [error, setError] = useState<Error>();
11
- const callbackError = useMemo(() => {
12
- if (searchParams.get("authenticator") !== authenticator.name) {
13
- return undefined;
14
- }
15
- const message = searchParams.get("error");
16
- return message ? new Error(message) : undefined;
17
- }, [authenticator.name, searchParams]);
18
-
19
- const signIn = useCallback(async () => {
20
- setError(undefined);
21
- setIsPending(true);
22
-
23
- try {
24
- const redirect =
25
- searchParams.get("to") || searchParams.get("redirect") || "/";
26
- const authUrl = await nocobaseClient.action<string>(
27
- "oidc",
28
- "getAuthUrl",
29
- {
30
- method: "POST",
31
- authenticator: authenticator.name,
32
- includeRole: false,
33
- withAclMeta: false,
34
- body: { redirect },
35
- }
36
- );
37
-
38
- if (!authUrl) {
39
- throw new Error("NocoBase did not return an OIDC authorization URL.");
40
- }
41
-
42
- window.location.replace(nocobaseClient.resolveUrl(authUrl));
43
- } catch (cause) {
44
- setError(
45
- cause instanceof Error
46
- ? cause
47
- : new Error("Unable to start OIDC sign-in.")
48
- );
49
- setIsPending(false);
50
- }
51
- }, [authenticator.name, searchParams]);
52
-
53
- return {
54
- signIn,
55
- isPending,
56
- error: error ?? callbackError,
57
- };
58
- }
@@ -1,6 +0,0 @@
1
- # NocoBase SAML authentication
2
-
3
- Adds dynamic SAML sign-in, callback error handling, automatic redirect support,
4
- and the `useSamlSignIn` hook for custom login UI.
5
-
6
- Installing the Registry also adds a SAML component Demo under Authentication.
@@ -1,13 +0,0 @@
1
- import type { PropsWithChildren } from "react";
2
-
3
- import { AuthAutoRedirectProvider } from "@/components/auth";
4
-
5
- export default function SamlAutoRedirectProvider({
6
- children,
7
- }: PropsWithChildren) {
8
- return (
9
- <AuthAutoRedirectProvider resource="saml" action="checkRedirect">
10
- {children}
11
- </AuthAutoRedirectProvider>
12
- );
13
- }
@@ -1,20 +0,0 @@
1
- import { AuthMethodDemo } from "@/components/auth/demo";
2
- import SamlSignInButton from "./saml-sign-in-button";
3
-
4
- const authenticator = {
5
- name: "company-saml",
6
- authType: "SAML",
7
- title: "Company SAML",
8
- };
9
-
10
- export function SamlAuthDemoPage() {
11
- return (
12
- <AuthMethodDemo
13
- authType="SAML"
14
- methodName="SAML"
15
- description="Start an identity-provider redirect and return through the Starter's shared authentication callback."
16
- >
17
- <SamlSignInButton authenticator={authenticator} onSignIn={() => {}} />
18
- </AuthMethodDemo>
19
- );
20
- }
@@ -1,51 +0,0 @@
1
- import { lazy } from "react";
2
- import { BadgeCheck } from "lucide-react";
3
- import { Route } from "react-router";
4
-
5
- import type { AppExtension } from "@/app/extension";
6
- import { AuthDemoRoute } from "@/components/auth/demo";
7
-
8
- const SamlSignInButton = lazy(() => import("./saml-sign-in-button"));
9
- const SamlAutoRedirectProvider = lazy(
10
- () => import("./auto-redirect-provider")
11
- );
12
- const SamlAuthDemoPage = lazy(() =>
13
- import("./demo").then((module) => ({ default: module.SamlAuthDemoPage }))
14
- );
15
-
16
- const samlAuthExtension: AppExtension = {
17
- id: "nocobase-auth-saml",
18
- AuthRuntimeProvider: SamlAutoRedirectProvider,
19
- authRuntimePriority: 20,
20
- resources: [
21
- {
22
- name: "auth-saml-demo",
23
- list: "/auth/saml",
24
- meta: {
25
- parent: "auth-components",
26
- label: "SAML",
27
- icon: <BadgeCheck />,
28
- acl: { type: "authenticated" },
29
- },
30
- },
31
- ],
32
- routes: (
33
- <Route
34
- path="/auth/saml"
35
- element={
36
- <AuthDemoRoute>
37
- <SamlAuthDemoPage />
38
- </AuthDemoRoute>
39
- }
40
- />
41
- ),
42
- authAdapters: [
43
- {
44
- authType: "SAML",
45
- placement: "button",
46
- Component: SamlSignInButton,
47
- },
48
- ],
49
- };
50
-
51
- export default samlAuthExtension;
@@ -1 +0,0 @@
1
- export { useSamlSignIn } from "./use-saml-sign-in";
@@ -1,43 +0,0 @@
1
- import { LogIn } from "lucide-react";
2
-
3
- import type { AuthenticatorComponentProps } from "@/components/auth/types";
4
- import {
5
- Alert,
6
- AlertDescription,
7
- AlertTitle,
8
- } from "@/components/ui/alert";
9
- import { Button } from "@/components/ui/button";
10
- import { resolveTranslatableText } from "@/lib/i18n";
11
-
12
- import { useSamlSignIn } from "./use-saml-sign-in";
13
-
14
- export default function SamlSignInButton({
15
- authenticator,
16
- onSignIn,
17
- }: AuthenticatorComponentProps & { onSignIn?: () => void }) {
18
- const { signIn, isPending, error } = useSamlSignIn(authenticator);
19
- return (
20
- <div className="space-y-3">
21
- {error && (
22
- <Alert variant="destructive">
23
- <AlertTitle>SAML sign-in failed</AlertTitle>
24
- <AlertDescription>{error.message}</AlertDescription>
25
- </Alert>
26
- )}
27
- <Button
28
- type="button"
29
- variant="outline"
30
- className="w-full"
31
- disabled={!onSignIn && isPending}
32
- onClick={onSignIn ?? signIn}
33
- >
34
- <LogIn />
35
- {!onSignIn && isPending
36
- ? "Redirecting…"
37
- : resolveTranslatableText(
38
- authenticator.title || authenticator.authTypeTitle || "SAML"
39
- )}
40
- </Button>
41
- </div>
42
- );
43
- }
@@ -1,51 +0,0 @@
1
- import { useCallback, useMemo, useState } from "react";
2
- import { useSearchParams } from "react-router";
3
-
4
- import type { Authenticator } from "@/components/auth/types";
5
- import { nocobaseClient } from "@/lib/nocobase/client";
6
-
7
- export function useSamlSignIn(authenticator: Authenticator) {
8
- const [searchParams] = useSearchParams();
9
- const [isPending, setIsPending] = useState(false);
10
- const [error, setError] = useState<Error>();
11
- const callbackError = useMemo(() => {
12
- if (searchParams.get("authenticator") !== authenticator.name) {
13
- return undefined;
14
- }
15
- const message = searchParams.get("error");
16
- return message ? new Error(message) : undefined;
17
- }, [authenticator.name, searchParams]);
18
-
19
- const signIn = useCallback(async () => {
20
- setError(undefined);
21
- setIsPending(true);
22
- try {
23
- const redirect =
24
- searchParams.get("to") || searchParams.get("redirect") || "/";
25
- const authUrl = await nocobaseClient.action<string>(
26
- "saml",
27
- "getAuthUrl",
28
- {
29
- method: "POST",
30
- authenticator: authenticator.name,
31
- includeRole: false,
32
- withAclMeta: false,
33
- body: { redirect },
34
- }
35
- );
36
- if (!authUrl) {
37
- throw new Error("NocoBase did not return a SAML authorization URL.");
38
- }
39
- window.location.replace(nocobaseClient.resolveUrl(authUrl));
40
- } catch (cause) {
41
- setError(
42
- cause instanceof Error
43
- ? cause
44
- : new Error("Unable to start SAML sign-in.")
45
- );
46
- setIsPending(false);
47
- }
48
- }, [authenticator.name, searchParams]);
49
-
50
- return { signIn, isPending, error: error ?? callbackError };
51
- }
@@ -1,7 +0,0 @@
1
- # NocoBase SMS authentication
2
-
3
- Adds phone and verification-code sign-in to the dynamic login page. The
4
- `useSmsSignIn` hook exposes code delivery, countdown state, and sign-in for
5
- custom UI.
6
-
7
- Installing the Registry also adds an SMS component Demo under Authentication.
@@ -1,25 +0,0 @@
1
- import { AuthMethodDemo } from "@/components/auth/demo";
2
- import SmsSignInForm from "./sms-sign-in-form";
3
-
4
- const authenticator = {
5
- name: "company-sms",
6
- authType: "SMS",
7
- title: "SMS verification code",
8
- options: { autoSignup: true, verifier: "login-sms" },
9
- };
10
-
11
- export function SmsAuthDemoPage() {
12
- return (
13
- <AuthMethodDemo
14
- authType="SMS"
15
- methodName="SMS verification code"
16
- description="Collect a phone number and one-time code while keeping delivery and sign-in bound to the configured verifier."
17
- >
18
- <SmsSignInForm
19
- authenticator={authenticator}
20
- onSendCode={() => {}}
21
- onSignIn={() => {}}
22
- />
23
- </AuthMethodDemo>
24
- );
25
- }
@@ -1,46 +0,0 @@
1
- import { lazy } from "react";
2
- import { Smartphone } from "lucide-react";
3
- import { Route } from "react-router";
4
-
5
- import type { AppExtension } from "@/app/extension";
6
- import { AuthDemoRoute } from "@/components/auth/demo";
7
-
8
- const SmsSignInForm = lazy(() => import("./sms-sign-in-form"));
9
- const SmsAuthDemoPage = lazy(() =>
10
- import("./demo").then((module) => ({ default: module.SmsAuthDemoPage }))
11
- );
12
-
13
- const smsAuthExtension: AppExtension = {
14
- id: "nocobase-auth-sms",
15
- resources: [
16
- {
17
- name: "auth-sms-demo",
18
- list: "/auth/sms",
19
- meta: {
20
- parent: "auth-components",
21
- label: "SMS",
22
- icon: <Smartphone />,
23
- acl: { type: "authenticated" },
24
- },
25
- },
26
- ],
27
- routes: (
28
- <Route
29
- path="/auth/sms"
30
- element={
31
- <AuthDemoRoute>
32
- <SmsAuthDemoPage />
33
- </AuthDemoRoute>
34
- }
35
- />
36
- ),
37
- authAdapters: [
38
- {
39
- authType: "SMS",
40
- placement: "form",
41
- Component: SmsSignInForm,
42
- },
43
- ],
44
- };
45
-
46
- export default smsAuthExtension;
@@ -1 +0,0 @@
1
- export { useSmsSignIn } from "./use-sms-sign-in";
@@ -1,107 +0,0 @@
1
- import { useState } from "react";
2
-
3
- import type { AuthenticatorComponentProps } from "@/components/auth/types";
4
- import {
5
- Alert,
6
- AlertDescription,
7
- AlertTitle,
8
- } from "@/components/ui/alert";
9
- import { Button } from "@/components/ui/button";
10
- import { Input } from "@/components/ui/input";
11
- import { Label } from "@/components/ui/label";
12
-
13
- import { useSmsSignIn } from "./use-sms-sign-in";
14
-
15
- export default function SmsSignInForm({
16
- authenticator,
17
- onSendCode,
18
- onSignIn,
19
- }: AuthenticatorComponentProps & {
20
- onSendCode?: (phone: string) => void;
21
- onSignIn?: (values: { phone: string; code: string }) => void;
22
- }) {
23
- const [phone, setPhone] = useState("");
24
- const [code, setCode] = useState("");
25
- const sms = useSmsSignIn(authenticator);
26
- const autoSignup = authenticator.options?.autoSignup === true;
27
-
28
- return (
29
- <form
30
- className="space-y-5"
31
- onSubmit={(event) => {
32
- event.preventDefault();
33
- if (onSignIn) {
34
- onSignIn({ phone, code });
35
- return;
36
- }
37
- void sms.signIn(phone, code);
38
- }}
39
- >
40
- {sms.error && (
41
- <Alert variant="destructive">
42
- <AlertTitle>SMS sign-in failed</AlertTitle>
43
- <AlertDescription>{sms.error.message}</AlertDescription>
44
- </Alert>
45
- )}
46
- <div className="space-y-2">
47
- <Label htmlFor={`${authenticator.name}-phone`}>Phone</Label>
48
- <Input
49
- id={`${authenticator.name}-phone`}
50
- type="tel"
51
- value={phone}
52
- onChange={(event) => setPhone(event.target.value)}
53
- autoComplete="tel"
54
- required
55
- />
56
- </div>
57
- <div className="space-y-2">
58
- <Label htmlFor={`${authenticator.name}-code`}>
59
- Verification code
60
- </Label>
61
- <div className="grid grid-cols-[minmax(0,1fr)_auto] gap-2">
62
- <Input
63
- id={`${authenticator.name}-code`}
64
- inputMode="numeric"
65
- value={code}
66
- onChange={(event) => setCode(event.target.value)}
67
- autoComplete="one-time-code"
68
- required
69
- />
70
- <Button
71
- type="button"
72
- variant="outline"
73
- disabled={
74
- (!onSendCode && (sms.isSendingCode || sms.retryAfter > 0)) ||
75
- !phone
76
- }
77
- onClick={() => {
78
- if (onSendCode) {
79
- onSendCode(phone);
80
- return;
81
- }
82
- void sms.sendCode(phone).catch(() => undefined);
83
- }}
84
- >
85
- {!onSendCode && sms.isSendingCode
86
- ? "Sending…"
87
- : !onSendCode && sms.retryAfter > 0
88
- ? `Retry in ${sms.retryAfter}s`
89
- : "Send code"}
90
- </Button>
91
- </div>
92
- </div>
93
- <Button
94
- type="submit"
95
- className="w-full"
96
- disabled={!onSignIn && sms.isSigningIn}
97
- >
98
- {!onSignIn && sms.isSigningIn ? "Signing in…" : "Sign in"}
99
- </Button>
100
- {autoSignup && (
101
- <p className="text-xs leading-5 text-muted-foreground">
102
- An account will be created automatically if this phone number is new.
103
- </p>
104
- )}
105
- </form>
106
- );
107
- }
@@ -1,88 +0,0 @@
1
- import { useCallback, useEffect, useMemo, useState } from "react";
2
-
3
- import { useAuthenticatorSignIn } from "@/components/auth";
4
- import type { Authenticator } from "@/components/auth/types";
5
- import { nocobaseClient } from "@/lib/nocobase/client";
6
-
7
- type SmsCodeResponse = {
8
- expiresAt?: string;
9
- };
10
-
11
- export function useSmsSignIn(authenticator: Authenticator) {
12
- const auth = useAuthenticatorSignIn(authenticator.name);
13
- const [isSendingCode, setIsSendingCode] = useState(false);
14
- const [sendError, setSendError] = useState<Error>();
15
- const [expiresAt, setExpiresAt] = useState<number>();
16
- const [now, setNow] = useState(Date.now());
17
- const verifier =
18
- typeof authenticator.options?.verifier === "string"
19
- ? authenticator.options.verifier
20
- : "";
21
-
22
- useEffect(() => {
23
- if (!expiresAt || expiresAt <= Date.now()) return;
24
- const timer = window.setInterval(() => setNow(Date.now()), 1_000);
25
- return () => window.clearInterval(timer);
26
- }, [expiresAt]);
27
-
28
- const retryAfter = useMemo(
29
- () =>
30
- expiresAt ? Math.max(0, Math.ceil((expiresAt - now) / 1_000)) : 0,
31
- [expiresAt, now]
32
- );
33
-
34
- const sendCode = useCallback(
35
- async (phone: string) => {
36
- if (!phone) throw new Error("Enter a phone number first.");
37
- if (!verifier) {
38
- throw new Error("The SMS verifier is not configured.");
39
- }
40
- if (retryAfter > 0) return;
41
-
42
- setSendError(undefined);
43
- setIsSendingCode(true);
44
- try {
45
- const result = await nocobaseClient.action<SmsCodeResponse>(
46
- "smsOTP",
47
- "publicCreate",
48
- {
49
- method: "POST",
50
- authenticator: null,
51
- includeRole: false,
52
- withAclMeta: false,
53
- body: {
54
- action: "auth:signIn",
55
- verifier,
56
- uuid: phone,
57
- },
58
- }
59
- );
60
- const nextExpiry = result?.expiresAt
61
- ? Date.parse(result.expiresAt)
62
- : Date.now() + 60_000;
63
- setNow(Date.now());
64
- setExpiresAt(nextExpiry);
65
- } catch (cause) {
66
- const error =
67
- cause instanceof Error
68
- ? cause
69
- : new Error("Unable to send the verification code.");
70
- setSendError(error);
71
- throw error;
72
- } finally {
73
- setIsSendingCode(false);
74
- }
75
- },
76
- [retryAfter, verifier]
77
- );
78
-
79
- return {
80
- sendCode,
81
- signIn: (phone: string, code: string) =>
82
- auth.signIn({ uuid: phone, code }),
83
- isSendingCode,
84
- isSigningIn: auth.isPending,
85
- retryAfter,
86
- error: sendError ?? auth.error,
87
- };
88
- }
@@ -1,6 +0,0 @@
1
- # NocoBase WeCom authentication
2
-
3
- Adds the WeCom sign-in button, callback errors, WeCom-browser automatic login,
4
- and `useWecomSignIn` for custom UI.
5
-
6
- Installing the Registry also adds a WeCom component Demo under Authentication.
@@ -1,21 +0,0 @@
1
- import type { PropsWithChildren } from "react";
2
-
3
- import { AuthAutoRedirectProvider } from "@/components/auth";
4
-
5
- import { isWecomBrowser } from "./use-wecom-sign-in";
6
-
7
- export default function WecomAutoLoginProvider({
8
- children,
9
- }: PropsWithChildren) {
10
- const insideWecom = isWecomBrowser();
11
- return (
12
- <AuthAutoRedirectProvider
13
- resource="wecom"
14
- action="checkLogin"
15
- enabled={insideWecom}
16
- query={{ isWeComBrowser: insideWecom }}
17
- >
18
- {children}
19
- </AuthAutoRedirectProvider>
20
- );
21
- }
@@ -1,23 +0,0 @@
1
- import { AuthMethodDemo } from "@/components/auth/demo";
2
- import WecomSignInButton from "./wecom-sign-in-button";
3
-
4
- const authenticator = {
5
- name: "company-wecom",
6
- authType: "wecom",
7
- title: "Sign in via WeCom",
8
- options: {
9
- btnTooltip: "Use the account associated with your company directory.",
10
- },
11
- };
12
-
13
- export function WecomAuthDemoPage() {
14
- return (
15
- <AuthMethodDemo
16
- authType="wecom"
17
- methodName="WeCom"
18
- description="Provide a WeCom sign-in button and support automatic login when the application is opened inside WeCom."
19
- >
20
- <WecomSignInButton authenticator={authenticator} onSignIn={() => {}} />
21
- </AuthMethodDemo>
22
- );
23
- }
@@ -1,49 +0,0 @@
1
- import { lazy } from "react";
2
- import { Building2 } from "lucide-react";
3
- import { Route } from "react-router";
4
-
5
- import type { AppExtension } from "@/app/extension";
6
- import { AuthDemoRoute } from "@/components/auth/demo";
7
-
8
- const WecomSignInButton = lazy(() => import("./wecom-sign-in-button"));
9
- const WecomAutoLoginProvider = lazy(() => import("./auto-login-provider"));
10
- const WecomAuthDemoPage = lazy(() =>
11
- import("./demo").then((module) => ({ default: module.WecomAuthDemoPage }))
12
- );
13
-
14
- const wecomAuthExtension: AppExtension = {
15
- id: "nocobase-auth-wecom",
16
- AuthRuntimeProvider: WecomAutoLoginProvider,
17
- authRuntimePriority: 10,
18
- resources: [
19
- {
20
- name: "auth-wecom-demo",
21
- list: "/auth/wecom",
22
- meta: {
23
- parent: "auth-components",
24
- label: "WeCom",
25
- icon: <Building2 />,
26
- acl: { type: "authenticated" },
27
- },
28
- },
29
- ],
30
- routes: (
31
- <Route
32
- path="/auth/wecom"
33
- element={
34
- <AuthDemoRoute>
35
- <WecomAuthDemoPage />
36
- </AuthDemoRoute>
37
- }
38
- />
39
- ),
40
- authAdapters: [
41
- {
42
- authType: "wecom",
43
- placement: "button",
44
- Component: WecomSignInButton,
45
- },
46
- ],
47
- };
48
-
49
- export default wecomAuthExtension;
@@ -1 +0,0 @@
1
- export { isWecomBrowser, useWecomSignIn } from "./use-wecom-sign-in";