package static import ( "fmt" "io/fs" "net/http" "path" "strings" ) // CompressionFileServer serves static files with compression support func CompressionFileServer(fsys fs.FS) http.Handler { return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { // Check if this is a UUID path (starts with / followed by 36 characters) isUUIDPath := len(r.URL.Path) == 37 && r.URL.Path[0] == '/' && !strings.Contains(r.URL.Path[1:], "/") if r.URL.Path == "/" || isUUIDPath { // Serve index.html at root or for routes with /{uuid} content, err := fs.ReadFile(fsys, "frontend/dist/index.html") if err != nil { http.Error(w, "Not found", 404) return } w.Header().Set("Content-Type", "text/html") w.Write(content) return } // Get the requested file path relative to frontend/dist filePath := strings.TrimPrefix(r.URL.Path, "/") if filePath == "" { filePath = "index.html" } fullPath := "frontend/dist/" + filePath // Check if client accepts compression acceptEncoding := r.Header.Get("Accept-Encoding") acceptsGzip := strings.Contains(acceptEncoding, "gzip") acceptsBrotli := strings.Contains(acceptEncoding, "br") // Try to serve compressed version if client supports it if acceptsBrotli { if compressedContent, err := fs.ReadFile(fsys, fullPath+".br"); err == nil { w.Header().Set("Content-Encoding", "br") w.Header().Set("Content-Length", fmt.Sprintf("%d", len(compressedContent))) w.Header().Set("Vary", "Accept-Encoding") // Set appropriate content type based on file extension setContentType(w, filePath) w.Write(compressedContent) return } } if acceptsGzip { if compressedContent, err := fs.ReadFile(fsys, fullPath+".gz"); err == nil { w.Header().Set("Content-Encoding", "gzip") w.Header().Set("Content-Length", fmt.Sprintf("%d", len(compressedContent))) w.Header().Set("Vary", "Accept-Encoding") // Set appropriate content type based on file extension setContentType(w, filePath) w.Write(compressedContent) return } } // Fall back to uncompressed file if content, err := fs.ReadFile(fsys, fullPath); err == nil { w.Header().Set("Vary", "Accept-Encoding") // Set appropriate content type based on file extension setContentType(w, filePath) w.Write(content) return } // File not found http.NotFound(w, r) }) } // setContentType sets the appropriate Content-Type header based on file extension func setContentType(w http.ResponseWriter, filePath string) { ext := strings.ToLower(path.Ext(filePath)) switch ext { case ".html": w.Header().Set("Content-Type", "text/html; charset=utf-8") case ".css": w.Header().Set("Content-Type", "text/css; charset=utf-8") case ".js": w.Header().Set("Content-Type", "application/javascript; charset=utf-8") case ".json": w.Header().Set("Content-Type", "application/json; charset=utf-8") case ".svg": w.Header().Set("Content-Type", "image/svg+xml") case ".png": w.Header().Set("Content-Type", "image/png") case ".jpg", ".jpeg": w.Header().Set("Content-Type", "image/jpeg") case ".gif": w.Header().Set("Content-Type", "image/gif") case ".ico": w.Header().Set("Content-Type", "image/x-icon") case ".woff": w.Header().Set("Content-Type", "font/woff") case ".woff2": w.Header().Set("Content-Type", "font/woff2") case ".ttf": w.Header().Set("Content-Type", "font/ttf") case ".eot": w.Header().Set("Content-Type", "application/vnd.ms-fontobject") default: // Default to octet-stream for unknown types w.Header().Set("Content-Type", "application/octet-stream") } }