Compare commits

..

4 Commits

Author SHA1 Message Date
Aveline 4c06f33c07
Merge 1e2cee7060 into 37b1a8bf34 2025-09-12 11:35:56 +02:00
Aveline 37b1a8bf34
docs: update pprof section of DEVELOPMENT.md (#802) 2025-09-12 11:11:28 +02:00
Marc Brooks ca8b06f4cf
chore: enhance the gzip and cacheable handling of static files
Add SVG and ICO to cacheable files.
Emit robots.txt directly.
Recognize WOFF2 (font) files as assets (so the get the immutable treatment)
Pre-gzip the entire /static/ directory (not just /static/assets/) and include SVG, ICO, and HTML files
Ensure fonts.css is processed by vite/rollup so that the preload and css reference the same immutable files (which get long-cached with hashes)
Add CircularXXWeb-Black to the preload list as it is used in the hot-path.
Handle system-driven color-scheme changes from dark to light correctly.
2025-09-12 08:41:41 +02:00
Aveline 33e099f258
update netboot.xyz-multiarch.iso to 2.0.88 (#799)
* chore: update netboot.xyz-multiarch.iso to 2.0.88

* feat: add script to update netboot.xyz iso
2025-09-12 08:41:17 +02:00
8 changed files with 123 additions and 28 deletions

View File

@ -301,13 +301,14 @@ export JETKVM_PROXY_URL="ws://<IP>"
### Performance Profiling ### Performance Profiling
```bash 1. Enable `Developer Mode` on your JetKVM device
# Enable profiling 2. Add a password on the `Access` tab
go build -o bin/jetkvm_app -ldflags="-X main.enableProfiling=true" cmd/main.go
```bash
# Access profiling # Access profiling
curl http://<IP>:6060/debug/pprof/ curl http://api:$JETKVM_PASSWORD@YOUR_DEVICE_IP/developer/pprof/
``` ```
### Advanced Environment Variables ### Advanced Environment Variables
```bash ```bash

View File

@ -63,14 +63,17 @@ build_dev_test: build_test2json build_gotestsum
frontend: frontend:
cd ui && npm ci && npm run build:device && \ cd ui && npm ci && npm run build:device && \
find ../static/assets \ find ../static/ \
-type f \ -type f \
\( -name '*.js' \ \( -name '*.js' \
-o -name '*.css' \ -o -name '*.css' \
-o -name '*.html' \
-o -name '*.ico' \
-o -name '*.png' \ -o -name '*.png' \
-o -name '*.jpg' \ -o -name '*.jpg' \
-o -name '*.jpeg' \ -o -name '*.jpeg' \
-o -name '*.gif' \ -o -name '*.gif' \
-o -name '*.svg' \
-o -name '*.webp' \ -o -name '*.webp' \
-o -name '*.woff2' \ -o -name '*.woff2' \
\) \ \) \

Binary file not shown.

77
scripts/update_netboot_xyz.sh Executable file
View File

@ -0,0 +1,77 @@
#!/usr/bin/env bash
#
# Exit immediately if a command exits with a non-zero status
set -e
C_RST="$(tput sgr0)"
C_ERR="$(tput setaf 1)"
C_OK="$(tput setaf 2)"
C_WARN="$(tput setaf 3)"
C_INFO="$(tput setaf 5)"
msg() { printf '%s%s%s\n' $2 "$1" $C_RST; }
msg_info() { msg "$1" $C_INFO; }
msg_ok() { msg "$1" $C_OK; }
msg_err() { msg "$1" $C_ERR; }
msg_warn() { msg "$1" $C_WARN; }
# Get the latest release information
msg_info "Getting latest release information ..."
LATEST_RELEASE=$(curl -s \
-H "Accept: application/vnd.github+json" \
-H "X-GitHub-Api-Version: 2022-11-28" \
https://api.github.com/repos/netbootxyz/netboot.xyz/releases | jq '
[.[] | select(.prerelease == false and .draft == false and .assets != null and (.assets | length > 0))] |
sort_by(.created_at) |
.[-1]')
# Extract version, download URL, and digest
VERSION=$(echo "$LATEST_RELEASE" | jq -r '.tag_name')
ISO_URL=$(echo "$LATEST_RELEASE" | jq -r '.assets[] | select(.name == "netboot.xyz-multiarch.iso") | .browser_download_url')
EXPECTED_CHECKSUM=$(echo "$LATEST_RELEASE" | jq -r '.assets[] | select(.name == "netboot.xyz-multiarch.iso") | .digest' | sed 's/sha256://')
msg_ok "Latest version: $VERSION"
msg_ok "ISO URL: $ISO_URL"
msg_ok "Expected SHA256: $EXPECTED_CHECKSUM"
# Check if we already have the same version
if [ -f "resource/netboot.xyz-multiarch.iso" ]; then
msg_info "Checking current resource file ..."
# First check by checksum (fastest)
CURRENT_CHECKSUM=$(shasum -a 256 resource/netboot.xyz-multiarch.iso | awk '{print $1}')
if [ "$CURRENT_CHECKSUM" = "$EXPECTED_CHECKSUM" ]; then
msg_ok "Resource file is already up to date (version $VERSION). No update needed."
exit 0
else
msg_info "Checksums differ, proceeding with download ..."
fi
fi
# Download ISO file
TMP_ISO=$(mktemp -t netbootxyziso)
msg_info "Downloading ISO file ..."
curl -L -o "$TMP_ISO" "$ISO_URL"
# Verify SHA256 checksum
msg_info "Verifying SHA256 checksum ..."
ACTUAL_CHECKSUM=$(shasum -a 256 "$TMP_ISO" | awk '{print $1}')
if [ "$EXPECTED_CHECKSUM" = "$ACTUAL_CHECKSUM" ]; then
msg_ok "Verified SHA256 checksum."
mv -f "$TMP_ISO" "resource/netboot.xyz-multiarch.iso"
msg_ok "Updated ISO file."
git add "resource/netboot.xyz-multiarch.iso"
git commit -m "chore: update netboot.xyz-multiarch.iso to $VERSION"
msg_ok "Committed changes."
msg_ok "You can now push the changes to the remote repository."
exit 0
else
msg_err "Inconsistent SHA256 checksum."
msg_err "Expected: $EXPECTED_CHECKSUM"
msg_err "Actual: $ACTUAL_CHECKSUM"
exit 1
fi

View File

@ -6,27 +6,34 @@
<!-- These are the fonts used in the app --> <!-- These are the fonts used in the app -->
<link <link
rel="preload" rel="preload"
href="/fonts/CircularXXWeb-Medium.woff2" href="./public/fonts/CircularXXWeb-Medium.woff2"
as="font" as="font"
type="font/woff2" type="font/woff2"
crossorigin crossorigin
/> />
<link <link
rel="preload" rel="preload"
href="/fonts/CircularXXWeb-Book.woff2" href="./public/fonts/CircularXXWeb-Book.woff2"
as="font" as="font"
type="font/woff2" type="font/woff2"
crossorigin crossorigin
/> />
<link <link
rel="preload" rel="preload"
href="/fonts/CircularXXWeb-Regular.woff2" href="./public/fonts/CircularXXWeb-Regular.woff2"
as="font"
type="font/woff2"
crossorigin
/>
<link
rel="preload"
href="./public/fonts/CircularXXWeb-Black.woff2"
as="font" as="font"
type="font/woff2" type="font/woff2"
crossorigin crossorigin
/> />
<title>JetKVM</title> <title>JetKVM</title>
<link rel="stylesheet" href="/fonts/fonts.css" /> <link rel="stylesheet" href="./public/fonts/fonts.css" />
<link rel="icon" type="image/png" href="/favicon-96x96.png" sizes="96x96" /> <link rel="icon" type="image/png" href="/favicon-96x96.png" sizes="96x96" />
<link rel="icon" type="image/svg+xml" href="/favicon.svg" /> <link rel="icon" type="image/svg+xml" href="/favicon.svg" />
<link rel="shortcut icon" href="/favicon.ico" /> <link rel="shortcut icon" href="/favicon.ico" />
@ -36,23 +43,21 @@
<meta name="theme-color" content="#051946" /> <meta name="theme-color" content="#051946" />
<meta name="description" content="A web-based KVM console for managing remote servers." /> <meta name="description" content="A web-based KVM console for managing remote servers." />
<script> <script>
// Initial theme setup function applyThemeFromPreference() {
document.documentElement.classList.toggle( // dark theme setup
"dark", var darkDesired = localStorage.theme === "dark" ||
localStorage.theme === "dark" ||
(!("theme" in localStorage) && (!("theme" in localStorage) &&
window.matchMedia("(prefers-color-scheme: dark)").matches), window.matchMedia("(prefers-color-scheme: dark)").matches)
);
document.documentElement.classList.toggle("dark", darkDesired)
}
// initial theme application
applyThemeFromPreference();
// Listen for system theme changes // Listen for system theme changes
window window.matchMedia("(prefers-color-scheme: dark)").addEventListener("change", applyThemeFromPreference);
.matchMedia("(prefers-color-scheme: dark)") window.matchMedia("(prefers-color-scheme: light)").addEventListener("change", applyThemeFromPreference);
.addEventListener("change", ({ matches }) => {
if (!("theme" in localStorage)) {
// Only auto-switch if user hasn't manually set a theme
document.documentElement.classList.toggle("dark", matches);
}
});
</script> </script>
</head> </head>
<body <body

View File

@ -1,2 +0,0 @@
User-agent: *
Disallow: /

View File

@ -31,6 +31,7 @@ export default defineConfig(({ mode, command }) => {
esbuild: { esbuild: {
pure: ["console.debug"], pure: ["console.debug"],
}, },
assetsInclude: ["**/*.woff2"],
build: { build: {
outDir: isCloud ? "dist" : "../static", outDir: isCloud ? "dist" : "../static",
rollupOptions: { rollupOptions: {

16
web.go
View File

@ -69,8 +69,7 @@ type SetupRequest struct {
} }
var cachableFileExtensions = []string{ var cachableFileExtensions = []string{
".jpg", ".jpeg", ".png", ".gif", ".webp", ".woff2", ".jpg", ".jpeg", ".png", ".svg", ".gif", ".webp", ".ico", ".woff2",
".ico",
} }
func setupRouter() *gin.Engine { func setupRouter() *gin.Engine {
@ -83,7 +82,10 @@ func setupRouter() *gin.Engine {
}), }),
)) ))
staticFS, _ := fs.Sub(staticFiles, "static") staticFS, err := fs.Sub(staticFiles, "static")
if err != nil {
logger.Fatal().Err(err).Msg("failed to get rooted static files subdirectory")
}
staticFileServer := http.StripPrefix("/static", statigz.FileServer( staticFileServer := http.StripPrefix("/static", statigz.FileServer(
staticFS.(fs.ReadDirFS), staticFS.(fs.ReadDirFS),
)) ))
@ -109,9 +111,17 @@ func setupRouter() *gin.Engine {
c.Next() c.Next()
}) })
r.GET("/robots.txt", func(c *gin.Context) {
c.Header("Content-Type", "text/plain")
c.Header("Cache-Control", "public, max-age=31536000, immutable") // Cache for 1 year
c.String(http.StatusOK, "User-agent: *\nDisallow: /")
})
r.Any("/static/*w", func(c *gin.Context) { r.Any("/static/*w", func(c *gin.Context) {
staticFileServer.ServeHTTP(c.Writer, c.Request) staticFileServer.ServeHTTP(c.Writer, c.Request)
}) })
// Public routes (no authentication required)
r.POST("/auth/login-local", handleLogin) r.POST("/auth/login-local", handleLogin)
// We use this to determine if the device is setup // We use this to determine if the device is setup