@@ -44,31 +44,68 @@ func (s *SPAFileServer) ServeHTTP(w http.ResponseWriter, r *http.Request) {
4444 return
4545 }
4646
47- // Check if the requested file exists
47+ // Try to serve the file directly first
4848 if f , err := s .fileSystem .Open (path ); err == nil {
4949 if err = f .Close (); err != nil {
50- http .Error (w , "Internal server error " , http .StatusInternalServerError )
50+ http .Error (w , "Internal Server Error " , http .StatusInternalServerError )
5151 return
5252 }
53- // File exists, serve it directly
5453 s .fileServer .ServeHTTP (w , r )
55- } else if os .IsNotExist (err ) {
56- // For SPA routing: only serve index.html for non-asset requests
57- // If the path contains /assets/, /api/, or has a file extension, it's likely a static asset
58- if strings .Contains (path , "/assets/" ) || strings .Contains (path , "/api/" ) ||
59- (strings .Contains (path , "." ) && ! strings .HasSuffix (path , "/" )) {
60- // This is a static asset or API request that doesn't exist, return 404
61- http .NotFound (w , r )
54+ return
55+ } else if ! os .IsNotExist (err ) {
56+ http .Error (w , "Internal Server Error" , http .StatusInternalServerError )
57+ return
58+ }
59+
60+ // File not found - check if it's a static asset
61+ if isStaticAsset (path ) {
62+ // Try to serve the asset by stripping any /clusters/{id} prefix
63+ cleanPath := stripClustersPrefix (path )
64+ if f , err := s .fileSystem .Open (cleanPath ); err == nil {
65+ if err = f .Close (); err != nil {
66+ http .Error (w , "Internal Server Error" , http .StatusInternalServerError )
67+ return
68+ }
69+ r .URL .Path = cleanPath
70+ s .fileServer .ServeHTTP (w , r )
6271 return
6372 }
64-
65- // This is likely a SPA route, serve index.html
66- r .URL .Path = "/"
67- s .fileServer .ServeHTTP (w , r )
73+ // Asset not found even with clean path, return 404
74+ http .NotFound (w , r )
6875 return
69- } else {
70- http .Error (w , "Internal server error" , http .StatusInternalServerError )
7176 }
77+
78+ // For navigation routes, serve index.html
79+ r .URL .Path = "/"
80+ s .fileServer .ServeHTTP (w , r )
81+ }
82+
83+ // isStaticAsset checks if the path is for a static asset
84+ func isStaticAsset (path string ) bool {
85+ return strings .Contains (path , "/assets/" ) ||
86+ strings .HasSuffix (path , ".js" ) ||
87+ strings .HasSuffix (path , ".css" ) ||
88+ strings .HasSuffix (path , ".png" ) ||
89+ strings .HasSuffix (path , ".jpg" ) ||
90+ strings .HasSuffix (path , ".jpeg" ) ||
91+ strings .HasSuffix (path , ".gif" ) ||
92+ strings .HasSuffix (path , ".svg" ) ||
93+ strings .HasSuffix (path , ".ico" ) ||
94+ strings .HasSuffix (path , ".woff" ) ||
95+ strings .HasSuffix (path , ".woff2" ) ||
96+ strings .HasSuffix (path , ".ttf" ) ||
97+ strings .HasSuffix (path , ".eot" )
98+ }
99+
100+ // stripClustersPrefix removes /clusters/{id} prefix from paths
101+ func stripClustersPrefix (path string ) string {
102+ // Match pattern /clusters/{clusterId}/... and extract the part after the clusterId
103+ parts := strings .Split (path , "/" )
104+ if len (parts ) >= 4 && parts [1 ] == "clusters" {
105+ // Path like /clusters/1blitmxjy8lczleb/assets/file.js -> /assets/file.js
106+ return "/" + strings .Join (parts [3 :], "/" )
107+ }
108+ return path
72109}
73110
74111// SPAReverseProxyServer is used for local development or in theory it could be used.
0 commit comments