export function renderVisualGameHttpConfig(config) { return `${renderFrontendMap(config)} server { listen 80; listen [::]:80; server_name ${renderServerNames(config)}; location ^~ /.well-known/acme-challenge/ { root ${config.nginx.acme_webroot}; } location / { return 301 https://$host$request_uri; } } ` } export function renderVisualGameHttpsConfig(config) { return `${renderFrontendMap(config)} server { listen 80; listen [::]:80; server_name ${renderServerNames(config)}; location ^~ /.well-known/acme-challenge/ { root ${config.nginx.acme_webroot}; } location / { return 301 https://$host$request_uri; } } server { listen 443 ssl http2; listen [::]:443 ssl http2; server_name ${renderServerNames(config)}; ssl_certificate ${config.nginx.certificate_directory}/fullchain.pem; ssl_certificate_key ${config.nginx.certificate_directory}/key.pem; ssl_protocols TLSv1.2 TLSv1.3; ssl_session_cache shared:${config.nginx.root_variable}_ssl:10m; ssl_session_timeout 1d; ssl_session_tickets off; client_max_body_size 36m; location = /healthz { proxy_pass ${config.backend_origin}/healthz; proxy_http_version 1.1; proxy_set_header Host $host; proxy_set_header X-Real-IP $remote_addr; proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for; proxy_set_header X-Forwarded-Proto $scheme; } location ^~ /api/ { proxy_pass ${config.backend_origin}; proxy_http_version 1.1; proxy_set_header Host $host; proxy_set_header X-Real-IP $remote_addr; proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for; proxy_set_header X-Forwarded-Proto $scheme; proxy_buffering off; proxy_request_buffering off; proxy_read_timeout 300s; proxy_send_timeout 300s; } location = /ws/asr { proxy_pass ${config.backend_origin}; proxy_http_version 1.1; proxy_set_header Upgrade $http_upgrade; proxy_set_header Connection "upgrade"; proxy_set_header Host $host; proxy_set_header X-Real-IP $remote_addr; proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for; proxy_set_header X-Forwarded-Proto $scheme; proxy_read_timeout 60s; proxy_send_timeout 60s; } ${renderFrontendLocation(config)} } ` } function renderFrontendMap(config) { const entries = config.domains.map((domain) => ` ${domain.hostname} ${frontendTarget(config, domain)};`) return `map $host $${config.nginx.root_variable} { hostnames; ${entries.join('\n')} }` } function frontendTarget(config, domain) { switch (config.frontend_mode) { case 'static': return `${config.application_root}/${domain.frontend_directory}` case 'proxy': return domain.frontend_origin default: throw new Error(`不支持的 frontend_mode:${config.frontend_mode}`) } } function renderFrontendLocation(config) { switch (config.frontend_mode) { case 'static': return ` root $${config.nginx.root_variable}; index index.html; location / { try_files $uri $uri/ /index.html; }` case 'proxy': return ` location / { proxy_pass $${config.nginx.root_variable}; proxy_http_version 1.1; proxy_set_header Host $host; proxy_set_header X-Real-IP $remote_addr; proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for; proxy_set_header X-Forwarded-Proto $scheme; proxy_set_header Upgrade $http_upgrade; proxy_set_header Connection "upgrade"; }` default: throw new Error(`不支持的 frontend_mode:${config.frontend_mode}`) } } function renderServerNames(config) { return config.domains.map((domain) => ` ${domain.hostname}`).join('\n') }