@nocobase/portal-template-default 1.0.4 → 1.0.6

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 (139) hide show
  1. package/README.MD +77 -0
  2. package/package.json +23 -17
  3. package/registry/nocobase-acl/demo/prompt-generator.tsx +6 -39
  4. package/registry/nocobase-acl/demo/role-switcher-prompt-generator.tsx +6 -42
  5. package/registry/nocobase-ai/demo/page-context-prompt-generator.tsx +1 -1
  6. package/registry/nocobase-ai/demo/prompt-card.tsx +7 -28
  7. package/registry/nocobase-ai/demo/prompt-generator.tsx +9 -30
  8. package/registry/nocobase-ai/demo/shortcut.tsx +1 -1
  9. package/registry/nocobase-ai/demo/tool-cards.tsx +10 -30
  10. package/registry/nocobase-auth-cas/README.md +6 -0
  11. package/registry/nocobase-auth-cas/cas-sign-in-button.tsx +41 -0
  12. package/registry/nocobase-auth-cas/demo.tsx +20 -0
  13. package/registry/nocobase-auth-cas/extension.tsx +46 -0
  14. package/registry/nocobase-auth-cas/index.ts +1 -0
  15. package/registry/nocobase-auth-cas/use-cas-sign-in.ts +29 -0
  16. package/registry/nocobase-auth-dingtalk/README.md +7 -0
  17. package/registry/nocobase-auth-dingtalk/auto-login-provider.tsx +154 -0
  18. package/registry/nocobase-auth-dingtalk/demo.tsx +23 -0
  19. package/registry/nocobase-auth-dingtalk/dingtalk-sign-in-button.tsx +45 -0
  20. package/registry/nocobase-auth-dingtalk/extension.tsx +55 -0
  21. package/registry/nocobase-auth-dingtalk/index.ts +4 -0
  22. package/registry/nocobase-auth-dingtalk/use-dingtalk-sign-in.ts +64 -0
  23. package/registry/nocobase-auth-ldap/README.md +6 -0
  24. package/registry/nocobase-auth-ldap/demo.tsx +21 -0
  25. package/registry/nocobase-auth-ldap/extension.tsx +46 -0
  26. package/registry/nocobase-auth-ldap/index.ts +1 -0
  27. package/registry/nocobase-auth-ldap/ldap-sign-in-form.tsx +69 -0
  28. package/registry/nocobase-auth-ldap/use-ldap-sign-in.ts +5 -0
  29. package/registry/nocobase-auth-oidc/README.md +11 -0
  30. package/registry/nocobase-auth-oidc/auto-redirect-provider.tsx +13 -0
  31. package/registry/nocobase-auth-oidc/demo.tsx +20 -0
  32. package/registry/nocobase-auth-oidc/extension.tsx +51 -0
  33. package/registry/nocobase-auth-oidc/index.ts +1 -0
  34. package/registry/nocobase-auth-oidc/oidc-sign-in-button.tsx +44 -0
  35. package/registry/nocobase-auth-oidc/use-oidc-sign-in.ts +58 -0
  36. package/registry/nocobase-auth-saml/README.md +6 -0
  37. package/registry/nocobase-auth-saml/auto-redirect-provider.tsx +13 -0
  38. package/registry/nocobase-auth-saml/demo.tsx +20 -0
  39. package/registry/nocobase-auth-saml/extension.tsx +51 -0
  40. package/registry/nocobase-auth-saml/index.ts +1 -0
  41. package/registry/nocobase-auth-saml/saml-sign-in-button.tsx +43 -0
  42. package/registry/nocobase-auth-saml/use-saml-sign-in.ts +51 -0
  43. package/registry/nocobase-auth-sms/README.md +7 -0
  44. package/registry/nocobase-auth-sms/demo.tsx +25 -0
  45. package/registry/nocobase-auth-sms/extension.tsx +46 -0
  46. package/registry/nocobase-auth-sms/index.ts +1 -0
  47. package/registry/nocobase-auth-sms/sms-sign-in-form.tsx +107 -0
  48. package/registry/nocobase-auth-sms/use-sms-sign-in.ts +88 -0
  49. package/registry/nocobase-auth-wecom/README.md +6 -0
  50. package/registry/nocobase-auth-wecom/auto-login-provider.tsx +21 -0
  51. package/registry/nocobase-auth-wecom/demo.tsx +23 -0
  52. package/registry/nocobase-auth-wecom/extension.tsx +49 -0
  53. package/registry/nocobase-auth-wecom/index.ts +1 -0
  54. package/registry/nocobase-auth-wecom/use-wecom-sign-in.ts +62 -0
  55. package/registry/nocobase-auth-wecom/wecom-sign-in-button.tsx +70 -0
  56. package/registry/nocobase-i18n/demo/prompt-generator.tsx +30 -54
  57. package/registry/nocobase-route-surfaces/demo/prompt-generator.tsx +18 -21
  58. package/registry.config.json +112 -0
  59. package/scripts/build-html.mjs +106 -0
  60. package/src/App.tsx +34 -8
  61. package/src/app/extension.ts +4 -0
  62. package/src/app/extensions.tsx +24 -1
  63. package/src/components/auth/authenticator-adapters.ts +20 -0
  64. package/src/components/auth/auto-redirect-provider.tsx +84 -0
  65. package/src/components/auth/basic-sign-in-form.tsx +101 -0
  66. package/src/components/auth/default-sign-in-page.tsx +20 -0
  67. package/src/components/auth/demo/auth-demo-page.tsx +236 -0
  68. package/src/components/auth/demo/auth-demo-prompt-generator.tsx +93 -0
  69. package/src/components/auth/demo/auth-demo-route.tsx +11 -0
  70. package/src/components/auth/demo/auth-method-demo.tsx +48 -0
  71. package/src/components/auth/demo/index.ts +4 -0
  72. package/src/components/auth/dynamic-sign-in.tsx +191 -0
  73. package/src/components/auth/forgot-password-form.tsx +13 -5
  74. package/src/components/auth/index.ts +13 -0
  75. package/src/components/auth/sign-in-form.tsx +1 -111
  76. package/src/components/auth/sign-up-form.tsx +14 -37
  77. package/src/components/auth/types.ts +38 -0
  78. package/src/components/auth/use-authenticator-sign-in.ts +14 -0
  79. package/src/components/auth/use-public-authenticators.ts +26 -0
  80. package/src/components/data-table/data-table-filter.tsx +107 -153
  81. package/src/components/demo/prompt-output.tsx +71 -0
  82. package/src/components/ui/alert-dialog.tsx +2 -0
  83. package/src/components/ui/attachment.tsx +207 -0
  84. package/src/components/ui/bubble.tsx +128 -0
  85. package/src/components/ui/button-group.tsx +87 -0
  86. package/src/components/ui/button.tsx +6 -6
  87. package/src/components/ui/calendar.tsx +0 -2
  88. package/src/components/ui/card.tsx +1 -1
  89. package/src/components/ui/carousel.tsx +2 -0
  90. package/src/components/ui/combobox.tsx +297 -0
  91. package/src/components/ui/command.tsx +43 -41
  92. package/src/components/ui/dialog.tsx +0 -2
  93. package/src/components/ui/direction.tsx +4 -0
  94. package/src/components/ui/drawer.tsx +2 -0
  95. package/src/components/ui/dropdown-menu.tsx +0 -2
  96. package/src/components/ui/empty.tsx +104 -0
  97. package/src/components/ui/field.tsx +238 -0
  98. package/src/components/ui/hover-card.tsx +2 -0
  99. package/src/components/ui/input-group.tsx +0 -2
  100. package/src/components/ui/input-otp.tsx +0 -2
  101. package/src/components/ui/input.tsx +1 -1
  102. package/src/components/ui/item.tsx +201 -0
  103. package/src/components/ui/kbd.tsx +26 -0
  104. package/src/components/ui/label.tsx +2 -0
  105. package/src/components/ui/marker.tsx +71 -0
  106. package/src/components/ui/message-scroller.tsx +131 -0
  107. package/src/components/ui/message.tsx +92 -0
  108. package/src/components/ui/native-select.tsx +61 -0
  109. package/src/components/ui/popover.tsx +0 -2
  110. package/src/components/ui/progress.tsx +2 -0
  111. package/src/components/ui/radio-group.tsx +0 -2
  112. package/src/components/ui/resizable.tsx +2 -0
  113. package/src/components/ui/scroll-area.tsx +0 -2
  114. package/src/components/ui/select.tsx +6 -4
  115. package/src/components/ui/separator.tsx +0 -2
  116. package/src/components/ui/sheet.tsx +21 -22
  117. package/src/components/ui/sidebar.tsx +1 -3
  118. package/src/components/ui/sonner.tsx +2 -0
  119. package/src/components/ui/spinner.tsx +10 -0
  120. package/src/components/ui/switch.tsx +0 -2
  121. package/src/components/ui/table.tsx +4 -2
  122. package/src/components/ui/tabs.tsx +0 -2
  123. package/src/components/ui/textarea.tsx +1 -1
  124. package/src/components/ui/toggle-group.tsx +2 -0
  125. package/src/components/ui/toggle.tsx +2 -0
  126. package/src/components/ui/tooltip.tsx +0 -2
  127. package/src/index.tsx +3 -0
  128. package/src/lib/nocobase/client.ts +32 -2
  129. package/src/lib/utils.ts +2 -1
  130. package/src/locales/en-US.ts +4 -2
  131. package/src/locales/zh-CN.ts +4 -2
  132. package/src/pages/login/index.tsx +2 -2
  133. package/src/pages/users/list.tsx +144 -16
  134. package/src/providers/auth-callback.ts +21 -0
  135. package/src/providers/auth.ts +108 -22
  136. package/src/providers/constants.ts +4 -1
  137. package/src/providers/runtime-config.ts +19 -0
  138. package/src/vite-env.d.ts +5 -0
  139. package/vite.config.ts +55 -2
