diff --git a/bun.lock b/bun.lock index 39695bce..945a1ec2 100644 --- a/bun.lock +++ b/bun.lock @@ -105,6 +105,7 @@ "react-map-gl": "8.0.4", "react-qrcode-logo": "^3.0.0", "rfc4648": "^1.5.4", + "vite": "^7.0.4", "vite-plugin-html": "^3.2.2", "zod": "^4.0.5", "zustand": "5.0.6", @@ -132,7 +133,6 @@ "tar": "^7.4.3", "testing-library": "^0.0.2", "typescript": "^5.8.3", - "vite": "^7.0.4", "vitest": "^3.2.4", }, }, diff --git a/packages/web/index.html b/packages/web/index.html index a3f799e3..1564f641 100644 --- a/packages/web/index.html +++ b/packages/web/index.html @@ -1,6 +1,9 @@ + + <%- cookieYesScript %> + diff --git a/packages/web/package.json b/packages/web/package.json index cc603dcd..5c5bc429 100644 --- a/packages/web/package.json +++ b/packages/web/package.json @@ -79,6 +79,7 @@ "react-qrcode-logo": "^3.0.0", "rfc4648": "^1.5.4", "vite-plugin-html": "^3.2.2", + "vite": "^7.0.4", "zod": "^4.0.5", "zustand": "5.0.6" }, @@ -105,7 +106,6 @@ "tar": "^7.4.3", "testing-library": "^0.0.2", "typescript": "^5.8.3", - "vite": "^7.0.4", "vitest": "^3.2.4" } } diff --git a/packages/web/tsconfig.json b/packages/web/tsconfig.json index 2cf253d2..ca292952 100644 --- a/packages/web/tsconfig.json +++ b/packages/web/tsconfig.json @@ -22,7 +22,7 @@ "@layouts/*": ["./src/layouts/*"] } }, - "include": ["src", "./vite-env.d.ts"], + "include": ["src", ".d.ts"], "exclude": [ "routeTree.gen.ts", "node_modules", diff --git a/packages/web/vercel.json b/packages/web/vercel.json index 6d0ce863..ba67b055 100644 --- a/packages/web/vercel.json +++ b/packages/web/vercel.json @@ -10,13 +10,25 @@ { "source": "/", "headers": [ + { + "key": "Cross-Origin-Opener-Policy", + "value": "same-origin" + }, { "key": "Cross-Origin-Embedder-Policy", - "value": "require-corp" + "value": "credentialless" }, { - "key": "Cross-Origin-Opener-Policy", - "value": "same-origin" + "key": "X-Content-Type-Options", + "value": "nosniff" + }, + { + "key": "Strict-Transport-Security", + "value": "max-age=63072000; includeSubDomains; preload" + }, + { + "key": "Referrer-Policy", + "value": "strict-origin-when-cross-origin" } ] } diff --git a/packages/web/vite-env.d.ts b/packages/web/vite-env.d.ts index 59e405fe..227e23be 100644 --- a/packages/web/vite-env.d.ts +++ b/packages/web/vite-env.d.ts @@ -1,10 +1,12 @@ /// +interface ViteTypeOptions { + strictImportMetaEnv: unknown; +} + interface ImportMetaEnv { - readonly env: { - readonly VITE_COMMIT_HASH: string; - readonly VITE_VERSION: string; - }; + readonly VITE_COMMIT_HASH: string; + readonly VITE_VERSION: string; } interface ImportMeta { diff --git a/packages/web/vite.config.ts b/packages/web/vite.config.ts index 9c3e0c00..90447309 100644 --- a/packages/web/vite.config.ts +++ b/packages/web/vite.config.ts @@ -3,7 +3,7 @@ import path from "node:path"; import process from "node:process"; import tailwindcss from "@tailwindcss/vite"; import react from "@vitejs/plugin-react"; -import { defineConfig } from "vite"; +import { defineConfig, loadEnv } from "vite"; import { createHtmlPlugin } from "vite-plugin-html"; let hash = ""; @@ -24,60 +24,69 @@ try { } const CONTENT_SECURITY_POLICY = - "script-src 'self' 'unsafe-inline' https://cdn.jsdelivr.net; style-src 'self' 'unsafe-inline' data: https://rsms.me https://cdn.jsdelivr.net; img-src 'self' data:; font-src 'self' data: https://rsms.me https://cdn.jsdelivr.net; worker-src 'self' blob:; object-src 'none'; base-uri 'self';"; + "script-src 'self' 'unsafe-inline' https://cdn.jsdelivr.net https://cdn-cookieyes.com; style-src 'self' 'unsafe-inline' data: https://rsms.me https://cdn.jsdelivr.net; img-src 'self' data:; font-src 'self' data: https://rsms.me https://cdn.jsdelivr.net; worker-src 'self' blob:; object-src 'none'; base-uri 'self';"; -export default defineConfig({ - plugins: [ - react(), - tailwindcss(), - // adding CookieYes script if VITE_COOKIEYES_CLIENT_ID is set (it only runs while in Vercel) for GDPR/CCPA compliance - createHtmlPlugin({ - inject: { - data: { - cookieScript: process.env.VITE_COOKIEYES_CLIENT_ID - ? `` - : "", +export default defineConfig(({ mode }) => { + const env = loadEnv(mode, process.cwd()); + + return { + plugins: [ + react(), + tailwindcss(), + // This is for GDPR/CCPA compliance + createHtmlPlugin({ + inject: { + data: { + cookieYesScript: + mode === "production" && env.VITE_COOKIEYES_CLIENT_ID + ? `` + : "", + }, }, + }), + // VitePWA({ + // registerType: "autoUpdate", + // strategies: "generateSW", + // devOptions: { + // enabled: true, + // }, + // workbox: { + // cleanupOutdatedCaches: true, + // sourcemap: true, + // }, + // }), + ], + optimizeDeps: { + include: ["react/jsx-runtime"], + }, + define: { + "import.meta.env.VITE_COMMIT_HASH": JSON.stringify(hash), + "import.meta.env.VITE_VERSION": JSON.stringify(version), + }, + build: { + emptyOutDir: true, + assetsDir: "./", + }, + resolve: { + alias: { + "@app": path.resolve(process.cwd(), "./src"), + "@pages": path.resolve(process.cwd(), "./src/pages"), + "@components": path.resolve(process.cwd(), "./src/components"), + "@core": path.resolve(process.cwd(), "./src/core"), + "@layouts": path.resolve(process.cwd(), "./src/layouts"), }, - }), - // VitePWA({ - // registerType: "autoUpdate", - // strategies: "generateSW", - // devOptions: { - // enabled: true, - // }, - // workbox: { - // cleanupOutdatedCaches: true, - // sourcemap: true, - // }, - // }), - ], - optimizeDeps: { - include: ["react/jsx-runtime"], - }, - define: { - "import.meta.env.VITE_COMMIT_HASH": JSON.stringify(hash), - "import.meta.env.VITE_VERSION": JSON.stringify(version), - }, - build: { - emptyOutDir: true, - assetsDir: "./", - }, - resolve: { - alias: { - "@app": path.resolve(process.cwd(), "./src"), - "@pages": path.resolve(process.cwd(), "./src/pages"), - "@components": path.resolve(process.cwd(), "./src/components"), - "@core": path.resolve(process.cwd(), "./src/core"), - "@layouts": path.resolve(process.cwd(), "./src/layouts"), }, - }, - server: { - port: 3000, - headers: { - "content-security-policy": CONTENT_SECURITY_POLICY, - "Cross-Origin-Opener-Policy": "same-origin", - "Cross-Origin-Embedder-Policy": "require-corp", + server: { + port: 3000, + headers: { + "content-security-policy": CONTENT_SECURITY_POLICY, + "Cross-Origin-Opener-Policy": "same-origin", + "Cross-Origin-Embedder-Policy": "credentialless", + "X-Content-Type-Options": "nosniff", + "Strict-Transport-Security": + "max-age=63072000; includeSubDomains; preload", + "Referrer-Policy": "strict-origin-when-cross-origin", + }, }, - }, + }; });