openxiangda 1.0.62 → 1.0.64

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 (28) hide show
  1. package/package.json +1 -1
  2. package/packages/sdk/dist/runtime/index.cjs +276 -13
  3. package/packages/sdk/dist/runtime/index.cjs.map +1 -1
  4. package/packages/sdk/dist/runtime/index.d.mts +1 -1
  5. package/packages/sdk/dist/runtime/index.d.ts +1 -1
  6. package/packages/sdk/dist/runtime/index.mjs +276 -13
  7. package/packages/sdk/dist/runtime/index.mjs.map +1 -1
  8. package/packages/sdk/dist/runtime/react.cjs +277 -13
  9. package/packages/sdk/dist/runtime/react.cjs.map +1 -1
  10. package/packages/sdk/dist/runtime/react.d.mts +60 -7
  11. package/packages/sdk/dist/runtime/react.d.ts +60 -7
  12. package/packages/sdk/dist/runtime/react.mjs +277 -13
  13. package/packages/sdk/dist/runtime/react.mjs.map +1 -1
  14. package/templates/openxiangda-react-spa/src/app/router.tsx +12 -1
  15. package/templates/openxiangda-react-spa/src/layouts/AdminShell.tsx +384 -96
  16. package/templates/openxiangda-react-spa/src/layouts/PublicShell.tsx +25 -3
  17. package/templates/openxiangda-react-spa/src/layouts/UserShell.tsx +101 -22
  18. package/templates/openxiangda-react-spa/src/pages/admin/RuntimeWorkspacePage.tsx +203 -41
  19. package/templates/openxiangda-react-spa/src/pages/defaults/DataRoutePage.tsx +80 -11
  20. package/templates/openxiangda-react-spa/src/pages/defaults/FilePreviewRoutePage.tsx +67 -14
  21. package/templates/openxiangda-react-spa/src/pages/defaults/FormRoutePage.tsx +153 -30
  22. package/templates/openxiangda-react-spa/src/pages/portal/UserPortalPage.tsx +53 -14
  23. package/templates/openxiangda-react-spa/src/pages/public/PublicHomePage.tsx +46 -6
  24. package/templates/openxiangda-react-spa/src/pages/states/NotFoundPage.tsx +26 -11
  25. package/templates/openxiangda-react-spa/src/shared/mac-admin.tsx +332 -0
  26. package/templates/openxiangda-react-spa/src/styles/index.css +42 -4
  27. package/templates/openxiangda-react-spa/tailwind.config.cjs +8 -0
  28. package/templates/openxiangda-react-spa/vite.config.ts +31 -0
@@ -1,5 +1,5 @@
1
1
  import React__default, { Dispatch, SetStateAction } from 'react';
2
- export { OpenXiangdaProvider, OpenXiangdaProviderProps, PermissionBoundary, PermissionBoundaryProps, RouteAccessResult, RuntimeBootstrap, RuntimeMenuItem, RuntimePagePermissions, RuntimeRequestState, UseCanAccessRouteInput, useAppMenus, useCanAccessRoute, useOpenXiangda, usePermission, useRuntimeBootstrap } from './react.mjs';
2
+ export { OpenXiangdaProvider, OpenXiangdaProviderProps, PermissionBoundary, PermissionBoundaryFallback, PermissionBoundaryFallbackState, PermissionBoundaryProps, RouteAccessResult, RuntimeBootstrap, RuntimeErrorSnapshot, RuntimeErrorType, RuntimeLogoutOptions, RuntimeMenuItem, RuntimePagePermissions, RuntimeRedirectLoginOptions, RuntimeRequestError, RuntimeRequestState, RuntimeResolveLoginOptions, UseCanAccessRouteInput, useAppMenus, useCanAccessRoute, useOpenXiangda, usePermission, useRuntimeAuth, useRuntimeBootstrap } from './react.mjs';
3
3
  import * as react_jsx_runtime from 'react/jsx-runtime';
4
4
  import { _ as FormSchema, X as FormRuntimeApi, Y as FormRuntimeApiConfig, J as FieldDefinition } from '../types-U26h_FIh.mjs';
5
5
 
@@ -1,5 +1,5 @@
1
1
  import React__default, { Dispatch, SetStateAction } from 'react';
