openxiangda 1.0.61 → 1.0.63

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 (32) hide show
  1. package/package.json +1 -1
  2. package/packages/sdk/dist/components/index.cjs +3 -3
  3. package/packages/sdk/dist/components/index.cjs.map +1 -1
  4. package/packages/sdk/dist/components/index.mjs +3 -3
  5. package/packages/sdk/dist/components/index.mjs.map +1 -1
  6. package/packages/sdk/dist/runtime/index.cjs +279 -16
  7. package/packages/sdk/dist/runtime/index.cjs.map +1 -1
  8. package/packages/sdk/dist/runtime/index.d.mts +1 -1
  9. package/packages/sdk/dist/runtime/index.d.ts +1 -1
  10. package/packages/sdk/dist/runtime/index.mjs +279 -16
  11. package/packages/sdk/dist/runtime/index.mjs.map +1 -1
  12. package/packages/sdk/dist/runtime/react.cjs +277 -13
  13. package/packages/sdk/dist/runtime/react.cjs.map +1 -1
  14. package/packages/sdk/dist/runtime/react.d.mts +60 -7
  15. package/packages/sdk/dist/runtime/react.d.ts +60 -7
  16. package/packages/sdk/dist/runtime/react.mjs +277 -13
  17. package/packages/sdk/dist/runtime/react.mjs.map +1 -1
  18. package/templates/openxiangda-react-spa/src/app/router.tsx +12 -1
  19. package/templates/openxiangda-react-spa/src/layouts/AdminShell.tsx +374 -96
  20. package/templates/openxiangda-react-spa/src/layouts/PublicShell.tsx +25 -3
  21. package/templates/openxiangda-react-spa/src/layouts/UserShell.tsx +101 -22
  22. package/templates/openxiangda-react-spa/src/pages/admin/RuntimeWorkspacePage.tsx +203 -41
  23. package/templates/openxiangda-react-spa/src/pages/defaults/DataRoutePage.tsx +80 -11
  24. package/templates/openxiangda-react-spa/src/pages/defaults/FilePreviewRoutePage.tsx +67 -14
  25. package/templates/openxiangda-react-spa/src/pages/defaults/FormRoutePage.tsx +153 -30
  26. package/templates/openxiangda-react-spa/src/pages/portal/UserPortalPage.tsx +53 -14
  27. package/templates/openxiangda-react-spa/src/pages/public/PublicHomePage.tsx +46 -6
  28. package/templates/openxiangda-react-spa/src/pages/states/NotFoundPage.tsx +26 -11
  29. package/templates/openxiangda-react-spa/src/shared/mac-admin.tsx +332 -0
  30. package/templates/openxiangda-react-spa/src/styles/index.css +42 -4
  31. package/templates/openxiangda-react-spa/tailwind.config.cjs +8 -0
  32. package/templates/openxiangda-react-spa/vite.config.ts +31 -0
@@ -856,6 +856,7 @@ __export(runtime_exports, {
856
856
  usePageRoute: () => usePageRoute,
857
857
  usePageSdk: () => usePageSdk,
858
858
  usePermission: () => usePermission,
859
+ useRuntimeAuth: () => useRuntimeAuth,
859
860
  useRuntimeBootstrap: () => useRuntimeBootstrap
860
861
  });
861
862
  module.exports = __toCommonJS(runtime_exports);
