package server import ( "net/http" "os" "path/filepath" "strings" ) // handleStatic serves static files from Odoo addon directories. // URL pattern: //static/ // Maps to: //static/ func (s *Server) handleStatic(w http.ResponseWriter, r *http.Request) { if r.Method != http.MethodGet && r.Method != http.MethodHead { http.Error(w, "Method not allowed", http.StatusMethodNotAllowed) return } path := strings.TrimPrefix(r.URL.Path, "/") parts := strings.SplitN(path, "/", 3) if len(parts) < 3 || parts[1] != "static" { http.NotFound(w, r) return } addonName := parts[0] filePath := parts[2] // Security: prevent directory traversal if strings.Contains(filePath, "..") { http.NotFound(w, r) return } // For JS/CSS files: check build dir first (transpiled/compiled files) if s.config.BuildDir != "" && (strings.HasSuffix(filePath, ".js") || strings.HasSuffix(filePath, ".css")) { buildPath := filepath.Join(s.config.BuildDir, addonName, "static", filePath) if _, err := os.Stat(buildPath); err == nil { w.Header().Set("Cache-Control", "public, max-age=3600") http.ServeFile(w, r, buildPath) return } } // Search through addon paths (original files) for _, addonsDir := range s.config.OdooAddonsPath { fullPath := filepath.Join(addonsDir, addonName, "static", filePath) if _, err := os.Stat(fullPath); err == nil { w.Header().Set("Cache-Control", "public, max-age=3600") // Serve SCSS as compiled CSS if available if strings.HasSuffix(fullPath, ".scss") && s.config.BuildDir != "" { buildCSS := filepath.Join(s.config.BuildDir, addonName, "static", strings.TrimSuffix(filePath, ".scss")+".css") if _, err := os.Stat(buildCSS); err == nil { fullPath = buildCSS } } http.ServeFile(w, r, fullPath) return } } http.NotFound(w, r) }