2
- export { OpenXiangdaProvider, OpenXiangdaProviderProps, PermissionBoundary, PermissionBoundaryProps, RouteAccessResult, RuntimeBootstrap, RuntimeMenuItem, RuntimePagePermissions, RuntimeRequestState, UseCanAccessRouteInput, useAppMenus, useCanAccessRoute, useOpenXiangda, usePermission, useRuntimeBootstrap } from './react.js';
2
+ export { OpenXiangdaProvider, OpenXiangdaProviderProps, PermissionBoundary, PermissionBoundaryFallback, PermissionBoundaryFallbackState, PermissionBoundaryProps, RouteAccessResult, RuntimeBootstrap, RuntimeErrorSnapshot, RuntimeErrorType, RuntimeLogoutOptions, RuntimeMenuItem, RuntimePagePermissions, RuntimeRedirectLoginOptions, RuntimeRequestError, RuntimeRequestState, RuntimeResolveLoginOptions, UseCanAccessRouteInput, useAppMenus, useCanAccessRoute, useOpenXiangda, usePermission, useRuntimeAuth, useRuntimeBootstrap } from './react.js';
3
3
  import * as react_jsx_runtime from 'react/jsx-runtime';
4
4
  import { _ as FormSchema, X as FormRuntimeApi, Y as FormRuntimeApiConfig, J as FieldDefinition } from '../types-U26h_FIh.js';
5
5
 
