feat: enhance iframe communication and caching mechanisms
Some checks failed
lotteryfront CI / build (push) Has been cancelled

- Improved the entry lifecycle management to prevent duplicate token requests and ensure smooth transitions.
- Updated the `resolvePostMessageTargetOrigin` function to return null when no allowed origins are present, enhancing security.
- Cached runtime origins to optimize performance and reduce unnecessary API calls.
- Added a new error message for frozen lottery wallets in the player notifications.
This commit is contained in:
2026-06-16 17:08:34 +08:00
parent 9a42f47ac7
commit 9711909893
9 changed files with 59 additions and 19 deletions

View File

@@ -28,6 +28,9 @@ export function IframeBridge({ children }: { children: ReactNode }): ReactNode {
(type: string, payload?: Record<string, unknown>): void => {
if (typeof window === "undefined" || window.parent === window) return;
const targetOrigin = resolvePostMessageTargetOrigin();
if (!targetOrigin) return;
window.parent.postMessage(
{
type: `LOTTERY_${type}`,
@@ -35,7 +38,7 @@ export function IframeBridge({ children }: { children: ReactNode }): ReactNode {
timestamp: Date.now(),
source: "lottery-iframe",
},
resolvePostMessageTargetOrigin(),
targetOrigin,
);
},
[],

View File

@@ -87,12 +87,15 @@ export function useTokenRefresh(): {
if (typeof window === "undefined") return;
// 向主站请求新 Token
const targetOrigin = resolvePostMessageTargetOrigin();
if (!targetOrigin) return;
window.parent.postMessage(
{
type: "LOTTERY_TOKEN_REFRESH_REQUEST",
timestamp: Date.now(),
},
resolvePostMessageTargetOrigin(),
targetOrigin,
);
}, []);

View File

@@ -336,6 +336,7 @@
"2008": "Odds or play configuration has changed. Close the preview and try again.",
"2009": "This order was refunded or cannot be resubmitted. Close the preview and place a new bet.",
"1003": "Stake amount is outside the allowed range for this play type.",
"1007": "The lottery wallet is frozen. Betting is temporarily unavailable.",
"fallback": "Bet failed. Please try again later."
},
"amountHint": {

View File

@@ -4,18 +4,19 @@
* 支持 iframe 嵌入场景,允许主站加载彩票系统
*/
// 允许的主站来源
// 允许的主站来源(生产须通过环境变量配置)
const ALLOWED_PARENT_ORIGINS: string[] = [
process.env.NEXT_PUBLIC_MAIN_SITE_URL,
process.env.NEXT_PUBLIC_PARENT_ORIGIN,
// 开发环境
"http://localhost:5173",
"http://127.0.0.1:5173",
"http://192.168.0.101:5173",
"http://localhost:3801",
"http://127.0.0.1:3801",
"http://192.168.0.101:3801",
// 生产环境应从环境变量读取
...(process.env.NODE_ENV === "development"
? [
"http://localhost:5173",
"http://127.0.0.1:5173",
"http://localhost:3801",
"http://127.0.0.1:3801",
]
: []),
].filter((o): o is string => Boolean(o));
function normalizeOrigin(value: string): string | null {
@@ -104,10 +105,9 @@ export function generateCSP(extraParentOrigins: string[] = []): string {
* @param parentOrigin 父窗口来源
*/
export function isAllowedParent(parentOrigin: string): boolean {
if (ALLOWED_PARENT_ORIGINS.length === 0) return true; // 未配置时允许所有
return ALLOWED_PARENT_ORIGINS.some(
(origin) => origin && parentOrigin.startsWith(origin),
);
const origins = staticAllowedParentOrigins();
if (origins.length === 0) return false;
return origins.some((origin) => parentOrigin.startsWith(origin));
}
/**

View File

@@ -111,11 +111,11 @@ export function isIframeOriginAllowed(origin: string): boolean {
return allowedOrigins.includes(normalized);
}
/** postMessage 目标:优先 referrer 对应白名单,否则取首个白名单 origin */
export function resolvePostMessageTargetOrigin(): string {
/** postMessage 目标:优先 referrer 对应白名单,否则取首个白名单 origin;无白名单时拒绝发送 */
export function resolvePostMessageTargetOrigin(): string | null {
const allowedOrigins = getKnownIframeAllowedOrigins();
if (allowedOrigins.length === 0) {
return "*";
return null;
}
if (typeof document !== "undefined" && document.referrer) {