@@ -3328,6 +3329,16 @@ var createBoundFetch = (fetchImpl) => {
3328
3329
 
3329
3330
  // packages/sdk/src/runtime/react/openxiangdaProvider.tsx
3330
3331
  var import_jsx_runtime3 = require("react/jsx-runtime");
3332
+ var RuntimeHttpError = class extends Error {
3333
+ constructor(snapshot) {
3334
+ super(snapshot.message);
3335
+ this.name = "RuntimeHttpError";
3336
+ this.type = snapshot.type;
3337
+ this.status = snapshot.status;
3338
+ this.code = snapshot.code;
3339
+ this.payload = snapshot.payload;
3340
+ }
3341
+ };
3331
3342
  var OpenXiangdaRuntimeContext = (0, import_react7.createContext)(null);
3332
3343
  var OpenXiangdaProvider = ({
3333
3344
  appType,
@@ -3350,7 +3361,10 @@ var OpenXiangdaProvider = ({
3350
3361
  setState({
3351
3362
  data: null,
3352
3363
  loading: false,
3353
- error: new Error("appType \u4E0D\u80FD\u4E3A\u7A7A")
3364
+ error: createRuntimeError({
3365
+ message: "appType \u4E0D\u80FD\u4E3A\u7A7A",
3366
+ type: "unknown"
3367
+ })
3354
3368
  });
3355
3369
  return;
3356
3370
  }
@@ -3368,9 +3382,13 @@ var OpenXiangdaProvider = ({
3368
3382
  headers: { accept: "application/json" }
3369
3383
  }
3370
3384
  );
3371
- const payload = await response.json();
3385
+ const payload = await readJsonPayload(response);
3372
3386
  if (!response.ok || payload?.code >= 400) {
3373
- throw new Error(payload?.message || `Runtime bootstrap failed: ${response.status}`);
3387
+ throw createRuntimeHttpError(
3388
+ response,
3389
+ payload,
3390
+ payload?.message || `Runtime bootstrap failed: ${response.status}`
3391
+ );
3374
3392
  }
3375
3393
  setState({
3376
3394
  data: payload?.data || null,
@@ -3381,7 +3399,7 @@ var OpenXiangdaProvider = ({
3381
3399
  setState({
3382
3400
  data: null,
3383
3401
  loading: false,
3384
- error: error instanceof Error ? error : new Error(String(error))
3402
+ error: normalizeRuntimeError(error)
3385
3403
  });
3386
3404
  }
3387
3405
  }, [resolvedAppType, resolvedFetch, servicePrefix]);
@@ -3437,6 +3455,23 @@ var useCanAccessRoute = (input) => {
3437
3455
  setState((prev) => ({ ...prev, loading: runtime.loading }));
3438
3456
  return;
3439
3457
  }
3458
+ if (runtime.error && !runtime.data) {
3459
+ const snapshot = toRuntimeErrorSnapshot(runtime.error);
3460
+ setState({
3461
+ data: {
3462
+ appType: runtime.appType,
3463
+ canAccess: false,
3464
+ status: snapshot.status,
3465
+ code: snapshot.code,
3466
+ message: snapshot.message,
3467
+ errorType: snapshot.type,
3468
+ payload: snapshot.payload
3469
+ },
3470
+ loading: false,
3471
+ error: runtime.error
3472
+ });
3473
+ return;
3474
+ }
3440
3475
  if (permissions?.hasFullAccess) {
3441
3476
  setState({
3442
3477
  data: { appType: runtime.appType, canAccess: true, permissions },
@@ -3464,15 +3499,33 @@ var useCanAccessRoute = (input) => {
3464
3499
  body: JSON.stringify(input)
3465
3500
  }
3466
3501
  );
3467
- const payload = await response.json();
3502
+ const payload = await readJsonPayload(response);
3503
+ const code = payload?.code ?? response.status;
3504
+ const canAccess = Boolean(payload?.data?.canAccess);
3505
+ const errorType = canAccess ? void 0 : classifyRuntimeError(response.status, code);
3506
+ const data = {
3507
+ ...payload?.data || {
3508
+ appType: runtime.appType,
3509
+ canAccess: false
3510
+ },
3511
+ appType: payload?.data?.appType || runtime.appType,
3512
+ canAccess,
3513
+ status: response.status,
3514
+ code,
3515
+ message: payload?.message,
3516
+ errorType,
3517
+ payload
3518
+ };
3468
3519
  if (!disposed) {
3520
+ const shouldTreatAsError = !response.ok || typeof code === "number" && code >= 500;
3469
3521
  setState({
3470
- data: payload?.data || {
3471
- appType: runtime.appType,
3472
- canAccess: false
3473
- },
3522
+ data,
3474
3523
  loading: false,
3475
- error: response.ok && payload?.code < 500 ? null : new Error(payload?.message || `Route check failed: ${response.status}`)
3524
+ error: shouldTreatAsError ? createRuntimeHttpError(
3525
+ response,
3526
+ payload,
3527
+ payload?.message || `Route check failed: ${response.status}`
3528
+ ) : null
3476
3529
  });
3477
3530
  }
3478
3531
  } catch (error) {
@@ -3480,7 +3533,7 @@ var useCanAccessRoute = (input) => {
3480
3533
  setState({
3481
3534
  data: null,
3482
3535
  loading: false,
3483
- error: error instanceof Error ? error : new Error(String(error))
3536
+ error: normalizeRuntimeError(error)
3484
3537
  });
3485
3538
  }
3486
3539
  }
@@ -3512,16 +3565,226 @@ var PermissionBoundary = ({
3512
3565
  menuCode,
3513
3566
  path
3514
3567
  }) => {
3568
+ const runtime = useOpenXiangda();
3515
3569
  const access = useCanAccessRoute({ routeCode, menuCode, path });
3516
- if (access.loading) return /* @__PURE__ */ (0, import_jsx_runtime3.jsx)(import_jsx_runtime3.Fragment, { children: loadingFallback });
3517
- if (!access.canAccess) return /* @__PURE__ */ (0, import_jsx_runtime3.jsx)(import_jsx_runtime3.Fragment, { children: fallback });
3570
+ const fallbackState = createPermissionFallbackState(access, runtime);
3571
+ if (access.loading) {
3572
+ return /* @__PURE__ */ (0, import_jsx_runtime3.jsx)(import_jsx_runtime3.Fragment, { children: renderBoundaryFallback(loadingFallback, fallbackState) });
3573
+ }
3574
+ if (!access.canAccess) {
3575
+ return /* @__PURE__ */ (0, import_jsx_runtime3.jsx)(import_jsx_runtime3.Fragment, { children: renderBoundaryFallback(fallback, fallbackState) });
3576
+ }
3518
3577
  return /* @__PURE__ */ (0, import_jsx_runtime3.jsx)(import_jsx_runtime3.Fragment, { children });
3519
3578
  };
3579
+ var useRuntimeAuth = () => {
3580
+ const runtime = useOpenXiangda();
3581
+ const resolveLoginUrl = (0, import_react7.useCallback)(
3582
+ async (options = {}) => {
3583
+ const redirectUri = options.redirectUri || getCurrentHref2();
3584
+ const domain = options.domain || getCurrentHostname();
3585
+ const appTenantId = getRecordString(runtime.data?.app, "tenantId");
3586
+ try {
3587
+ const statusUrl = withQuery(
3588
+ buildServiceUrl(runtime.servicePrefix, "/api/sso/status"),
3589
+ { domain }
3590
+ );
3591
+ const statusResponse = await runtime.fetchImpl(statusUrl, {
3592
+ credentials: "include",
3593
+ headers: { accept: "application/json" }
3594
+ });
3595
+ const statusPayload = await readJsonPayload(statusResponse);
3596
+ const sso = statusPayload?.data || {};
3597
+ const enabled = Boolean(sso.enabled);
3598
+ const shouldUseSso = Boolean(
3599
+ enabled && (sso.forceLogin ?? sso.autoRedirect ?? true)
3600
+ );
3601
+ if (shouldUseSso) {
3602
+ const loginUrlResponse = await runtime.fetchImpl(
3603
+ withQuery(buildServiceUrl(runtime.servicePrefix, "/api/sso/login-url"), {
3604
+ domain,
3605
+ redirectUri,
3606
+ ...sso.tenantId || appTenantId ? { tenantId: String(sso.tenantId || appTenantId) } : {},
3607
+ ...sso.protocol ? { protocol: String(sso.protocol) } : {}
3608
+ }),
3609
+ {
3610
+ credentials: "include",
3611
+ headers: { accept: "application/json" }
3612
+ }
3613
+ );
3614
+ const loginUrlPayload = await readJsonPayload(loginUrlResponse);
3615
+ const loginUrl = loginUrlPayload?.data?.loginUrl || loginUrlPayload?.data?.url || loginUrlPayload?.loginUrl || loginUrlPayload?.url;
3616
+ if (loginUrl) return String(loginUrl);
3617
+ }
3618
+ } catch {
3619
+ }
3620
+ const configuredLoginUrl = options.loginUrl || getRuntimeEnv("OPENXIANGDA_LOGIN_URL") || getRuntimeEnv("VITE_OPENXIANGDA_LOGIN_URL") || getRuntimeEnv("APP_LOGIN_URL") || getRuntimeEnv("VITE_APP_LOGIN_URL") || getRuntimeEnv("VITE_BATH_AUTH_URL") || getRuntimeEnv("BATH_AUTH_URL");
3621
+ if (configuredLoginUrl) {
3622
+ return attachCallback(configuredLoginUrl, redirectUri);
3623
+ }
3624
+ return attachCallback("/login", redirectUri);
3625
+ },
3626
+ [runtime.data?.app, runtime.fetchImpl, runtime.servicePrefix]
3627
+ );
3628
+ const redirectToLogin = (0, import_react7.useCallback)(
3629
+ async (options = {}) => {
3630
+ const loginUrl = await resolveLoginUrl(options);
3631
+ if (typeof window !== "undefined") {
3632
+ if (options.replace === false) {
3633
+ window.location.assign(loginUrl);
3634
+ } else {
3635
+ window.location.replace(loginUrl);
3636
+ }
3637
+ }
3638
+ return loginUrl;
3639
+ },
3640
+ [resolveLoginUrl]
3641
+ );
3642
+ const logout = (0, import_react7.useCallback)(async () => {
3643
+ const response = await runtime.fetchImpl(
3644
+ buildServiceUrl(runtime.servicePrefix, "/api/auth/logout"),
3645
+ {
3646
+ method: "POST",
3647
+ credentials: "include",
3648
+ headers: { accept: "application/json" }
3649
+ }
3650
+ );
3651
+ const payload = await readJsonPayload(response);
3652
+ if (!response.ok || payload?.code >= 400) {
3653
+ throw createRuntimeHttpError(
3654
+ response,
3655
+ payload,
3656
+ payload?.message || `Logout failed: ${response.status}`
3657
+ );
3658
+ }
3659
+ return payload;
3660
+ }, [runtime.fetchImpl, runtime.servicePrefix]);
3661
+ const logoutAndRedirect = (0, import_react7.useCallback)(
3662
+ async (options = {}) => {
3663
+ try {
3664
+ await logout();
3665
+ } catch (error) {
3666
+ if (options.continueOnError === false) throw error;
3667
+ }
3668
+ return redirectToLogin(options);
3669
+ },
3670
+ [logout, redirectToLogin]
3671
+ );
3672
+ return {
3673
+ logout,
3674
+ logoutAndRedirect,
3675
+ redirectToLogin,
3676
+ resolveLoginUrl
3677
+ };
3678
+ };
3520
3679
  var buildServiceUrl = (servicePrefix, path) => {
3521
3680
  const prefix2 = servicePrefix.endsWith("/") ? servicePrefix.slice(0, -1) : servicePrefix;
3522
3681
  const suffix = path.startsWith("/") ? path : `/${path}`;
3523
3682
  return `${prefix2}${suffix}`;
3524
3683
  };
3684
+ var readJsonPayload = async (response) => {
3685
+ try {
3686
+ return await response.json();
3687
+ } catch {
3688
+ return null;
3689
+ }
3690
+ };
3691
+ var createRuntimeHttpError = (response, payload, fallbackMessage) => createRuntimeError({
3692
+ type: classifyRuntimeError(
3693
+ response.status,
3694
+ getRecordValue(payload, "code")
3695
+ ),
3696
+ status: response.status,
3697
+ code: getRecordValue(payload, "code"),
3698
+ message: getRecordValue(payload, "message") || fallbackMessage,
3699
+ payload
3700
+ });
3701
+ var createRuntimeError = (snapshot) => new RuntimeHttpError({
3702
+ ...snapshot,
3703
+ type: snapshot.type || "unknown",
3704
+ message: snapshot.message || "Runtime request failed"
3705
+ });
3706
+ var normalizeRuntimeError = (error) => {
3707
+ if (error instanceof RuntimeHttpError) return error;
3708
+ if (error instanceof Error) {
3709
+ const runtimeError = error;
3710
+ runtimeError.type = runtimeError.type || classifyRuntimeError(runtimeError.status, runtimeError.code);
3711
+ return runtimeError;
3712
+ }
3713
+ return createRuntimeError({
3714
+ type: "unknown",
3715
+ message: String(error)
3716
+ });
3717
+ };
3718
+ var toRuntimeErrorSnapshot = (error) => ({
3719
+ type: error.type || classifyRuntimeError(error.status, error.code),
3720
+ status: error.status,
3721
+ code: error.code,
3722
+ message: error.message || "Runtime request failed",
3723
+ payload: error.payload
3724
+ });
3725
+ var classifyRuntimeError = (status, code) => {
3726
+ const normalizedCode = typeof code === "string" ? Number(code) : code;
3727
+ if (status === 401 || normalizedCode === 401) return "unauthenticated";
3728
+ if (status === 403 || normalizedCode === 403) return "forbidden";
3729
+ if (!status && !normalizedCode) return "network";
3730
+ return "unknown";
3731
+ };
3732
+ var createPermissionFallbackState = (access, runtime) => {
3733
+ const error = access.error || runtime.error;
3734
+ const accessData = access.data;
3735
+ const errorType = accessData?.errorType || error?.type || (!access.canAccess ? "forbidden" : "unknown");
3736
+ return {
3737
+ access,
3738
+ runtime,
3739
+ error,
3740
+ errorType,
3741
+ status: accessData?.status || error?.status,
3742
+ code: accessData?.code || error?.code,
3743
+ message: accessData?.message || error?.message || (errorType === "unauthenticated" ? "\u5F53\u524D\u8D26\u53F7\u5C1A\u672A\u767B\u5F55" : "\u5F53\u524D\u8D26\u53F7\u6CA1\u6709\u8BBF\u95EE\u6743\u9650")
3744
+ };
3745
+ };
3746
+ var renderBoundaryFallback = (fallback, state) => typeof fallback === "function" ? fallback(state) : fallback;
3747
+ var withQuery = (url2, params) => {
3748
+ const query = new URLSearchParams();
3749
+ Object.entries(params).forEach(([key, value]) => {
3750
+ if (value === void 0 || value === "") return;
3751
+ query.set(key, String(value));
3752
+ });
3753
+ const serialized = query.toString();
3754
+ if (!serialized) return url2;
3755
+ return `${url2}${url2.includes("?") ? "&" : "?"}${serialized}`;
3756
+ };
3757
+ var attachCallback = (loginUrl, callback) => {
3758
+ if (!callback) return loginUrl;
3759
+ try {
3760
+ const base = typeof window !== "undefined" ? window.location.origin : "http://localhost";
3761
+ const parsed = new URL(loginUrl, base);
3762
+ if (!parsed.searchParams.has("callback") && !parsed.searchParams.has("redirectUri")) {
3763
+ parsed.searchParams.set("callback", callback);
3764
+ }
3765
+ if (loginUrl.startsWith("http://") || loginUrl.startsWith("https://")) {
3766
+ return parsed.toString();
3767
+ }
3768
+ return `${parsed.pathname}${parsed.search}${parsed.hash}`;
3769
+ } catch {
3770
+ const separator = loginUrl.includes("?") ? "&" : "?";
3771
+ return `${loginUrl}${separator}callback=${encodeURIComponent(callback)}`;
3772
+ }
3773
+ };
3774
+ var getCurrentHref2 = () => typeof window === "undefined" ? "" : window.location.href;
3775
+ var getCurrentHostname = () => typeof window === "undefined" ? "" : window.location.hostname;
3776
+ var getRuntimeEnv = (key) => {
3777
+ const env = typeof process !== "undefined" ? process.env : void 0;
3778
+ return env?.[key];
3779
+ };
3780
+ var getRecordValue = (value, key) => {
3781
+ if (!value || typeof value !== "object") return void 0;
3782
+ return value[key];
3783
+ };
3784
+ var getRecordString = (value, key) => {
3785
+ const result = getRecordValue(value, key);
3786
+ return typeof result === "string" ? result : void 0;
3787
+ };
3525
3788
  var resolveAppTypeFromLocation = () => {
3526
3789
  if (typeof window === "undefined") return "";
3527
3790
  const segments = window.location.pathname.split("/").filter(Boolean);
@@ -49997,7 +50260,7 @@ var DataManagementList = ({
49997
50260
  getContainer: getOverlayContainer,
49998
50261
  title: recordTab === "import" ? "\u5BFC\u5165\u8BB0\u5F55" : "\u5BFC\u51FA\u8BB0\u5F55",
49999
50262
  open: recordsOpen,
50000
- width: drawerWidth,
50263
+ size: drawerWidth,
50001
50264
  onClose: () => setRecordsOpen(false),
50002
50265
  children: [
50003
50266
  /* @__PURE__ */ (0, import_jsx_runtime97.jsx)(
@@ -50030,7 +50293,7 @@ var DataManagementList = ({
50030
50293
  getContainer: getOverlayContainer,
50031
50294
  title: "\u8BE6\u60C5",
50032
50295
  open: detailOpen,
50033
- width: drawerWidth,
50296
+ size: drawerWidth,
50034
50297
  onClose: () => setDetailOpen(false),
50035
50298
  destroyOnClose: true,
50036
50299
  children: activeRecord && detailRenderer ? detailRenderer({
@@ -50058,7 +50321,7 @@ var DataManagementList = ({
50058
50321
  getContainer: getOverlayContainer,
50059
50322
  title: "\u65B0\u589E\u6570\u636E",
50060
50323
  open: submitOpen,
50061
- width: drawerWidth,
50324
+ size: drawerWidth,
50062
50325
  onClose: () => setSubmitOpen(false),
50063
50326
  destroyOnClose: true,
50064
50327
  children: submitRenderer ? submitRenderer({