mirror of
https://github.com/mauriceboe/TREK.git
synced 2026-06-19 13:21:46 +00:00
0b218d53b2
Co-hosted NestJS app behind the existing Express server via a strangler-fig dispatcher, sharing the same better-sqlite3 connection and JWT httpOnly cookie. Additive and dormant: default routing stays on Express, Nest only serves its own /api/_nest diagnostics until a module opts in. F1 @trek/shared Zod contract package; F2 Nest bootstrap co-hosted (fall-through, single Dockerfile/port); F3 shared better-sqlite3 provider; F4 JWT cookie auth guard (+ @CurrentUser, admin guard); F5 Zod validation pipe + error-envelope parity; F6 Nest test + coverage gates; F7 per-prefix strangler toggle (env, default Express); F8 CI build/typecheck/test/coverage. Remaining F4/F6/F8 checklist items (trip-access + permission levels + MFA policy, e2e harness/seed + 80% gate, Nest↔Express parity test, Playwright PR-comment workflow) are tracked on the first consuming module cards (L1/A1/C1).
150 lines
5.1 KiB
JavaScript
150 lines
5.1 KiB
JavaScript
import { defineConfig } from 'vite'
|
|
import react from '@vitejs/plugin-react'
|
|
import { VitePWA } from 'vite-plugin-pwa'
|
|
import { fileURLToPath } from 'node:url'
|
|
|
|
export default defineConfig({
|
|
plugins: [
|
|
react(),
|
|
VitePWA({
|
|
registerType: 'autoUpdate',
|
|
workbox: {
|
|
maximumFileSizeToCacheInBytes: 10 * 1024 * 1024,
|
|
globPatterns: ['**/*.{js,css,html,svg,png,woff,woff2,ttf}'],
|
|
navigateFallback: 'index.html',
|
|
navigateFallbackDenylist: [/^\/api/, /^\/uploads/, /^\/mcp/, /^\/oauth\//, /^\/.well-known\//],
|
|
runtimeCaching: [
|
|
{
|
|
// Carto map tiles (default provider)
|
|
urlPattern: /^https:\/\/[a-d]\.basemaps\.cartocdn\.com\/.*/i,
|
|
handler: 'CacheFirst',
|
|
options: {
|
|
cacheName: 'map-tiles',
|
|
expiration: { maxEntries: 1000, maxAgeSeconds: 30 * 24 * 60 * 60 },
|
|
cacheableResponse: { statuses: [0, 200] },
|
|
},
|
|
},
|
|
{
|
|
// OpenStreetMap tiles (fallback / alternative)
|
|
urlPattern: /^https:\/\/[a-c]\.tile\.openstreetmap\.org\/.*/i,
|
|
handler: 'CacheFirst',
|
|
options: {
|
|
cacheName: 'map-tiles',
|
|
expiration: { maxEntries: 1000, maxAgeSeconds: 30 * 24 * 60 * 60 },
|
|
cacheableResponse: { statuses: [0, 200] },
|
|
},
|
|
},
|
|
{
|
|
// Leaflet CSS/JS from unpkg CDN
|
|
urlPattern: /^https:\/\/unpkg\.com\/.*/i,
|
|
handler: 'CacheFirst',
|
|
options: {
|
|
cacheName: 'cdn-libs',
|
|
expiration: { maxEntries: 30, maxAgeSeconds: 365 * 24 * 60 * 60 },
|
|
cacheableResponse: { statuses: [0, 200] },
|
|
},
|
|
},
|
|
{
|
|
// API calls — prefer network, fall back to cache
|
|
// Exclude sensitive endpoints (auth, admin, backup, settings)
|
|
urlPattern: /\/api\/(?!auth|admin|backup|settings|health).*/i,
|
|
handler: 'NetworkFirst',
|
|
options: {
|
|
cacheName: 'api-data',
|
|
expiration: { maxEntries: 200, maxAgeSeconds: 24 * 60 * 60 },
|
|
networkTimeoutSeconds: 5,
|
|
cacheableResponse: { statuses: [200] },
|
|
},
|
|
},
|
|
{
|
|
// Uploaded files (photos, covers — public assets only)
|
|
urlPattern: /\/uploads\/(?:covers|avatars)\/.*/i,
|
|
handler: 'CacheFirst',
|
|
options: {
|
|
cacheName: 'user-uploads',
|
|
expiration: { maxEntries: 300, maxAgeSeconds: 7 * 24 * 60 * 60 },
|
|
cacheableResponse: { statuses: [200] },
|
|
},
|
|
},
|
|
],
|
|
},
|
|
manifest: {
|
|
name: 'TREK \u2014 Travel Planner',
|
|
short_name: 'TREK',
|
|
description: 'Travel Resource & Exploration Kit',
|
|
theme_color: '#111827',
|
|
background_color: '#0f172a',
|
|
display: 'standalone',
|
|
scope: '/',
|
|
start_url: '/',
|
|
orientation: 'any',
|
|
categories: ['travel', 'navigation'],
|
|
icons: [
|
|
{ src: 'icons/apple-touch-icon-180x180.png', sizes: '180x180', type: 'image/png' },
|
|
{ src: 'icons/icon-192x192.png', sizes: '192x192', type: 'image/png' },
|
|
{ src: 'icons/icon-512x512.png', sizes: '512x512', type: 'image/png' },
|
|
{ src: 'icons/icon-512x512.png', sizes: '512x512', type: 'image/png', purpose: 'maskable' },
|
|
{ src: 'icons/icon.svg', sizes: 'any', type: 'image/svg+xml' },
|
|
],
|
|
},
|
|
}),
|
|
],
|
|
resolve: {
|
|
alias: {
|
|
// @trek/shared — Zod contract package (dev: resolved to TS source).
|
|
'@trek/shared': fileURLToPath(new URL('../shared/src/index.ts', import.meta.url)),
|
|
},
|
|
// @trek/shared imports zod from its own source; it lives outside this root,
|
|
// so pin zod to the client's copy (one instance, resolvable from anywhere).
|
|
dedupe: ['zod'],
|
|
},
|
|
build: {
|
|
sourcemap: false,
|
|
modulePreload: { polyfill: true },
|
|
},
|
|
server: {
|
|
port: 5173,
|
|
proxy: {
|
|
'/api': {
|
|
target: 'http://localhost:3001',
|
|
changeOrigin: true,
|
|
},
|
|
'/uploads': {
|
|
target: 'http://localhost:3001',
|
|
changeOrigin: true,
|
|
},
|
|
'/ws': {
|
|
target: 'http://localhost:3001',
|
|
ws: true,
|
|
},
|
|
'/mcp': {
|
|
target: 'http://localhost:3001',
|
|
changeOrigin: true,
|
|
},
|
|
// OAuth 2.1 endpoints handled by backend (SDK authorize handler + token/revoke)
|
|
// /oauth/authorize goes to backend so the SDK can redirect to /oauth/consent
|
|
// /oauth/consent is served by Vite as a SPA route (no proxy entry needed)
|
|
'/oauth/authorize': {
|
|
target: 'http://localhost:3001',
|
|
changeOrigin: true,
|
|
},
|
|
'/oauth/token': {
|
|
target: 'http://localhost:3001',
|
|
changeOrigin: true,
|
|
},
|
|
'/oauth/register': {
|
|
target: 'http://localhost:3001',
|
|
changeOrigin: true,
|
|
},
|
|
'/oauth/revoke': {
|
|
target: 'http://localhost:3001',
|
|
changeOrigin: true,
|
|
},
|
|
'/.well-known': {
|
|
target: 'http://localhost:3001',
|
|
changeOrigin: true,
|
|
},
|
|
}
|
|
}
|
|
})
|