feat: 增强国际化支持与安全头配置
- 在 .env.example 中新增 i18next 相关配置项以支持多语言功能 - 在 next.config.ts 中添加安全头配置以支持 iframe 嵌入 - 更新 Providers 组件以引入 i18n 配置 - 在 PlayerAppShell 中集成 LanguageSwitcher 组件以实现语言切换功能 - 优化 HallWalletStrip 组件的网络状态管理逻辑 - 更新多个组件以支持国际化文本
This commit is contained in:
109
src/lib/csp-config.ts
Normal file
109
src/lib/csp-config.ts
Normal file
@@ -0,0 +1,109 @@
|
||||
/**
|
||||
* Content Security Policy (CSP) 配置
|
||||
*
|
||||
* 支持 iframe 嵌入场景,允许主站加载彩票系统
|
||||
*/
|
||||
|
||||
// 允许的主站来源
|
||||
const ALLOWED_PARENT_ORIGINS: string[] = [
|
||||
process.env.NEXT_PUBLIC_MAIN_SITE_URL,
|
||||
process.env.NEXT_PUBLIC_PARENT_ORIGIN,
|
||||
// 开发环境
|
||||
"http://localhost:3001",
|
||||
"http://127.0.0.1:3001",
|
||||
// 生产环境应从环境变量读取
|
||||
].filter((o): o is string => Boolean(o));
|
||||
|
||||
/**
|
||||
* 生成 CSP 指令字符串
|
||||
*/
|
||||
export function generateCSP(): string {
|
||||
const directives: Record<string, string[]> = {
|
||||
// 默认只允许同源
|
||||
"default-src": ["'self'"],
|
||||
|
||||
// 脚本允许同源和内联(Next.js 需要)
|
||||
"script-src": ["'self'", "'unsafe-inline'", "'unsafe-eval'"],
|
||||
|
||||
// 样式允许同源和内联
|
||||
"style-src": ["'self'", "'unsafe-inline'"],
|
||||
|
||||
// 图片允许同源、data URL 和 blob
|
||||
"img-src": ["'self'", "data:", "blob:"],
|
||||
|
||||
// 字体允许同源
|
||||
"font-src": ["'self'"],
|
||||
|
||||
// 连接允许同源和 API 域名
|
||||
"connect-src": [
|
||||
"'self'",
|
||||
process.env.NEXT_PUBLIC_API_URL || "",
|
||||
// WebSocket 连接
|
||||
"ws:",
|
||||
"wss:",
|
||||
].filter(Boolean),
|
||||
|
||||
// 媒体允许同源和 blob
|
||||
"media-src": ["'self'", "blob:"],
|
||||
|
||||
// 对象不允许
|
||||
"object-src": ["'none'"],
|
||||
|
||||
// 框架允许同源和指定父站
|
||||
"frame-src": ["'self'", ...ALLOWED_PARENT_ORIGINS],
|
||||
|
||||
// 允许被嵌入到指定父站
|
||||
"frame-ancestors": ["'self'", ...ALLOWED_PARENT_ORIGINS],
|
||||
|
||||
// 表单提交允许同源
|
||||
"form-action": ["'self'"],
|
||||
|
||||
// 不升级 HTTPS
|
||||
"upgrade-insecure-requests": [],
|
||||
};
|
||||
|
||||
// 构建 CSP 字符串
|
||||
return Object.entries(directives)
|
||||
.map(([key, values]) => {
|
||||
if (values.length === 0) return key;
|
||||
return `${key} ${values.join(" ")}`;
|
||||
})
|
||||
.join("; ");
|
||||
}
|
||||
|
||||
/**
|
||||
* 检测是否允许被 iframe 嵌入
|
||||
* @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),
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* 安全头配置(用于 next.config.ts)
|
||||
*/
|
||||
export const securityHeaders = [
|
||||
{
|
||||
key: "Content-Security-Policy",
|
||||
value: generateCSP(),
|
||||
},
|
||||
{
|
||||
key: "X-Frame-Options",
|
||||
value: "SAMEORIGIN", // 允许同源,通过 CSP frame-ancestors 控制跨域
|
||||
},
|
||||
{
|
||||
key: "X-Content-Type-Options",
|
||||
value: "nosniff",
|
||||
},
|
||||
{
|
||||
key: "Referrer-Policy",
|
||||
value: "strict-origin-when-cross-origin",
|
||||
},
|
||||
{
|
||||
key: "Permissions-Policy",
|
||||
value: "camera=(), microphone=(), geolocation=()",
|
||||
},
|
||||
];
|
||||
Reference in New Issue
Block a user