171 lines
5.1 KiB
Go
171 lines
5.1 KiB
Go
package main
|
|
|
|
import (
|
|
"fmt"
|
|
"net"
|
|
"os"
|
|
"os/signal"
|
|
"strconv"
|
|
"strings"
|
|
"syscall"
|
|
"time"
|
|
|
|
"git.solsynth.dev/goatworks/turbine/pkg/ring/clients" // Add this import
|
|
"git.solsynth.dev/goatworks/turbine/pkg/ring/infra"
|
|
"git.solsynth.dev/goatworks/turbine/pkg/ring/routes"
|
|
"git.solsynth.dev/goatworks/turbine/pkg/ring/services"
|
|
"git.solsynth.dev/goatworks/turbine/pkg/ring/websocket" // Add this import
|
|
"git.solsynth.dev/goatworks/turbine/pkg/shared/hash"
|
|
pb "git.solsynth.dev/goatworks/turbine/pkg/shared/proto/gen"
|
|
"git.solsynth.dev/goatworks/turbine/pkg/shared/registrar"
|
|
|
|
fiber_websocket "github.com/gofiber/contrib/v3/websocket" // Add this alias import
|
|
"github.com/gofiber/fiber/v3"
|
|
"github.com/google/uuid"
|
|
"github.com/rs/zerolog"
|
|
"github.com/rs/zerolog/log"
|
|
"github.com/samber/lo"
|
|
"github.com/spf13/viper"
|
|
"google.golang.org/grpc"
|
|
)
|
|
|
|
func init() {
|
|
log.Logger = log.Output(zerolog.ConsoleWriter{Out: os.Stderr})
|
|
}
|
|
|
|
func main() {
|
|
log.Info().Msg("Starting Turbine Ring...")
|
|
viper.SetConfigName("settings")
|
|
viper.AddConfigPath(".")
|
|
viper.AddConfigPath("/etc/turbine/")
|
|
viper.SetConfigType("toml")
|
|
|
|
log.Info().Msg("Reading configuration...")
|
|
if err := viper.ReadInConfig(); err != nil {
|
|
log.Fatal().Err(err).Msg("Failed to read config file...")
|
|
}
|
|
log.Info().Msg("Configuration loaded.")
|
|
|
|
clients.InitPushClients() // Initialize APNs and Firebase clients
|
|
clients.InitSMTPSettings() // Initialize SMTP client settings
|
|
clients.InitNATSClient() // Initialize NATS client
|
|
|
|
// --- gRPC Server ---
|
|
grpcListenAddr := viper.GetString("grpc.listen")
|
|
if grpcListenAddr == "" {
|
|
log.Fatal().Msg("grpc.listen not configured")
|
|
}
|
|
|
|
grpcServer := grpc.NewServer()
|
|
wsManager := websocket.NewManager(clients.GetNATSClient()) // Pass NATS client to WebSocket Manager
|
|
ringService := &services.RingServiceServerImpl{
|
|
WsManager: wsManager, // Inject WebSocket Manager into RingService
|
|
}
|
|
pb.RegisterRingServiceServer(grpcServer, ringService)
|
|
pb.RegisterRingHandlerServiceServer(grpcServer, ringService)
|
|
|
|
grpcLis, err := net.Listen("tcp", grpcListenAddr)
|
|
if err != nil {
|
|
log.Fatal().Err(err).Msgf("Failed to listen for gRPC: %s", grpcListenAddr)
|
|
}
|
|
|
|
go func() {
|
|
log.Info().Msgf("gRPC server listening on %s", grpcListenAddr)
|
|
if err := grpcServer.Serve(grpcLis); err != nil {
|
|
log.Fatal().Err(err).Msg("Failed to serve gRPC")
|
|
}
|
|
}()
|
|
|
|
// --- Service Registration ---
|
|
etcdEndpoints := viper.GetStringSlice("etcd.endpoints")
|
|
if len(etcdEndpoints) == 0 {
|
|
log.Fatal().Msg("etcd.endpoints not configured")
|
|
}
|
|
|
|
if err := infra.ConnectDb(); err != nil {
|
|
log.Fatal().Err(err).Msg("Failed to connect database.")
|
|
} else {
|
|
infra.Db.AutoMigrate()
|
|
}
|
|
|
|
if viper.GetBool("etcd.insecure") {
|
|
for i, ep := range etcdEndpoints {
|
|
if !strings.HasPrefix(ep, "http://") && !strings.HasPrefix(ep, "https://") {
|
|
etcdEndpoints[i] = "http://" + ep
|
|
}
|
|
}
|
|
}
|
|
|
|
serviceReg, err := registrar.NewServiceRegistrar(etcdEndpoints)
|
|
if err != nil {
|
|
log.Fatal().Err(err).Msg("Failed to create service registrar")
|
|
}
|
|
|
|
listenAddr := viper.GetString("listen")
|
|
host := viper.GetString("host")
|
|
if host == "" {
|
|
log.Fatal().Msg("host not configured")
|
|
}
|
|
portStr := strings.TrimPrefix(listenAddr, ":")
|
|
port, err := strconv.Atoi(portStr)
|
|
if err != nil {
|
|
log.Fatal().Err(err).Msg("Invalid listen address")
|
|
}
|
|
|
|
serviceName := "ring" // This should probably be "ring"
|
|
instanceID := fmt.Sprint(hash.Hash(fmt.Sprintf("%s-%s-%d", serviceName, host, port)))[:8]
|
|
|
|
err = serviceReg.Register(serviceName, "http", instanceID, host, port, 30)
|
|
if err != nil {
|
|
log.Fatal().Err(err).Msg("Failed to register service")
|
|
}
|
|
log.Info().Str("service", serviceName).Str("instanceID", instanceID).Msg("Service registered successfully")
|
|
|
|
// --- Web Server ---
|
|
app := fiber.New(fiber.Config{
|
|
ServerHeader: "Turbine Ring",
|
|
})
|
|
|
|
api := app.Group("/api")
|
|
{
|
|
api.Post("/notifications/subscription", routes.CreatePushSubscription)
|
|
}
|
|
|
|
// Initialize WebSocket Controller
|
|
wsController := websocket.NewWebSocketController(wsManager)
|
|
|
|
// WebSocket endpoint
|
|
app.Use("/ws", func(c fiber.Ctx) error {
|
|
// Mock authentication based on C# example
|
|
// In a real scenario, you'd extract user/session from JWT or similar
|
|
// and set c.Locals("currentUser") and c.Locals("currentSession")
|
|
c.Locals("currentUser", &pb.Account{Id: uuid.New().String(), Name: "mock_user", Nick: "Mock User"})
|
|
c.Locals("currentSession", &pb.AuthSession{ClientId: lo.ToPtr(uuid.New().String())})
|
|
|
|
if fiber_websocket.IsWebSocketUpgrade(c) {
|
|
return c.Next()
|
|
}
|
|
return fiber.ErrUpgradeRequired
|
|
})
|
|
app.Get("/ws", fiber_websocket.New(wsController.HandleWebSocket))
|
|
|
|
// Graceful shutdown
|
|
c := make(chan os.Signal, 1)
|
|
signal.Notify(c, os.Interrupt, syscall.SIGTERM)
|
|
|
|
go func() {
|
|
<-c
|
|
log.Info().Msg("Shutting down servers...")
|
|
if err := app.ShutdownWithTimeout(5 * time.Second); err != nil {
|
|
log.Error().Err(err).Msg("Fiber server shutdown error")
|
|
}
|
|
grpcServer.GracefulStop()
|
|
log.Info().Msg("Servers gracefully stopped")
|
|
}()
|
|
|
|
err = app.Listen(listenAddr, fiber.ListenConfig{DisableStartupMessage: true})
|
|
if err != nil {
|
|
log.Fatal().Err(err).Msg("Failed to start the server...")
|
|
}
|
|
}
|