package main import ( "embed" "flag" "fmt" "io" "io/fs" "log" "net/http" "net/http/httputil" "net/url" "os" "strings" ) //go:embed all:static var static embed.FS func main() { port := flag.Int("port", 8080, "puerto del servidor") flag.Parse() staticFS, err := fs.Sub(static, "static") if err != nil { log.Fatal(err) } // Proxy a EasyAppointments API // El frontend llama a /ea-api/v1/... y aquí se reescribe y se añade auth eaTarget := "http://easyappointments:80" if t := os.Getenv("EA_TARGET"); t != "" { eaTarget = t } eaURL, err := url.Parse(eaTarget) if err != nil { log.Fatal(err) } eaProxy := httputil.NewSingleHostReverseProxy(eaURL) // Evita que el navegador muestre el popup de credenciales. // Nunca exponemos WWW-Authenticate ni mensajes de auth del backend. eaProxy.ModifyResponse = func(resp *http.Response) error { resp.Header.Del("Www-Authenticate") // Evitamos que cookies de sesión de EA lleguen al navegador del usuario público resp.Header.Del("Set-Cookie") // Limpiamos cabeceras que revelan el backend resp.Header.Del("Server") resp.Header.Del("X-Powered-By") if resp.StatusCode == http.StatusUnauthorized || resp.StatusCode == http.StatusForbidden { resp.StatusCode = http.StatusBadGateway resp.Status = http.StatusText(http.StatusBadGateway) // Reemplazamos el body para no filtrar detalles del backend resp.Body.Close() resp.Body = io.NopCloser(strings.NewReader(`{"error": "service_unavailable"}`)) resp.ContentLength = -1 resp.Header.Set("Content-Type", "application/json") resp.Header.Del("Content-Length") } return nil } http.HandleFunc("/ea-api/", func(w http.ResponseWriter, r *http.Request) { // /ea-api/v1/services → /index.php/api/v1/services r.URL.Path = strings.Replace(r.URL.Path, "/ea-api", "/index.php/api", 1) // Auth (server-side, nunca se expone al navegador) if apiKey := os.Getenv("EA_API_KEY"); apiKey != "" { r.Header.Set("Authorization", "Bearer " + apiKey) } else if user := os.Getenv("EA_API_USERNAME"); user != "" { if pass := os.Getenv("EA_API_PASSWORD"); pass != "" { r.SetBasicAuth(user, pass) } } eaProxy.ServeHTTP(w, r) }) http.Handle("/", http.FileServer(http.FS(staticFS))) addr := fmt.Sprintf(":%d", *port) log.Printf("Servidor iniciado en http://localhost%s", addr) log.Fatal(http.ListenAndServe(addr, nil)) }