@@ -25,6 +25,10 @@ type NocoBaseSignInResponse = {
25
25
  user?: NocoBaseUser;
26
26
  };
27
27
 
28
+ type NocoBaseSignOutResponse = {
29
+ redirect?: string;
30
+ };
31
+
28
32
  type CurrentUserCache = {
29
33
  token: string;
30
34
  user: NocoBaseUser;
@@ -77,25 +81,26 @@ const getCurrentUser = async (): Promise<NocoBaseUser> => {
77
81
  };
78
82
 
79
83
  export const authProvider: AuthProvider = {
80
- login: async ({ username, email, password, providerName }) => {
81
- if (providerName) {
82
- return {
83
- success: false,
84
- error: {
85
- name: "UnsupportedAuthenticator",
86
- message:
87
- "Configure this provider as a NocoBase authenticator before using social sign-in.",
88
- },
89
- };
90
- }
84
+ login: async (params) => {
85
+ const {
86
+ authenticator = nocobaseClient.getAuthenticator(),
87
+ username,
88
+ email,
89
+ redirectTo = "/",
90
+ ...values
91
+ } = params ?? {};
92
+ const account = values.account ?? username ?? email;
93
+ const body = {
94
+ ...values,
95
+ ...(account ? { account } : {}),
96
+ };
91
97
 
92
- const account = username ?? email;
93
- if (!account || !password) {
98
+ if (Object.keys(body).length === 0) {
94
99
  return {
95
100
  success: false,
96
101
  error: {
97
102
  name: "LoginError",
98
- message: "Please enter your account and password.",
103
+ message: "Please enter your sign-in details.",
99
104
  },
100
105
  };
101
106
  }
@@ -106,8 +111,8 @@ export const authProvider: AuthProvider = {
106
111
  "signIn",
107
112
  {
108
113
  method: "POST",
109
- includeAuthenticator: true,
110
- body: { account, password },
114
+ authenticator,
115
+ body,
111
116
  }
112
117
  );
113
118
  if (!result.token) {
@@ -120,11 +125,12 @@ export const authProvider: AuthProvider = {
120
125
  };
121
126
  }
122
127
 
128
+ nocobaseClient.setAuthenticator(authenticator);
123
129
  nocobaseClient.setToken(result.token);
124
130
  nocobaseClient.setRole(null);
125
131
  clearCurrentUserCache();
126
132
  clearAcl();
127
- return { success: true, redirectTo: "/" };
133
+ return { success: true, redirectTo };
128
134
  } catch (error) {
129
135
  return {
130
136
  success: false,
@@ -140,23 +146,101 @@ export const authProvider: AuthProvider = {
140
146
  }
141
147
  },
142
148
 
149
+ register: async (params) => {
150
+ const {
151
+ authenticator = nocobaseClient.getAuthenticator(),
152
+ redirectTo = "/login",
153
+ ...values
154
+ } = params ?? {};
155
+
156
+ try {
157
+ await nocobaseClient.action("auth", "signUp", {
158
+ method: "POST",
159
+ authenticator,
160
+ body: values,
161
+ });
162
+ return {
163
+ success: true,
164
+ redirectTo,
165
+ successNotification: {
166
+ message: "Account created",
167
+ description: "You can now sign in with your new account.",
168
+ },
169
+ };
170
+ } catch (error) {
171
+ return {
172
+ success: false,
173
+ error: {
174
+ name: "RegistrationError",
175
+ message: getErrorMessage(error, "Unable to create the account."),
176
+ },
177
+ };
178
+ }
179
+ },
180
+
181
+ forgotPassword: async (params) => {
182
+ const {
183
+ authenticator = nocobaseClient.getAuthenticator(),
184
+ ...values
185
+ } = params ?? {};
186
+ const baseURL =
187
+ typeof window === "undefined"
188
+ ? undefined
189
+ : window.location.href.split("/forgot-password")[0];
190
+
191
+ try {
192
+ await nocobaseClient.action("auth", "lostPassword", {
193
+ method: "POST",
194
+ authenticator,
195
+ body: { ...values, baseURL },
196
+ });
197
+ return {
198
+ success: true,
199
+ successNotification: {
200
+ message: "Reset link sent",
201
+ description: "Check your inbox for password reset instructions.",
202
+ },
203
+ };
204
+ } catch (error) {
205
+ return {
206
+ success: false,
207
+ error: {
208
+ name: "PasswordResetError",
209
+ message: getErrorMessage(error, "Unable to send the reset link."),
210
+ },
211
+ };
212
+ }
213
+ },
214
+
143
215
  logout: async () => {
144
216
  const token = nocobaseClient.getToken();
217
+ let redirect: string | undefined;
145
218
  try {
146
219
  if (token) {
147
- await nocobaseClient.action("auth", "signOut", {
148
- method: "POST",
149
- token,
150
- includeAuthenticator: true,
151
- });
220
+ const result = await nocobaseClient.action<NocoBaseSignOutResponse>(
221
+ "auth",
222
+ "signOut",
223
+ {
224
+ method: "POST",
225
+ token,
226
+ includeAuthenticator: true,
227
+ }
228
+ );
229
+ redirect = result?.redirect;
152
230
  }
153
231
  } finally {
154
232
  nocobaseClient.setToken(null);
233
+ nocobaseClient.setAuthenticator(null);
155
234
  nocobaseClient.setRole(null);
156
235
  clearCurrentUserCache();
157
236
  clearAcl();
158
237
  }
159
238
 
239
+ if (redirect && typeof window !== "undefined") {
240
+ window.location.assign(nocobaseClient.resolveUrl(redirect));
241
+ return { success: true };
242
+ }
243
+
160
244
  return { success: true, redirectTo: "/login" };
161
245
  },
162
246
 
@@ -166,6 +250,7 @@ export const authProvider: AuthProvider = {
166
250
  return { authenticated: true };
167
251
  } catch {
168
252
  nocobaseClient.setToken(null);
253
+ nocobaseClient.setAuthenticator(null);
169
254
  nocobaseClient.setRole(null);
170
255
  clearCurrentUserCache();
171
256
  clearAcl();
@@ -207,6 +292,7 @@ export const authProvider: AuthProvider = {
207
292
 
208
293
  if (status === 401) {
209
294
  nocobaseClient.setToken(null);
295
+ nocobaseClient.setAuthenticator(null);
210
296
  nocobaseClient.setRole(null);
211
297
  clearCurrentUserCache();
212
298
  clearAcl();
@@ -1,5 +1,7 @@
1
+ import { getRuntimeApiUrl } from "./runtime-config";
2
+
1
3
  const rawApiUrl =
2
- import.meta.env.NOCOBASE_API_URL ?? "http://127.0.0.1:13000/api";
4
+ getRuntimeApiUrl() ?? "http://127.0.0.1:13000/api";
3
5
 
4
6
  const getDefaultProxyTarget = (apiUrl?: string) => {
5
7
  if (!apiUrl || apiUrl.startsWith("/")) return undefined;
@@ -35,6 +37,7 @@ const toProxyRelativeUrl = (url: string, target?: string) => {
35
37
  export const API_URL = toProxyRelativeUrl(rawApiUrl, proxyTarget);
36
38
  export const API_ORIGIN = getDefaultProxyTarget(rawApiUrl);
37
39
  export const NOCOBASE_TOKEN_KEY = "nocobase-auth-token";
40
+ export const NOCOBASE_AUTHENTICATOR_KEY = "nocobase-authenticator";
38
41
  export const NOCOBASE_ROLE_KEY = "nocobase-auth-role";
39
42
  export const NOCOBASE_LOCALE_KEY = "nocobase-locale";
40
43
  export const NOCOBASE_AUTHENTICATOR =
@@ -0,0 +1,19 @@
1
+ const getWindowValue = (key: "NOCOBASE_PORTAL_BASE" | "NOCOBASE_API_URL") => {
2
+ if (typeof window === "undefined") return undefined;
3
+ const value = window[key];
4
+ return typeof value === "string" && value.trim() ? value.trim() : undefined;
5
+ };
6
+
7
+ export const normalizePortalBase = (base?: string) => {
8
+ const normalized = String(base || "/").trim();
9
+ if (!normalized || normalized === "/") return "/";
10
+ return `/${normalized.replace(/^\/+|\/+$/g, "")}/`;
11
+ };
12
+
13
+ export const getPortalBase = () =>
14
+ normalizePortalBase(
15
+ getWindowValue("NOCOBASE_PORTAL_BASE") ?? import.meta.env.BASE_URL
16
+ );
17
+
18
+ export const getRuntimeApiUrl = () =>
19
+ getWindowValue("NOCOBASE_API_URL") ?? import.meta.env.NOCOBASE_API_URL;
package/src/vite-env.d.ts CHANGED
@@ -1 +1,6 @@
1
1
  /// <reference types="vite/client" />
2
+
3
+ interface Window {
4
+ NOCOBASE_PORTAL_BASE?: string;
5
+ NOCOBASE_API_URL?: string;
6
+ }
package/vite.config.ts CHANGED
@@ -1,5 +1,6 @@
1
1
  import tailwindcss from "@tailwindcss/vite";
2
2
  import react from "@vitejs/plugin-react";
3
+ import fs from "node:fs";
3
4
  import path from "path";
4
5
  import { defineConfig, loadEnv } from "vite";
5
6
 
@@ -30,6 +31,56 @@ const normalizeBase = (base?: string) => {
30
31
  return `/${normalized.replace(/^\/+|\/+$/g, "")}/`;
31
32
  };
32
33
 
34
+ const getBasePrefix = (base: string) => base.replace(/\/$/, "");
35
+
36
+ const defaultRuntimeConfig = `<!-- nocobase-runtime-config:start -->
37
+ <script>
38
+ window.NOCOBASE_PORTAL_BASE = "/";
39
+ window.NOCOBASE_API_URL = "/api";
40
+ </script>
41
+ <!-- nocobase-runtime-config:end -->
42
+ `;
43
+
44
+ const stripBaseFromIndexHtml = (html: string, base: string) => {
45
+ const basePrefix = getBasePrefix(base);
46
+ if (!basePrefix) return html;
47
+
48
+ const attributePattern = /\b(src|href|content)=(["'])\/(?!\/)([^"']*)\2/g;
49
+
50
+ return html.replace(attributePattern, (match, attribute, quote, path) => {
51
+ const value = `/${path}`;
52
+ if (!value.startsWith(`${basePrefix}/`)) return match;
53
+ return `${attribute}=${quote}${value.slice(basePrefix.length) || "/"}${quote}`;
54
+ });
55
+ };
56
+
57
+ const copyRawIndexHtmlPlugin = (base: string) => ({
58
+ name: "copy-raw-index-html",
59
+ closeBundle() {
60
+ const distDir = path.resolve(__dirname, "dist");
61
+ const indexHtml = path.join(distDir, "index.html");
62
+ const rawIndexHtml = path.join(distDir, "index.raw.html");
63
+
64
+ if (fs.existsSync(indexHtml)) {
65
+ const html = fs.readFileSync(indexHtml, "utf8");
66
+ const rawHtml = stripBaseFromIndexHtml(html, base);
67
+ const moduleScriptPattern =
68
+ /<script\s+[^>]*type=["']module["'][^>]*>/i;
69
+ const moduleScriptMatch = rawHtml.match(moduleScriptPattern);
70
+
71
+ if (moduleScriptMatch?.index === undefined) {
72
+ fs.writeFileSync(rawIndexHtml, rawHtml);
73
+ return;
74
+ }
75
+
76
+ fs.writeFileSync(
77
+ rawIndexHtml,
78
+ `${rawHtml.slice(0, moduleScriptMatch.index)}${defaultRuntimeConfig}${rawHtml.slice(moduleScriptMatch.index)}`
79
+ );
80
+ }
81
+ },
82
+ });
83
+
33
84
  // https://vite.dev/config/
34
85
  export default defineConfig(({ mode }) => {
35
86
  const env = loadEnv(mode, process.cwd(), "");
@@ -44,10 +95,12 @@ export default defineConfig(({ mode }) => {
44
95
  })()
45
96
  : undefined;
46
97
 
98
+ const portalBase = normalizeBase(env.NOCOBASE_PORTAL_BASE);
99
+
47
100
  return {
48
- base: normalizeBase(env.NOCOBASE_PORTAL_BASE),
101
+ base: portalBase,
49
102
  envPrefix: ["VITE_", "NOCOBASE_"],
50
- plugins: [react(), tailwindcss()],
103
+ plugins: [react(), tailwindcss(), copyRawIndexHtmlPlugin(portalBase)],
51
104
  resolve: {
52
105
  alias: {
53
106
  "@": path.resolve(__dirname, "./src"),