@@ -3228,6 +3228,16 @@ var createBoundFetch = (fetchImpl) => {
3228
3228
 
3229
3229
  // packages/sdk/src/runtime/react/openxiangdaProvider.tsx
3230
3230
  import { Fragment as Fragment2, jsx as jsx3 } from "react/jsx-runtime";
3231
+ var RuntimeHttpError = class extends Error {
3232
+ constructor(snapshot) {
3233
+ super(snapshot.message);
3234
+ this.name = "RuntimeHttpError";
3235
+ this.type = snapshot.type;
3236
+ this.status = snapshot.status;
3237
+ this.code = snapshot.code;
3238
+ this.payload = snapshot.payload;
3239
+ }
3240
+ };
3231
3241
  var OpenXiangdaRuntimeContext = createContext2(null);
3232
3242
  var OpenXiangdaProvider = ({
3233
3243
  appType,
@@ -3250,7 +3260,10 @@ var OpenXiangdaProvider = ({
3250
3260
  setState({
3251
3261
  data: null,
3252
3262
  loading: false,
3253
- error: new Error("appType \u4E0D\u80FD\u4E3A\u7A7A")
3263
+ error: createRuntimeError({
3264
+ message: "appType \u4E0D\u80FD\u4E3A\u7A7A",
3265
+ type: "unknown"
3266
+ })
3254
3267
  });
3255
3268
  return;
3256
3269
  }
@@ -3268,9 +3281,13 @@ var OpenXiangdaProvider = ({
3268
3281
  headers: { accept: "application/json" }
3269
3282
  }
3270
3283
  );
3271
- const payload = await response.json();
3284
+ const payload = await readJsonPayload(response);
3272
3285
  if (!response.ok || payload?.code >= 400) {
3273
- throw new Error(payload?.message || `Runtime bootstrap failed: ${response.status}`);
3286
+ throw createRuntimeHttpError(
3287
+ response,
3288
+ payload,
3289
+ payload?.message || `Runtime bootstrap failed: ${response.status}`
3290
+ );
3274
3291
  }
3275
3292
  setState({
3276
3293
  data: payload?.data || null,
@@ -3281,7 +3298,7 @@ var OpenXiangdaProvider = ({
3281
3298
  setState({
3282
3299
  data: null,
3283
3300
  loading: false,
3284
- error: error instanceof Error ? error : new Error(String(error))
3301
+ error: normalizeRuntimeError(error)
3285
3302
  });
3286
3303
  }
3287
3304
  }, [resolvedAppType, resolvedFetch, servicePrefix]);
@@ -3337,6 +3354,23 @@ var useCanAccessRoute = (input) => {
3337
3354
  setState((prev) => ({ ...prev, loading: runtime.loading }));
3338
3355
  return;
3339
3356
  }
3357
+ if (runtime.error && !runtime.data) {
3358
+ const snapshot = toRuntimeErrorSnapshot(runtime.error);
3359
+ setState({
3360
+ data: {
3361
+ appType: runtime.appType,
3362
+ canAccess: false,
3363
+ status: snapshot.status,
3364
+ code: snapshot.code,
3365
+ message: snapshot.message,
3366
+ errorType: snapshot.type,
3367
+ payload: snapshot.payload
3368
+ },
3369
+ loading: false,
3370
+ error: runtime.error
3371
+ });
3372
+ return;
3373
+ }
3340
3374
  if (permissions?.hasFullAccess) {
3341
3375
  setState({
3342
3376
  data: { appType: runtime.appType, canAccess: true, permissions },
@@ -3364,15 +3398,33 @@ var useCanAccessRoute = (input) => {
3364
3398
  body: JSON.stringify(input)
3365
3399
  }
3366
3400
  );
3367
- const payload = await response.json();
3401
+ const payload = await readJsonPayload(response);
3402
+ const code = payload?.code ?? response.status;
3403
+ const canAccess = Boolean(payload?.data?.canAccess);
3404
+ const errorType = canAccess ? void 0 : classifyRuntimeError(response.status, code);
3405
+ const data = {
3406
+ ...payload?.data || {
3407
+ appType: runtime.appType,
3408
+ canAccess: false
3409
+ },
3410
+ appType: payload?.data?.appType || runtime.appType,
3411
+ canAccess,
3412
+ status: response.status,
3413
+ code,
3414
+ message: payload?.message,
3415
+ errorType,
3416
+ payload
3417
+ };
3368
3418
  if (!disposed) {
3419
+ const shouldTreatAsError = !response.ok || typeof code === "number" && code >= 500;
3369
3420
  setState({
3370
- data: payload?.data || {
3371
- appType: runtime.appType,
3372
- canAccess: false
3373
- },
3421
+ data,
3374
3422
  loading: false,
3375
- error: response.ok && payload?.code < 500 ? null : new Error(payload?.message || `Route check failed: ${response.status}`)
3423
+ error: shouldTreatAsError ? createRuntimeHttpError(
3424
+ response,
3425
+ payload,
3426
+ payload?.message || `Route check failed: ${response.status}`
3427
+ ) : null
3376
3428
  });
3377
3429
  }
3378
3430
  } catch (error) {
@@ -3380,7 +3432,7 @@ var useCanAccessRoute = (input) => {
3380
3432
  setState({
3381
3433
  data: null,
3382
3434
  loading: false,
3383
- error: error instanceof Error ? error : new Error(String(error))
3435
+ error: normalizeRuntimeError(error)
3384
3436
  });
3385
3437
  }
3386
3438
  }
@@ -3412,16 +3464,226 @@ var PermissionBoundary = ({
3412
3464
  menuCode,
3413
3465
  path
3414
3466
  }) => {
3467
+ const runtime = useOpenXiangda();
3415
3468
  const access = useCanAccessRoute({ routeCode, menuCode, path });
3416
- if (access.loading) return /* @__PURE__ */ jsx3(Fragment2, { children: loadingFallback });
3417
- if (!access.canAccess) return /* @__PURE__ */ jsx3(Fragment2, { children: fallback });
3469
+ const fallbackState = createPermissionFallbackState(access, runtime);
3470
+ if (access.loading) {
3471
+ return /* @__PURE__ */ jsx3(Fragment2, { children: renderBoundaryFallback(loadingFallback, fallbackState) });
3472
+ }
3473
+ if (!access.canAccess) {
3474
+ return /* @__PURE__ */ jsx3(Fragment2, { children: renderBoundaryFallback(fallback, fallbackState) });
3475
+ }
3418
3476
  return /* @__PURE__ */ jsx3(Fragment2, { children });
3419
3477
  };
3478
+ var useRuntimeAuth = () => {
3479
+ const runtime = useOpenXiangda();
3480
+ const resolveLoginUrl = useCallback3(
3481
+ async (options = {}) => {
3482
+ const redirectUri = options.redirectUri || getCurrentHref2();
3483
+ const domain = options.domain || getCurrentHostname();
3484
+ const appTenantId = getRecordString(runtime.data?.app, "tenantId");
3485
+ try {
3486
+ const statusUrl = withQuery(
3487
+ buildServiceUrl(runtime.servicePrefix, "/api/sso/status"),
3488
+ { domain }
3489
+ );
3490
+ const statusResponse = await runtime.fetchImpl(statusUrl, {
3491
+ credentials: "include",
3492
+ headers: { accept: "application/json" }
3493
+ });
3494
+ const statusPayload = await readJsonPayload(statusResponse);
3495
+ const sso = statusPayload?.data || {};
3496
+ const enabled = Boolean(sso.enabled);
3497
+ const shouldUseSso = Boolean(
3498
+ enabled && (sso.forceLogin ?? sso.autoRedirect ?? true)
3499
+ );
3500
+ if (shouldUseSso) {
3501
+ const loginUrlResponse = await runtime.fetchImpl(
3502
+ withQuery(buildServiceUrl(runtime.servicePrefix, "/api/sso/login-url"), {
3503
+ domain,
3504
+ redirectUri,
3505
+ ...sso.tenantId || appTenantId ? { tenantId: String(sso.tenantId || appTenantId) } : {},
3506
+ ...sso.protocol ? { protocol: String(sso.protocol) } : {}
3507
+ }),
3508
+ {
3509
+ credentials: "include",
3510
+ headers: { accept: "application/json" }
3511
+ }
3512
+ );
3513
+ const loginUrlPayload = await readJsonPayload(loginUrlResponse);
3514
+ const loginUrl = loginUrlPayload?.data?.loginUrl || loginUrlPayload?.data?.url || loginUrlPayload?.loginUrl || loginUrlPayload?.url;
3515
+ if (loginUrl) return String(loginUrl);
3516
+ }
3517
+ } catch {
3518
+ }
3519
+ 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");
3520
+ if (configuredLoginUrl) {
3521
+ return attachCallback(configuredLoginUrl, redirectUri);
3522
+ }
3523
+ return attachCallback("/login", redirectUri);
3524
+ },
3525
+ [runtime.data?.app, runtime.fetchImpl, runtime.servicePrefix]
3526
+ );
3527
+ const redirectToLogin = useCallback3(
3528
+ async (options = {}) => {
3529
+ const loginUrl = await resolveLoginUrl(options);
3530
+ if (typeof window !== "undefined") {
3531
+ if (options.replace === false) {
3532
+ window.location.assign(loginUrl);
3533
+ } else {
3534
+ window.location.replace(loginUrl);
3535
+ }
3536
+ }
3537
+ return loginUrl;
3538
+ },
3539
+ [resolveLoginUrl]
3540
+ );
3541
+ const logout = useCallback3(async () => {
3542
+ const response = await runtime.fetchImpl(
3543
+ buildServiceUrl(runtime.servicePrefix, "/api/auth/logout"),
3544
+ {
3545
+ method: "POST",
3546
+ credentials: "include",
3547
+ headers: { accept: "application/json" }
3548
+ }
3549
+ );
3550
+ const payload = await readJsonPayload(response);
3551
+ if (!response.ok || payload?.code >= 400) {
3552
+ throw createRuntimeHttpError(
3553
+ response,
3554
+ payload,
3555
+ payload?.message || `Logout failed: ${response.status}`
3556
+ );
3557
+ }
3558
+ return payload;
3559
+ }, [runtime.fetchImpl, runtime.servicePrefix]);
3560
+ const logoutAndRedirect = useCallback3(
3561
+ async (options = {}) => {
3562
+ try {
3563
+ await logout();
3564
+ } catch (error) {
3565
+ if (options.continueOnError === false) throw error;
3566
+ }
3567
+ return redirectToLogin(options);
3568
+ },
3569
+ [logout, redirectToLogin]
3570
+ );
3571
+ return {
3572
+ logout,
3573
+ logoutAndRedirect,
3574
+ redirectToLogin,
3575
+ resolveLoginUrl
3576
+ };
3577
+ };
3420
3578
  var buildServiceUrl = (servicePrefix, path) => {
3421
3579
  const prefix2 = servicePrefix.endsWith("/") ? servicePrefix.slice(0, -1) : servicePrefix;
3422
3580
  const suffix = path.startsWith("/") ? path : `/${path}`;
3423
3581
  return `${prefix2}${suffix}`;
3424
3582
  };
3583
+ var readJsonPayload = async (response) => {
3584
+ try {
3585
+ return await response.json();
3586
+ } catch {
3587
+ return null;
3588
+ }
3589
+ };
3590
+ var createRuntimeHttpError = (response, payload, fallbackMessage) => createRuntimeError({
3591
+ type: classifyRuntimeError(
3592
+ response.status,
3593
+ getRecordValue(payload, "code")
3594
+ ),
3595
+ status: response.status,
3596
+ code: getRecordValue(payload, "code"),
3597
+ message: getRecordValue(payload, "message") || fallbackMessage,
3598
+ payload
3599
+ });
3600
+ var createRuntimeError = (snapshot) => new RuntimeHttpError({
3601
+ ...snapshot,
3602
+ type: snapshot.type || "unknown",
3603
+ message: snapshot.message || "Runtime request failed"
3604
+ });
3605
+ var normalizeRuntimeError = (error) => {
3606
+ if (error instanceof RuntimeHttpError) return error;
3607
+ if (error instanceof Error) {
3608
+ const runtimeError = error;
3609
+ runtimeError.type = runtimeError.type || classifyRuntimeError(runtimeError.status, runtimeError.code);
3610
+ return runtimeError;
3611
+ }
3612
+ return createRuntimeError({
3613
+ type: "unknown",
3614
+ message: String(error)
3615
+ });
3616
+ };
3617
+ var toRuntimeErrorSnapshot = (error) => ({
3618
+ type: error.type || classifyRuntimeError(error.status, error.code),
3619
+ status: error.status,
3620
+ code: error.code,
3621
+ message: error.message || "Runtime request failed",
3622
+ payload: error.payload
3623
+ });
3624
+ var classifyRuntimeError = (status, code) => {
3625
+ const normalizedCode = typeof code === "string" ? Number(code) : code;
3626
+ if (status === 401 || normalizedCode === 401) return "unauthenticated";
3627
+ if (status === 403 || normalizedCode === 403) return "forbidden";
3628
+ if (!status && !normalizedCode) return "network";
3629
+ return "unknown";
3630
+ };
3631
+ var createPermissionFallbackState = (access, runtime) => {
3632
+ const error = access.error || runtime.error;
3633
+ const accessData = access.data;
3634
+ const errorType = accessData?.errorType || error?.type || (!access.canAccess ? "forbidden" : "unknown");
3635
+ return {
3636
+ access,
3637
+ runtime,
3638
+ error,
3639
+ errorType,
3640
+ status: accessData?.status || error?.status,
3641
+ code: accessData?.code || error?.code,
3642
+ 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")
3643
+ };
3644
+ };
3645
+ var renderBoundaryFallback = (fallback, state) => typeof fallback === "function" ? fallback(state) : fallback;
3646
+ var withQuery = (url2, params) => {
3647
+ const query = new URLSearchParams();
3648
+ Object.entries(params).forEach(([key, value]) => {
3649
+ if (value === void 0 || value === "") return;
3650
+ query.set(key, String(value));
3651
+ });
3652
+ const serialized = query.toString();
3653
+ if (!serialized) return url2;
3654
+ return `${url2}${url2.includes("?") ? "&" : "?"}${serialized}`;
3655
+ };
3656
+ var attachCallback = (loginUrl, callback) => {
3657
+ if (!callback) return loginUrl;
3658
+ try {
3659
+ const base = typeof window !== "undefined" ? window.location.origin : "http://localhost";
3660
+ const parsed = new URL(loginUrl, base);
3661
+ if (!parsed.searchParams.has("callback") && !parsed.searchParams.has("redirectUri")) {
3662
+ parsed.searchParams.set("callback", callback);
3663
+ }
3664
+ if (loginUrl.startsWith("http://") || loginUrl.startsWith("https://")) {
3665
+ return parsed.toString();
3666
+ }
3667
+ return `${parsed.pathname}${parsed.search}${parsed.hash}`;
3668
+ } catch {
3669
+ const separator = loginUrl.includes("?") ? "&" : "?";
3670
+ return `${loginUrl}${separator}callback=${encodeURIComponent(callback)}`;
3671
+ }
3672
+ };
3673
+ var getCurrentHref2 = () => typeof window === "undefined" ? "" : window.location.href;
3674
+ var getCurrentHostname = () => typeof window === "undefined" ? "" : window.location.hostname;
3675
+ var getRuntimeEnv = (key) => {
3676
+ const env = typeof process !== "undefined" ? process.env : void 0;
3677
+ return env?.[key];
3678
+ };
3679
+ var getRecordValue = (value, key) => {
3680
+ if (!value || typeof value !== "object") return void 0;
3681
+ return value[key];
3682
+ };
3683
+ var getRecordString = (value, key) => {
3684
+ const result = getRecordValue(value, key);
3685
+ return typeof result === "string" ? result : void 0;
3686
+ };
3425
3687
  var resolveAppTypeFromLocation = () => {
3426
3688
  if (typeof window === "undefined") return "";
3427
3689
  const segments = window.location.pathname.split("/").filter(Boolean);
@@ -49660,6 +49922,7 @@ export {
49660
49922
  usePageRoute,
49661
49923
  usePageSdk,
49662
49924
  usePermission,
49925
+ useRuntimeAuth,
49663
49926
  useRuntimeBootstrap
49664
49927
  };
49665
49928
  /*! Bundled license information: