openxiangda 1.0.62 → 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.
- package/package.json +1 -1
- package/packages/sdk/dist/runtime/index.cjs +276 -13
- package/packages/sdk/dist/runtime/index.cjs.map +1 -1
- package/packages/sdk/dist/runtime/index.d.mts +1 -1
- package/packages/sdk/dist/runtime/index.d.ts +1 -1
- package/packages/sdk/dist/runtime/index.mjs +276 -13
- package/packages/sdk/dist/runtime/index.mjs.map +1 -1
- package/packages/sdk/dist/runtime/react.cjs +277 -13
- package/packages/sdk/dist/runtime/react.cjs.map +1 -1
- package/packages/sdk/dist/runtime/react.d.mts +60 -7
- package/packages/sdk/dist/runtime/react.d.ts +60 -7
- package/packages/sdk/dist/runtime/react.mjs +277 -13
- package/packages/sdk/dist/runtime/react.mjs.map +1 -1
- package/templates/openxiangda-react-spa/src/app/router.tsx +12 -1
- package/templates/openxiangda-react-spa/src/layouts/AdminShell.tsx +374 -96
- package/templates/openxiangda-react-spa/src/layouts/PublicShell.tsx +25 -3
- package/templates/openxiangda-react-spa/src/layouts/UserShell.tsx +101 -22
- package/templates/openxiangda-react-spa/src/pages/admin/RuntimeWorkspacePage.tsx +203 -41
- package/templates/openxiangda-react-spa/src/pages/defaults/DataRoutePage.tsx +80 -11
- package/templates/openxiangda-react-spa/src/pages/defaults/FilePreviewRoutePage.tsx +67 -14
- package/templates/openxiangda-react-spa/src/pages/defaults/FormRoutePage.tsx +153 -30
- package/templates/openxiangda-react-spa/src/pages/portal/UserPortalPage.tsx +53 -14
- package/templates/openxiangda-react-spa/src/pages/public/PublicHomePage.tsx +46 -6
- package/templates/openxiangda-react-spa/src/pages/states/NotFoundPage.tsx +26 -11
- package/templates/openxiangda-react-spa/src/shared/mac-admin.tsx +332 -0
- package/templates/openxiangda-react-spa/src/styles/index.css +42 -4
- package/templates/openxiangda-react-spa/tailwind.config.cjs +8 -0
- package/templates/openxiangda-react-spa/vite.config.ts +31 -0
package/package.json
CHANGED
|
@@ -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:
|
|
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
|
|
3385
|
+
const payload = await readJsonPayload(response);
|
|
3372
3386
|
if (!response.ok || payload?.code >= 400) {
|
|
3373
|
-
throw
|
|
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:
|
|
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
|
|
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
|
|
3471
|
-
appType: runtime.appType,
|
|
3472
|
-
canAccess: false
|
|
3473
|
-
},
|
|
3522
|
+
data,
|
|
3474
3523
|
loading: false,
|
|
3475
|
-
error:
|
|
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:
|
|
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
|
-
|
|
3517
|
-
if (
|
|
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);
|