111 lines
3.3 KiB
TypeScript
111 lines
3.3 KiB
TypeScript
import type { NextConfig } from "next";
|
||
|
||
const nextConfig: NextConfig = {
|
||
poweredByHeader: false,
|
||
// 禁用 cacheComponents 以支持 dynamic = "force-dynamic"
|
||
// cacheComponents: true,
|
||
// 外部包配置(Next.js 16+)
|
||
serverExternalPackages: ["sharp"],
|
||
transpilePackages: ["@sparkjsdev/spark"],
|
||
typescript: {
|
||
ignoreBuildErrors: process.env.NEXT_IGNORE_BUILD_TYPECHECK === "1",
|
||
},
|
||
// 实验性功能:增加代理请求体大小限制以支持大文件上传(视频等)
|
||
experimental: {
|
||
proxyClientMaxBodySize: "500mb", // 代理/中间件请求体限制
|
||
},
|
||
images: {
|
||
dangerouslyAllowLocalIP: false,
|
||
contentDispositionType: "attachment",
|
||
remotePatterns: [
|
||
{
|
||
hostname: "avatar.vercel.sh",
|
||
},
|
||
{
|
||
protocol: "https",
|
||
//https://nextjs.org/docs/messages/next-image-unconfigured-host
|
||
hostname: "*.public.blob.vercel-storage.com",
|
||
},
|
||
{
|
||
// 支持 HTTP 协议的 MinIO / S3(本地或内网部署)
|
||
protocol: "http",
|
||
hostname: "**", // 允许所有 HTTP 域名(MinIO 等)
|
||
},
|
||
{
|
||
// 支持 HTTPS 协议的 S3 / MinIO(公网部署)
|
||
protocol: "https",
|
||
hostname: "**", // 允许所有 HTTPS 域名
|
||
},
|
||
],
|
||
},
|
||
// HTTP 安全头
|
||
async headers() {
|
||
const isDev = process.env.NODE_ENV === "development";
|
||
const csp = [
|
||
"default-src 'self'",
|
||
// Next.js App Router/RSC emits inline bootstrap scripts in production.
|
||
// WebAssembly is required by in-browser 3D decoders (Spark SPZ, KTX2/Basis).
|
||
`script-src 'self' 'unsafe-inline' 'wasm-unsafe-eval'${isDev ? " 'unsafe-eval'" : ""}`,
|
||
// Next.js 需要 unsafe-inline 用于样式 (CSS-in-JS / Tailwind)
|
||
"style-src 'self' 'unsafe-inline'",
|
||
"img-src 'self' data: blob: https: http:",
|
||
"font-src 'self' data:",
|
||
// Spark bundles its WASM decoder as a data: URL and initializes it via fetch().
|
||
"connect-src 'self' data: https: wss:",
|
||
"media-src 'self' blob: https:",
|
||
"frame-src 'self'",
|
||
"worker-src 'self' blob:",
|
||
"object-src 'none'",
|
||
"base-uri 'self'",
|
||
"form-action 'self'",
|
||
].join("; ");
|
||
|
||
return [
|
||
{
|
||
source: "/(.*)",
|
||
headers: [
|
||
{ key: "X-Content-Type-Options", value: "nosniff" },
|
||
{ key: "X-Frame-Options", value: "SAMEORIGIN" },
|
||
{
|
||
key: "Strict-Transport-Security",
|
||
value: "max-age=31536000; includeSubDomains; preload",
|
||
},
|
||
{ key: "Referrer-Policy", value: "strict-origin-when-cross-origin" },
|
||
{
|
||
key: "Permissions-Policy",
|
||
value: "camera=(), microphone=(), geolocation=()",
|
||
},
|
||
{ key: "Content-Security-Policy", value: csp },
|
||
],
|
||
},
|
||
// 对需要嵌入的页面放宽 frame 限制
|
||
{
|
||
source: "/worlds/share/:path*",
|
||
headers: [
|
||
{
|
||
key: "X-Frame-Options",
|
||
value: "ALLOW-FROM https://www.risesail.com",
|
||
},
|
||
],
|
||
},
|
||
];
|
||
},
|
||
|
||
async redirects() {
|
||
return [
|
||
{
|
||
source: "/favicon.ico",
|
||
destination: "/api/favicon",
|
||
permanent: false,
|
||
},
|
||
{
|
||
source: "/apple-touch-icon.png",
|
||
destination: "/api/favicon",
|
||
permanent: false,
|
||
},
|
||
];
|
||
},
|
||
};
|
||
|
||
export default nextConfig;
|