Files
goclaw/cmd/gateway_tsnet.go
T
viettranx b62d46e50e refactor(lint): apply Go best practices across codebase
- Use errors.Is() instead of direct sentinel comparison (13 instances)
- Convert if/else-if chains to switch/case for same-variable comparisons
- Remove redundant bitwise OR with zero
- Add post-implementation checklist to CLAUDE.md
2026-03-07 20:51:39 +07:00

83 lines
1.6 KiB
Go

//go:build tsnet
package cmd
import (
"context"
"errors"
"log/slog"
"net"
"net/http"
"time"
"tailscale.com/tsnet"
"github.com/nextlevelbuilder/goclaw/internal/config"
)
// initTailscale starts an additional Tailscale listener alongside the main gateway.
// Only compiled with -tags tsnet. The listener shares the same http.Handler (mux).
func initTailscale(ctx context.Context, cfg *config.Config, mux http.Handler) func() {
tc := cfg.Tailscale
if tc.Hostname == "" {
slog.Debug("Tailscale available but not configured (set GOCLAW_TSNET_HOSTNAME to enable)")
return nil
}
srv := &tsnet.Server{
Hostname: tc.Hostname,
AuthKey: tc.AuthKey,
Ephemeral: tc.Ephemeral,
}
if tc.StateDir != "" {
srv.Dir = tc.StateDir
}
var (
ln net.Listener
err error
)
if tc.EnableTLS {
ln, err = srv.ListenTLS("tcp", ":443")
} else {
ln, err = srv.Listen("tcp", ":80")
}
if err != nil {
slog.Warn("Tailscale listener failed to start", "error", err)
srv.Close()
return nil
}
port := ":80"
if tc.EnableTLS {
port = ":443 (TLS)"
}
slog.Info("Tailscale listener started",
"hostname", tc.Hostname,
"port", port,
)
httpSrv := &http.Server{Handler: mux}
go func() {
if err := httpSrv.Serve(ln); err != nil && !errors.Is(err, http.ErrServerClosed) {
slog.Warn("Tailscale HTTP server error", "error", err)
}
}()
// Graceful shutdown on context cancellation
go func() {
<-ctx.Done()
shutdownCtx, cancel := context.WithTimeout(context.Background(), 5*time.Second)
defer cancel()
httpSrv.Shutdown(shutdownCtx)
}()
return func() {
httpSrv.Close()
ln.Close()
srv.Close()
slog.Info("Tailscale listener stopped")
}
}