Some checks failed
		
		
	
	Build docker container / Build image (push) Failing after 2m5s
				
			
		
			
				
	
	
		
			154 lines
		
	
	
		
			3.4 KiB
		
	
	
	
		
			Go
		
	
	
	
	
	
			
		
		
	
	
			154 lines
		
	
	
		
			3.4 KiB
		
	
	
	
		
			Go
		
	
	
	
	
	
| package main
 | |
| 
 | |
| import (
 | |
| 	"context"
 | |
| 	"embed"
 | |
| 	"encoding/json"
 | |
| 	"fmt"
 | |
| 	"html/template"
 | |
| 	"log"
 | |
| 	"net"
 | |
| 	"net/http"
 | |
| 	"path"
 | |
| 	"os"
 | |
| )
 | |
| 
 | |
| type AddressType string
 | |
| 
 | |
| const (
 | |
| 	IPv4 AddressType = "IPv4"
 | |
| 	IPv6 AddressType = "IPv6"
 | |
| )
 | |
| 
 | |
| type TplData struct {
 | |
| 	NetAddress   NetAddress `json:"net_address"`
 | |
| 	IPv4Endpoint string     `json:"ipv4_endpoint"`
 | |
| 	IPv6Endpoint string     `json:"ipv6_endpoint"`
 | |
| }
 | |
| 
 | |
| type NetAddress struct {
 | |
| 	Address string      `json:"address"`
 | |
| 	Type    AddressType `json:"type"`
 | |
| }
 | |
| 
 | |
| type contextKey string
 | |
| 
 | |
| const netAddrKey = contextKey("netAddr")
 | |
| 
 | |
| //go:embed templates/*
 | |
| var templatesFS embed.FS
 | |
| 
 | |
| func RealIpMiddleware(next http.Handler) http.Handler {
 | |
| 	return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
 | |
| 		netAddr := extractNetAddress(r)
 | |
| 		if netAddr.Address == "" {
 | |
| 			http.Error(w, "Internal Server Error", http.StatusInternalServerError)
 | |
| 			return
 | |
| 		}
 | |
| 		ctx := context.WithValue(r.Context(), netAddrKey, netAddr)
 | |
| 		next.ServeHTTP(w, r.WithContext(ctx))
 | |
| 	})
 | |
| }
 | |
| 
 | |
| func extractNetAddress(r *http.Request) NetAddress {
 | |
| 	realIpHeader := r.Header.Get("X-Real-Ip")
 | |
| 	if realIpHeader != "" {
 | |
| 		if ip := net.ParseIP(realIpHeader); ip != nil {
 | |
| 			return NetAddress{
 | |
| 				Address: realIpHeader,
 | |
| 				Type:    determineIPType(ip),
 | |
| 			}
 | |
| 		}
 | |
| 		log.Printf("Invalid X-Real-Ip header: %s", realIpHeader)
 | |
| 	}
 | |
| 
 | |
| 	host, _, err := net.SplitHostPort(r.RemoteAddr)
 | |
| 	if err != nil {
 | |
| 		log.Printf("Failed to split host and port: %v", err)
 | |
| 		return NetAddress{}
 | |
| 	}
 | |
| 
 | |
| 	if ip := net.ParseIP(host); ip != nil {
 | |
| 		return NetAddress{
 | |
| 			Address: host,
 | |
| 			Type:    determineIPType(ip),
 | |
| 		}
 | |
| 	}
 | |
| 	return NetAddress{}
 | |
| }
 | |
| 
 | |
| func determineIPType(ip net.IP) AddressType {
 | |
| 	if ip.To4() != nil {
 | |
| 		return IPv4
 | |
| 	}
 | |
| 	return IPv6
 | |
| }
 | |
| 
 | |
| func mainHandler(w http.ResponseWriter, r *http.Request) {
 | |
| 	defaultIPv4Endpoint := "https://ip4.mziesel.nl/json"
 | |
| 	defaultIPv6Endpoint := "https://ip6.mziesel.nl/json"
 | |
| 	ipv4Endpoint := os.Getenv("IPV4_ENDPOINT")
 | |
| 	ipv6Endpoint := os.Getenv("IPV6_ENDPOINT")
 | |
| 
 | |
| 	if ipv4Endpoint == "" {
 | |
| 		ipv4Endpoint = defaultIPv4Endpoint
 | |
| 	}
 | |
| 
 | |
| 	if ipv6Endpoint == "" {
 | |
| 		ipv6Endpoint = defaultIPv6Endpoint
 | |
| 	}
 | |
| 
 | |
| 	netAddr, ok := r.Context().Value(netAddrKey).(NetAddress)
 | |
| 	if !ok {
 | |
| 		http.Error(w, "Internal Server Error", http.StatusInternalServerError)
 | |
| 		return
 | |
| 	}
 | |
| 
 | |
| 	switch r.Method {
 | |
| 	case http.MethodGet:
 | |
| 		switch r.URL.Path {
 | |
| 		case "/":
 | |
| 			fp := path.Join("templates", "index.html")
 | |
| 			tmpl, err := template.ParseFS(templatesFS, fp)
 | |
| 			if err != nil {
 | |
| 				http.Error(w, err.Error(), http.StatusInternalServerError)
 | |
| 				return
 | |
| 			}
 | |
| 			tplData := TplData{
 | |
| 				NetAddress: netAddr,
 | |
| 				IPv4Endpoint: ipv4Endpoint,
 | |
| 				IPv6Endpoint: ipv6Endpoint,
 | |
| 			}
 | |
| 			if err := tmpl.Execute(w, tplData); err != nil {
 | |
| 				http.Error(w, err.Error(), http.StatusInternalServerError)
 | |
| 				return
 | |
| 			}
 | |
| 		case "/raw":
 | |
| 			fmt.Fprintf(w, "%s\n", netAddr.Address)
 | |
| 		case "/json":
 | |
| 			responseJSON(w, netAddr)
 | |
| 		default:
 | |
| 			http.NotFound(w, r)
 | |
| 		}
 | |
| 	default:
 | |
| 		http.Error(w, "405 method not allowed", http.StatusMethodNotAllowed)
 | |
| 	}
 | |
| }
 | |
| 
 | |
| func responseJSON(w http.ResponseWriter, data interface{}) {
 | |
| 	w.Header().Set("Content-Type", "application/json")
 | |
| 	w.Header().Set("Access-Control-Allow-Origin", "*")
 | |
| 	err := json.NewEncoder(w).Encode(data)
 | |
| 	if err != nil {
 | |
| 		http.Error(w, "Failed to encode JSON", http.StatusInternalServerError)
 | |
| 	}
 | |
| }
 | |
| 
 | |
| func main() {
 | |
| 	httpHandler := http.HandlerFunc(mainHandler)
 | |
| 	log.Println("Starting server on :8080")
 | |
| 	if err := http.ListenAndServe(":8080", RealIpMiddleware(httpHandler)); err != nil {
 | |
| 		log.Fatalf("Server failed: %v", err)
 | |
| 	}
 | |
| }
 |