package main import ( "context" "fmt" "log/slog" "net" "net/http" "os" "os/signal" "forge.cadoles.com/cadoles/go-emlid/reach/discovery" socketio "github.com/googollee/go-socket.io" "github.com/grandcat/zeroconf" "github.com/pkg/errors" sloghttp "github.com/samber/slog-http" "github.com/wlynxg/anet" ) func main() { ctx := context.Background() server, err := startHTTPServer(ctx) if err != nil { slog.Error("could not start http server", slog.Any("error", errors.WithStack(err))) os.Exit(1) } defer server.Close() service, err := startMDNSService(ctx) if err != nil { slog.Error("could not start mdns service", slog.Any("error", errors.WithStack(err))) os.Exit(1) } defer service.Shutdown() interrupt := make(chan os.Signal, 1) signal.Notify(interrupt, os.Interrupt) slog.Info("Ctrl+C to interrupt") <-interrupt } func startHTTPServer(ctx context.Context) (*http.Server, error) { mux := http.NewServeMux() sioServer := NewSocketIOServer() go func() { if err := sioServer.Serve(); err != nil { slog.Error("could not serve socketio", slog.Any("error", errors.WithStack(err))) } }() sioServer.OnEvent("/", "action", func(s socketio.Conn, msg map[string]any) { slog.Info("action event", slog.Any("id", s.ID()), slog.Any("message", msg)) name, ok := msg["name"] if !ok { return } switch name { case "reboot": if err := s.Close(); err != nil { slog.Error("could not close connection", slog.Any("error", errors.WithStack(err))) } } }) mux.Handle("/socket.io/", sioServer) mux.HandleFunc("GET /info", GetInfo) mux.HandleFunc("GET /configuration", GetConfiguration) mux.HandleFunc("POST /configuration/base_mode/base_coordinates", PostJSON) handler := sloghttp.Recovery(mux) handler = sloghttp.New(slog.Default())(handler) server := &http.Server{ Addr: ":8080", Handler: handler, } go func() { defer sioServer.Close() slog.Info("starting http server", slog.Any("address", server.Addr)) if err := server.ListenAndServe(); err != nil { slog.Error("could not listen and serve http requests", slog.Any("error", errors.WithStack(err))) } }() return server, nil } func startMDNSService(ctx context.Context) (*zeroconf.Server, error) { hostname, err := os.Hostname() if err != nil { return nil, errors.WithStack(err) } ifaces, err := anet.Interfaces() if err != nil { return nil, errors.WithStack(err) } ips, err := GetLANIPv4Addrs() if err != nil { return nil, errors.WithStack(err) } info := []string{ fmt.Sprintf("local_name=%s", hostname), "device=FakeReach", "manufacturer_data=egoHIABggkMnZWSuoy0KAgAA", } slog.Info("announcing mdns service", slog.Any("ips", ips), slog.Any("info", info)) server, err := zeroconf.RegisterProxy("Reach", discovery.ReachService, "local.", 8080, hostname, ips, info, ifaces) if err != nil { return nil, errors.WithStack(err) } return server, nil } var ( _, lanA, _ = net.ParseCIDR("10.0.0.0/8") _, lanB, _ = net.ParseCIDR("172.16.0.0/12") _, lanC, _ = net.ParseCIDR("192.168.0.0/16") ) func GetLANIPv4Addrs() ([]string, error) { ips := make([]string, 0) addrs, err := anet.InterfaceAddrs() if err != nil { return nil, errors.WithStack(err) } for _, address := range addrs { if ipnet, ok := address.(*net.IPNet); ok && !ipnet.IP.IsLoopback() { ipv4 := ipnet.IP.To4() if ipv4 == nil { continue } isLAN := lanA.Contains(ipv4) || lanB.Contains(ipv4) || lanC.Contains(ipv4) if !isLAN { continue } ips = append(ips, ipv4.String()) } } return ips, nil }