package main import ( "context" "fmt" "net" "net/http" "os" "os/signal" "syscall" "github.com/flowy-live/llink/internal/utils/flog" "github.com/flowy-live/llink/internal" "github.com/flowy-live/llink/internal/auth" "github.com/flowy-live/llink/internal/db" "github.com/flowy-live/llink/internal/network" "github.com/flowy-live/llink/internal/pusher" "github.com/flowy-live/llink/internal/utils" "google.golang.org/grpc" pbpusher "github.com/flowy-live/llink/genproto/llink/pusher" ) func main() { port := utils.MustGetEnv("PORT") grpcPort := utils.MustGetEnv("GRPC_PORT") db.Init() defer db.Cleanup() authRedis := internal.ConnectAndTestRedis(db.RedisDBAuth) // shared with orion pusherRedis := internal.ConnectAndTestRedis(db.RedisDBPusher) // presence + pub/sub sessionReader := auth.NewSessionReader(authRedis) networkReader := network.NewReader(db.Pool()) // Hostname is the k8s pod name. podID, err := os.Hostname() if err != nil { podID = fmt.Sprintf("pod-%d", os.Getpid()) } bridge := pusher.NewRedisBridge(pusherRedis, podID) ctx, cancel := context.WithCancel(context.Background()) defer cancel() authorizer := pusher.NewAuthorizer(networkReader) hub := pusher.NewHub(bridge, authorizer) bridge.SetHub(hub) server := pusher.NewServer(ctx, hub, bridge, sessionReader) go hub.Run(ctx) go bridge.Listen(ctx) go bridge.Heartbeat(ctx) // --- gRPC server (internal presence queries) --- grpcListener, err := net.Listen("tcp", fmt.Sprintf("0.0.0.0:%s", grpcPort)) if err != nil { flog.Error("failed to listen for gRPC", "port", grpcPort, "error", err) os.Exit(1) } grpcServer := grpc.NewServer() pbpusher.RegisterPusherServiceServer(grpcServer, server) go func() { flog.Info("gRPC server listening", "port", grpcPort) if err := grpcServer.Serve(grpcListener); err != nil { flog.Error("gRPC server failed", "error", err) } }() // --- HTTP server (WebSocket + health) --- mux := http.NewServeMux() mux.HandleFunc("GET /health", func(w http.ResponseWriter, r *http.Request) { w.WriteHeader(http.StatusOK) }) mux.HandleFunc("GET /ws", server.HandleWebSocket) httpAddr := fmt.Sprintf("0.0.0.0:%s", port) httpServer := &http.Server{Addr: httpAddr, Handler: mux} go func() { flog.Info("HTTP server listening", "addr", httpAddr) if err := httpServer.ListenAndServe(); err != nil && err != http.ErrServerClosed { flog.Error("HTTP server failed", "error", err) os.Exit(1) } }() // --- Graceful shutdown --- sigCh := make(chan os.Signal, 1) signal.Notify(sigCh, syscall.SIGTERM, syscall.SIGINT) <-sigCh flog.Info("shutting down...") cancel() grpcServer.GracefulStop() httpServer.Shutdown(context.Background()) flog.Info("shutdown complete") }