fix(ui): integrate PWA build and avoid api caching

Move PWA config into the default Vite build, ensure the PWA icon source is generated, and restrict Workbox caching to static assets only. Update server UI build wiring and clarify TLS requirements in docs.
This commit is contained in:
Shantur Rathore
2026-02-07 21:33:14 +00:00
parent 99474955af
commit 7b6ed88be4
6 changed files with 4625 additions and 110 deletions

4618
package-lock.json generated

File diff suppressed because it is too large Load Diff

View File

@@ -71,7 +71,8 @@ When running as a server CodeNomad can also be installed as a PWA from any suppo
> **TLS requirement**
> Browsers require a secure (`https://`) connection for PWA installation.
> If you host CodeNomad on a remote machine, serve it behind a reverse proxy (e.g. Caddy, nginx) with a valid TLS certificate — self-signed certificates will not work.
> If you host CodeNomad on a remote machine, serve it behind a reverse proxy (e.g. Caddy, nginx) with a valid TLS certificate.
> Self-signed certificates generally won't work unless they are explicitly trusted by the device/browser (e.g., via a custom CA).
### Data Storage
- **Config**: `~/.config/codenomad/config.json`

View File

@@ -18,7 +18,7 @@
},
"scripts": {
"build": "npm run build:ui && npm run prepare-ui && tsc -p tsconfig.json && node ./scripts/copy-auth-pages.mjs && npm run prepare-config",
"build:ui": "npm run build:pwa --prefix ../ui",
"build:ui": "npm run build --prefix ../ui",
"prepare-ui": "node ./scripts/copy-ui-dist.mjs",
"prepare-config": "node ./scripts/copy-opencode-config.mjs",
"dev": "cross-env CODENOMAD_DEV=1 CODENOMAD_SERVER_PASSWORD=codenomad-dev CLI_UI_DEV_SERVER=http://localhost:3000 tsx src/index.ts",

View File

@@ -7,7 +7,6 @@
"scripts": {
"dev": "vite dev",
"build": "vite build",
"build:pwa": "vite build -c vite.config.pwa.ts",
"preview": "vite preview",
"typecheck": "tsc --noEmit -p tsconfig.json"
},

View File

@@ -1,61 +0,0 @@
import { copyFileSync } from "fs"
import { defineConfig } from "vite"
import { VitePWA } from "vite-plugin-pwa"
import { resolve } from "path"
import baseConfig from "./vite.config"
export default defineConfig({
...baseConfig,
plugins: [
...(baseConfig.plugins ?? []),
{
name: "copy-pwa-source-icon",
buildStart() {
// vite-pwa-assets requires the source image inside public/
copyFileSync(
resolve(__dirname, "src/images/CodeNomad-Icon.png"),
resolve(__dirname, "src/renderer/public/logo.png"),
)
},
},
VitePWA({
registerType: "autoUpdate",
injectRegister: "auto",
pwaAssets: {
preset: "minimal-2023",
image: "public/logo.png",
},
manifest: {
name: "CodeNomad",
short_name: "CodeNomad",
id: "/",
start_url: "/",
display: "standalone",
display_override: ["window-controls-overlay", "standalone"],
background_color: "#1a1a1a",
theme_color: "#1a1a1a",
},
workbox: {
navigateFallback: null,
runtimeCaching: [
{
urlPattern: /^\/api\/.*$/i,
handler: "NetworkFirst",
options: {
cacheName: "api-cache",
expiration: { maxEntries: 50, maxAgeSeconds: 60 },
},
},
{
urlPattern: /.*\.(?:js|css|png|jpg|jpeg|svg|webp|woff2?)$/i,
handler: "CacheFirst",
options: {
cacheName: "asset-cache",
expiration: { maxEntries: 200, maxAgeSeconds: 60 * 60 * 24 * 30 },
},
},
],
},
}),
],
})

View File

@@ -1,6 +1,7 @@
import fs from "fs"
import { defineConfig } from "vite"
import solid from "vite-plugin-solid"
import { VitePWA } from "vite-plugin-pwa"
import { resolve } from "path"
const uiPackageJson = JSON.parse(fs.readFileSync(resolve(__dirname, "package.json"), "utf-8")) as { version?: string }
@@ -20,6 +21,55 @@ export default defineConfig({
})
},
},
{
name: "prepare-pwa-source-icon",
apply: "build",
buildStart() {
// vite-pwa-assets requires the source image inside root/public/
const source = resolve(__dirname, "src/images/CodeNomad-Icon.png")
const publicDir = resolve(__dirname, "src/renderer/public")
const dest = resolve(publicDir, "logo.png")
fs.mkdirSync(publicDir, { recursive: true })
fs.copyFileSync(source, dest)
},
},
VitePWA({
registerType: "autoUpdate",
injectRegister: "auto",
pwaAssets: {
preset: "minimal-2023",
image: "public/logo.png",
},
manifest: {
name: "CodeNomad",
short_name: "CodeNomad",
id: "/",
start_url: "/",
display: "standalone",
display_override: ["window-controls-overlay", "standalone"],
background_color: "#1a1a1a",
theme_color: "#1a1a1a",
},
workbox: {
// Preserve server-side auth redirects (e.g., /login) instead of serving cached index.html.
navigateFallback: null,
// Only cache static UI assets; never cache API traffic.
runtimeCaching: [
{
urlPattern: ({ url, request }) => {
if (url.pathname.startsWith("/api/")) return false
return ["script", "style", "image", "font"].includes(request.destination)
},
handler: "CacheFirst",
options: {
cacheName: "asset-cache",
expiration: { maxEntries: 200, maxAgeSeconds: 60 * 60 * 24 * 30 },
cacheableResponse: { statuses: [0, 200] },
},
},
],
},
}),
],
css: {
postcss: "./postcss.config.js",