Files
lotteryFront/public/parent-integration-example.html
kang 587a6ad66c feat: 增强国际化支持与安全头配置
- 在 .env.example 中新增 i18next 相关配置项以支持多语言功能
- 在 next.config.ts 中添加安全头配置以支持 iframe 嵌入
- 更新 Providers 组件以引入 i18n 配置
- 在 PlayerAppShell 中集成 LanguageSwitcher 组件以实现语言切换功能
- 优化 HallWalletStrip 组件的网络状态管理逻辑
- 更新多个组件以支持国际化文本
2026-05-13 17:53:56 +08:00

359 lines
12 KiB
HTML
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
<!DOCTYPE html>
<html lang="zh-CN">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>主站集成示例 - 彩票 iframe 嵌入</title>
<style>
* {
margin: 0;
padding: 0;
box-sizing: border-box;
}
body {
font-family: -apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, sans-serif;
background: #f5f5f5;
padding: 20px;
}
.container {
max-width: 1200px;
margin: 0 auto;
}
h1 {
margin-bottom: 20px;
color: #333;
}
.controls {
background: white;
padding: 20px;
border-radius: 8px;
margin-bottom: 20px;
box-shadow: 0 2px 8px rgba(0,0,0,0.1);
}
.control-group {
margin-bottom: 15px;
}
label {
display: block;
margin-bottom: 5px;
font-weight: 500;
color: #555;
}
input[type="text"] {
width: 100%;
padding: 10px;
border: 1px solid #ddd;
border-radius: 4px;
font-size: 14px;
}
button {
padding: 10px 20px;
border: none;
border-radius: 4px;
cursor: pointer;
font-size: 14px;
transition: background 0.2s;
}
.btn-primary {
background: #52c41a;
color: white;
}
.btn-primary:hover {
background: #389e0d;
}
.btn-warning {
background: #faad14;
color: white;
}
.btn-warning:hover {
background: #d48806;
}
.btn-danger {
background: #ff4d4f;
color: white;
}
.btn-danger:hover {
background: #cf1322;
}
.iframe-container {
background: white;
padding: 20px;
border-radius: 8px;
box-shadow: 0 2px 8px rgba(0,0,0,0.1);
}
iframe {
width: 100%;
height: 800px;
border: 1px solid #ddd;
border-radius: 4px;
}
.status {
margin-top: 10px;
padding: 10px;
border-radius: 4px;
font-size: 14px;
}
.status-success {
background: #f6ffed;
border: 1px solid #b7eb8f;
color: #389e0d;
}
.status-error {
background: #fff2f0;
border: 1px solid #ffccc7;
color: #cf1322;
}
.log {
margin-top: 20px;
background: #1e1e1e;
color: #d4d4d4;
padding: 15px;
border-radius: 4px;
font-family: 'Courier New', monospace;
font-size: 13px;
max-height: 300px;
overflow-y: auto;
}
.log-entry {
margin: 5px 0;
padding: 5px;
border-left: 3px solid #52c41a;
padding-left: 10px;
}
.log-entry.error {
border-left-color: #ff4d4f;
}
.log-entry.warn {
border-left-color: #faad14;
}
</style>
</head>
<body>
<div class="container">
<h1>🎰 彩票系统 - 主站 iframe 集成示例</h1>
<div class="controls">
<h3>控制面板</h3>
<div class="control-group">
<label>JWT Token模拟从主站获取</label>
<input type="text" id="tokenInput" placeholder="输入 JWT Token 或点击生成测试 Token">
</div>
<div class="control-group">
<label>彩票系统地址:</label>
<input type="text" id="lotteryUrl" value="http://localhost:3000/hall?token=">
</div>
<div style="display: flex; gap: 10px; flex-wrap: wrap;">
<button class="btn-primary" onclick="initIframe()">初始化 iframe</button>
<button class="btn-warning" onclick="refreshToken()">刷新 Token</button>
<button class="btn-danger" onclick="expireToken()">模拟 Token 过期</button>
<button class="btn-primary" onclick="generateTestToken()">生成测试 Token</button>
</div>
<div id="status" class="status" style="display: none;"></div>
</div>
<div class="iframe-container">
<h3>彩票系统 iframe</h3>
<iframe id="lotteryFrame" src="" sandbox="allow-scripts allow-same-origin allow-forms allow-popups"></iframe>
</div>
<div class="log" id="log">
<div class="log-entry">等待初始化...</div>
</div>
</div>
<script>
const lotteryOrigin = 'http://localhost:3000';
let currentToken = null;
let tokenExpiryTime = null;
// 日志输出
function log(message, type = 'info') {
const logEl = document.getElementById('log');
const entry = document.createElement('div');
entry.className = `log-entry ${type}`;
entry.textContent = `[${new Date().toLocaleTimeString()}] ${message}`;
logEl.appendChild(entry);
logEl.scrollTop = logEl.scrollHeight;
console.log(message);
}
// 显示状态
function showStatus(message, isError = false) {
const statusEl = document.getElementById('status');
statusEl.textContent = message;
statusEl.className = `status ${isError ? 'status-error' : 'status-success'}`;
statusEl.style.display = 'block';
}
// 生成测试 JWT Token实际生产环境由主站后端生成
function generateTestToken() {
// 这是一个示例 JWT 结构,实际需要主站用密钥签名
const header = btoa(JSON.stringify({ alg: 'HS256', typ: 'JWT' }));
const now = Math.floor(Date.now() / 1000);
const payload = btoa(JSON.stringify({
site_code: 'main_site',
site_player_id: 'player_12345',
iat: now,
exp: now + 300, // 5 分钟过期
sub: 'lottery_player'
}));
const signature = btoa('dummy_signature_replace_in_production');
const token = `${header}.${payload}.${signature}`;
document.getElementById('tokenInput').value = token;
log('已生成测试 Token5 分钟有效期)', 'warn');
}
// 初始化 iframe
function initIframe() {
const token = document.getElementById('tokenInput').value.trim();
const baseUrl = document.getElementById('lotteryUrl').value.trim();
if (!token) {
showStatus('请先输入或生成 Token', true);
return;
}
currentToken = token;
tokenExpiryTime = Date.now() + 5 * 60 * 1000; // 5 分钟后过期
const iframe = document.getElementById('lotteryFrame');
const url = baseUrl + encodeURIComponent(token);
iframe.src = url;
log(`iframe 已初始化Token 将在 5 分钟后过期`);
showStatus('iframe 初始化成功');
// 监听 iframe 消息
window.addEventListener('message', handleIframeMessage);
}
// 处理 iframe 消息
function handleIframeMessage(event) {
// 安全验证:检查来源
if (!event.origin.includes('localhost') && !event.origin.includes('lottery')) {
log(`拒绝来自未知来源的消息: ${event.origin}`, 'error');
return;
}
const { data } = event;
if (!data || typeof data !== 'object') return;
log(`收到消息: ${data.type}`);
switch (data.type) {
case 'LOTTERY_READY':
log('彩票系统已就绪', 'success');
// 发送初始 Token
sendToIframe('MAIN_INIT_TOKEN', { token: currentToken });
break;
case 'LOTTERY_HEARTBEAT':
// 心跳响应,可以更新连接状态
break;
case 'LOTTERY_TOKEN_NEEDED':
log('彩票系统请求新 Token', 'warn');
// 这里应该调用主站后端获取新 Token
refreshTokenFromBackend();
break;
case 'LOTTERY_TOKEN_REFRESHED':
log('Token 刷新成功', 'success');
break;
case 'LOTTERY_ERROR':
log(`彩票系统错误: ${data.payload?.error}`, 'error');
break;
default:
log(`未知消息类型: ${data.type}`);
}
}
// 向 iframe 发送消息
function sendToIframe(type, payload) {
const iframe = document.getElementById('lotteryFrame');
if (!iframe || !iframe.contentWindow) {
log('iframe 未加载,无法发送消息', 'error');
return;
}
iframe.contentWindow.postMessage({
type,
payload,
timestamp: Date.now()
}, lotteryOrigin);
log(`已发送消息: ${type}`);
}
// 刷新 Token模拟主站后端调用
function refreshToken() {
if (!currentToken) {
showStatus('请先初始化 iframe', true);
return;
}
// 模拟从后端获取新 Token
generateTestToken();
const newToken = document.getElementById('tokenInput').value.trim();
currentToken = newToken;
tokenExpiryTime = Date.now() + 5 * 60 * 1000;
// 通知 iframe 新 Token
sendToIframe('MAIN_REFRESH_TOKEN', { token: newToken });
showStatus('Token 已刷新');
}
// 模拟从后端刷新 Token
function refreshTokenFromBackend() {
log('正在从后端获取新 Token...');
// 实际实现:调用主站后端 API 获取新 JWT
setTimeout(() => {
refreshToken();
}, 1000);
}
// 模拟 Token 过期
function expireToken() {
if (!currentToken) {
showStatus('请先初始化 iframe', true);
return;
}
// 通知 iframe Token 即将过期
sendToIframe('MAIN_TOKEN_EXPIRING', {
expiresIn: 10, // 10 秒后过期
});
log('已发送 Token 过期警告');
// 10 秒后使 Token 失效
setTimeout(() => {
currentToken = 'expired_token';
log('Token 已过期', 'error');
}, 10000);
}
// 自动检测 Token 过期
setInterval(() => {
if (tokenExpiryTime && Date.now() > tokenExpiryTime - 60000) {
// Token 将在 1 分钟内过期,自动刷新
log('Token 即将过期,自动刷新...', 'warn');
refreshTokenFromBackend();
tokenExpiryTime = null; // 防止重复刷新
}
}, 30000); // 每 30 秒检查一次
// 初始化提示
log('页面加载完成,点击"生成测试 Token"开始');
</script>
</body>
</html>