Spaces:
Sleeping
Sleeping
| const { createApp, ref, computed, watch, nextTick, onMounted } = Vue; | |
| const app = createApp({ | |
| delimiters: ['[[', ']]'], | |
| setup() { | |
| const currentTab = ref('server'); | |
| const tabs = [ | |
| { id: 'server', name: '基础设置', icon: 'fa-solid fa-sliders' }, | |
| { id: 'proxy', name: '反向代理', icon: 'fa-solid fa-route' }, | |
| { id: 'security', name: '安全防护', icon: 'fa-solid fa-shield-halved' }, | |
| { id: 'performance', name: '性能优化', icon: 'fa-solid fa-gauge-high' } | |
| ]; | |
| const defaultConfig = { | |
| port: 80, | |
| serverName: 'example.com', | |
| root: '/var/www/html', | |
| index: 'index.html index.htm', | |
| forceHttps: false, | |
| hideVersion: true, | |
| hsts: false, | |
| preventClickjacking: true, | |
| gzip: true, | |
| clientMaxBodySize: 10, | |
| cacheStatic: true, | |
| cacheExtensions: 'jpg jpeg png gif ico css js', | |
| locations: [ | |
| { path: '/', proxyPass: '', websocket: false, tryFiles: '$uri $uri/ /index.html' }, | |
| { path: '/api', proxyPass: 'http://localhost:3000', websocket: false, tryFiles: '' } | |
| ] | |
| }; | |
| const config = ref(JSON.parse(JSON.stringify(defaultConfig))); | |
| const resetConfig = () => { | |
| if(confirm('确定要重置配置吗?')) { | |
| config.value = JSON.parse(JSON.stringify(defaultConfig)); | |
| } | |
| }; | |
| const addLocation = () => { | |
| config.value.locations.push({ path: '/new-path', proxyPass: 'http://localhost:8080', websocket: false, tryFiles: '' }); | |
| }; | |
| const removeLocation = (index) => { | |
| config.value.locations.splice(index, 1); | |
| }; | |
| const generatedConfig = computed(() => { | |
| const c = config.value; | |
| let lines = []; | |
| // Header | |
| lines.push(`server {`); | |
| lines.push(` listen ${c.port};`); | |
| lines.push(` server_name ${c.serverName};`); | |
| if (c.root) lines.push(` root ${c.root};`); | |
| if (c.index) lines.push(` index ${c.index};`); | |
| lines.push(``); | |
| // Security | |
| if (c.hideVersion) lines.push(` # Hide Nginx version\n server_tokens off;`); | |
| if (c.forceHttps) lines.push(` # Force HTTPS (Redirect)\n if ($scheme != "https") {\n return 301 https://$host$request_uri;\n }`); | |
| let headers = []; | |
| if (c.hsts) headers.push('add_header Strict-Transport-Security "max-age=31536000; includeSubDomains" always;'); | |
| if (c.preventClickjacking) headers.push('add_header X-Frame-Options "SAMEORIGIN" always;'); | |
| if (headers.length > 0) { | |
| lines.push(` # Security Headers`); | |
| headers.forEach(h => lines.push(` ${h}`)); | |
| lines.push(``); | |
| } | |
| // Performance | |
| if (c.clientMaxBodySize) lines.push(` client_max_body_size ${c.clientMaxBodySize}M;`); | |
| if (c.gzip) { | |
| lines.push(` # Gzip Compression`); | |
| lines.push(` gzip on;`); | |
| lines.push(` gzip_types text/plain text/css application/json application/javascript text/xml application/xml application/xml+rss text/javascript;`); | |
| lines.push(``); | |
| } | |
| if (c.cacheStatic) { | |
| lines.push(` # Static File Caching`); | |
| lines.push(` location ~* \\.(${c.cacheExtensions.split(' ').join('|')})$ {`); | |
| lines.push(` expires 30d;`); | |
| lines.push(` access_log off;`); | |
| lines.push(` }`); | |
| lines.push(``); | |
| } | |
| // Locations | |
| c.locations.forEach(loc => { | |
| lines.push(` location ${loc.path} {`); | |
| if (loc.proxyPass) { | |
| lines.push(` proxy_pass ${loc.proxyPass};`); | |
| lines.push(` proxy_set_header Host $host;`); | |
| lines.push(` proxy_set_header X-Real-IP $remote_addr;`); | |
| lines.push(` proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;`); | |
| if (loc.websocket) { | |
| lines.push(` # WebSocket Support`); | |
| lines.push(` proxy_http_version 1.1;`); | |
| lines.push(` proxy_set_header Upgrade $http_upgrade;`); | |
| lines.push(` proxy_set_header Connection "upgrade";`); | |
| } | |
| } else if (loc.tryFiles) { | |
| lines.push(` try_files ${loc.tryFiles};`); | |
| } | |
| lines.push(` }`); | |
| lines.push(``); | |
| }); | |
| lines.push(`}`); | |
| return lines.join('\n'); | |
| }); | |
| const highlightCode = () => { | |
| nextTick(() => { | |
| const codeBlock = document.getElementById('code-block'); | |
| if (codeBlock && window.Prism) { | |
| codeBlock.textContent = generatedConfig.value; // Update text content explicitly for Prism | |
| window.Prism.highlightElement(codeBlock); | |
| } | |
| }); | |
| }; | |
| watch(generatedConfig, () => { | |
| highlightCode(); | |
| }); | |
| onMounted(() => { | |
| highlightCode(); | |
| }); | |
| const copyConfig = () => { | |
| navigator.clipboard.writeText(generatedConfig.value).then(() => { | |
| alert('配置已复制到剪贴板!'); | |
| }); | |
| }; | |
| const downloadConfig = () => { | |
| const blob = new Blob([generatedConfig.value], { type: 'text/plain' }); | |
| const url = window.URL.createObjectURL(blob); | |
| const a = document.createElement('a'); | |
| a.href = url; | |
| a.download = 'nginx.conf'; | |
| a.click(); | |
| window.URL.revokeObjectURL(url); | |
| }; | |
| return { | |
| tabs, | |
| currentTab, | |
| config, | |
| addLocation, | |
| removeLocation, | |
| generatedConfig, | |
| copyConfig, | |
| downloadConfig, | |
| resetConfig | |
| }; | |
| } | |
| }); | |
| app.mount('#app'); | |