- Service worker (vite-plugin-pwa/Workbox): add /share/, /mcp, and /robots.txt to navigateFallbackDenylist so the SPA app-shell never shadows those server-rendered routes (they mirror the server static-serve exclude list — the share SEO/OG HTML, the MCP endpoint, and robots.txt must come from the server). - Remove the dead /api GET NetworkFirst Workbox rule (api-get-cache): offline reads are served by the persisted TanStack Query cache (IndexedDB) + y-indexeddb, never by an SW HTTP cache, so caching GET /api only risked stale responses. All /api is now NetworkOnly. clearOfflineCache still deletes any legacy api-get-cache defensively (comment updated to note it is no longer created). - CORS: drop the cleartext 'http://localhost' native-WebView origin. The Capacitor shell uses the secure scheme (capacitor.config cleartext:false, default Android scheme https, iOS hosted via CAP_SERVER_URL), so no native client uses it; allowing it only widened the credentialed-CORS surface. Keeps capacitor://, ionic://, and https://localhost. - docs/mobile-bootstrap.md: replace the inaccurate 'hand-rolled service worker' description with the real Workbox generateSW setup (prompt registration via virtual:pwa-register, production-only, denylist, NetworkOnly, RQ/y-indexeddb offline reads) and drop http://localhost from the CORS origins list. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
141 lines
4.3 KiB
TypeScript
141 lines
4.3 KiB
TypeScript
import { defineConfig, loadEnv } from "vite";
|
|
import react from "@vitejs/plugin-react";
|
|
import { VitePWA } from "vite-plugin-pwa";
|
|
import * as path from "path";
|
|
import { execSync } from "node:child_process";
|
|
|
|
const envPath = path.resolve(process.cwd(), "..", "..");
|
|
|
|
// Resolve the version string shown in the UI.
|
|
// Priority: explicit APP_VERSION env (injected by Docker/CI, where .git is absent),
|
|
// then `git describe` for local builds, then the package.json version as a fallback.
|
|
function resolveAppVersion(cwd: string): string {
|
|
const fromEnv = process.env.APP_VERSION?.trim();
|
|
if (fromEnv) return fromEnv;
|
|
try {
|
|
return execSync("git describe --tags --always", {
|
|
cwd,
|
|
stdio: ["ignore", "pipe", "ignore"],
|
|
})
|
|
.toString()
|
|
.trim();
|
|
} catch {
|
|
return `v${process.env.npm_package_version ?? "0.0.0"}`;
|
|
}
|
|
}
|
|
|
|
export default defineConfig(({ mode }) => {
|
|
const {
|
|
APP_URL,
|
|
FILE_UPLOAD_SIZE_LIMIT,
|
|
FILE_IMPORT_SIZE_LIMIT,
|
|
DRAWIO_URL,
|
|
CLOUD,
|
|
SUBDOMAIN_HOST,
|
|
COLLAB_URL,
|
|
BILLING_TRIAL_DAYS,
|
|
POSTHOG_HOST,
|
|
POSTHOG_KEY,
|
|
} = loadEnv(mode, envPath, "");
|
|
|
|
return {
|
|
define: {
|
|
"process.env": {
|
|
APP_URL,
|
|
FILE_UPLOAD_SIZE_LIMIT,
|
|
FILE_IMPORT_SIZE_LIMIT,
|
|
DRAWIO_URL,
|
|
CLOUD,
|
|
SUBDOMAIN_HOST,
|
|
COLLAB_URL,
|
|
BILLING_TRIAL_DAYS,
|
|
POSTHOG_HOST,
|
|
POSTHOG_KEY,
|
|
},
|
|
APP_VERSION: JSON.stringify(resolveAppVersion(envPath)),
|
|
},
|
|
plugins: [
|
|
react(),
|
|
VitePWA({
|
|
registerType: "prompt",
|
|
injectRegister: null,
|
|
strategies: "generateSW",
|
|
manifest: false,
|
|
workbox: {
|
|
globPatterns: ["**/*.{js,css,html,svg,png,ico,woff2,json}"],
|
|
navigateFallback: "index.html",
|
|
// Segment-anchored (`^/<seg>(/|$)`) so navigation requests to these
|
|
// segments are consistently excluded from the SPA fallback, mirroring
|
|
// the runtimeCaching urlPattern regexes below.
|
|
//
|
|
// `/share`, `/mcp`, and `/robots.txt` mirror the server static-serve
|
|
// exclude list (apps/server/src/main.ts setGlobalPrefix `exclude`):
|
|
// robots.txt, the SEO/OG/analytics-injected public share HTML, and the
|
|
// embedded MCP endpoint are served by server controllers, so the SW must
|
|
// never shadow them with the precached index.html app shell (doing so
|
|
// would break SEO and MCP).
|
|
navigateFallbackDenylist: [
|
|
/^\/api(\/|$)/,
|
|
/^\/collab(\/|$)/,
|
|
/^\/socket\.io(\/|$)/,
|
|
/^\/share(\/|$)/,
|
|
/^\/mcp(\/|$)/,
|
|
/^\/robots\.txt$/,
|
|
],
|
|
cleanupOutdatedCaches: true,
|
|
clientsClaim: true,
|
|
// The urlPattern regexes below mirror apps/client/src/pwa/sw-strategy.ts
|
|
// and MUST be kept in sync with it. Workbox `generateSW` serializes these
|
|
// functions standalone into the generated service worker, so they cannot
|
|
// import the module — the matching logic is intentionally duplicated as
|
|
// self-contained inline regex literals anchored to a path segment boundary.
|
|
runtimeCaching: [
|
|
{ urlPattern: ({ url }) => /^\/(collab|socket\.io)(\/|$)/.test(url.pathname), handler: "NetworkOnly" },
|
|
// All /api stays network-only; offline reads come from the persisted
|
|
// React Query cache (IndexedDB) + y-indexeddb, not the SW HTTP cache.
|
|
{ urlPattern: ({ url }) => /^\/api(\/|$)/.test(url.pathname), handler: "NetworkOnly" },
|
|
],
|
|
},
|
|
devOptions: { enabled: false },
|
|
}),
|
|
],
|
|
build: {
|
|
rolldownOptions: {
|
|
output: {
|
|
advancedChunks: {
|
|
groups: [
|
|
{
|
|
name: "vendor-mantine",
|
|
test: /[\\/]node_modules[\\/]@mantine[\\/]/,
|
|
},
|
|
],
|
|
},
|
|
},
|
|
},
|
|
},
|
|
resolve: {
|
|
alias: {
|
|
"@": "/src",
|
|
},
|
|
},
|
|
server: {
|
|
proxy: {
|
|
"/api": {
|
|
target: APP_URL,
|
|
changeOrigin: false,
|
|
},
|
|
"/socket.io": {
|
|
target: APP_URL,
|
|
ws: true,
|
|
rewriteWsOrigin: true,
|
|
},
|
|
"/collab": {
|
|
target: APP_URL,
|
|
ws: true,
|
|
rewriteWsOrigin: true,
|
|
},
|
|
},
|
|
},
|
|
};
|
|
});
|