🎉 Initial Commit of Ring

This commit is contained in:
2025-12-13 22:51:11 +08:00
parent aed5d53f9e
commit 445363406b
47 changed files with 31140 additions and 48 deletions

View File

@@ -2,17 +2,31 @@ 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() {
@@ -31,12 +45,49 @@ func main() {
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://") {
@@ -61,7 +112,7 @@ func main() {
log.Fatal().Err(err).Msg("Invalid listen address")
}
serviceName := "config"
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)
@@ -75,23 +126,45 @@ func main() {
ServerHeader: "Turbine Ring",
})
// This is the main endpoint that serves the configuration as JSON.
app.Get("/", func(c fiber.Ctx) error {
log.Info().Msg("Serving shared configuration as JSON")
api := app.Group("/api")
{
api.Post("/notifications/subscription", routes.CreatePushSubscription)
}
// Use a new Viper instance to read the shared config
v := viper.New()
v.SetConfigName("shared_config")
v.AddConfigPath(".") // Look in the current directory (pkg/config)
v.SetConfigType("toml")
// Initialize WebSocket Controller
wsController := websocket.NewWebSocketController(wsManager)
if err := v.ReadInConfig(); err != nil {
log.Error().Err(err).Msg("Failed to read shared_config.toml")
return c.Status(fiber.StatusInternalServerError).JSON(fiber.Map{
"error": "could not load configuration",
})
// 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 c.JSON(v.AllSettings())
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...")
}
}