Use nginx to serve the website
All checks were successful
Build and test project / build-and-test (push) Successful in 1m29s
All checks were successful
Build and test project / build-and-test (push) Successful in 1m29s
This commit is contained in:
13
README.md
13
README.md
@@ -42,16 +42,15 @@ web/
|
|||||||
js/
|
js/
|
||||||
app.js # ES module entry point: step navigation, flow orchestration
|
app.js # ES module entry point: step navigation, flow orchestration
|
||||||
kobo-device.js # KoboModels, KoboDevice class
|
kobo-device.js # KoboModels, KoboDevice class
|
||||||
kobo-software-urls.js # KoboSoftwareUrls, getSoftwareUrl, getDevicesForVersion
|
kobo-software-urls.js # Fetches download URLs from JSON, getSoftwareUrl, getDevicesForVersion
|
||||||
nickelmenu.js # NickelMenuInstaller: downloads/bundles NickelMenu + config
|
nickelmenu.js # NickelMenuInstaller: downloads/bundles NickelMenu + config
|
||||||
patch-ui.js # PatchUI: loads patches, parses YAML, renders toggle UI
|
patch-ui.js # PatchUI: loads patches, parses YAML, renders toggle UI
|
||||||
patch-runner.js # KoboPatchRunner: spawns Web Worker per build
|
patch-runner.js # KoboPatchRunner: spawns Web Worker per build
|
||||||
patch-worker.js # Web Worker: loads WASM, runs patchFirmware()
|
patch-worker.js # Web Worker: loads WASM, runs patchFirmware()
|
||||||
wasm_exec.js # Go WASM runtime (generated by build.sh, gitignored)
|
wasm_exec.js # Go WASM runtime (copied from Go SDK by build.sh, gitignored)
|
||||||
wasm/
|
|
||||||
kobopatch.wasm # Compiled WASM binary (generated by build.sh, gitignored)
|
|
||||||
patches/
|
patches/
|
||||||
index.json # Available patch manifest
|
index.json # Available patch manifest
|
||||||
|
downloads.json # Firmware download URLs by version/serial (may be auto-generated)
|
||||||
patches_*.zip # Patch files per firmware version
|
patches_*.zip # Patch files per firmware version
|
||||||
nickelmenu/ # NickelMenu assets (generated by setup.sh, gitignored)
|
nickelmenu/ # NickelMenu assets (generated by setup.sh, gitignored)
|
||||||
NickelMenu.zip
|
NickelMenu.zip
|
||||||
@@ -85,7 +84,7 @@ tests/
|
|||||||
## Adding a new software version
|
## Adding a new software version
|
||||||
|
|
||||||
1. Add the patch zip to `web/src/patches/` and update `index.json`
|
1. Add the patch zip to `web/src/patches/` and update `index.json`
|
||||||
2. Add download URLs to `KoboSoftwareUrls` in `web/src/js/kobo-software-urls.js` (keyed by version then serial prefix)
|
2. Add download URLs to `web/src/patches/downloads.json` (keyed by version then serial prefix)
|
||||||
3. The Kobo CDN prefix per device family (e.g. `kobo12`, `kobo13`) is stable; the date path segment changes per release
|
3. The Kobo CDN prefix per device family (e.g. `kobo12`, `kobo13`) is stable; the date path segment changes per release
|
||||||
|
|
||||||
## Building the WASM binary
|
## Building the WASM binary
|
||||||
@@ -138,7 +137,9 @@ You can delete the entire `web/dist/` folder and re-run `serve-locally.sh` to re
|
|||||||
The E2E tests cover all major user flows:
|
The E2E tests cover all major user flows:
|
||||||
|
|
||||||
- **NickelMenu** — install with config (manual download), install NickelMenu only, remove option disabled without device
|
- **NickelMenu** — install with config (manual download), install NickelMenu only, remove option disabled without device
|
||||||
- **Custom patches** — full patching pipeline, restore original firmware
|
- **Custom patches** — full patching pipeline, restore original firmware, build failure with "Go Back" recovery
|
||||||
|
- **Device detection** — firmware version validation (4.x supported, 5.x incompatible), unknown model warning
|
||||||
|
- **Back navigation** — verifies every back button returns to the correct previous screen in both auto and manual mode
|
||||||
- **With simulated Kobo Libra Color** — install NickelMenu with config, remove NickelMenu, install custom patches, restore firmware
|
- **With simulated Kobo Libra Color** — install NickelMenu with config, remove NickelMenu, install custom patches, restore firmware
|
||||||
|
|
||||||
The simulated device tests mock the File System Access API with an in-memory filesystem that mimics a Kobo Libra Color (serial prefix N428, firmware 4.45.23646).
|
The simulated device tests mock the File System Access API with an in-memory filesystem that mimics a Kobo Libra Color (serial prefix N428, firmware 4.45.23646).
|
||||||
|
|||||||
26
nginx.conf.template
Normal file
26
nginx.conf.template
Normal file
@@ -0,0 +1,26 @@
|
|||||||
|
worker_processes auto;
|
||||||
|
error_log stderr;
|
||||||
|
pid /tmp/nginx.pid;
|
||||||
|
|
||||||
|
events {
|
||||||
|
worker_connections 1024;
|
||||||
|
}
|
||||||
|
|
||||||
|
http {
|
||||||
|
include /etc/nginx/mime.types;
|
||||||
|
default_type application/octet-stream;
|
||||||
|
access_log off;
|
||||||
|
sendfile on;
|
||||||
|
gzip on;
|
||||||
|
gzip_types text/html text/css application/javascript application/json application/wasm;
|
||||||
|
|
||||||
|
server {
|
||||||
|
listen ${PORT} default_server;
|
||||||
|
root web/dist;
|
||||||
|
index index.html;
|
||||||
|
|
||||||
|
location / {
|
||||||
|
try_files $uri $uri/ /index.html;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -1,7 +1,7 @@
|
|||||||
providers = ["python"]
|
providers = ["node"]
|
||||||
|
|
||||||
[phases.setup]
|
[phases.setup]
|
||||||
nixPkgs = ["git", "curl", "python3Minimal", "zip", "gnutar", "nodejs_22"]
|
nixPkgs = ["git", "curl", "zip", "gnutar", "nginx"]
|
||||||
paths = ["/usr/local/go/bin"]
|
paths = ["/usr/local/go/bin"]
|
||||||
cmds = [
|
cmds = [
|
||||||
"curl -sSfL https://go.dev/dl/go1.23.12.linux-amd64.tar.gz | tar -xz -C /usr/local",
|
"curl -sSfL https://go.dev/dl/go1.23.12.linux-amd64.tar.gz | tar -xz -C /usr/local",
|
||||||
@@ -16,4 +16,4 @@ cmds = [
|
|||||||
]
|
]
|
||||||
|
|
||||||
[start]
|
[start]
|
||||||
cmd = "python3 -m http.server ${PORT:-8080} -d web/dist"
|
cmd = "./start.sh"
|
||||||
|
|||||||
@@ -26,4 +26,11 @@ if [ ! -f "$DIST_DIR/wasm/kobopatch.wasm" ]; then
|
|||||||
fi
|
fi
|
||||||
|
|
||||||
echo "Serving at http://localhost:8888"
|
echo "Serving at http://localhost:8888"
|
||||||
python3 -m http.server -d "$DIST_DIR" 8888
|
if command -v nginx &>/dev/null; then
|
||||||
|
export PORT=8888
|
||||||
|
envsubst '${PORT}' < "$SCRIPT_DIR/nginx.conf.template" > /tmp/kobopatch-nginx.conf
|
||||||
|
nginx -c /tmp/kobopatch-nginx.conf -g 'daemon off;'
|
||||||
|
else
|
||||||
|
echo "(nginx not found, using Node.js server)"
|
||||||
|
node serve.mjs
|
||||||
|
fi
|
||||||
|
|||||||
11
start.sh
Normal file
11
start.sh
Normal file
@@ -0,0 +1,11 @@
|
|||||||
|
#!/usr/bin/env bash
|
||||||
|
set -euo pipefail
|
||||||
|
|
||||||
|
SCRIPT_DIR="$(cd "$(dirname "$0")" && pwd)"
|
||||||
|
PORT="${PORT:-8080}"
|
||||||
|
|
||||||
|
# Generate nginx config from template
|
||||||
|
export PORT
|
||||||
|
envsubst '${PORT}' < "$SCRIPT_DIR/nginx.conf.template" > /tmp/nginx.conf
|
||||||
|
|
||||||
|
exec nginx -c /tmp/nginx.conf -g 'daemon off;'
|
||||||
@@ -17,7 +17,7 @@ module.exports = defineConfig({
|
|||||||
},
|
},
|
||||||
},
|
},
|
||||||
webServer: {
|
webServer: {
|
||||||
command: 'cd ../../web && npm install && node build.mjs && cd ../kobopatch-wasm && bash build.sh && cd ../web && python3 -m http.server -d dist 8889',
|
command: 'cd ../../web && npm install && node build.mjs && cd ../kobopatch-wasm && bash build.sh && cd ../web && PORT=8889 node serve.mjs',
|
||||||
port: 8889,
|
port: 8889,
|
||||||
reuseExistingServer: true,
|
reuseExistingServer: true,
|
||||||
},
|
},
|
||||||
|
|||||||
40
web/serve.mjs
Normal file
40
web/serve.mjs
Normal file
@@ -0,0 +1,40 @@
|
|||||||
|
import { createServer } from 'node:http';
|
||||||
|
import { createReadStream, statSync, existsSync } from 'node:fs';
|
||||||
|
import { join, extname } from 'node:path';
|
||||||
|
|
||||||
|
const DIST = join(import.meta.dirname, 'dist');
|
||||||
|
const PORT = process.env.PORT || 8888;
|
||||||
|
|
||||||
|
const MIME = {
|
||||||
|
'.html': 'text/html',
|
||||||
|
'.css': 'text/css',
|
||||||
|
'.js': 'application/javascript',
|
||||||
|
'.json': 'application/json',
|
||||||
|
'.wasm': 'application/wasm',
|
||||||
|
'.zip': 'application/zip',
|
||||||
|
'.tgz': 'application/gzip',
|
||||||
|
'.svg': 'image/svg+xml',
|
||||||
|
'.png': 'image/png',
|
||||||
|
'.ico': 'image/x-icon',
|
||||||
|
'.webmanifest': 'application/manifest+json',
|
||||||
|
};
|
||||||
|
|
||||||
|
createServer((req, res) => {
|
||||||
|
const url = new URL(req.url, `http://localhost`);
|
||||||
|
let filePath = join(DIST, decodeURIComponent(url.pathname));
|
||||||
|
|
||||||
|
if (filePath.endsWith('/')) filePath = join(filePath, 'index.html');
|
||||||
|
if (!extname(filePath) && existsSync(filePath + '/index.html')) filePath += '/index.html';
|
||||||
|
|
||||||
|
try {
|
||||||
|
const stat = statSync(filePath);
|
||||||
|
if (!stat.isFile()) throw new Error();
|
||||||
|
res.writeHead(200, { 'Content-Type': MIME[extname(filePath)] || 'application/octet-stream' });
|
||||||
|
createReadStream(filePath).pipe(res);
|
||||||
|
} catch {
|
||||||
|
res.writeHead(404, { 'Content-Type': 'text/plain' });
|
||||||
|
res.end('Not found');
|
||||||
|
}
|
||||||
|
}).listen(PORT, () => {
|
||||||
|
console.log(`Serving web/dist on http://localhost:${PORT}`);
|
||||||
|
});
|
||||||
Reference in New Issue
Block a user