vite.config.ts 6.0 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170
  1. import { defineConfig } from 'vite'
  2. import react from '@vitejs/plugin-react'
  3. import { VitePWA } from 'vite-plugin-pwa'
  4. import path from 'path'
  5. // https://vite.dev/config/
  6. export default defineConfig({
  7. plugins: [
  8. react(),
  9. VitePWA({
  10. registerType: 'autoUpdate',
  11. includeAssets: ['favicon.ico', 'apple-touch-icon.png', 'android-chrome-192x192.png', 'android-chrome-512x512.png'],
  12. manifest: false, // We use our own manifest at /static/site.webmanifest
  13. workbox: {
  14. // Cache static assets
  15. globPatterns: ['**/*.{js,css,html,ico,png,svg,woff,woff2}'],
  16. // Runtime caching rules
  17. runtimeCaching: [
  18. {
  19. // Cache pattern preview images
  20. urlPattern: /\/static\/.*\.webp$/,
  21. handler: 'CacheFirst',
  22. options: {
  23. cacheName: 'pattern-previews',
  24. expiration: {
  25. maxEntries: 500,
  26. maxAgeSeconds: 60 * 60 * 24 * 30, // 30 days
  27. },
  28. },
  29. },
  30. {
  31. // Cache static assets from backend
  32. urlPattern: /\/static\/.*\.(png|jpg|ico|svg)$/,
  33. handler: 'CacheFirst',
  34. options: {
  35. cacheName: 'static-assets',
  36. expiration: {
  37. maxEntries: 100,
  38. maxAgeSeconds: 60 * 60 * 24 * 7, // 7 days
  39. },
  40. },
  41. },
  42. {
  43. // Network-first for API calls (always want fresh data, but cache as fallback)
  44. urlPattern: /\/api\//,
  45. handler: 'NetworkFirst',
  46. options: {
  47. cacheName: 'api-cache',
  48. expiration: {
  49. maxEntries: 50,
  50. maxAgeSeconds: 60 * 5, // 5 minutes
  51. },
  52. networkTimeoutSeconds: 10,
  53. },
  54. },
  55. ],
  56. },
  57. devOptions: {
  58. enabled: false, // Disable in dev mode to avoid caching issues
  59. },
  60. }),
  61. ],
  62. resolve: {
  63. alias: {
  64. '@': path.resolve(__dirname, './src'),
  65. },
  66. },
  67. server: {
  68. port: parseInt(process.env.PORT || '5173'),
  69. host: true, // Listen on all interfaces (0.0.0.0) for mobile/network access
  70. allowedHosts: true, // Allow all hosts for local network development
  71. proxy: {
  72. // WebSocket endpoints
  73. '/ws': {
  74. target: 'ws://localhost:8080',
  75. ws: true,
  76. // Suppress connection errors (common during backend restarts)
  77. configure: (proxy, _options) => {
  78. // Handle proxy errors silently for expected connection issues
  79. const isConnectionError = (err: Error & { code?: string }) => {
  80. const msg = err.message || ''
  81. const code = err.code || ''
  82. // Check error code (most reliable for AggregateError)
  83. if (['ECONNRESET', 'ECONNREFUSED', 'EPIPE', 'ETIMEDOUT'].includes(code)) {
  84. return true
  85. }
  86. // Check message as fallback
  87. if (msg.includes('ECONNRESET') || msg.includes('ECONNREFUSED') ||
  88. msg.includes('EPIPE') || msg.includes('ETIMEDOUT') ||
  89. msg.includes('AggregateError')) {
  90. return true
  91. }
  92. return false
  93. }
  94. const handleError = (err: Error) => {
  95. if (isConnectionError(err)) {
  96. return // Silently ignore connection errors
  97. }
  98. // Only log unexpected errors
  99. console.error('WebSocket proxy error:', err.message)
  100. }
  101. proxy.on('error', handleError)
  102. proxy.on('proxyReqWs', (_proxyReq, _req, socket) => {
  103. socket.on('error', (err) => {
  104. if (!isConnectionError(err)) {
  105. console.error('WebSocket socket error:', err.message)
  106. }
  107. })
  108. })
  109. },
  110. },
  111. // All /api endpoints
  112. '/api': 'http://localhost:8080',
  113. // Static assets
  114. '/static': 'http://localhost:8080',
  115. // Preview images
  116. '/preview': 'http://localhost:8080',
  117. // Legacy root-level API endpoints (for backwards compatibility)
  118. // Pattern execution
  119. '/send_home': 'http://localhost:8080',
  120. '/send_coordinate': 'http://localhost:8080',
  121. '/stop_execution': 'http://localhost:8080',
  122. '/force_stop': 'http://localhost:8080',
  123. '/soft_reset': 'http://localhost:8080',
  124. '/controller_restart': 'http://localhost:8080',
  125. '/pause_execution': 'http://localhost:8080',
  126. '/resume_execution': 'http://localhost:8080',
  127. '/skip_pattern': 'http://localhost:8080',
  128. '/reorder_playlist': 'http://localhost:8080',
  129. '/add_to_queue': 'http://localhost:8080',
  130. '/run_theta_rho': 'http://localhost:8080',
  131. '/run_playlist': 'http://localhost:8080',
  132. // Movement
  133. '/move_to_center': 'http://localhost:8080',
  134. '/move_to_perimeter': 'http://localhost:8080',
  135. // Speed
  136. '/set_speed': 'http://localhost:8080',
  137. '/get_speed': 'http://localhost:8080',
  138. // Connection
  139. '/serial_status': 'http://localhost:8080',
  140. '/list_serial_ports': 'http://localhost:8080',
  141. '/connect': 'http://localhost:8080',
  142. '/disconnect': 'http://localhost:8080',
  143. // Patterns
  144. '/list_theta_rho_files': 'http://localhost:8080',
  145. '/list_theta_rho_files_with_metadata': 'http://localhost:8080',
  146. '/preview_thr': 'http://localhost:8080',
  147. '/preview_thr_batch': 'http://localhost:8080',
  148. '/get_theta_rho_coordinates': 'http://localhost:8080',
  149. '/delete_theta_rho_file': 'http://localhost:8080',
  150. // Playlists
  151. '/list_all_playlists': 'http://localhost:8080',
  152. '/get_playlist': 'http://localhost:8080',
  153. '/create_playlist': 'http://localhost:8080',
  154. '/modify_playlist': 'http://localhost:8080',
  155. '/delete_playlist': 'http://localhost:8080',
  156. '/rename_playlist': 'http://localhost:8080',
  157. '/add_to_playlist': 'http://localhost:8080',
  158. // LED
  159. '/get_led_config': 'http://localhost:8080',
  160. '/set_led_config': 'http://localhost:8080',
  161. },
  162. },
  163. build: {
  164. outDir: '../static/dist',
  165. emptyOutDir: true,
  166. },
  167. })