feat: self-host HTMX 2.0.10 and Hyperscript 0.9.91, remove unpkg CDN

- Download htmx.min.js v2.0.10 and _hyperscript.min.js v0.9.91 locally
- Update head-scripts.html to load from /static/ instead of unpkg CDN
- Remove https://unpkg.com from CSP script-src whitelist
- Update all documentation references to reflect self-hosted paths
- No breaking changes: all hx-* attributes are HTMX 2.0 compatible
This commit is contained in:
juanatsap
2026-05-14 12:59:30 +01:00
parent 20f0e79343
commit 8f4d0e9433
10 changed files with 47 additions and 26 deletions
+1 -1
View File
@@ -1316,7 +1316,7 @@ end
<script type="text/hyperscript" src="/static/hyperscript/color-theme._hs"></script>
<!-- 2. Then load hyperscript library -->
<script src="https://unpkg.com/hyperscript.org@0.9.14"></script>
<script src="/static/hyperscript/_hyperscript.min.js"></script>
```
**Benefits:**
+2 -2
View File
@@ -988,7 +988,7 @@ isHTMX := r.Header.Get("HX-Request") != ""
hx-push-url="/?lang=en"
class="lang-btn">
English
</button>
</button>
<button
hx-get="/cv?lang=es"
@@ -1669,7 +1669,7 @@ func RateLimitMiddleware(limiter *IPRateLimiter) func(http.Handler) http.Handler
class="lang-btn active"
hx-get="/cv?lang=en"
hx-target="#cv-content"
hx-swap="innerHTML"
hx-swap="innerHTML"
hx-push-url="/?lang=en"
onclick="setActive(this)">
English
+2 -2
View File
@@ -147,7 +147,7 @@ static/hyperscript/
<script type="text/hyperscript" src="/static/hyperscript/keyboard._hs"></script>
<script type="text/hyperscript" src="/static/hyperscript/zoom._hs"></script>
<script type="text/hyperscript" src="/static/hyperscript/pdf-modal._hs"></script>
<script src="https://unpkg.com/hyperscript.org@0.9.14"></script>
<script src="/static/hyperscript/_hyperscript.min.js"></script>
```
## Required Functions
@@ -288,5 +288,5 @@ end
---
**Last Updated**: 2025-11-30
**Hyperscript Version**: 0.9.14
**Hyperscript Version**: 0.9.91
**Status**: MANDATORY - ALWAYS FOLLOW
+1 -1
View File
@@ -202,7 +202,7 @@ func Setup(cvHandler *handlers.CVHandler, healthHandler *handlers.HealthHandler)
<meta charset="UTF-8">
<title>Contact Form</title>
<!-- Include HTMX -->
<script src="https://unpkg.com/htmx.org@1.9.10"></script>
<script src="/static/htmx/htmx.min.js"></script>
<style>
.form-group { margin-bottom: 1rem; }
label { display: block; margin-bottom: 0.5rem; }
+11 -11
View File
@@ -170,7 +170,7 @@ POST /switch-language
```go
// Strong CSP policy
Content-Security-Policy: default-src 'self';
script-src 'self' 'unsafe-inline' https://unpkg.com https://cdn.jsdelivr.net;
script-src 'self' 'unsafe-inline' https://cdn.jsdelivr.net;
style-src 'self' 'unsafe-inline' https://fonts.googleapis.com;
...
@@ -221,8 +221,8 @@ go-git/go-git v5.16.4 // Git operations (no shell commands)
**Frontend Dependencies:**
```javascript
// index.html - Using CDN with SRI
htmx.org@1.9.10 (SRI: sha384-D1Kt99CQMDuVetoL1lrYwg5t+9QdHe7NLX...)
hyperscript.org@0.9.14 (no SRI - ADD THIS)
htmx 2.0.10 (self-hosted at /static/htmx/htmx.min.js)
hyperscript 0.9.91 (self-hosted at /static/hyperscript/_hyperscript.min.js)
iconify-icon@2.1.0 (no SRI - ADD THIS)
```
@@ -259,9 +259,9 @@ iconify-icon@2.1.0 (no SRI - ADD THIS)
**Recommendations:**
```html
<!-- Add SRI hashes -->
<script src="https://unpkg.com/hyperscript.org@0.9.14"
integrity="sha384-[GENERATE_SRI_HASH]"
<!-- HTMX and Hyperscript are now self-hosted (no SRI needed) -->
<script src="/static/htmx/htmx.min.js"></script>
<script src="/static/hyperscript/_hyperscript.min.js"></script>
crossorigin="anonymous"></script>
<script src="https://cdn.jsdelivr.net/npm/iconify-icon@2.1.0/dist/iconify-icon.min.js"
@@ -1112,7 +1112,7 @@ server {
add_header Cross-Origin-Embedder-Policy "require-corp" always;
# CSP (delegated to Go app, but backup here)
add_header Content-Security-Policy "default-src 'self'; script-src 'self' 'unsafe-inline' https://unpkg.com https://cdn.jsdelivr.net https://matomo.morenorub.io; style-src 'self' 'unsafe-inline' https://fonts.googleapis.com; font-src 'self' https://fonts.gstatic.com; img-src 'self' data: https:; connect-src 'self' https://api.iconify.design https://matomo.morenorub.io; frame-ancestors 'self'; base-uri 'self'; form-action 'self'" always;
add_header Content-Security-Policy "default-src 'self'; script-src 'self' 'unsafe-inline' https://cdn.jsdelivr.net https://matomo.morenorub.io; style-src 'self' 'unsafe-inline' https://fonts.googleapis.com; font-src 'self' https://fonts.gstatic.com; img-src 'self' data: https:; connect-src 'self' https://api.iconify.design https://matomo.morenorub.io; frame-ancestors 'self'; base-uri 'self'; form-action 'self'" always;
# Hide Nginx version
server_tokens off;
@@ -1224,10 +1224,10 @@ go mod tidy
### Frontend Dependencies
```bash
# Check CDN resources for updates
# HTMX: https://unpkg.com/htmx.org@latest
# Hyperscript: https://unpkg.com/hyperscript.org@latest
# Iconify: https://cdn.jsdelivr.net/npm/iconify-icon@latest
# HTMX and Hyperscript are self-hosted (update by downloading new versions)
# HTMX: static/htmx/htmx.min.js (currently 2.0.10)
# Hyperscript: static/hyperscript/_hyperscript.min.js (currently 0.9.91)
# Iconify (CDN): https://cdn.jsdelivr.net/npm/iconify-icon@latest
# Generate SRI hashes
https://www.srihash.org/
+1 -1
View File
@@ -222,7 +222,7 @@ func (h *CVHandler) renderContactError(w http.ResponseWriter, r *http.Request, e
}
// Render the error template
// Return 200 OK with error content - HTMX 1.9.x logs console.error for non-2xx responses
// Return 200 OK with error content - HTMX logs console.error for non-2xx responses
// Validation errors are expected form feedback, not system errors
w.Header().Set(c.HeaderContentType, c.ContentTypeHTML)
w.WriteHeader(http.StatusOK)
+1 -1
View File
@@ -32,7 +32,7 @@ func SecurityHeaders(next http.Handler) http.Handler {
// Content Security Policy (comprehensive)
csp := "default-src 'self'; " +
"script-src 'self' 'unsafe-inline' https://unpkg.com https://cdn.jsdelivr.net https://esm.sh https://matomo.txeo.club; " +
"script-src 'self' 'unsafe-inline' https://cdn.jsdelivr.net https://esm.sh https://matomo.txeo.club; " +
"style-src 'self' 'unsafe-inline' https://fonts.googleapis.com; " +
"font-src 'self' https://fonts.gstatic.com; " +
"img-src 'self' data: https:; " +
+1
View File
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
+5 -7
View File
@@ -2,10 +2,8 @@
<!-- Device Detection - Detect real mobile devices vs desktop browser -->
<script src="/static/js/device-detection.js"></script>
<!-- HTMX with SRI (Subresource Integrity) -->
<script src="https://unpkg.com/htmx.org@1.9.10"
integrity="sha384-D1Kt99CQMDuVetoL1lrYwg5t+9QdHe7NLX/SoJYkXDFfX37iInKRy5xLSi8nO7UC"
crossorigin="anonymous"></script>
<!-- HTMX 2.0.10 (self-hosted) -->
<script src="/static/htmx/htmx.min.js"></script>
<!-- Hyperscript Functions - Must load BEFORE hyperscript library -->
<!-- NOTE: cv-functions.js removed - hyperscript def statements are globally available -->
@@ -23,12 +21,12 @@
<!-- NOTE: footer-buttons-interaction.js removed - moved to hyperscript on footer element -->
<!-- NOTE: scroll-at-bottom-handler.js removed - duplicate of handleScroll() in utils._hs -->
<!-- Hyperscript - Declarative event handling for enhanced interactivity -->
<script src="https://unpkg.com/hyperscript.org@0.9.14"></script>
<!-- Hyperscript 0.9.91 (self-hosted) -->
<script src="/static/hyperscript/_hyperscript.min.js"></script>
<!-- Ninja Keys - Lazy loaded on CMD+K (see body-scripts for loader) -->
<!-- Iconify - Load synchronously for immediate rendering -->
<!-- Using unpkg CDN (more reliable than code.iconify.design) -->
<!-- Using jsdelivr CDN (more reliable than code.iconify.design) -->
<script src="https://cdn.jsdelivr.net/npm/iconify-icon@2.1.0/dist/iconify-icon.min.js"></script>
{{end}}