diff --git a/README.md b/README.md index 8180cfa..f221831 100644 --- a/README.md +++ b/README.md @@ -293,5 +293,26 @@ All database queries use parameterized statements (`?` placeholders). `nginx.conf` contains **location blocks only** — drop them into an existing `server { }` block. Adjust the `alias` paths to match your deployment layout. -When running without nginx (Docker Compose or local dev), the Python backend serves the frontend directly at the base URL. +When running without nginx (Docker Compose or local dev), the Python backend serves the frontend directly at the base URL. Set `"production": true` in config to disable backend static serving (nginx handles it). + +### Caching strategy + +The nginx config uses a layered caching strategy designed for deployments behind Cloudflare or other CDNs: + +| Resource | `Cache-Control` | Why | +|---|---|---| +| `/s/` (index.html) | `no-store` | Never cached by CDN or browser. This is the HTML entry point (~2KB) that contains `?v=` cache-buster query strings for CSS/JS. Must always be fresh so that version bumps take effect immediately. | +| `/s/static/*.css?v=…` | `no-cache` | Cached but revalidated on each request. The `?v=` query string acts as a cache key — Cloudflare and browsers treat each version as a distinct resource. Bump the `?v=` value in `index.html` whenever CSS/JS files change. | +| `/s/static/*.js?v=…` | `no-cache` | Same as CSS. | +| `/s/api/*`, `/s/` | (proxied) | Not cached by nginx; backend controls caching via response headers. | + +### Updating static files + +When you modify `style.css` or `app.js`: + +1. Deploy the updated files to the server +2. Bump the `?v=` value in `static/index.html` (e.g. `?v=20260318` → `?v=20260319`) +3. Reload nginx (`nginx -s reload`) + +Since `index.html` has `no-store`, browsers and Cloudflare always fetch the latest version, which in turn references the new `?v=` URLs for CSS/JS — busting all downstream caches automatically. No manual CDN purge is needed. diff --git a/static/app.js b/static/app.js index b1659e6..f4e1149 100644 --- a/static/app.js +++ b/static/app.js @@ -177,7 +177,6 @@ function setupUrlInput() { input.addEventListener('input', () => { clearTimeout(_lookupTimer); const val = input.value.trim(); - console.log('[input event] value:', val); if (!val) { clearResult(); if (state.isAdmin) renderAdminPanel(); @@ -190,7 +189,6 @@ function setupUrlInput() { }); input.addEventListener('keydown', (e) => { if (e.key === 'Enter') { - console.log('[keydown] Enter pressed'); e.preventDefault(); handleEnter(); } @@ -198,29 +196,23 @@ function setupUrlInput() { } async function lookupUrl(url) { - console.log('[lookupUrl] called with:', url); if (!isValidUrl(url)) { - console.log('[lookupUrl] invalid URL, clearing'); clearResult(); return; } try { const res = await apiGet('/api/lookup?url=' + encodeURIComponent(url)); - console.log('[lookupUrl] response status:', res.status); if (res.ok) { const data = await res.json(); - console.log('[lookupUrl] found existing:', data); state.currentMeta = data; renderMeta(data); setStatus('exists', 'ok'); } else { - console.log('[lookupUrl] not found, clearing meta'); state.currentMeta = null; clearResult(); setStatus('', ''); } - } catch (e) { - console.error('[lookupUrl] error:', e); + } catch { clearResult(); } } @@ -229,12 +221,8 @@ async function handleEnter() { clearTimeout(_lookupTimer); // cancel any pending debounced lookup const input = document.getElementById('url-input'); const url = input.value.trim(); - console.log('[handleEnter] url:', url); - console.log('[handleEnter] state.apiKey:', state.apiKey ? '(set)' : '(empty)'); - console.log('[handleEnter] state.currentMeta:', state.currentMeta); if (!url || !isValidUrl(url)) { - console.log('[handleEnter] invalid/empty URL, clearing'); clearResult(); setStatus('', ''); return; @@ -242,20 +230,16 @@ async function handleEnter() { // If the debounced lookup already found this URL exists, just show it if (state.currentMeta && state.currentMeta.original_url === url) { - console.log('[handleEnter] URL already found by lookup, showing existing'); setStatus('exists', 'ok'); return; } // Create a new short URL - console.log('[handleEnter] proceeding to create...'); setStatus('creating…', ''); try { const res = await apiPost('/api/shorten', { url: url }); - console.log('[handleEnter] POST /api/shorten status:', res.status); if (res.status === 201) { const shortUrl = await res.text(); - console.log('[handleEnter] created:', shortUrl); const code = shortUrl.split('/').pop(); const metaRes = await apiGet('/api/urls/' + encodeURIComponent(code)); if (metaRes.ok) { @@ -267,11 +251,9 @@ async function handleEnter() { } } else { const err = await res.json().catch(() => null); - console.log('[handleEnter] create failed:', res.status, err); setStatus(err?.error || 'error', 'err'); } - } catch (e) { - console.error('[handleEnter] network error:', e); + } catch { setStatus('network error', 'err'); } } diff --git a/static/index.html b/static/index.html index 2cec6ea..92713ea 100644 --- a/static/index.html +++ b/static/index.html @@ -7,7 +7,7 @@ Short · URL Shortener - + @@ -58,7 +58,7 @@
- +