From c303b61eb25bad9cb0f4c280482c564e2adb595f Mon Sep 17 00:00:00 2001 From: Karl Breuer Date: Mon, 24 Mar 2025 14:48:01 +0100 Subject: [PATCH] initial --- .gitignore | 2 +- pkg/palanticshelper/server.go | 132 ++++++++++++++++++++++++++++++++++ 2 files changed, 133 insertions(+), 1 deletion(-) create mode 100644 pkg/palanticshelper/server.go diff --git a/.gitignore b/.gitignore index 1d6812e..6e2ee72 100644 --- a/.gitignore +++ b/.gitignore @@ -1,3 +1,3 @@ .env deploy.sh -palanticshelper \ No newline at end of file +/palanticshelper \ No newline at end of file diff --git a/pkg/palanticshelper/server.go b/pkg/palanticshelper/server.go new file mode 100644 index 0000000..d9eba63 --- /dev/null +++ b/pkg/palanticshelper/server.go @@ -0,0 +1,132 @@ +package palanticshelper + +import ( + "encoding/json" + "fmt" + "log" + "net" + "net/http" + "strconv" + "strings" + + "gitea.karlbreuer.com/karl/palanticshelper/pkg/settings" + "github.com/go-chi/chi" + "github.com/go-chi/chi/middleware" + "github.com/go-chi/cors" +) + +func StartServer() { + r := chi.NewRouter() + r.Use(middleware.RealIP) + + allowedOrigins := []string{"*." + settings.Settings.Domain} + if settings.Settings.Debug { + allowedOrigins = []string{"*"} + } + + r.Use(cors.Handler(cors.Options{ + AllowedOrigins: allowedOrigins, + AllowedHeaders: []string{"Accept", "Authorization", "Content-Type", "X-CSRF-Token", "Id"}, + AllowedMethods: []string{"GET", "POST", "PUT", "DELETE", "OPTIONS", "Settings"}, + ExposedHeaders: []string{"Link"}, + AllowCredentials: false, + MaxAge: 300, // Maximum value not ignored by any of major browsers + })) + + r.Get("/healthz", health) + r.Post("/*", forwardToTracker) + + portNum, err := strconv.Atoi(strings.TrimSpace(settings.Settings.Port)) + if err != nil { + log.Fatalf("Invalid port number: %v", err) + } + srv := &http.Server{ + Handler: r, + Addr: fmt.Sprintf(":%d", portNum), + } + + log.Printf("runs on: %s", srv.Addr) + err = srv.ListenAndServe() + if err != nil { + fmt.Println(err) + } +} + +func forwardToTracker(w http.ResponseWriter, r *http.Request) { + if err := r.ParseForm(); err != nil { + log.Printf("Error parsing form: %v", err) + RespondWithJSON(w, http.StatusBadRequest, struct{}{}) + return + } + + forwardURL := "https://" + settings.Settings.TrackingServerURL + "/spur" + + req, err := http.NewRequest(r.Method, forwardURL, nil) + if err != nil { + log.Printf("Error creating request: %v", err) + RespondWithJSON(w, http.StatusInternalServerError, struct{}{}) + return + } + + q := r.URL.Query() + + realIp := getRealIP(r) + q.Add("ip", realIp) + + req.URL.RawQuery = q.Encode() + + for name, values := range r.Header { + for _, value := range values { + req.Header.Add(name, value) + } + } + + client := &http.Client{} + resp, err := client.Do(req) + if err != nil { + log.Printf("Error forwarding request: %v", err) + RespondWithJSON(w, http.StatusInternalServerError, struct{}{}) + return + } + defer resp.Body.Close() + + RespondWithJSON(w, http.StatusOK, struct{}{}) +} + +func health(w http.ResponseWriter, r *http.Request) { + RespondWithJSON(w, 200, struct{}{}) +} + +func RespondWithJSON(w http.ResponseWriter, statuscode int, payload interface{}) { + var dat []byte + w.Header().Add("Content-Type", "application/json") + dat, _ = json.Marshal(payload) + w.WriteHeader(statuscode) + w.Write(dat) +} + +func getRealIP(r *http.Request) string { + // NGINX + ip := r.Header.Get("X-Real-IP") + if ip != "" { + return ip + } + // TRAEFIK + forwardedFor := r.Header.Get("X-Forwarded-For") + if forwardedFor != "" { + // X-Forwarded-For might contain multiple IPs - the leftmost is the original client + ips := strings.Split(forwardedFor, ",") + if len(ips) > 0 { + return strings.TrimSpace(ips[0]) + } + } + + log.Println("ERROR getting forwared IP!") + ip, _, err := net.SplitHostPort(r.RemoteAddr) + if err != nil { + + ip = r.RemoteAddr + } + + return ip +}