From bbce447e85b4e2fd4246ae95b3f1080523ccbe8d Mon Sep 17 00:00:00 2001 From: Juan Denis Date: Tue, 8 Sep 2026 00:25:25 -0400 Subject: [PATCH] fix(nginx): serve /serverkit-vendor/*.mjs shims as text/javascript Stock nginx (1.18-1.24 on Debian/Ubuntu) has no `.mjs` entry in mime.types, so the runtime-extension vendor shims the import map points at came back as application/octet-stream. Browsers refuse to execute an ES module with that type, so every runtime-loaded extension (WordPress, Git, Mail, Fail2ban, ...) failed on fresh installs with "Failed to fetch dynamically imported module: blob:..." (Chrome) or "'application/octet-stream' is not a valid JavaScript MIME type" (Safari). Add a location block to the shipped vhosts (and the container frontend config) that forces text/javascript for /serverkit-vendor/*.mjs. The regex is anchored to that folder: a bare `\.mjs$` would also capture the /api/v1/plugins//assets/dist/index.mjs route and 404 every bundle. update.sh already re-copies these vhosts, so existing installs pick it up on their next update. Co-Authored-By: Claude Fable 5.1 Claude-Session: https://claude.ai/code/session_01NWc84WDsWR7M55Mn7qtpie --- CHANGELOG.md | 5 +++++ frontend/nginx.conf | 15 +++++++++++++++ nginx/sites-available/serverkit-insecure.conf | 19 +++++++++++++++++++ nginx/sites-available/serverkit.conf | 19 +++++++++++++++++++ 4 files changed, 58 insertions(+) diff --git a/CHANGELOG.md b/CHANGELOG.md index 933c913f..a709861a 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -24,6 +24,11 @@ Changes after 1.9.28 on the development branch. ### Fixed +- Serve the runtime-extension vendor shims (`/serverkit-vendor/*.mjs`) as + `text/javascript` from the shipped nginx vhosts. Stock nginx has no `.mjs` + MIME entry and returned `application/octet-stream`, so every runtime-loaded + extension (WordPress, Git, Mail, Fail2ban, ...) failed with "Failed to fetch + dynamically imported module: blob:..." on fresh installs. - Evaluate fleet alert thresholds every minute and send opened/resolved alerts to administrators through the notification bus. - Preserve memory, disk and network measurements from agent heartbeats through diff --git a/frontend/nginx.conf b/frontend/nginx.conf index 0cbf93fe..baf3980b 100644 --- a/frontend/nginx.conf +++ b/frontend/nginx.conf @@ -57,6 +57,21 @@ server { proxy_read_timeout 7d; } + # Runtime-extension vendor shims (/serverkit-vendor/*.mjs). nginx builds + # without an `.mjs` MIME entry serve application/octet-stream, which the + # browser refuses to import as an ES module and every runtime-loaded + # extension fails with "Failed to fetch dynamically imported module". + # Force the type; the shims are not hashed, so they must revalidate. + # Anchored to the shim folder: a bare `\.mjs$` regex would also capture + # /api/v1/plugins//assets/dist/index.mjs (regex locations beat the + # /api prefix block) and 404 every extension bundle instead. + location ~* ^/serverkit-vendor/.+\.mjs$ { + types { } + default_type text/javascript; + try_files $uri =404; + expires -1; + } + # Cache static assets location ~* \.(js|css|png|jpg|jpeg|gif|ico|svg|woff|woff2|ttf|eot)$ { expires 1y; diff --git a/nginx/sites-available/serverkit-insecure.conf b/nginx/sites-available/serverkit-insecure.conf index 93416801..c417aca0 100644 --- a/nginx/sites-available/serverkit-insecure.conf +++ b/nginx/sites-available/serverkit-insecure.conf @@ -43,6 +43,25 @@ server { add_header Cache-Control "public, immutable"; } + # Runtime-extension vendor shims (/serverkit-vendor/*.mjs). The import map in + # index.html resolves react / react-dom / react-router-dom / i18next / + # serverkit-sdk to these, and the browser refuses to run an ES module served + # with a non-JavaScript MIME type. Stock nginx (1.18-1.24, Debian/Ubuntu + # mime.types) has no `.mjs` entry and falls back to application/octet-stream, + # which surfaces as "Failed to fetch dynamically imported module: blob:..." + # (Chrome) or "'application/octet-stream' is not a valid JavaScript MIME + # type" (Safari) on EVERY runtime-loaded extension. Force the type here + # instead of editing the system mime.types. Not hashed, so revalidate. + # Anchored to the shim folder: a bare `\.mjs$` regex would also capture + # /api/v1/plugins//assets/dist/index.mjs (regex locations beat the + # /api prefix block) and 404 every extension bundle instead. + location ~* ^/serverkit-vendor/.+\.mjs$ { + types { } + default_type text/javascript; + try_files $uri =404; + expires -1; + } + location / { # expires -1 → Cache-Control: no-cache — the entrypoint must # revalidate every load or a cached index.html references hashed diff --git a/nginx/sites-available/serverkit.conf b/nginx/sites-available/serverkit.conf index b09887fd..a81a86a8 100644 --- a/nginx/sites-available/serverkit.conf +++ b/nginx/sites-available/serverkit.conf @@ -123,6 +123,25 @@ server { add_header Cache-Control "public, immutable"; } + # Runtime-extension vendor shims (/serverkit-vendor/*.mjs). The import map in + # index.html resolves react / react-dom / react-router-dom / i18next / + # serverkit-sdk to these, and the browser refuses to run an ES module served + # with a non-JavaScript MIME type. Stock nginx (1.18-1.24, Debian/Ubuntu + # mime.types) has no `.mjs` entry and falls back to application/octet-stream, + # which surfaces as "Failed to fetch dynamically imported module: blob:..." + # (Chrome) or "'application/octet-stream' is not a valid JavaScript MIME + # type" (Safari) on EVERY runtime-loaded extension. Force the type here + # instead of editing the system mime.types. Not hashed, so revalidate. + # Anchored to the shim folder: a bare `\.mjs$` regex would also capture + # /api/v1/plugins//assets/dist/index.mjs (regex locations beat the + # /api prefix block) and 404 every extension bundle instead. + location ~* ^/serverkit-vendor/.+\.mjs$ { + types { } + default_type text/javascript; + try_files $uri =404; + expires -1; + } + location / { # expires -1 (not add_header): emits Cache-Control: no-cache without # discarding the server-level security headers, and forces every load