diff --git a/index.html b/index.html index 17a4012..c3c2dd7 100644 --- a/index.html +++ b/index.html @@ -1,185 +1,187 @@ - - + + - - Zachary Gaetano | Broadcast Systems Design, Integration & Commissioning — Wild Dragon - - + + + + + + +Zachary Gaetano | Broadcast Systems Design, Integration & Commissioning — Wild Dragon + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + -
- - - - - - - - WILD DRAGON - BROADCAST SYSTEMS - +
+ +
-
Unpacking...
- + - // Error sink persists across replaceWith since it's on window, not the DOM. - window.addEventListener('error', function(e) { - var p = document.body || document.documentElement; - var d = document.getElementById('__bundler_err') || p.appendChild(document.createElement('div')); - d.id = '__bundler_err'; - d.style.cssText = 'position:fixed;bottom:12px;left:12px;right:12px;font:12px/1.4 ui-monospace,monospace;background:#2a1215;color:#ff8a80;padding:10px 14px;border-radius:8px;border:1px solid #5c2b2e;z-index:99999;white-space:pre-wrap;max-height:40vh;overflow:auto'; - d.textContent = (d.textContent ? d.textContent + String.fromCharCode(10) : '') + - '[bundle] ' + (e.message || e.type) + - (e.filename ? ' (' + e.filename.slice(0, 60) + ':' + e.lineno + ')' : ''); - }, true); + + + + + - try { - const manifestEl = document.querySelector('script[type="__bundler/manifest"]'); - const templateEl = document.querySelector('script[type="__bundler/template"]'); - if (!manifestEl || !templateEl) { - setStatus('Error: missing bundle data'); - console.error('[bundler] Missing script tags — manifestEl:', !!manifestEl, 'templateEl:', !!templateEl); - return; - } - - const manifest = JSON.parse(manifestEl.textContent); - let template = JSON.parse(templateEl.textContent); - - const uuids = Object.keys(manifest); - setStatus('Unpacking ' + uuids.length + ' assets...'); - - const blobUrls = {}; - await Promise.all(uuids.map(async (uuid) => { - const entry = manifest[uuid]; - try { - const binaryStr = atob(entry.data); - const bytes = new Uint8Array(binaryStr.length); - for (let i = 0; i < binaryStr.length; i++) bytes[i] = binaryStr.charCodeAt(i); - - let finalBytes = bytes; - if (entry.compressed) { - if (typeof DecompressionStream !== 'undefined') { - const ds = new DecompressionStream('gzip'); - const writer = ds.writable.getWriter(); - const reader = ds.readable.getReader(); - writer.write(bytes); - writer.close(); - const chunks = []; - let totalLen = 0; - while (true) { - const { done, value } = await reader.read(); - if (done) break; - chunks.push(value); - totalLen += value.length; - } - finalBytes = new Uint8Array(totalLen); - let offset = 0; - for (const chunk of chunks) { finalBytes.set(chunk, offset); offset += chunk.length; } - } else { - console.warn('DecompressionStream not available, asset ' + uuid + ' may not render'); - } - } - - blobUrls[uuid] = URL.createObjectURL(new Blob([finalBytes], { type: entry.mime })); - } catch (err) { - console.error('Failed to decode asset ' + uuid + ':', err); - blobUrls[uuid] = URL.createObjectURL(new Blob([], { type: entry.mime })); - } - })); - - const extResEl = document.querySelector('script[type="__bundler/ext_resources"]'); - const extResources = extResEl ? JSON.parse(extResEl.textContent) : []; - const resourceMap = {}; - for (const entry of extResources) { - if (blobUrls[entry.uuid]) resourceMap[entry.id] = blobUrls[entry.uuid]; - } - - setStatus('Rendering...'); - for (const uuid of uuids) template = template.split(uuid).join(blobUrls[uuid]); - - // Strip integrity + crossorigin — blob URLs from a file:// document inherit - // a null origin, so crossorigin forces a CORS fetch that SRI then rejects. - // The manifest bytes are ours; SRI protects against CDN compromise, not this. - template = template.replace(/\s+integrity="[^"]*"/gi, '').replace(/\s+crossorigin="[^"]*"/gi, ''); - - const resourceScript = ' - - - - - - + - \ No newline at end of file + diff --git a/nginx.conf b/nginx.conf index 1152a99..f6cb92c 100644 --- a/nginx.conf +++ b/nginx.conf @@ -1,6 +1,5 @@ server { - listen 43036 default_server; - listen [::]:43036 default_server; + listen 80; server_name _; root /usr/share/nginx/html; @@ -9,30 +8,50 @@ server { charset utf-8; gzip on; - gzip_types text/plain text/css text/javascript application/javascript application/json image/svg+xml; + gzip_types text/plain text/css text/javascript application/javascript application/json application/xml image/svg+xml; gzip_min_length 1024; + gzip_comp_level 6; + gzip_vary on; - # Long cache for static assets — they're content-addressed by path, - # bust via filename when they change. - location ~* \.(png|jpg|jpeg|gif|webp|svg|ico|woff2?|ttf)$ { - expires 30d; + # Security headers + add_header X-Frame-Options SAMEORIGIN always; + add_header X-Content-Type-Options nosniff always; + add_header Referrer-Policy strict-origin-when-cross-origin always; + add_header Permissions-Policy "camera=(), microphone=(), geolocation=()" always; + + # Vendored libs — versioned, cache hard + location /vendor/ { + expires 365d; add_header Cache-Control "public, immutable"; } - # Short cache for the HTML/JS that drive the SPA so edits show up fast. - location ~* \.(html|css|js|jsx)$ { - expires 5m; + # Images/fonts — long cache + location ~* \.(png|jpg|jpeg|gif|webp|avif|svg|ico|woff2?|ttf)$ { + expires 30d; + add_header Cache-Control "public, no-transform"; + } + + # App JS/CSS — short cache so edits show fast + location ~* \.(css|js)$ { + expires 10m; add_header Cache-Control "public, must-revalidate"; } - # Single-page app — unknown paths fall back to index.html, which - # handles routing via location.hash (#/projects/). + # Never index build sources or patch scripts + location ~* \.(jsx|py)$ { return 404; } + location /scripts/ { return 404; } + + location = /robots.txt { default_type text/plain; } + location = /llms.txt { default_type text/plain; } + location = /sitemap.xml { default_type application/xml; } + + # HTML — no stale cache + location = /index.html { + add_header Cache-Control "no-cache, must-revalidate"; + } + + # SPA fallback (routing via location.hash) location / { try_files $uri $uri/ /index.html; } - - # llms.txt — let crawlers pull it without rewrites. - location = /llms.txt { - default_type text/plain; - } } diff --git a/robots.txt b/robots.txt new file mode 100644 index 0000000..bee52ce --- /dev/null +++ b/robots.txt @@ -0,0 +1,6 @@ +User-agent: * +Allow: / +Disallow: /vendor/ +Disallow: /scripts/ + +Sitemap: https://wilddragon.net/sitemap.xml diff --git a/sitemap.xml b/sitemap.xml new file mode 100644 index 0000000..0e9c036 --- /dev/null +++ b/sitemap.xml @@ -0,0 +1,34 @@ + + + + https://wilddragon.net/ + 2026-07-03 + monthly + 1.0 + + https://wilddragon.net/images/wild-dragon-logo.jpg + Wild Dragon — Broadcast Systems Integration + + + https://wilddragon.net/images/photos/production-switcher.jpg + Production switcher in a broadcast control room + + + https://wilddragon.net/images/photos/commanders-stadium.jpg + Washington Commanders stadium broadcast production + + + https://wilddragon.net/images/photos/betmgm-production.jpg + BetMGM cloud production facility + + + https://wilddragon.net/images/photos/cvs-aetna.jpg + CVS/Aetna broadcast facility + + + https://wilddragon.net/images/photos/xr-monitor.jpg + XR virtual production stage monitor + + +