ip-svc/main.go
Mans Ziesel ef915583aa
All checks were successful
Build docker container / Build image (push) Successful in 28s
change html template
2024-06-03 14:15:07 +02:00

129 lines
2.8 KiB
Go

package main
import (
"context"
"embed"
"encoding/json"
"fmt"
"html/template"
"log"
"net"
"net/http"
"path"
)
type AddressType string
const (
IPv4 AddressType = "IPv4"
IPv6 AddressType = "IPv6"
)
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) {
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
}
if err := tmpl.Execute(w, netAddr); 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)
}
}