From 9ad1170564248e886aa5154e9d3ba665935eeacf Mon Sep 17 00:00:00 2001 From: pieceowater Date: Sat, 30 Nov 2024 19:12:27 +0500 Subject: [PATCH] v2.0. what's new: - database factory - server factory --- cmd/example/app.go | 102 ++----- go.mod | 52 ++-- go.sum | 191 ++++-------- gossiper.go | 200 ++++--------- internal/boot/bootstrap.go | 61 ---- internal/conf/conf.go | 82 ----- internal/core/db/database.go | 46 +++ internal/core/db/pg/pgsql.go | 97 ++++++ internal/core/servers/grpc/grpc.go | 41 +++ internal/core/servers/rabbitmq/rabbitmq.go | 21 ++ internal/core/servers/rest/rest.go | 38 +++ internal/core/servers/server.go | 47 +++ internal/env/env.go | 105 ------- internal/index.go | 155 ---------- internal/infra/amqp/amqp.go | 87 ------ internal/infra/amqp/client.go | 177 ----------- internal/infra/amqp/handler.go | 40 --- internal/infra/amqp/publisher.go | 26 -- internal/infra/db/ch/clickhouse.go | 106 ------- internal/infra/db/pg/pg.go | 102 ------- internal/infra/infra.go | 11 - internal/tools/formats/errors/error.go | 36 --- internal/tools/formats/filter/filter.go | 46 --- .../tools/formats/pagination/pagination.go | 23 -- internal/tools/logger.go | 49 --- internal/tools/panics/handler.go | 15 - internal/tools/panics/safely.go | 24 -- internal/tools/satisfies.go | 282 ------------------ internal/tools/tools.go | 84 ------ types/filter.go | 26 -- types/message.go | 8 - 31 files changed, 456 insertions(+), 1924 deletions(-) delete mode 100644 internal/boot/bootstrap.go delete mode 100644 internal/conf/conf.go create mode 100644 internal/core/db/database.go create mode 100644 internal/core/db/pg/pgsql.go create mode 100644 internal/core/servers/grpc/grpc.go create mode 100644 internal/core/servers/rabbitmq/rabbitmq.go create mode 100644 internal/core/servers/rest/rest.go create mode 100644 internal/core/servers/server.go delete mode 100644 internal/env/env.go delete mode 100644 internal/index.go delete mode 100644 internal/infra/amqp/amqp.go delete mode 100644 internal/infra/amqp/client.go delete mode 100644 internal/infra/amqp/handler.go delete mode 100644 internal/infra/amqp/publisher.go delete mode 100644 internal/infra/db/ch/clickhouse.go delete mode 100644 internal/infra/db/pg/pg.go delete mode 100644 internal/infra/infra.go delete mode 100644 internal/tools/formats/errors/error.go delete mode 100644 internal/tools/formats/filter/filter.go delete mode 100644 internal/tools/formats/pagination/pagination.go delete mode 100644 internal/tools/logger.go delete mode 100644 internal/tools/panics/handler.go delete mode 100644 internal/tools/panics/safely.go delete mode 100644 internal/tools/satisfies.go delete mode 100644 internal/tools/tools.go delete mode 100644 types/filter.go delete mode 100644 types/message.go diff --git a/cmd/example/app.go b/cmd/example/app.go index 81a556d..2583567 100644 --- a/cmd/example/app.go +++ b/cmd/example/app.go @@ -1,94 +1,30 @@ package main import ( - "encoding/json" + "github.com/gin-gonic/gin" "github.com/pieceowater-dev/lotof.lib.gossiper" - "gorm.io/gorm" - "log" + "google.golang.org/grpc" ) -// HandleMessage processes incoming RabbitMQ messages. -// It receives an AMQMessage, logs the pattern, and returns a response. -// This is where you can add custom logic to route or process messages. -func HandleMessage(msg gossiper.AMQMessage) any { - // Log the received message's pattern - log.Printf("Received message: %s", msg.Pattern) - return "OK" // Return a response; modify this as needed -} - -//type SomeData struct { -// gorm.Model -// ID int `json:"id"` -// Data json.RawMessage `json:"data"` -//} - func main() { - // Define the Gossiper configuration for RabbitMQ and PostgreSQL - conf := gossiper.Config{ - Env: gossiper.EnvConfig{ - Required: []string{"RABBITMQ_DSN", "DATABASE_DSN"}, // Specify required environment variables for RabbitMQ and PostgreSQL - }, - AMQPConsumer: gossiper.AMQPConsumerConfig{ - DSNEnv: "RABBITMQ_DSN", // Environment variable for RabbitMQ DSN - Queues: []gossiper.QueueConfig{ - { - Name: "template_queue", // Queue name from which messages will be consumed - Durable: true, // Set queue as persistent (survives RabbitMQ restarts) - }, - }, - Consume: []gossiper.AMQPConsumeConfig{ - { - Queue: "template_queue", // Queue name to consume from - Consumer: "example_consumer", // Unique consumer tag for the connection - AutoAck: true, // Automatically acknowledge receipt of messages - }, - }, - }, - Database: gossiper.DatabaseConfig{ - PG: gossiper.DBPGConfig{ - EnvPostgresDBDSN: "DATABASE_DSN", // Environment variable key for PostgreSQL DSN - AutoMigrate: true, // Enable auto-migration of models - Models: []any{ - // Your models go here - // &yourModel{}, // Example: Define the models that will be auto-migrated - }, - }, - ClickHouse: gossiper.DBClickHouseConfig{ - EnvClickHouseDBDSN: "CLICKHOUSE_DSN", - AutoMigrate: true, - Models: []any{ - //&SomeData{}, - }, - GORMConfig: &gorm.Config{}, - }, - }, - } + // Создаём менеджер серверов + serverManager := gossiper.NewServerManager() - // Initialize the Gossiper application and setup RabbitMQ consumers and PostgreSQL connection - // Pass a handler function to process each message that is consumed - app := gossiper.Bootstrap{} - app.Setup( - conf, - func() any { - // Custom startup logic to execute after initialization (if needed) - log.Println("Custom Setup here") - return nil - }, - func(msg []byte) any { - var customMessage gossiper.AMQMessage - - // Attempt to unmarshal the received message into a custom structure - err := json.Unmarshal(msg, &customMessage) - if err != nil { - log.Println("Failed to unmarshal custom message:", err) - return nil // Return nil in case of unmarshalling failure - } + // Инициализация gRPC сервера + grpcInitRoute := func(server *grpc.Server) { + // Пример: добавить маршруты + } + serverManager.AddServer(gossiper.NewGRPCServ("50051", grpc.NewServer(), grpcInitRoute)) - // Delegate message processing to the HandleMessage function - return HandleMessage(customMessage) - }, - ) + // Инициализация REST сервера + restInitRoute := func(router *gin.Engine) { + router.GET("/health", func(c *gin.Context) { + c.JSON(200, gin.H{"status": "ok"}) + }) + } + serverManager.AddServer(gossiper.NewRESTServ("8080", gin.Default(), restInitRoute)) - // Log that the application has started successfully - log.Println("Application started") + // Запуск всех серверов + serverManager.StartAll() + defer serverManager.StopAll() } diff --git a/go.mod b/go.mod index 4f7df62..26e0f51 100644 --- a/go.mod +++ b/go.mod @@ -3,49 +3,47 @@ module github.com/pieceowater-dev/lotof.lib.gossiper go 1.23.0 require ( - github.com/fatih/color v1.17.0 - github.com/go-playground/validator/v10 v10.22.1 - github.com/joho/godotenv v1.5.1 - github.com/rabbitmq/amqp091-go v1.10.0 - github.com/rs/zerolog v1.33.0 - github.com/sirupsen/logrus v1.9.3 - gorm.io/driver/clickhouse v0.6.1 + github.com/gin-gonic/gin v1.10.0 + google.golang.org/grpc v1.68.0 gorm.io/driver/postgres v1.5.9 gorm.io/gorm v1.25.12 ) require ( - github.com/ClickHouse/ch-go v0.61.5 // indirect - github.com/ClickHouse/clickhouse-go/v2 v2.23.2 // indirect - github.com/andybalholm/brotli v1.1.0 // indirect + github.com/bytedance/sonic v1.11.6 // indirect + github.com/bytedance/sonic/loader v0.1.1 // indirect + github.com/cloudwego/base64x v0.1.4 // indirect + github.com/cloudwego/iasm v0.2.0 // indirect github.com/gabriel-vasile/mimetype v1.4.3 // indirect - github.com/go-faster/city v1.0.1 // indirect - github.com/go-faster/errors v0.7.1 // indirect + github.com/gin-contrib/sse v0.1.0 // indirect github.com/go-playground/locales v0.14.1 // indirect github.com/go-playground/universal-translator v0.18.1 // indirect - github.com/google/uuid v1.6.0 // indirect - github.com/hashicorp/go-version v1.6.0 // indirect + github.com/go-playground/validator/v10 v10.20.0 // indirect + github.com/goccy/go-json v0.10.2 // indirect github.com/jackc/pgpassfile v1.0.0 // indirect github.com/jackc/pgservicefile v0.0.0-20221227161230-091c0ba34f0a // indirect github.com/jackc/pgx/v5 v5.5.5 // indirect github.com/jackc/puddle/v2 v2.2.1 // indirect github.com/jinzhu/inflection v1.0.0 // indirect github.com/jinzhu/now v1.1.5 // indirect - github.com/klauspost/compress v1.17.8 // indirect + github.com/json-iterator/go v1.1.12 // indirect + github.com/klauspost/cpuid/v2 v2.2.7 // indirect + github.com/kr/text v0.2.0 // indirect github.com/leodido/go-urn v1.4.0 // indirect - github.com/mattn/go-colorable v0.1.13 // indirect github.com/mattn/go-isatty v0.0.20 // indirect - github.com/paulmach/orb v0.11.1 // indirect - github.com/pierrec/lz4/v4 v4.1.21 // indirect - github.com/pkg/errors v0.9.1 // indirect - github.com/segmentio/asm v1.2.0 // indirect - github.com/shopspring/decimal v1.4.0 // indirect - go.opentelemetry.io/otel v1.26.0 // indirect - go.opentelemetry.io/otel/trace v1.26.0 // indirect - golang.org/x/crypto v0.21.0 // indirect - golang.org/x/net v0.22.0 // indirect - golang.org/x/sync v0.6.0 // indirect + github.com/modern-go/concurrent v0.0.0-20180306012644-bacd9c7ef1dd // indirect + github.com/modern-go/reflect2 v1.0.2 // indirect + github.com/pelletier/go-toml/v2 v2.2.2 // indirect + github.com/rogpeppe/go-internal v1.10.0 // indirect + github.com/twitchyliquid64/golang-asm v0.15.1 // indirect + github.com/ugorji/go/codec v1.2.12 // indirect + golang.org/x/arch v0.8.0 // indirect + golang.org/x/crypto v0.27.0 // indirect + golang.org/x/net v0.29.0 // indirect + golang.org/x/sync v0.8.0 // indirect golang.org/x/sys v0.25.0 // indirect - golang.org/x/text v0.14.0 // indirect + golang.org/x/text v0.18.0 // indirect + google.golang.org/genproto/googleapis/rpc v0.0.0-20240903143218-8af14fe29dc1 // indirect + google.golang.org/protobuf v1.35.2 // indirect gopkg.in/yaml.v3 v3.0.1 // indirect ) diff --git a/go.sum b/go.sum index d7337b6..78aea69 100644 --- a/go.sum +++ b/go.sum @@ -1,41 +1,36 @@ -github.com/ClickHouse/ch-go v0.61.5 h1:zwR8QbYI0tsMiEcze/uIMK+Tz1D3XZXLdNrlaOpeEI4= -github.com/ClickHouse/ch-go v0.61.5/go.mod h1:s1LJW/F/LcFs5HJnuogFMta50kKDO0lf9zzfrbl0RQg= -github.com/ClickHouse/clickhouse-go/v2 v2.23.2 h1:+DAKPMnxLS7pduQZsrJc8OhdLS2L9MfDEJ2TS+hpYDM= -github.com/ClickHouse/clickhouse-go/v2 v2.23.2/go.mod h1:aNap51J1OM3yxQJRgM+AlP/MPkGBCL8A74uQThoQhR0= -github.com/andybalholm/brotli v1.1.0 h1:eLKJA0d02Lf0mVpIDgYnqXcUn0GqVmEFny3VuID1U3M= -github.com/andybalholm/brotli v1.1.0/go.mod h1:sms7XGricyQI9K10gOSf56VKKWS4oLer58Q+mhRPtnY= -github.com/coreos/go-systemd/v22 v22.5.0/go.mod h1:Y58oyj3AT4RCenI/lSvhwexgC+NSVTIJ3seZv2GcEnc= +github.com/bytedance/sonic v1.11.6 h1:oUp34TzMlL+OY1OUWxHqsdkgC/Zfc85zGqw9siXjrc0= +github.com/bytedance/sonic v1.11.6/go.mod h1:LysEHSvpvDySVdC2f87zGWf6CIKJcAvqab1ZaiQtds4= +github.com/bytedance/sonic/loader v0.1.1 h1:c+e5Pt1k/cy5wMveRDyk2X4B9hF4g7an8N3zCYjJFNM= +github.com/bytedance/sonic/loader v0.1.1/go.mod h1:ncP89zfokxS5LZrJxl5z0UJcsk4M4yY2JpfqGeCtNLU= +github.com/cloudwego/base64x v0.1.4 h1:jwCgWpFanWmN8xoIUHa2rtzmkd5J2plF/dnLS6Xd/0Y= +github.com/cloudwego/base64x v0.1.4/go.mod h1:0zlkT4Wn5C6NdauXdJRhSKRlJvmclQ1hhJgA0rcu/8w= +github.com/cloudwego/iasm v0.2.0 h1:1KNIy1I1H9hNNFEEH3DVnI4UujN+1zjpuk6gwHLTssg= +github.com/cloudwego/iasm v0.2.0/go.mod h1:8rXZaNYT2n95jn+zTI1sDr+IgcD2GVs0nlbbQPiEFhY= +github.com/creack/pty v1.1.9/go.mod h1:oKZEueFk5CKHvIhNR5MUki03XCEU+Q6VDXinZuGJ33E= github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= github.com/davecgh/go-spew v1.1.1 h1:vj9j/u1bqnvCEfJOwUhtlOARqs3+rkHYY13jYWTU97c= github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= -github.com/fatih/color v1.17.0 h1:GlRw1BRJxkpqUCBKzKOw098ed57fEsKeNjpTe3cSjK4= -github.com/fatih/color v1.17.0/go.mod h1:YZ7TlrGPkiz6ku9fK3TLD/pl3CpsiFyu8N92HLgmosI= github.com/gabriel-vasile/mimetype v1.4.3 h1:in2uUcidCuFcDKtdcBxlR0rJ1+fsokWf+uqxgUFjbI0= github.com/gabriel-vasile/mimetype v1.4.3/go.mod h1:d8uq/6HKRL6CGdk+aubisF/M5GcPfT7nKyLpA0lbSSk= -github.com/go-faster/city v1.0.1 h1:4WAxSZ3V2Ws4QRDrscLEDcibJY8uf41H6AhXDrNDcGw= -github.com/go-faster/city v1.0.1/go.mod h1:jKcUJId49qdW3L1qKHH/3wPeUstCVpVSXTM6vO3VcTw= -github.com/go-faster/errors v0.7.1 h1:MkJTnDoEdi9pDabt1dpWf7AA8/BaSYZqibYyhZ20AYg= -github.com/go-faster/errors v0.7.1/go.mod h1:5ySTjWFiphBs07IKuiL69nxdfd5+fzh1u7FPGZP2quo= +github.com/gin-contrib/sse v0.1.0 h1:Y/yl/+YNO8GZSjAhjMsSuLt29uWRFHdHYUb5lYOV9qE= +github.com/gin-contrib/sse v0.1.0/go.mod h1:RHrZQHXnP2xjPF+u1gW/2HnVO7nvIa9PG3Gm+fLHvGI= +github.com/gin-gonic/gin v1.10.0 h1:nTuyha1TYqgedzytsKYqna+DfLos46nTv2ygFy86HFU= +github.com/gin-gonic/gin v1.10.0/go.mod h1:4PMNQiOhvDRa013RKVbsiNwoyezlm2rm0uX/T7kzp5Y= github.com/go-playground/assert/v2 v2.2.0 h1:JvknZsQTYeFEAhQwI4qEt9cyV5ONwRHC+lYKSsYSR8s= github.com/go-playground/assert/v2 v2.2.0/go.mod h1:VDjEfimB/XKnb+ZQfWdccd7VUvScMdVu0Titje2rxJ4= github.com/go-playground/locales v0.14.1 h1:EWaQ/wswjilfKLTECiXz7Rh+3BjFhfDFKv/oXslEjJA= github.com/go-playground/locales v0.14.1/go.mod h1:hxrqLVvrK65+Rwrd5Fc6F2O76J/NuW9t0sjnWqG1slY= github.com/go-playground/universal-translator v0.18.1 h1:Bcnm0ZwsGyWbCzImXv+pAJnYK9S473LQFuzCbDbfSFY= github.com/go-playground/universal-translator v0.18.1/go.mod h1:xekY+UJKNuX9WP91TpwSH2VMlDf28Uj24BCp08ZFTUY= -github.com/go-playground/validator/v10 v10.22.1 h1:40JcKH+bBNGFczGuoBYgX4I6m/i27HYW8P9FDk5PbgA= -github.com/go-playground/validator/v10 v10.22.1/go.mod h1:dbuPbCMFw/DrkbEynArYaCwl3amGuJotoKCe95atGMM= -github.com/godbus/dbus/v5 v5.0.4/go.mod h1:xhWf0FNVPg57R7Z0UbKHbJfkEywrmjJnf7w5xrFpKfA= -github.com/gogo/protobuf v1.3.2/go.mod h1:P1XiOD3dCwIKUDQYPy72D8LYyHL2YPYrpS2s69NZV8Q= -github.com/golang/protobuf v1.5.0/go.mod h1:FsONVRAS9T7sI+LIUmWTfcYkHO4aIWwzhcaSAoJOfIk= -github.com/golang/snappy v0.0.1/go.mod h1:/XxbfmMg8lxefKM7IXC3fBNl/7bRcc72aCRzEWrmP2Q= -github.com/google/go-cmp v0.5.2/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE= -github.com/google/go-cmp v0.5.5/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE= +github.com/go-playground/validator/v10 v10.20.0 h1:K9ISHbSaI0lyB2eWMPJo+kOS/FBExVwjEviJTixqxL8= +github.com/go-playground/validator/v10 v10.20.0/go.mod h1:dbuPbCMFw/DrkbEynArYaCwl3amGuJotoKCe95atGMM= +github.com/goccy/go-json v0.10.2 h1:CrxCmQqYDkv1z7lO7Wbh2HN93uovUHgrECaO5ZrCXAU= +github.com/goccy/go-json v0.10.2/go.mod h1:6MelG93GURQebXPDq3khkgXZkazVtN9CRI+MGFi0w8I= +github.com/golang/protobuf v1.5.4 h1:i7eJL8qZTpSEXOPTxNKhASYpMn+8e5Q6AdndVa1dWek= +github.com/golang/protobuf v1.5.4/go.mod h1:lnTiLA8Wa4RWRcIUkrtSVa5nRhsEGBg48fD6rSs7xps= github.com/google/go-cmp v0.6.0 h1:ofyhxvXcZhMsU5ulbFiLKl/XBFqE1GSq7atu8tAmTRI= github.com/google/go-cmp v0.6.0/go.mod h1:17dUlkBOakJ0+DkrSSNjCkIjxS6bF9zb3elmeNGIjoY= -github.com/google/uuid v1.6.0 h1:NIvaJDMOsjHA8n1jAhLSgzrAzy1Hgr+hNrb57e+94F0= -github.com/google/uuid v1.6.0/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo= -github.com/hashicorp/go-version v1.6.0 h1:feTTfFNnjP967rlCxM/I9g701jU+RN74YKx2mOkIeek= -github.com/hashicorp/go-version v1.6.0/go.mod h1:fltr4n8CU8Ke44wwGCBoEymUuxUHl09ZGVZPK5anwXA= +github.com/google/gofuzz v1.0.0/go.mod h1:dBl0BpW6vV/+mYPU4Po3pmUjxk6FQPldtuIdl/M65Eg= github.com/jackc/pgpassfile v1.0.0 h1:/6Hmqy13Ss2zCq62VdNG8tM1wchn8zjSGOBJ6icpsIM= github.com/jackc/pgpassfile v1.0.0/go.mod h1:CEx0iS5ambNFdcRtxPj5JhEz+xB6uRky5eyVu/W2HEg= github.com/jackc/pgservicefile v0.0.0-20221227161230-091c0ba34f0a h1:bbPeKD0xmW/Y25WS6cokEszi5g+S0QxI/d45PkRi7Nk= @@ -48,131 +43,77 @@ github.com/jinzhu/inflection v1.0.0 h1:K317FqzuhWc8YvSVlFMCCUb36O/S9MCKRDI7QkRKD github.com/jinzhu/inflection v1.0.0/go.mod h1:h+uFLlag+Qp1Va5pdKtLDYj+kHp5pxUVkryuEj+Srlc= github.com/jinzhu/now v1.1.5 h1:/o9tlHleP7gOFmsnYNz3RGnqzefHA47wQpKrrdTIwXQ= github.com/jinzhu/now v1.1.5/go.mod h1:d3SSVoowX0Lcu0IBviAWJpolVfI5UJVZZ7cO71lE/z8= -github.com/joho/godotenv v1.5.1 h1:7eLL/+HRGLY0ldzfGMeQkb7vMd0as4CfYvUVzLqw0N0= -github.com/joho/godotenv v1.5.1/go.mod h1:f4LDr5Voq0i2e/R5DDNOoa2zzDfwtkZa6DnEwAbqwq4= -github.com/kisielk/errcheck v1.5.0/go.mod h1:pFxgyoBC7bSaBwPgfKdkLd5X25qrDl4LWUI2bnpBCr8= -github.com/kisielk/gotool v1.0.0/go.mod h1:XhKaO+MFFWcvkIS/tQcRk01m1F5IRFswLeQ+oQHNcck= -github.com/klauspost/compress v1.13.6/go.mod h1:/3/Vjq9QcHkK5uEr5lBEmyoZ1iFhe47etQ6QUkpK6sk= -github.com/klauspost/compress v1.17.8 h1:YcnTYrq7MikUT7k0Yb5eceMmALQPYBW/Xltxn0NAMnU= -github.com/klauspost/compress v1.17.8/go.mod h1:Di0epgTjJY877eYKx5yC51cX2A2Vl2ibi7bDH9ttBbw= -github.com/kr/pretty v0.1.0/go.mod h1:dAy3ld7l9f0ibDNOQOHHMYYIIbhfbHSm3C4ZsoJORNo= -github.com/kr/pretty v0.3.1 h1:flRD4NNwYAUpkphVc1HcthR4KEIFJ65n8Mw5qdRn3LE= -github.com/kr/pretty v0.3.1/go.mod h1:hoEshYVHaxMs3cyo3Yncou5ZscifuDolrwPKZanG3xk= -github.com/kr/pty v1.1.1/go.mod h1:pFQYn66WHrOpPYNljwOMqo10TkYh1fy3cYio2l3bCsQ= -github.com/kr/text v0.1.0/go.mod h1:4Jbv+DJW3UT/LiOwJeYQe1efqtUx/iVham/4vfdArNI= +github.com/json-iterator/go v1.1.12 h1:PV8peI4a0ysnczrg+LtxykD8LfKY9ML6u2jnxaEnrnM= +github.com/json-iterator/go v1.1.12/go.mod h1:e30LSqwooZae/UwlEbR2852Gd8hjQvJoHmT4TnhNGBo= +github.com/klauspost/cpuid/v2 v2.0.9/go.mod h1:FInQzS24/EEf25PyTYn52gqo7WaD8xa0213Md/qVLRg= +github.com/klauspost/cpuid/v2 v2.2.7 h1:ZWSB3igEs+d0qvnxR/ZBzXVmxkgt8DdzP6m9pfuVLDM= +github.com/klauspost/cpuid/v2 v2.2.7/go.mod h1:Lcz8mBdAVJIBVzewtcLocK12l3Y+JytZYpaMropDUws= +github.com/knz/go-libedit v1.10.1/go.mod h1:MZTVkCWyz0oBc7JOWP3wNAzd002ZbM/5hgShxwh4x8M= +github.com/kr/pretty v0.3.0 h1:WgNl7dwNpEZ6jJ9k1snq4pZsg7DOEN8hP9Xw0Tsjwk0= +github.com/kr/pretty v0.3.0/go.mod h1:640gp4NfQd8pI5XOwp5fnNeVWj67G7CFk/SaSQn7NBk= github.com/kr/text v0.2.0 h1:5Nx0Ya0ZqY2ygV366QzturHI13Jq95ApcVaJBhpS+AY= github.com/kr/text v0.2.0/go.mod h1:eLer722TekiGuMkidMxC/pM04lWEeraHUUmBw8l2grE= github.com/leodido/go-urn v1.4.0 h1:WT9HwE9SGECu3lg4d/dIA+jxlljEa1/ffXKmRjqdmIQ= github.com/leodido/go-urn v1.4.0/go.mod h1:bvxc+MVxLKB4z00jd1z+Dvzr47oO32F/QSNjSBOlFxI= -github.com/mattn/go-colorable v0.1.13 h1:fFA4WZxdEF4tXPZVKMLwD8oUnCTTo08duU7wxecdEvA= -github.com/mattn/go-colorable v0.1.13/go.mod h1:7S9/ev0klgBDR4GtXTXX8a3vIGJpMovkB8vQcUbaXHg= -github.com/mattn/go-isatty v0.0.16/go.mod h1:kYGgaQfpe5nmfYZH+SKPsOc2e4SrIfOl2e/yFXSvRLM= -github.com/mattn/go-isatty v0.0.19/go.mod h1:W+V8PltTTMOvKvAeJH7IuucS94S2C6jfK/D7dTCTo3Y= github.com/mattn/go-isatty v0.0.20 h1:xfD0iDuEKnDkl03q4limB+vH+GxLEtL/jb4xVJSWWEY= github.com/mattn/go-isatty v0.0.20/go.mod h1:W+V8PltTTMOvKvAeJH7IuucS94S2C6jfK/D7dTCTo3Y= -github.com/montanaflynn/stats v0.0.0-20171201202039-1bf9dbcd8cbe/go.mod h1:wL8QJuTMNUDYhXwkmfOly8iTdp5TEcJFWZD2D7SIkUc= -github.com/paulmach/orb v0.11.1 h1:3koVegMC4X/WeiXYz9iswopaTwMem53NzTJuTF20JzU= -github.com/paulmach/orb v0.11.1/go.mod h1:5mULz1xQfs3bmQm63QEJA6lNGujuRafwA5S/EnuLaLU= -github.com/paulmach/protoscan v0.2.1/go.mod h1:SpcSwydNLrxUGSDvXvO0P7g7AuhJ7lcKfDlhJCDw2gY= -github.com/pierrec/lz4/v4 v4.1.21 h1:yOVMLb6qSIDP67pl/5F7RepeKYu/VmTyEXvuMI5d9mQ= -github.com/pierrec/lz4/v4 v4.1.21/go.mod h1:gZWDp/Ze/IJXGXf23ltt2EXimqmTUXEy0GFuRQyBid4= -github.com/pkg/errors v0.9.1 h1:FEBLx1zS214owpjy7qsBeixbURkuhQAwrK5UwLGTwt4= -github.com/pkg/errors v0.9.1/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0= +github.com/modern-go/concurrent v0.0.0-20180228061459-e0a39a4cb421/go.mod h1:6dJC0mAP4ikYIbvyc7fijjWJddQyLn8Ig3JB5CqoB9Q= +github.com/modern-go/concurrent v0.0.0-20180306012644-bacd9c7ef1dd h1:TRLaZ9cD/w8PVh93nsPXa1VrQ6jlwL5oN8l14QlcNfg= +github.com/modern-go/concurrent v0.0.0-20180306012644-bacd9c7ef1dd/go.mod h1:6dJC0mAP4ikYIbvyc7fijjWJddQyLn8Ig3JB5CqoB9Q= +github.com/modern-go/reflect2 v1.0.2 h1:xBagoLtFs94CBntxluKeaWgTMpvLxC4ur3nMaC9Gz0M= +github.com/modern-go/reflect2 v1.0.2/go.mod h1:yWuevngMOJpCy52FWWMvUC8ws7m/LJsjYzDa0/r8luk= +github.com/pelletier/go-toml/v2 v2.2.2 h1:aYUidT7k73Pcl9nb2gScu7NSrKCSHIDE89b3+6Wq+LM= +github.com/pelletier/go-toml/v2 v2.2.2/go.mod h1:1t835xjRzz80PqgE6HHgN2JOsmgYu/h4qDAS4n929Rs= github.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZbAQM= github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4= -github.com/rabbitmq/amqp091-go v1.10.0 h1:STpn5XsHlHGcecLmMFCtg7mqq0RnD+zFr4uzukfVhBw= -github.com/rabbitmq/amqp091-go v1.10.0/go.mod h1:Hy4jKW5kQART1u+JkDTF9YYOQUHXqMuhrgxOEeS7G4o= github.com/rogpeppe/go-internal v1.10.0 h1:TMyTOH3F/DB16zRVcYyreMH6GnZZrwQVAoYjRBZyWFQ= github.com/rogpeppe/go-internal v1.10.0/go.mod h1:UQnix2H7Ngw/k4C5ijL5+65zddjncjaFoBhdsK/akog= -github.com/rs/xid v1.5.0/go.mod h1:trrq9SKmegXys3aeAKXMUTdJsYXVwGY3RLcfgqegfbg= -github.com/rs/zerolog v1.33.0 h1:1cU2KZkvPxNyfgEmhHAz/1A9Bz+llsdYzklWFzgp0r8= -github.com/rs/zerolog v1.33.0/go.mod h1:/7mN4D5sKwJLZQ2b/znpjC3/GQWY/xaDXUM0kKWRHss= -github.com/segmentio/asm v1.2.0 h1:9BQrFxC+YOHJlTlHGkTrFWf59nbL3XnCoFLTwDCI7ys= -github.com/segmentio/asm v1.2.0/go.mod h1:BqMnlJP91P8d+4ibuonYZw9mfnzI9HfxselHZr5aAcs= -github.com/shopspring/decimal v1.4.0 h1:bxl37RwXBklmTi0C79JfXCEBD1cqqHt0bbgBAGFp81k= -github.com/shopspring/decimal v1.4.0/go.mod h1:gawqmDU56v4yIKSwfBSFip1HdCCXN8/+DMd9qYNcwME= -github.com/sirupsen/logrus v1.9.3 h1:dueUQJ1C2q9oE3F7wvmSGAaVtTmUizReu6fjN8uqzbQ= -github.com/sirupsen/logrus v1.9.3/go.mod h1:naHLuLoDiP4jHNo9R0sCBMtWGeIprob74mVsIT4qYEQ= github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME= +github.com/stretchr/objx v0.4.0/go.mod h1:YvHI0jy2hoMjB+UWwv71VJQ9isScKT/TqJzVSSt89Yw= +github.com/stretchr/objx v0.5.0/go.mod h1:Yh+to48EsGEfYuaHDzXPcE3xhTkx73EhmCGUpEOglKo= +github.com/stretchr/objx v0.5.2/go.mod h1:FRsXN1f5AsAjCGJKqEizvkpNtU+EGNCLh3NxZ/8L+MA= github.com/stretchr/testify v1.3.0/go.mod h1:M5WIy9Dh21IEIfnGCwXGc5bZfKNJtfHm1UVUgZn+9EI= -github.com/stretchr/testify v1.6.1/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg= github.com/stretchr/testify v1.7.0/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg= +github.com/stretchr/testify v1.7.1/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg= +github.com/stretchr/testify v1.8.0/go.mod h1:yNjHg4UonilssWZ8iaSj1OCr/vHnekPRkoO+kdMU+MU= +github.com/stretchr/testify v1.8.1/go.mod h1:w2LPCIKwWwSfY2zedu0+kehJoqGctiVI29o6fzry7u4= +github.com/stretchr/testify v1.8.4/go.mod h1:sz/lmYIOXD/1dqDmKjjqLyZ2RngseejIcXlSw2iwfAo= github.com/stretchr/testify v1.9.0 h1:HtqpIVDClZ4nwg75+f6Lvsy/wHu+3BoSGCbBAcpTsTg= github.com/stretchr/testify v1.9.0/go.mod h1:r2ic/lqez/lEtzL7wO/rwa5dbSLXVDPFyf8C91i36aY= -github.com/tidwall/pretty v1.0.0/go.mod h1:XNkn88O1ChpSDQmQeStsy+sBenx6DDtFZJxhVysOjyk= -github.com/xdg-go/pbkdf2 v1.0.0/go.mod h1:jrpuAogTd400dnrH08LKmI/xc1MbPOebTwRqcT5RDeI= -github.com/xdg-go/scram v1.1.1/go.mod h1:RaEWvsqvNKKvBPvcKeFjrG2cJqOkHTiyTpzz23ni57g= -github.com/xdg-go/stringprep v1.0.3/go.mod h1:W3f5j4i+9rC0kuIEJL0ky1VpHXQU3ocBgklLGvcBnW8= -github.com/youmark/pkcs8 v0.0.0-20181117223130-1be2e3e5546d/go.mod h1:rHwXgn7JulP+udvsHwJoVG1YGAP6VLg4y9I5dyZdqmA= -github.com/yuin/goldmark v1.1.27/go.mod h1:3hX8gzYuyVAZsxl0MRgGTJEmQBFcNTphYh9decYSb74= -github.com/yuin/goldmark v1.2.1/go.mod h1:3hX8gzYuyVAZsxl0MRgGTJEmQBFcNTphYh9decYSb74= -go.mongodb.org/mongo-driver v1.11.4/go.mod h1:PTSz5yu21bkT/wXpkS7WR5f0ddqw5quethTUn9WM+2g= -go.opentelemetry.io/otel v1.26.0 h1:LQwgL5s/1W7YiiRwxf03QGnWLb2HW4pLiAhaA5cZXBs= -go.opentelemetry.io/otel v1.26.0/go.mod h1:UmLkJHUAidDval2EICqBMbnAd0/m2vmpf/dAM+fvFs4= -go.opentelemetry.io/otel/trace v1.26.0 h1:1ieeAUb4y0TE26jUFrCIXKpTuVK7uJGN9/Z/2LP5sQA= -go.opentelemetry.io/otel/trace v1.26.0/go.mod h1:4iDxvGDQuUkHve82hJJ8UqrwswHYsZuWCBllGV2U2y0= -go.uber.org/goleak v1.3.0 h1:2K3zAYmnTNqV73imy9J1T3WC+gmCePx2hEGkimedGto= -go.uber.org/goleak v1.3.0/go.mod h1:CoHD4mav9JJNrW/WLlf7HGZPjdw8EucARQHekz1X6bE= -golang.org/x/crypto v0.0.0-20190308221718-c2843e01d9a2/go.mod h1:djNgcEr1/C05ACkg1iLfiJU5Ep61QUkGW8qpdssI0+w= -golang.org/x/crypto v0.0.0-20191011191535-87dc89f01550/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI= -golang.org/x/crypto v0.0.0-20200622213623-75b288015ac9/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto= -golang.org/x/crypto v0.0.0-20220622213112-05595931fe9d/go.mod h1:IxCIyHEi3zRg3s0A5j5BB6A9Jmi73HwBIUl50j+osU4= -golang.org/x/crypto v0.21.0 h1:X31++rzVUdKhX5sWmSOFZxx8UW/ldWx55cbf08iNAMA= -golang.org/x/crypto v0.21.0/go.mod h1:0BP7YvVV9gBbVKyeTG0Gyn+gZm94bibOW5BjDEYAOMs= -golang.org/x/mod v0.2.0/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA= -golang.org/x/mod v0.3.0/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA= -golang.org/x/net v0.0.0-20190404232315-eb5bcb51f2a3/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg= -golang.org/x/net v0.0.0-20190620200207-3b0461eec859/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= -golang.org/x/net v0.0.0-20200226121028-0de0cce0169b/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= -golang.org/x/net v0.0.0-20201021035429-f5854403a974/go.mod h1:sp8m0HH+o8qH0wwXwYZr8TS3Oi6o0r6Gce1SSxlDquU= -golang.org/x/net v0.0.0-20211112202133-69e39bad7dc2/go.mod h1:9nx3DQGgdP8bBQD5qxJ1jj9UTztislL4KSBs9R2vV5Y= -golang.org/x/net v0.22.0 h1:9sGLhx7iRIHEiX0oAJ3MRZMUCElJgy7Br1nO+AMN3Tc= -golang.org/x/net v0.22.0/go.mod h1:JKghWKKOSdJwpW2GEx0Ja7fmaKnMsbu+MWVZTokSYmg= -golang.org/x/sync v0.0.0-20190423024810-112230192c58/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= -golang.org/x/sync v0.0.0-20190911185100-cd5d95a43a6e/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= -golang.org/x/sync v0.0.0-20201020160332-67f06af15bc9/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= -golang.org/x/sync v0.0.0-20210220032951-036812b2e83c/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= -golang.org/x/sync v0.6.0 h1:5BMeUDZ7vkXGfEr1x9B4bRcTH4lpkTkpdh0T/J+qjbQ= -golang.org/x/sync v0.6.0/go.mod h1:Czt+wKu1gCyEFDUtn0jG5QVvpJ6rzVqr5aXyt9drQfk= -golang.org/x/sys v0.0.0-20190215142949-d0b11bdaac8a/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= -golang.org/x/sys v0.0.0-20190412213103-97732733099d/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= -golang.org/x/sys v0.0.0-20200930185726-fdedc70b468f/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= -golang.org/x/sys v0.0.0-20201119102817-f84b799fce68/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= -golang.org/x/sys v0.0.0-20210423082822-04245dca01da/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= -golang.org/x/sys v0.0.0-20210615035016-665e8c7367d1/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= -golang.org/x/sys v0.0.0-20220715151400-c0bba94af5f8/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= -golang.org/x/sys v0.0.0-20220811171246-fbc7d0a398ab/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= +github.com/twitchyliquid64/golang-asm v0.15.1 h1:SU5vSMR7hnwNxj24w34ZyCi/FmDZTkS4MhqMhdFk5YI= +github.com/twitchyliquid64/golang-asm v0.15.1/go.mod h1:a1lVb/DtPvCB8fslRZhAngC2+aY1QWCk3Cedj/Gdt08= +github.com/ugorji/go/codec v1.2.12 h1:9LC83zGrHhuUA9l16C9AHXAqEV/2wBQ4nkvumAE65EE= +github.com/ugorji/go/codec v1.2.12/go.mod h1:UNopzCgEMSXjBc6AOMqYvWC1ktqTAfzJZUZgYf6w6lg= +golang.org/x/arch v0.0.0-20210923205945-b76863e36670/go.mod h1:5om86z9Hs0C8fWVUuoMHwpExlXzs5Tkyp9hOrfG7pp8= +golang.org/x/arch v0.8.0 h1:3wRIsP3pM4yUptoR96otTUOXI367OS0+c9eeRi9doIc= +golang.org/x/arch v0.8.0/go.mod h1:FEVrYAQjsQXMVJ1nsMoVVXPZg6p2JE2mx8psSWTDQys= +golang.org/x/crypto v0.27.0 h1:GXm2NjJrPaiv/h1tb2UH8QfgC/hOf/+z0p6PT8o1w7A= +golang.org/x/crypto v0.27.0/go.mod h1:1Xngt8kV6Dvbssa53Ziq6Eqn0HqbZi5Z6R0ZpwQzt70= +golang.org/x/net v0.29.0 h1:5ORfpBpCs4HzDYoodCDBbwHzdR5UrLBZ3sOnUJmFoHo= +golang.org/x/net v0.29.0/go.mod h1:gLkgy8jTGERgjzMic6DS9+SP0ajcu6Xu3Orq/SpETg0= +golang.org/x/sync v0.8.0 h1:3NFvSEYkUoMifnESzZl15y791HH1qU2xm6eCJU5ZPXQ= +golang.org/x/sync v0.8.0/go.mod h1:Czt+wKu1gCyEFDUtn0jG5QVvpJ6rzVqr5aXyt9drQfk= +golang.org/x/sys v0.5.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.6.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= -golang.org/x/sys v0.12.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.25.0 h1:r+8e+loiHxRqhXVl6ML1nO3l1+oFoWbnlu2Ehimmi34= golang.org/x/sys v0.25.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA= -golang.org/x/term v0.0.0-20201126162022-7de9c90e9dd1/go.mod h1:bj7SfCRtBDWHUb9snDiAeCFNEtKQo2Wmx5Cou7ajbmo= -golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ= -golang.org/x/text v0.3.3/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ= -golang.org/x/text v0.3.6/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ= -golang.org/x/text v0.3.7/go.mod h1:u+2+/6zg+i71rQMx5EYifcz6MCKuco9NR6JIITiCfzQ= -golang.org/x/text v0.14.0 h1:ScX5w1eTa3QqT8oi6+ziP7dTV1S2+ALU0bI+0zXKWiQ= -golang.org/x/text v0.14.0/go.mod h1:18ZOQIKpY8NJVqYksKHtTdi31H5itFRjB5/qKTNYzSU= -golang.org/x/tools v0.0.0-20180917221912-90fa682c2a6e/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ= -golang.org/x/tools v0.0.0-20191119224855-298f0cb1881e/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo= -golang.org/x/tools v0.0.0-20200619180055-7c47624df98f/go.mod h1:EkVYQZoAsY45+roYkvgYkIh4xh/qjgUK9TdY2XT94GE= -golang.org/x/tools v0.0.0-20210106214847-113979e3529a/go.mod h1:emZCQorbCU4vsT4fOWvOPXz4eW1wZW4PmDk9uLelYpA= -golang.org/x/xerrors v0.0.0-20190717185122-a985d3407aa7/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= -golang.org/x/xerrors v0.0.0-20191011141410-1b5146add898/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= -golang.org/x/xerrors v0.0.0-20191204190536-9bdfabe68543/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= -golang.org/x/xerrors v0.0.0-20200804184101-5ec99f83aff1/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= -google.golang.org/protobuf v1.26.0-rc.1/go.mod h1:jlhhOSvTdKEhbULTjvd4ARK9grFBp09yW+WbY/TyQbw= -google.golang.org/protobuf v1.27.1/go.mod h1:9q0QmTI4eRPtz6boOQmLYwt+qCgq0jsYwAQnmE0givc= +golang.org/x/text v0.18.0 h1:XvMDiNzPAl0jr17s6W9lcaIhGUfUORdGCNsuLmPG224= +golang.org/x/text v0.18.0/go.mod h1:BuEKDfySbSR4drPmRPG/7iBdf8hvFMuRexcpahXilzY= +google.golang.org/genproto/googleapis/rpc v0.0.0-20240903143218-8af14fe29dc1 h1:pPJltXNxVzT4pK9yD8vR9X75DaWYYmLGMsEvBfFQZzQ= +google.golang.org/genproto/googleapis/rpc v0.0.0-20240903143218-8af14fe29dc1/go.mod h1:UqMtugtsSgubUsoxbuAoiCXvqvErP7Gf0so0mK9tHxU= +google.golang.org/grpc v1.68.0 h1:aHQeeJbo8zAkAa3pRzrVjZlbz6uSfeOXlJNQM0RAbz0= +google.golang.org/grpc v1.68.0/go.mod h1:fmSPC5AsjSBCK54MyHRx48kpOti1/jRfOlwEWywNjWA= +google.golang.org/protobuf v1.35.2 h1:8Ar7bF+apOIoThw1EdZl0p1oWvMqTHmpA2fRTyZO8io= +google.golang.org/protobuf v1.35.2/go.mod h1:9fA7Ob0pmnwhb644+1+CVWFRbNajQ6iRojtC/QF5bRE= gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= -gopkg.in/check.v1 v1.0.0-20180628173108-788fd7840127/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= gopkg.in/check.v1 v1.0.0-20201130134442-10cb98267c6c h1:Hei/4ADfdWqJk1ZMxUNpqntNwaWcugrBjAiHlqqRiVk= gopkg.in/check.v1 v1.0.0-20201130134442-10cb98267c6c/go.mod h1:JHkPIbrfpd72SG/EVd6muEfDQjcINNoR0C8j2r3qZ4Q= gopkg.in/yaml.v3 v3.0.0-20200313102051-9f266ea9e77c/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM= gopkg.in/yaml.v3 v3.0.1 h1:fxVm/GzAzEWqLHuvctI91KS9hhNmmWOoWu0XTYJS7CA= gopkg.in/yaml.v3 v3.0.1/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM= -gorm.io/driver/clickhouse v0.6.1 h1:t7JMB6sLBXxN8hEO6RdzCbJCwq/jAEVZdwXlmQs1Sd4= -gorm.io/driver/clickhouse v0.6.1/go.mod h1:riMYpJcGZ3sJ/OAZZ1rEP1j/Y0H6cByOAnwz7fo2AyM= gorm.io/driver/postgres v1.5.9 h1:DkegyItji119OlcaLjqN11kHoUgZ/j13E0jkJZgD6A8= gorm.io/driver/postgres v1.5.9/go.mod h1:DX3GReXH+3FPWGrrgffdvCk3DQ1dwDPdmbenSkweRGI= gorm.io/gorm v1.25.12 h1:I0u8i2hWQItBq1WfE0o2+WuL9+8L21K9e2HHSTE/0f8= gorm.io/gorm v1.25.12/go.mod h1:xh7N7RHfYlNc5EmcI/El95gXusucDrQnHXe0+CgWcLQ= +nullprogram.com/x/optparse v1.0.0/go.mod h1:KdyPE+Igbe0jQUrVfMqDMeJQIJZEuyV7pjYmp6pbG50= +rsc.io/pdf v0.1.1/go.mod h1:n8OzWcQ6Sp37PL01nO98y4iUCRdTGarVfzxY20ICaU4= diff --git a/gossiper.go b/gossiper.go index d1c586f..2f25585 100644 --- a/gossiper.go +++ b/gossiper.go @@ -1,166 +1,78 @@ package gossiper import ( - "github.com/pieceowater-dev/lotof.lib.gossiper/internal" - "github.com/pieceowater-dev/lotof.lib.gossiper/internal/infra/amqp" - t "github.com/pieceowater-dev/lotof.lib.gossiper/types" + "github.com/gin-gonic/gin" + "github.com/pieceowater-dev/lotof.lib.gossiper/internal/core/db" + "github.com/pieceowater-dev/lotof.lib.gossiper/internal/core/servers" + grpcServ "github.com/pieceowater-dev/lotof.lib.gossiper/internal/core/servers/grpc" + "github.com/pieceowater-dev/lotof.lib.gossiper/internal/core/servers/rabbitmq" + rmqServ "github.com/pieceowater-dev/lotof.lib.gossiper/internal/core/servers/rabbitmq" + "github.com/pieceowater-dev/lotof.lib.gossiper/internal/core/servers/rest" + restServ "github.com/pieceowater-dev/lotof.lib.gossiper/internal/core/servers/rest" + "google.golang.org/grpc" ) -/* ENVIRONMENT */ +// Database aliases the internal database abstraction. +type Database = db.Database -// Env is an alias for the internal.Env. -// Provides access to environment-related utilities. -type Env = internal.Env +// DBFactory aliases the database factory for creating new database instances. +type DBFactory = db.DatabaseFactory -// EnvVars is a pointer alias for the internal.EnvVars. -// Used to manage environment variables globally. -var EnvVars = &internal.EnvVars +// DatabaseType represents the type of database being used. +type DatabaseType = db.DatabaseType -/* NETWORK */ - -// AMQP is an alias for the internal.AMQP. -// Provides RabbitMQ or AMQP related functions and configurations. -type AMQP = internal.AMQP - -func NewAMQPClient(queueName string, dsn string) (*amqp.Client, error) { - return internal.NewAMQPClient(queueName, dsn) -} - -// AMQMessage is an alias for the internal.DefaultMessage. -// Represents the structure of messages that are exchanged over AMQP. -type AMQMessage = t.DefaultMessage - -/* CONFIG */ - -// Config is an alias for the internal.Config. -// Represents the general application configuration structure. -type Config = internal.Config - -// EnvConfig is an alias for the internal.EnvConfig. -// Contains settings related to environment variables. -type EnvConfig = internal.EnvConfig - -// QueueConfig is an alias for the internal.QueueConfig. -// Defines configuration for RabbitMQ queues. -type QueueConfig = internal.QueueConfig - -// AMQPConsumerConfig is an alias for the internal.AMQPConsumerConfig. -// Holds the RabbitMQ consumer-specific configuration settings. -type AMQPConsumerConfig = internal.AMQPConsumerConfig - -// AMQPConsumeConfig is an alias for the internal.AMQPConsumeConfig. -// Describes how messages are consumed from RabbitMQ queues. -type AMQPConsumeConfig = internal.AMQPConsumeConfig - -// DBPGConfig is an alias for the internal.DBPGConfig. -// Defines PostgreSQL database configuration options. -type DBPGConfig = internal.DBPGConfig - -// DBClickHouseConfig is an alias for the internal.DBClickHouseConfig. -// Defines ClickHouse database configuration options. -type DBClickHouseConfig = internal.DBClickHouseConfig - -// DatabaseConfig is an alias for the internal.DatabaseConfig. -// Groups database configuration settings (e.g., PostgreSQL settings). -type DatabaseConfig = internal.DatabaseConfig - -/* TOOLS */ - -// Tools is an alias for the tools.Tools from the internal package. -// Provides various utility functions for the application. -type Tools = internal.Tools +// PostgresDB One of Supported database types. +const ( + PostgresDB DatabaseType = db.PostgresDB +) -// Satisfies checks if the provided data conforms to the destination structure. -// Useful for verifying if a structure meets the required interface or type. -func Satisfies(data any, dest any) error { - inst := Tools{} - return inst.Satisfies(data, dest) +// NewDB initializes a new database connection. +// - dbType: The type of database (e.g., PostgresDB). +// - dsn: The data source name for connecting to the database. +// - enableLogs: Whether to enable logging for the database. +// Returns a `Database` interface or an error if initialization fails. +func NewDB(dbType DatabaseType, dsn string, enableLogs bool) (Database, error) { + return db.New(dsn, enableLogs).Create(dbType) } -// LogAction logs an action with the provided data. -// A simple wrapper for logging actions within the application. -func LogAction(action string, data any) { - inst := Tools{} - inst.LogAction(action, data) -} +// ServerManager aliases the server manager for managing multiple servers. +type ServerManager = servers.ServerManager -// NewServiceError creates a new instance of internal.ServiceError. -// Used to generate a service-related error with an optional status code. -func NewServiceError(message string, statusCode ...int) *internal.ServiceError { - return internal.NewServiceError(message, statusCode...) -} +// GRPCServer represents a gRPC server instance. +type GRPCServer = grpcServ.Server -// Enum with aliases for predefined pagination page length. -// These constants define common pagination limits (e.g., 10, 20, 50 items per page). -const ( - TEN = internal.TEN - FIFTEEN = internal.FIFTEEN - TWENTY = internal.TWENTY - TWENTY_FIVE = internal.TWENTY_FIVE - THIRTY = internal.THIRTY - THIRTY_FIVE = internal.THIRTY_FIVE - FORTY = internal.FORTY - FORTY_FIVE = internal.FORTY_FIVE - FIFTY = internal.FIFTY - FIFTY_FIVE = internal.FIFTY_FIVE - SIXTY = internal.SIXTY - SIXTY_FIVE = internal.SIXTY_FIVE - SEVENTY = internal.SEVENTY - SEVENTY_FIVE = internal.SEVENTY_FIVE - EIGHTY = internal.EIGHTY - EIGHTY_FIVE = internal.EIGHTY_FIVE - NINETY = internal.NINETY - NINETY_FIVE = internal.NINETY_FIVE - ONE_HUNDRED = internal.ONE_HUNDRED -) - -// PaginatedEntity wraps internal.PaginatedEntity for convenience. -// A generic structure for paginated results, supporting any type `T`. -type PaginatedEntity[T any] struct { - internal.PaginatedEntity[T] -} +// RESTServer represents a REST server instance. +type RESTServer = rest.Server -// NewFilter creates a new types.DefaultFilter instance. -// This function initializes a default filter for any data type `T`. -func NewFilter[T any]() t.DefaultFilter[T] { - return internal.NewDefaultFilter[T]() -} +// RMQServer represents a RabbitMQ server instance. +type RMQServer = rabbitmq.Server -// ToPaginated converts items and count to a PaginatedEntity. -// Wraps a list of items and a count into a paginated entity for easier response formatting. -func ToPaginated[T any](items []T, count int) PaginatedEntity[T] { - return PaginatedEntity[T]{internal.ToPaginated[T](items, count)} +// NewServerManager creates a new instance of the server manager. +// The server manager is responsible for starting and stopping multiple server instances. +func NewServerManager() *ServerManager { + return servers.NewServerManager() } -// DontPanic is a wrapper for internal.DontPanic. -// It allows the application to recover from panics in the calling context. -func DontPanic() { - internal.DontPanic() +// NewGRPCServ creates a new gRPC server. +// - port: The port number for the server. +// - server: The gRPC server instance. +// - initRoute: A function to initialize the server's routes. +// Returns a `GRPCServer` instance. +func NewGRPCServ(port string, server *grpc.Server, initRoute func(server *grpc.Server)) *GRPCServer { + return grpcServ.New(port, server, initRoute) } -// Safely executes a function with panic recovery. -// It returns any errors that occur during execution, including panics. -// -// Parameters: -// -// fn - A function to be executed safely. -// -// Returns: -// -// An error if a panic occurred; otherwise, nil. -func Safely(fn func()) (err error) { - return internal.Safely(fn) +// NewRESTServ creates a new REST server. +// - port: The port number for the server. +// - router: The Gin router instance. +// - initRoute: A function to initialize the server's routes. +// Returns a `RESTServer` instance. +func NewRESTServ(port string, router *gin.Engine, initRoute func(router *gin.Engine)) *RESTServer { + return restServ.New(port, router, initRoute) } -/* BOOTSTRAP */ - -// Bootstrap is an alias for the internal.Bootstrap. -// This is used to set up the core of the application. -type Bootstrap = internal.Bootstrap - -// Setup initializes the bootstrap with the given configuration and handlers. -// This is where the application is configured, including startup logic and message handling. -func Setup(cfg internal.Config, startupFunc func() any, messageHandler func([]byte) any) { - b := Bootstrap{} - b.Setup(cfg, startupFunc, messageHandler) +// NewRMQServ creates a new RabbitMQ server. +// Returns an `RMQServer` instance. +func NewRMQServ() *RMQServer { + return rmqServ.New() } diff --git a/internal/boot/bootstrap.go b/internal/boot/bootstrap.go deleted file mode 100644 index 6917240..0000000 --- a/internal/boot/bootstrap.go +++ /dev/null @@ -1,61 +0,0 @@ -package boot - -import ( - "github.com/fatih/color" - "github.com/pieceowater-dev/lotof.lib.gossiper/internal/conf" - "github.com/pieceowater-dev/lotof.lib.gossiper/internal/env" - "github.com/pieceowater-dev/lotof.lib.gossiper/internal/infra" - "github.com/pieceowater-dev/lotof.lib.gossiper/internal/infra/db/ch" - "github.com/pieceowater-dev/lotof.lib.gossiper/internal/infra/db/pg" - "github.com/pieceowater-dev/lotof.lib.gossiper/internal/tools/panics" - "log" -) - -type Bootstrap struct { - PGDB *pg.PGDB - ClickHouse *ch.ClickHouseDB -} - -// Setup initializes the Gossiper package with the provided configuration and sets up AMQP consumers. -// It logs the process, handles the setup of environment variables, and executes a startup function. -// -// Parameters: -// - cfg: the configuration structure containing environment and AMQP settings. -// - messageHandler: a callback function to handle incoming RabbitMQ messages. -// - startupFunc: a function to execute after environment initialization. -func (b *Bootstrap) Setup(cfg conf.Config, startupFunc func() any, messageHandler func([]byte) any) { - defer panics.DontPanic() - color.Set(color.FgGreen) - log.SetFlags(log.LstdFlags) - log.Println("Setting up Gossiper...") - - // Initialize environment variables - envInst := &env.Env{} - envInst.Init(cfg.Env.Required) - - // Initialize the databases if it exists - if cfg.Database.PG.EnvPostgresDBDSN != "" { - b.PGDB = pg.NewPGDB(cfg.Database.PG) - b.PGDB.InitDB() - } - - if cfg.Database.ClickHouse.EnvClickHouseDBDSN != "" { - b.ClickHouse = ch.NewClickHouseDB(cfg.Database.ClickHouse) - b.ClickHouse.InitDB() - } - - color.Set(color.FgCyan) - log.Println("Setup complete.") - color.Set(color.Reset) - - // Execute the provided startup function if it exists - if startupFunc != nil { - startupFunc() - } - - // Setup AMQP Consumers if it exists - if len(cfg.AMQPConsumer.Consume) != 0 { - net := &infra.AMQP{ConsumerConfig: cfg.AMQPConsumer} - net.SetupAMQPConsumers(messageHandler) - } -} diff --git a/internal/conf/conf.go b/internal/conf/conf.go deleted file mode 100644 index a1cbcc5..0000000 --- a/internal/conf/conf.go +++ /dev/null @@ -1,82 +0,0 @@ -package conf - -import ( - "github.com/rabbitmq/amqp091-go" - "gorm.io/gorm" -) - -// Config holds the overall configuration for the gossiper package. -// It includes both the environment variable management (Env), RabbitMQ consumer configuration (AMQPConsumer), and Database settings (Database). -type Config struct { - Env EnvConfig // Environment variable settings (required for both RabbitMQ and Database) - AMQPConsumer AMQPConsumerConfig // RabbitMQ consumer settings - Database DatabaseConfig // Database configuration settings -} - -// DatabaseConfig defines the structure for database-related configurations. -type DatabaseConfig struct { - PG DBPGConfig // PG-specific configuration - ClickHouse DBClickHouseConfig // ClickHouse-specific configuration -} - -// DBPGConfig holds PostgreSQL-related configuration options. -type DBPGConfig struct { - EnvPostgresDBDSN string // Environment variable for PostgreSQL DSN - AutoMigrate bool // Whether to automatically run migrations - Models []any // List of models for auto-migration - GORMConfig *gorm.Config -} - -type DBClickHouseConfig struct { - EnvClickHouseDBDSN string // Environment variable for ClickHouse DSN - AutoMigrate bool // Whether to automatically run migrations - Models []any // List of models for auto-migration - GORMConfig *gorm.Config -} - -// EnvConfig defines the required environment variables needed by the application. -type EnvConfig struct { - Required []string // List of required environment variables for proper application functioning -} - -// Validate checks if all required environment variables are present. -// Returns nil for now, but can be extended to perform actual validation. -func (ec *EnvConfig) Validate() error { - return nil // Validation logic can be added here to ensure all required variables are set -} - -// QueueConfig defines the configuration for RabbitMQ queues. -type QueueConfig struct { - Name string // Name of the RabbitMQ queue to declare - Durable bool // If true, the queue persists across broker restarts - AutoDelete bool // If true, the queue is deleted when it's no longer used - Exclusive bool // If true, the queue is exclusive to the connection that declared it - NoWait bool // If true, the server doesn't wait for confirmation after declaring the queue - Args amqp091.Table // Custom arguments for queue declaration (advanced settings) -} - -// AMQPConsumerConfig holds the configuration for RabbitMQ consumers. -// It defines how RabbitMQ queues are consumed and what settings to apply for each consumer. -type AMQPConsumerConfig struct { - DSNEnv string // The environment variable that holds the RabbitMQ DSN - Queues []QueueConfig // List of queues to declare in RabbitMQ - Consume []AMQPConsumeConfig // List of consumers and their consumption settings -} - -// AMQPConsumeConfig defines the settings for consuming messages from a queue. -// Each consumer has specific settings such as auto-acknowledgment, exclusivity, etc. -type AMQPConsumeConfig struct { - Queue string // Name of the queue to consume messages from - Consumer string // Consumer tag that identifies this specific consumer - AutoAck bool // If true, the consumer automatically acknowledges messages after receiving them - Exclusive bool // If true, only this consumer can access the queue - NoLocal bool // If true, prevents messages published on the same connection from being consumed - NoWait bool // If true, the server doesn't wait for confirmation after setting up the consumer - Args amqp091.Table // Custom arguments for consumer setup (advanced settings) -} - -// Validate checks if the AMQP consumer configuration is valid. -// Currently, returns nil, but this function can be extended to validate the consumer setup. -func (acc *AMQPConsumerConfig) Validate() error { - return nil // Future validation logic to ensure correct consumer configurations can go here -} diff --git a/internal/core/db/database.go b/internal/core/db/database.go new file mode 100644 index 0000000..bb66e83 --- /dev/null +++ b/internal/core/db/database.go @@ -0,0 +1,46 @@ +package db + +import ( + "fmt" + postgresql "github.com/pieceowater-dev/lotof.lib.gossiper/internal/core/db/pg" + "gorm.io/gorm" +) + +const ( + PostgresDB DatabaseType = iota +) + +// Database defines the common methods for database operations +type Database interface { + GetDB() *gorm.DB + WithTransaction(func(tx *gorm.DB) error) error + SeedData(data []any) error +} + +// DatabaseType defines the type of databases supported +type DatabaseType int + +// DatabaseFactory is a factory for creating database instances +type DatabaseFactory struct { + DSN string + EnableLogs bool +} + +// New initializes a new DatabaseFactory +func New(dsn string, enableLogs bool) *DatabaseFactory { + return &DatabaseFactory{ + DSN: dsn, + EnableLogs: enableLogs, + } +} + +// Create creates a database instance based on the given type +func (f *DatabaseFactory) Create(dbType DatabaseType) (Database, error) { + switch dbType { + case PostgresDB: + return postgresql.NewPostgres(f.DSN, f.EnableLogs), nil + // Add more cases for other database types + default: + return nil, fmt.Errorf("unsupported database type: %v", dbType) + } +} diff --git a/internal/core/db/pg/pgsql.go b/internal/core/db/pg/pgsql.go new file mode 100644 index 0000000..5cc40b8 --- /dev/null +++ b/internal/core/db/pg/pgsql.go @@ -0,0 +1,97 @@ +package pg + +import ( + "fmt" + "gorm.io/driver/postgres" + "gorm.io/gorm" + "gorm.io/gorm/logger" + "log" + "os" + "reflect" + "time" +) + +type Postgres struct { + DB *gorm.DB +} + +// NewPostgres initializes the Postgres instance with a configurable logger +func NewPostgres(dsn string, enableLogs bool) *Postgres { + var newLogger logger.Interface + if enableLogs { + newLogger = logger.New( + log.New(os.Stdout, "\r\n", log.LstdFlags), + logger.Config{ + SlowThreshold: 200 * time.Millisecond, + LogLevel: logger.Info, + IgnoreRecordNotFoundError: true, + Colorful: true, + }, + ) + } else { + newLogger = logger.Default.LogMode(logger.Silent) + } + + db, err := gorm.Open(postgres.Open(dsn), &gorm.Config{ + Logger: newLogger, + }) + if err != nil { + log.Fatalf("Failed to connect to PostgreSQL: %v", err) + } + + sqlDB, err := db.DB() + if err != nil { + log.Fatalf("Failed to get DB instance: %v", err) + } + if err := sqlDB.Ping(); err != nil { + log.Fatalf("Failed to ping PostgreSQL: %v", err) + } + sqlDB.SetMaxOpenConns(25) + sqlDB.SetMaxIdleConns(10) + sqlDB.SetConnMaxLifetime(5 * time.Minute) + + //if err := db.AutoMigrate(&ent.Todo{}); err != nil { + // log.Fatalf("Failed to migrate database: %v", err) + //} + + return &Postgres{DB: db} +} + +// GetDB returns the GORM database instance +func (p *Postgres) GetDB() *gorm.DB { + return p.DB +} + +// WithTransaction executes a function within a transaction +func (p *Postgres) WithTransaction(fn func(tx *gorm.DB) error) error { + tx := p.DB.Begin() + if tx.Error != nil { + return tx.Error + } + + if err := fn(tx); err != nil { + tx.Rollback() + return err + } + + return tx.Commit().Error +} + +// SeedData populates the database with dynamic initial data +func (p *Postgres) SeedData(data []any) error { + value := reflect.ValueOf(data) + + for i := 0; i < value.Len(); i++ { + item := value.Index(i).Interface() + + elemType := reflect.TypeOf(item) + if elemType.Kind() != reflect.Ptr || elemType.Elem().Kind() != reflect.Struct { + return fmt.Errorf("invalid data type, expected a pointer to a struct, got %T", item) + } + + if err := p.DB.FirstOrCreate(item).Error; err != nil { + return fmt.Errorf("failed to seed data: %w", err) + } + } + return nil +} diff --git a/internal/core/servers/grpc/grpc.go b/internal/core/servers/grpc/grpc.go new file mode 100644 index 0000000..5247653 --- /dev/null +++ b/internal/core/servers/grpc/grpc.go @@ -0,0 +1,41 @@ +package grpc + +import ( + "google.golang.org/grpc" + "log" + "net" +) + +type Server struct { + Port string + Server *grpc.Server + InitRoute func(server *grpc.Server) +} + +func New(port string, server *grpc.Server, initRoute func(server *grpc.Server)) *Server { + if initRoute != nil { + initRoute(server) + } + return &Server{ + Port: port, + Server: server, + InitRoute: initRoute, + } +} + +func (g *Server) Start() error { + listener, err := net.Listen("tcp", ":"+g.Port) + if err != nil { + return err + } + log.Print("\033[32m") + log.Printf("gRPC server running on port %s", g.Port) + log.Print("\033[0m") + return g.Server.Serve(listener) +} + +func (g *Server) Stop() error { + g.Server.GracefulStop() + log.Println("gRPC server stopped") + return nil +} diff --git a/internal/core/servers/rabbitmq/rabbitmq.go b/internal/core/servers/rabbitmq/rabbitmq.go new file mode 100644 index 0000000..54f6a26 --- /dev/null +++ b/internal/core/servers/rabbitmq/rabbitmq.go @@ -0,0 +1,21 @@ +package rabbitmq + +import "log" + +type Server struct{} + +func New() *Server { + return &Server{} +} + +func (r *Server) Start() error { + log.Println("RabbitMQ server started") + // Add RabbitMQ listener initialization here + return nil +} + +func (r *Server) Stop() error { + log.Println("RabbitMQ server stopped") + // Add RabbitMQ cleanup here + return nil +} diff --git a/internal/core/servers/rest/rest.go b/internal/core/servers/rest/rest.go new file mode 100644 index 0000000..1ae173f --- /dev/null +++ b/internal/core/servers/rest/rest.go @@ -0,0 +1,38 @@ +package rest + +import ( + "github.com/gin-gonic/gin" + "log" +) + +type Server struct { + Port string + Router *gin.Engine + InitRoute func(router *gin.Engine) +} + +func New(port string, router *gin.Engine, initRoute func(router *gin.Engine)) *Server { + if err := router.SetTrustedProxies(nil); err != nil { + log.Fatalf("Failed to set trusted proxies: %v", err) + } + if initRoute != nil { + initRoute(router) + } + return &Server{ + Port: port, + Router: router, + InitRoute: initRoute, + } +} + +func (r *Server) Start() error { + log.Print("\033[32m") + log.Printf("REST server running on port %s", r.Port) + log.Print("\033[0m") + return r.Router.Run(":" + r.Port) +} + +func (r *Server) Stop() error { + log.Println("REST server stopping") + return nil +} diff --git a/internal/core/servers/server.go b/internal/core/servers/server.go new file mode 100644 index 0000000..aeef6cb --- /dev/null +++ b/internal/core/servers/server.go @@ -0,0 +1,47 @@ +package servers + +import ( + "log" + "sync" +) + +type Server interface { + Start() error + Stop() error +} + +type ServerManager struct { + servers []Server +} + +func NewServerManager() *ServerManager { + return &ServerManager{} +} + +func (sm *ServerManager) AddServer(server Server) { + sm.servers = append(sm.servers, server) +} + +func (sm *ServerManager) StartAll() { + + var wg sync.WaitGroup + for _, server := range sm.servers { + wg.Add(1) + go func(s Server) { + defer wg.Done() + if err := s.Start(); err != nil { + log.Printf("Error starting server: %v", err) + } + }(server) + } + wg.Wait() + +} + +func (sm *ServerManager) StopAll() { + for _, server := range sm.servers { + if err := server.Stop(); err != nil { + log.Printf("Error stopping server: %v", err) + } + } +} diff --git a/internal/env/env.go b/internal/env/env.go deleted file mode 100644 index 8a22e13..0000000 --- a/internal/env/env.go +++ /dev/null @@ -1,105 +0,0 @@ -package env - -import ( - "errors" - "github.com/fatih/color" - "github.com/joho/godotenv" - "github.com/pieceowater-dev/lotof.lib.gossiper/internal/tools" - "log" - "os" - "strings" -) - -// EnvVars holds the mapped environment variables. -var EnvVars map[string]string - -// Env provides environment handling methods such as loading, mapping, and validating environment variables. -type Env struct{} - -// Get retrieves the value of the environment variable identified by 'key'. -// Returns an error if the variable is not found. -func (e *Env) Get(key string) (string, error) { - value, exists := EnvVars[key] - if !exists { - return "", errors.New("environment variable not found: " + key) - } - return value, nil -} - -// LoadEnv loads environment variables from the `.env` file using the godotenv package. -// It returns an error if the file is missing or cannot be loaded. -func (e *Env) LoadEnv() error { - return godotenv.Load() -} - -// MapEnv maps all environment variables into the EnvVars map by splitting each variable on `=`. -// It uses the `tools.Split` function to handle splitting. -func (e *Env) MapEnv() { - EnvVars = make(map[string]string) - t := &tools.Tools{} // Instance of Tools to use the Split method. - for _, env := range os.Environ() { - pair := t.SplitOnce(env, '=') - if len(pair) == 2 { - EnvVars[pair[0]] = pair[1] // Store the key-value pair in EnvVars. - } - } -} - -// Init initializes the environment variables by loading them, mapping them, and validating required ones. -// It logs all user-defined environment variables and halts execution if required variables are missing. -func (e *Env) Init(required []string) { - // Load environment variables from the .env file (if present) - err := e.LoadEnv() - if err != nil { - log.Println("Error loading .env file:", err) - } - - // Map system environment variables and .env values into EnvVars - e.MapEnv() - - // List user-defined environment variables (ignoring system variables) - var userEnvVars []string - for key := range EnvVars { - if e.isUserEnvVar(key) { // Only include user-defined variables - userEnvVars = append(userEnvVars, key) - } - } - - // Log all user-defined environment variables in yellow - color.Set(color.FgYellow) - log.Println("Environment variables initialized:") - log.Printf("[%s]", strings.Join(userEnvVars, ", ")) - - // Validate the presence of required environment variables - for _, envKey := range required { - _, err := e.Get(envKey) - if err != nil { - // Log error in red if required variable is missing and stop execution - color.Set(color.FgRed) - log.Fatalf("Required environment variable %s not found: %v", envKey, err) - } - } -} - -// isUserEnvVar checks if the provided key is a user-defined environment variable. -// It excludes common system variables. -func (e *Env) isUserEnvVar(key string) bool { - systemVars := map[string]bool{ - "PATH": true, - "HOME": true, - "USER": true, - "PWD": true, - "SHELL": true, - "XPC_FLAGS": true, - "HOMEBREW_REPOSITORY": true, - "LC_CTYPE": true, - "HOMEBREW_PREFIX": true, - "SSH_AUTH_SOCK": true, - "OLDPWD": true, - "__CFBundleIdentifier": true, - "HOMEBREW_CELLAR": true, - "TMPDIR": true, - } - // Returns true if the key is not a common system variable - return !systemVars[key] -} diff --git a/internal/index.go b/internal/index.go deleted file mode 100644 index aea1724..0000000 --- a/internal/index.go +++ /dev/null @@ -1,155 +0,0 @@ -// Package internal provides core functionality and utilities for the Gossiper system. -package internal - -import ( - "github.com/pieceowater-dev/lotof.lib.gossiper/internal/boot" - "github.com/pieceowater-dev/lotof.lib.gossiper/internal/conf" - "github.com/pieceowater-dev/lotof.lib.gossiper/internal/env" - "github.com/pieceowater-dev/lotof.lib.gossiper/internal/infra" - "github.com/pieceowater-dev/lotof.lib.gossiper/internal/infra/amqp" - "github.com/pieceowater-dev/lotof.lib.gossiper/internal/tools" - "github.com/pieceowater-dev/lotof.lib.gossiper/internal/tools/formats/errors" - "github.com/pieceowater-dev/lotof.lib.gossiper/internal/tools/formats/filter" - "github.com/pieceowater-dev/lotof.lib.gossiper/internal/tools/formats/pagination" - "github.com/pieceowater-dev/lotof.lib.gossiper/internal/tools/panics" - t "github.com/pieceowater-dev/lotof.lib.gossiper/types" -) - -/* ENVIRONMENT */ - -// Env is an alias for the env.Env type. -// Handles environment configurations and is used to manage app-specific environment settings. -type Env = env.Env - -// EnvVars is a pointer to env.EnvVars, holding application environment variables globally. -var EnvVars = &env.EnvVars - -/* NETWORK */ - -// AMQP is an alias for the infra.AMQP type. -// Handles AMQP messaging operations, especially for RabbitMQ, providing key functions for producers and consumers. -type AMQP = infra.AMQP - -func NewAMQPClient(queueName string, dsn string) (*amqp.Client, error) { - return infra.NewAMQPClient(queueName, dsn) -} - -/* CONFIGURATION */ - -// Config is an alias for the conf.Config type. -// Manages global application configuration, including RabbitMQ and database configurations. -type Config = conf.Config - -// EnvConfig is an alias for the conf.EnvConfig type. -// Used to define and validate the required environment variables for the application. -type EnvConfig = conf.EnvConfig - -// QueueConfig is an alias for the conf.QueueConfig type. -// Specifies configurations for RabbitMQ queues, such as durability, exclusivity, and other settings. -type QueueConfig = conf.QueueConfig - -// AMQPConsumerConfig is an alias for the conf.AMQPConsumerConfig type. -// Contains settings specific to AMQP consumers like queue names and consumer tags. -type AMQPConsumerConfig = conf.AMQPConsumerConfig - -// AMQPConsumeConfig is an alias for the conf.AMQPConsumeConfig type. -// Manages configurations for message consumption (e.g., auto-acknowledge, exclusivity, etc.). -type AMQPConsumeConfig = conf.AMQPConsumeConfig - -// DBPGConfig is an alias for the conf.DBPGConfig database configuration. -type DBPGConfig = conf.DBPGConfig - -// DBClickHouseConfig is an alias for the conf.DBClickHouseConfig database configuration. -type DBClickHouseConfig = conf.DBClickHouseConfig - -// DatabaseConfig represents the overall database configuration. -type DatabaseConfig = conf.DatabaseConfig - -/* TOOLS */ - -// Tools is an alias for tools.Tools. -// A set of helper functions used throughout the system, such as logging and data validation. -type Tools = tools.Tools - -/* BOOTSTRAP */ - -// Bootstrap is an alias for boot.Bootstrap. -// Handles the application's initialization and setup processes, especially at startup. -type Bootstrap = boot.Bootstrap - -/* PAGINATION */ - -// ToPaginated converts a list of items and a total count into a paginated response. -// This function simplifies the creation of paginated responses for APIs. -func ToPaginated[T any](items []T, count int) PaginatedEntity[T] { - return PaginatedEntity[T]{pagination.ToPaginated(items, count)} -} - -// PaginatedEntity wraps the pagination.PaginatedEntity structure, simplifying its use. -type PaginatedEntity[T any] struct { - pagination.PaginatedEntity[T] -} - -// Predefined constants for common pagination page lengths. -// These values make it easy to implement paginated APIs with standard limits. -const ( - TEN = filter.TEN - FIFTEEN = filter.FIFTEEN - TWENTY = filter.TWENTY - TWENTY_FIVE = filter.TWENTY_FIVE - THIRTY = filter.THIRTY - THIRTY_FIVE = filter.THIRTY_FIVE - FORTY = filter.FORTY - FORTY_FIVE = filter.FORTY_FIVE - FIFTY = filter.FIFTY - FIFTY_FIVE = filter.FIFTY_FIVE - SIXTY = filter.SIXTY - SIXTY_FIVE = filter.SIXTY_FIVE - SEVENTY = filter.SEVENTY - SEVENTY_FIVE = filter.SEVENTY_FIVE - EIGHTY = filter.EIGHTY - EIGHTY_FIVE = filter.EIGHTY_FIVE - NINETY = filter.NINETY - NINETY_FIVE = filter.NINETY_FIVE - ONE_HUNDRED = filter.ONE_HUNDRED -) - -/* FILTERING */ - -// NewDefaultFilter creates a new filter for a given data type. -// This function wraps the filter.NewDefaultFilter method, making it easier to apply filtering logic. -func NewDefaultFilter[T any]() t.DefaultFilter[T] { - return filter.NewDefaultFilter[T]() -} - -/* ERROR HANDLING */ - -// ServiceError is an alias for errors.ServiceError. -// This type is used to represent errors within the application. -type ServiceError = errors.ServiceError - -// NewServiceError creates a new ServiceError instance. -// It accepts a message and an optional status code, facilitating structured error handling. -func NewServiceError(message string, statusCode ...int) *ServiceError { - return errors.NewServiceError(message, statusCode...) -} - -// DontPanic is a wrapper for panics.DontPanic. -// It allows the application to recover from panics in the calling context. -func DontPanic() { - panics.DontPanic() -} - -// Safely executes a function with panic recovery. -// It returns any errors that occur during execution, including panics. -// -// Parameters: -// -// fn - A function to be executed safely. -// -// Returns: -// -// An error if a panic occurred; otherwise, nil. -func Safely(fn func()) (err error) { - return panics.Safely(fn) -} diff --git a/internal/infra/amqp/amqp.go b/internal/infra/amqp/amqp.go deleted file mode 100644 index 3b777d9..0000000 --- a/internal/infra/amqp/amqp.go +++ /dev/null @@ -1,87 +0,0 @@ -package amqp - -import ( - "github.com/pieceowater-dev/lotof.lib.gossiper/internal/conf" - "github.com/pieceowater-dev/lotof.lib.gossiper/internal/env" - "github.com/pieceowater-dev/lotof.lib.gossiper/internal/tools/panics" - amqp "github.com/rabbitmq/amqp091-go" - "log" - "strings" -) - -// AMQP holds the RabbitMQ consumer configuration -type AMQP struct { - ConsumerConfig conf.AMQPConsumerConfig // Configuration for RabbitMQ consumers -} - -// SetupAMQPConsumers initializes and starts RabbitMQ consumers based on the configuration in AMQP. -// It processes incoming messages and uses the provided messageHandler to handle them. -func (n *AMQP) SetupAMQPConsumers(messageHandler func([]byte) any) { - defer panics.DontPanic() - if messageHandler == nil { - messageHandler = DefaultHandleMessage - } - - // Load RabbitMQ DSN from environment variables - envInst := env.Env{} - dsn, err := envInst.Get(n.ConsumerConfig.DSNEnv) - if err != nil { - log.Fatalf("Error loading DSN: %v", err) - } - - // Establish a connection to RabbitMQ - conn, err := amqp.Dial(dsn) - handleError(err, "Failed to connect to RabbitMQ") - defer conn.Close() // Ensure connection is closed when done - - // Open a channel over the connection - ch, err := conn.Channel() - handleError(err, "Failed to open a channel") - defer ch.Close() // Ensure channel is closed - - var queueNames []string - - // Declare queues first from QueueConfig - for _, queueConfig := range n.ConsumerConfig.Queues { - if err := declareQueue(ch, queueConfig); err != nil { - log.Fatalf("Failed to declare queue: %v", err) - } - queueNames = append(queueNames, queueConfig.Name) - } - - // Now set up the consumers from AMQPConsumeConfig - for _, consumeConfig := range n.ConsumerConfig.Consume { - msgs, err := ch.Consume( - consumeConfig.Queue, - consumeConfig.Consumer, - consumeConfig.AutoAck, - consumeConfig.Exclusive, - consumeConfig.NoLocal, - consumeConfig.NoWait, - consumeConfig.Args, - ) - handleError(err, "Failed to register a consumer") - - // Start a goroutine to handle messages asynchronously - go n.handleMessages(msgs, ch, messageHandler) - } - - // Log that the consumer setup is complete and the service is ready for messages - log.Printf("Service successfully started! [%s]", " ⇆"+strings.Join(queueNames, " ⇆")) - - // Block the function indefinitely, waiting for messages - select {} -} - -// declareQueue declares a RabbitMQ queue if it doesn't already exist. -func declareQueue(ch *amqp.Channel, queueConfig conf.QueueConfig) error { - _, err := ch.QueueDeclare( - queueConfig.Name, - queueConfig.Durable, - queueConfig.AutoDelete, - queueConfig.Exclusive, - queueConfig.NoWait, - queueConfig.Args, // Additional arguments for queue declaration - ) - return err -} diff --git a/internal/infra/amqp/client.go b/internal/infra/amqp/client.go deleted file mode 100644 index 6611694..0000000 --- a/internal/infra/amqp/client.go +++ /dev/null @@ -1,177 +0,0 @@ -package amqp - -import ( - "context" - "fmt" - "log" - "time" - - amqp "github.com/rabbitmq/amqp091-go" -) - -// Client struct to manage AMQP connections, channels, queues, etc. -type Client struct { - QueueName string - DSN string - conn *amqp.Connection - channel *amqp.Channel -} - -// New creates a new AMQP client -func New(queueName, dsn string) (*Client, error) { - client := &Client{ - QueueName: queueName, - DSN: dsn, - } - - if err := client.connect(); err != nil { - return nil, err - } - - return client, nil -} - -// connect establishes a connection to RabbitMQ -func (c *Client) connect() error { - conn, err := amqp.Dial(c.DSN) - if err != nil { - return fmt.Errorf("failed to connect to RabbitMQ: %w", err) - } - - ch, err := conn.Channel() - if err != nil { - return fmt.Errorf("failed to open a channel: %w", err) - } - - c.conn = conn - c.channel = ch - - return nil -} - -// Close closes the connection and channel -func (c *Client) Close() { - if c.channel != nil { - _ = c.channel.Close() - } - if c.conn != nil { - _ = c.conn.Close() - } -} - -// SendMessage sends a message to the queue with retry logic and optional reply -func (c *Client) SendMessage(body []byte, reply bool) ([]byte, error) { - var err error - for i := 0; i < 3; i++ { - if reply { - response, err := c.sendWithReply(body) - if err == nil { - return response, nil - } - } else { - err = c.send(body) - if err == nil { - return nil, nil - } - } - log.Printf("Error sending message, retrying... Attempt %d/3", i+1) - time.Sleep(3 * time.Second) - } - return nil, err -} - -// send sends a message without waiting for a reply -func (c *Client) send(body []byte) error { - return c.channel.PublishWithContext( - context.TODO(), - "", // exchange - c.QueueName, - false, // mandatory - false, // immediate - amqp.Publishing{ - ContentType: "application/json", - Body: body, - }, - ) -} - -// sendWithReply sends a message and waits for a reply -func (c *Client) sendWithReply(body []byte) ([]byte, error) { - // Declare an anonymous queue for replies - q, err := c.channel.QueueDeclare( - "", // name - false, // durable - true, // autoDelete - false, // exclusive - false, // noWait - nil, // args - ) - if err != nil { - return nil, fmt.Errorf("failed to declare reply queue: %w", err) - } - - //corrID := fmt.Sprintf("%d", time.Now().UnixNano()) - - // Set up the message publishing with a reply-to header - err = c.channel.PublishWithContext( - context.TODO(), - "", // exchange - c.QueueName, - false, // mandatory - false, // immediate - amqp.Publishing{ - ContentType: "application/json", - Body: body, - ReplyTo: q.Name, - CorrelationId: fmt.Sprintf("%d", time.Now().UnixNano()), // adjust as needed - }, - ) - if err != nil { - return nil, fmt.Errorf("failed to publish message: %w", err) - } - - // Consume the reply message - msgs, err := c.channel.Consume( - q.Name, // queue - "", // consumer - true, // autoAck - false, // exclusive - false, // noLocal - false, // noWait - nil, // args - ) - if err != nil { - return nil, fmt.Errorf("failed to consume reply: %w", err) - } - - // Wait for a single response - for msg := range msgs { - return msg.Body, nil - } - - return nil, fmt.Errorf("no reply received") -} - -// Consume starts consuming messages from the queue -func (c *Client) Consume(handler func([]byte)) error { - msgs, err := c.channel.Consume( - c.QueueName, - "", - true, // autoAck - false, // exclusive - false, // noLocal - false, // noWait - nil, - ) - if err != nil { - return fmt.Errorf("failed to register a consumer: %w", err) - } - - go func() { - for d := range msgs { - handler(d.Body) - } - }() - - return nil -} diff --git a/internal/infra/amqp/handler.go b/internal/infra/amqp/handler.go deleted file mode 100644 index 12c35f3..0000000 --- a/internal/infra/amqp/handler.go +++ /dev/null @@ -1,40 +0,0 @@ -package amqp - -import ( - "encoding/json" - t "github.com/pieceowater-dev/lotof.lib.gossiper/types" - amqp "github.com/rabbitmq/amqp091-go" - "log" -) - -// DefaultHandleMessage is the default message handler used by Gossiper. -// It unmarshals the message into a DefaultMessage structure and logs the pattern. -func DefaultHandleMessage(msg []byte) any { - var defaultMessage t.DefaultMessage - if err := json.Unmarshal(msg, &defaultMessage); err != nil { - log.Println("Failed to unmarshal message:", err) - return nil - } - log.Printf("Received message: %s", defaultMessage.Pattern) - return "OK" // Default response; modify based on message type -} - -// handleMessages processes incoming messages asynchronously. -func (n *AMQP) handleMessages(msgs <-chan amqp.Delivery, ch *amqp.Channel, messageHandler func([]byte) any) { - for d := range msgs { - response := messageHandler(d.Body) - - if d.ReplyTo != "" { - if err := n.publishResponse(ch, d.ReplyTo, d.CorrelationId, response); err != nil { - log.Println("Failed to publish response:", err) - } - } - } -} - -// handleError checks the error and logs it with a custom message. -func handleError(err error, msg string) { - if err != nil { - log.Fatalf("%s: %v", msg, err) - } -} diff --git a/internal/infra/amqp/publisher.go b/internal/infra/amqp/publisher.go deleted file mode 100644 index 229411c..0000000 --- a/internal/infra/amqp/publisher.go +++ /dev/null @@ -1,26 +0,0 @@ -package amqp - -import ( - "encoding/json" - amqp "github.com/rabbitmq/amqp091-go" -) - -// publishResponse publishes a response to the reply queue. -func (n *AMQP) publishResponse(ch *amqp.Channel, replyTo string, correlationID string, response any) error { - responseBytes, err := json.Marshal(response) - if err != nil { - return err - } - - return ch.Publish( - "", - replyTo, - false, - false, - amqp.Publishing{ - ContentType: "application/json", - CorrelationId: correlationID, - Body: responseBytes, - }, - ) -} diff --git a/internal/infra/db/ch/clickhouse.go b/internal/infra/db/ch/clickhouse.go deleted file mode 100644 index c9ad3b4..0000000 --- a/internal/infra/db/ch/clickhouse.go +++ /dev/null @@ -1,106 +0,0 @@ -package ch - -import ( - "github.com/pieceowater-dev/lotof.lib.gossiper/internal/conf" - "github.com/pieceowater-dev/lotof.lib.gossiper/internal/env" - "gorm.io/driver/clickhouse" - "gorm.io/gorm" - "log" -) - -type ClickHouseDB struct { - db *gorm.DB // Holds the database connection - autoMigrate bool // Determines if auto-migration should run - dsn string // DSN environment variable key for database connection - models []any // Models for database migration - gormConf *gorm.Config -} - -// NewClickHouseDB creates a new instance of ClickHouseDB, configuring it with options -// for automatic migration, the DSN key, and the models to be migrated. -// -// Parameters: -// - config: conf.DBClickHouseConfig struct containing AutoMigrate flag, the DSN key, -// and models slice for database migration. -// -// Returns: -// - A new ClickHouseDB instance. -func NewClickHouseDB(config conf.DBClickHouseConfig) *ClickHouseDB { - // Provide a default GORM config if none is passed to avoid nil pointer dereference. - if config.GORMConfig == nil { - config.GORMConfig = &gorm.Config{} - } - - return &ClickHouseDB{ - autoMigrate: config.AutoMigrate, - dsn: config.EnvClickHouseDBDSN, - models: config.Models, - gormConf: config.GORMConfig, - } -} - -// InitDB initializes the ClickHouse database connection using GORM and the provided DSN. -// If auto-migration is enabled, it will migrate the provided models automatically. -// -// If auto-migration is disabled, the method will log that manual migration is expected. -// -// Logs an error and terminates the program if the database connection or migration fails. -func (d *ClickHouseDB) InitDB() { - dsn := d.getClickHouseDSN() - if dsn == "" { - log.Fatalf("DSN not found: %s", d.dsn) - } - - var err error - d.db, err = gorm.Open(clickhouse.Open(dsn), d.gormConf) - if err != nil { - log.Fatalf("failed to connect to database: %v", err) - } - log.Printf("connected to database") - - if d.autoMigrate { - for _, model := range d.models { - if !d.db.Migrator().HasTable(model) { - err = d.db.AutoMigrate(model) - if err != nil { - log.Fatalf("failed to auto-migrate model %v: %v", model, err) - } - log.Printf("auto-migrated model: %v", model) - } else { - log.Printf("Table already exists for model: %v. Skipping migration.", model) - } - } - } else { - log.Println("Manual migration mode enabled. Skipping auto-migration.") - } -} - -// GetDB returns the active ClickHouse database connection. -// -// Returns: -// - The *gorm.DB instance representing the database connection. -func (d *ClickHouseDB) GetDB() *gorm.DB { - return d.db -} - -// getClickHouseDSN retrieves the ClickHouse DSN from the environment using the key -// provided in the ClickHouseDB instance. The DSN is required to establish the database connection. -// -// Logs the process of retrieving the connection string, and returns an empty string -// if the retrieval fails. -// -// Returns: -// - The DSN string for ClickHouse connection. -func (d *ClickHouseDB) getClickHouseDSN() string { - envInstance := &env.Env{} - log.Printf("retrieving connection string by " + d.dsn) - val, err := envInstance.Get(d.dsn) - if err != nil { - log.Printf("failed to retrieve DSN: %v", err) - return "" - } - if val == "" { - log.Printf("DSN is empty for key: %s", d.dsn) - } - return val -} diff --git a/internal/infra/db/pg/pg.go b/internal/infra/db/pg/pg.go deleted file mode 100644 index 43588ce..0000000 --- a/internal/infra/db/pg/pg.go +++ /dev/null @@ -1,102 +0,0 @@ -package pg - -import ( - "github.com/pieceowater-dev/lotof.lib.gossiper/internal/conf" - "github.com/pieceowater-dev/lotof.lib.gossiper/internal/env" - "gorm.io/driver/postgres" - "gorm.io/gorm" - "log" -) - -// PGDB is a wrapper struct for managing PostgreSQL database connections -// and handling migrations using GORM. -type PGDB struct { - db *gorm.DB // Holds the database connection - autoMigrate bool // Determines if auto-migration should run - dsn string // DSN environment variable key for database connection - models []any // Models for database migration - gormConf *gorm.Config -} - -// NewPGDB creates a new instance of PGDB, configuring it with options -// for automatic migration, the DSN key, and the models to be migrated. -// -// Parameters: -// - config: conf.DBPGConfig struct containing AutoMigrate flag, the DSN key, -// and models slice for database migration. -// -// Returns: -// - A new PGDB instance. -func NewPGDB(config conf.DBPGConfig) *PGDB { - if config.GORMConfig == nil { - // Provide a default GORM config if none is passed. - config.GORMConfig = &gorm.Config{} - } - - return &PGDB{ - autoMigrate: config.AutoMigrate, - dsn: config.EnvPostgresDBDSN, - models: config.Models, - gormConf: config.GORMConfig, - } -} - -// InitDB initializes the PostgreSQL database connection using GORM and the provided DSN. -// If auto-migration is enabled, it will migrate the provided models automatically. -// -// If auto-migration is disabled, the method will log that manual migration is expected. -// -// Logs an error and terminates the program if the database connection or migration fails. -func (d *PGDB) InitDB() { - dsn := d.getPostgresDSN() - if dsn == "" { - log.Fatalf("DSN not found: %s", d.dsn) - } - - var err error - d.db, err = gorm.Open(postgres.Open(dsn), d.gormConf) - if err != nil { - log.Fatalf("failed to connect to database: %v", err) - } - log.Printf("connected to database") - - if d.autoMigrate { - err = d.db.AutoMigrate(d.models...) - if err != nil { - log.Fatalf("failed to auto-migrate: %v", err) - } - log.Printf("auto-migrate complete") - } else { - log.Println("Manual migration mode enabled. Skipping auto-migration.") - } -} - -// GetDB returns the active PostgreSQL database connection. -// -// Returns: -// - The *gorm.DB instance representing the database connection. -func (d *PGDB) GetDB() *gorm.DB { - return d.db -} - -// getPostgresDSN retrieves the PostgreSQL DSN from the environment using the key -// provided in the PGDB instance. The DSN is required to establish the database connection. -// -// Logs the process of retrieving the connection string, and returns an empty string -// if the retrieval fails. -// -// Returns: -// - The DSN string for PostgreSQL connection. -func (d *PGDB) getPostgresDSN() string { - envInstance := &env.Env{} - log.Printf("retrieving connection string by " + d.dsn) - val, err := envInstance.Get(d.dsn) - if err != nil { - log.Printf("failed to retrieve DSN: %v", err) - return "" - } - if val == "" { - log.Printf("DSN is empty for key: %s", d.dsn) - } - return val -} diff --git a/internal/infra/infra.go b/internal/infra/infra.go deleted file mode 100644 index 61a1948..0000000 --- a/internal/infra/infra.go +++ /dev/null @@ -1,11 +0,0 @@ -package infra - -import ( - "github.com/pieceowater-dev/lotof.lib.gossiper/internal/infra/amqp" -) - -type AMQP = amqp.AMQP - -func NewAMQPClient(queueName string, dsn string) (*amqp.Client, error) { - return amqp.New(queueName, dsn) -} diff --git a/internal/tools/formats/errors/error.go b/internal/tools/formats/errors/error.go deleted file mode 100644 index ed02ec5..0000000 --- a/internal/tools/formats/errors/error.go +++ /dev/null @@ -1,36 +0,0 @@ -package errors - -import ( - "net/http" -) - -// ServiceError represents a custom error used throughout the application. -type ServiceError struct { - StatusCode int `json:"statusCode"` - Message string `json:"message"` -} - -// Error returns the error message, making ServiceError comply with the error interface. -func (e *ServiceError) Error() string { - return e.Message -} - -// GetError returns a map representing the error status code and message. -func (e *ServiceError) GetError() map[string]any { - return map[string]any{ - "message": e.Message, - "statusCode": e.StatusCode, - } -} - -// NewServiceError creates a new ServiceError instance with the given message and status code. -func NewServiceError(message string, statusCode ...int) *ServiceError { - code := http.StatusInternalServerError - if len(statusCode) > 0 { - code = statusCode[0] - } - return &ServiceError{ - Message: message, - StatusCode: code, - } -} diff --git a/internal/tools/formats/filter/filter.go b/internal/tools/formats/filter/filter.go deleted file mode 100644 index a6f09ab..0000000 --- a/internal/tools/formats/filter/filter.go +++ /dev/null @@ -1,46 +0,0 @@ -package filter - -import t "github.com/pieceowater-dev/lotof.lib.gossiper/types" - -const ( - // ASC represents ascending sort order. - ASC t.FilterSortByEnum = "ASC" - // DESC represents descending sort order. - DESC t.FilterSortByEnum = "DESC" -) - -const ( - TEN t.FilterPaginationLengthEnum = 10 - FIFTEEN t.FilterPaginationLengthEnum = 15 - TWENTY t.FilterPaginationLengthEnum = 20 - TWENTY_FIVE t.FilterPaginationLengthEnum = 25 - THIRTY t.FilterPaginationLengthEnum = 30 - THIRTY_FIVE t.FilterPaginationLengthEnum = 35 - FORTY t.FilterPaginationLengthEnum = 40 - FORTY_FIVE t.FilterPaginationLengthEnum = 45 - FIFTY t.FilterPaginationLengthEnum = 50 - FIFTY_FIVE t.FilterPaginationLengthEnum = 55 - SIXTY t.FilterPaginationLengthEnum = 60 - SIXTY_FIVE t.FilterPaginationLengthEnum = 65 - SEVENTY t.FilterPaginationLengthEnum = 70 - SEVENTY_FIVE t.FilterPaginationLengthEnum = 75 - EIGHTY t.FilterPaginationLengthEnum = 80 - EIGHTY_FIVE t.FilterPaginationLengthEnum = 85 - NINETY t.FilterPaginationLengthEnum = 90 - NINETY_FIVE t.FilterPaginationLengthEnum = 95 - ONE_HUNDRED t.FilterPaginationLengthEnum = 100 -) - -// NewDefaultFilter - Constructor for DefaultFilter with default pagination values. -func NewDefaultFilter[T any]() t.DefaultFilter[T] { - return t.DefaultFilter[T]{ - Sort: t.Sort[T]{ - Field: "id", - By: DESC, - }, - Pagination: t.Paginated{ - Page: 1, - Length: TEN, - }, - } -} diff --git a/internal/tools/formats/pagination/pagination.go b/internal/tools/formats/pagination/pagination.go deleted file mode 100644 index ac0b1fc..0000000 --- a/internal/tools/formats/pagination/pagination.go +++ /dev/null @@ -1,23 +0,0 @@ -package pagination - -// EntityInfo provides additional metadata for a paginated entity. -type EntityInfo struct { - Count int `json:"count"` // Total number of entities -} - -// PaginatedEntity wraps paginated results along with entity metadata. -type PaginatedEntity[T any] struct { - Rows []T `json:"rows"` // Slice of entities for the current page - Info EntityInfo `json:"info"` // Metadata including total count -} - -// ToPaginated converts raw data (a slice of entities and count) to a PaginatedEntity. -func ToPaginated[T any](items []T, count int) PaginatedEntity[T] { - // Return a PaginatedEntity with the rows and entity count - return PaginatedEntity[T]{ - Rows: items, - Info: EntityInfo{ - Count: count, - }, - } -} diff --git a/internal/tools/logger.go b/internal/tools/logger.go deleted file mode 100644 index 3b122e2..0000000 --- a/internal/tools/logger.go +++ /dev/null @@ -1,49 +0,0 @@ -package tools - -import ( - "encoding/json" - "github.com/rs/zerolog/log" - "github.com/sirupsen/logrus" - "os" -) - -func (inst *Tools) LogAction(action string, data any) { - body, err := json.Marshal(data) - if err != nil { - log.Error().Err(err).Msg("Failed to marshal log data") - return - } - - log.Info().Str("action", action).Bytes("data", body).Msg("Action logged") -} - -var Logger *logrus.Logger - -func init() { - Logger = logrus.New() - - // Set the output to stdout - Logger.SetOutput(os.Stdout) - - // Set the log level (can be adjusted based on the environment) - Logger.SetLevel(logrus.InfoLevel) - - // Optionally, set a formatter (e.g., JSON or Text) - Logger.SetFormatter(&logrus.TextFormatter{ - FullTimestamp: true, - }) -} - -// LogError logs an error message with additional context -func LogError(err error, context string) { - Logger.WithFields(logrus.Fields{ - "context": context, - }).Error(err) -} - -// LogPanic logs a panic message and recovers from it -func LogPanic(err interface{}) { - Logger.WithFields(logrus.Fields{ - "panic": err, - }).Panic("A panic occurred") -} diff --git a/internal/tools/panics/handler.go b/internal/tools/panics/handler.go deleted file mode 100644 index f6416b1..0000000 --- a/internal/tools/panics/handler.go +++ /dev/null @@ -1,15 +0,0 @@ -package panics - -import ( - "github.com/rs/zerolog/log" - "runtime/debug" -) - -// DontPanic is a utility function that handles and logs panics. -// It recovers from a panic, if one occurs, and logs the panic message. -// This function is useful for ensuring that panics do not crash the application. -func DontPanic() { - if r := recover(); r != nil { - log.Printf("Recovered from panic: %v\n%s", r, debug.Stack()) - } -} diff --git a/internal/tools/panics/safely.go b/internal/tools/panics/safely.go deleted file mode 100644 index 891bc74..0000000 --- a/internal/tools/panics/safely.go +++ /dev/null @@ -1,24 +0,0 @@ -package panics - -import "fmt" - -// Safely runs a function with panic recovery and returns any errors. -// It executes the provided function and catches any panic that occurs. -// If a panic happens, it returns an error detailing the panic. -// -// Parameters: -// -// fn - A function to be executed safely. -// -// Returns: -// -// An error if a panic occurred; otherwise, nil. -func Safely(fn func()) (err error) { - defer func() { - if r := recover(); r != nil { - err = fmt.Errorf("panic occurred: %v", r) - } - }() - fn() - return -} diff --git a/internal/tools/satisfies.go b/internal/tools/satisfies.go deleted file mode 100644 index 3b6b26b..0000000 --- a/internal/tools/satisfies.go +++ /dev/null @@ -1,282 +0,0 @@ -package tools - -import ( - "encoding/json" - "errors" - "github.com/go-playground/validator/v10" - "github.com/sirupsen/logrus" - "reflect" - "strconv" - "time" -) - -var validate = validator.New() - -// setDefaultsAndConvert sets default values for fields in the destination struct that are zero values. -// It also converts string representations of default values to their appropriate types. -func (inst *Tools) setDefaultsAndConvert(dest any) error { - v := reflect.ValueOf(dest).Elem() - t := v.Type() - - for i := 0; i < v.NumField(); i++ { - field := v.Field(i) - fieldType := t.Field(i) - - defaultValue, ok := fieldType.Tag.Lookup("default") - if ok && field.IsZero() { - switch field.Kind() { - case reflect.Ptr: - elemType := fieldType.Type.Elem().Kind() - if err := setDefaultValueForPointerField(field, elemType, defaultValue); err != nil { - return err - } - default: - if err := setDefaultValueForValueField(field, field.Kind(), defaultValue); err != nil { - return err - } - } - } - } - return nil -} - -// setDefaultValueForPointerField sets the default value for pointer fields. -func setDefaultValueForPointerField(field reflect.Value, elemType reflect.Kind, defaultValue string) error { - switch elemType { - case reflect.String: - field.Set(reflect.ValueOf(&defaultValue)) - case reflect.Int: - if intValue, err := strconv.Atoi(defaultValue); err == nil { - field.Set(reflect.ValueOf(&intValue)) - } else { - logrus.WithFields(logrus.Fields{"field": field.Type().Name(), "default": defaultValue}).Error("Failed to convert default value to int") - return errors.New("invalid default value for int") - } - case reflect.Float64: - if floatValue, err := strconv.ParseFloat(defaultValue, 64); err == nil { - field.Set(reflect.ValueOf(&floatValue)) - } else { - logrus.WithFields(logrus.Fields{"field": field.Type().Name(), "default": defaultValue}).Error("Failed to convert default value to float64") - return errors.New("invalid default value for float64") - } - case reflect.Bool: - if boolValue, err := strconv.ParseBool(defaultValue); err == nil { - field.Set(reflect.ValueOf(&boolValue)) - } else { - logrus.WithFields(logrus.Fields{"field": field.Type().Name(), "default": defaultValue}).Error("Failed to convert default value to bool") - return errors.New("invalid default value for bool") - } - case reflect.Struct: - // Check if the field is a pointer to time.Time - if field.Type().Elem() == reflect.TypeOf(time.Time{}) { - if timeValue, err := time.Parse(time.RFC3339, defaultValue); err == nil { - field.Set(reflect.ValueOf(&timeValue)) - } else { - logrus.WithFields(logrus.Fields{"field": field.Type().Name(), "default": defaultValue}).Error("Failed to convert default value to time.Time") - return errors.New("invalid default value for time.Time") - } - } - default: - logrus.WithFields(logrus.Fields{"field": field.Type().Name(), "default": defaultValue}).Error("Unhandled default case for pointer type") - return errors.New("unhandled default case for pointer type") - } - return nil -} - -// setDefaultValueForValueField sets the default value for non-pointer fields. -func setDefaultValueForValueField(field reflect.Value, kind reflect.Kind, defaultValue string) error { - switch kind { - case reflect.Int: - if intValue, err := strconv.Atoi(defaultValue); err == nil { - field.SetInt(int64(intValue)) - } else { - logrus.WithFields(logrus.Fields{"field": field.Type().Name(), "default": defaultValue}).Error("Failed to convert default value to int") - return errors.New("invalid default value for int") - } - case reflect.Float64: - if floatValue, err := strconv.ParseFloat(defaultValue, 64); err == nil { - field.SetFloat(floatValue) - } else { - logrus.WithFields(logrus.Fields{"field": field.Type().Name(), "default": defaultValue}).Error("Failed to convert default value to float64") - return errors.New("invalid default value for float64") - } - case reflect.Bool: - if boolValue, err := strconv.ParseBool(defaultValue); err == nil { - field.SetBool(boolValue) - } else { - logrus.WithFields(logrus.Fields{"field": field.Type().Name(), "default": defaultValue}).Error("Failed to convert default value to bool") - return errors.New("invalid default value for bool") - } - case reflect.Struct: - if field.Type() == reflect.TypeOf(time.Time{}) { - if timeValue, err := time.Parse(time.RFC3339, defaultValue); err == nil { - field.Set(reflect.ValueOf(timeValue)) - } else { - logrus.WithFields(logrus.Fields{"field": field.Type().Name(), "default": defaultValue}).Error("Failed to convert default value to time.Time") - return errors.New("invalid default value for time.Time") - } - } - default: - logrus.WithFields(logrus.Fields{"field": field.Type().Name()}).Error("Unhandled default case for value type") - return errors.New("unhandled default case for value type") - } - return nil -} - -// convertFields converts string values in the provided data map to the appropriate types -// in the destination struct. It supports converting basic types and pointer types. -func (inst *Tools) convertFields(data map[string]any, dest any) error { - v := reflect.ValueOf(dest).Elem() - t := v.Type() // Get the type of the destination struct - - for key, value := range data { - field := v.FieldByNameFunc(func(fieldName string) bool { - // Use the type of the struct to get the field type - fieldType, found := t.FieldByName(fieldName) - if !found { - return false - } - tag, _ := fieldType.Tag.Lookup("json") - return tag == key - }) - - if !field.IsValid() || !field.CanSet() { - logrus.WithFields(logrus.Fields{"key": key}).Error("Invalid field mapping") - continue - } - - if err := setValue(field, value); err != nil { - return err - } - } - - return nil -} - -// setValue sets the value for a given field based on the provided value. -func setValue(field reflect.Value, value any) error { - switch field.Kind() { - case reflect.String: - if strVal, ok := value.(string); ok { - field.SetString(strVal) - } - case reflect.Int: - if strVal, ok := value.(float64); ok { - field.SetInt(int64(strVal)) - } - case reflect.Float64: - if strVal, ok := value.(float64); ok { - field.SetFloat(strVal) - } - case reflect.Uint: - if strVal, ok := value.(float64); ok { - field.SetUint(uint64(strVal)) - } - case reflect.Bool: - if strVal, ok := value.(bool); ok { - field.SetBool(strVal) - } - case reflect.Struct: - if field.Type() == reflect.TypeOf(time.Time{}) { - if strVal, ok := value.(string); ok { - if timeValue, err := time.Parse(time.RFC3339, strVal); err == nil { - field.Set(reflect.ValueOf(timeValue)) - } else { - return errors.New("invalid value for time.Time") - } - } - } - case reflect.Map: - newMap := reflect.MakeMap(field.Type()) - for k, v := range value.(map[string]any) { - key := reflect.ValueOf(k) - val := reflect.New(field.Type().Elem()).Elem() - if err := setValue(val, v); err != nil { - return err - } - newMap.SetMapIndex(key, val) - } - field.Set(newMap) - case reflect.Ptr: - return setValueForPointerField(field, value) - default: - logrus.WithFields(logrus.Fields{"key": value}).Error("Unhandled default case") - return errors.New("unhandled default case") - } - return nil -} - -// setValueForPointerField sets the value for pointer fields. -func setValueForPointerField(field reflect.Value, value any) error { - switch field.Type().Elem().Kind() { - case reflect.String: - if strVal, ok := value.(string); ok { - field.Set(reflect.ValueOf(&strVal)) - } - case reflect.Int: - if strVal, ok := value.(float64); ok { - intValue := int(strVal) - field.Set(reflect.ValueOf(&intValue)) - } - case reflect.Float64: - if strVal, ok := value.(float64); ok { - field.Set(reflect.ValueOf(&strVal)) - } - case reflect.Uint: - if strVal, ok := value.(float64); ok { - uintValue := uint(strVal) - field.Set(reflect.ValueOf(&uintValue)) - } - case reflect.Bool: - if strVal, ok := value.(bool); ok { - field.Set(reflect.ValueOf(&strVal)) - } - case reflect.Struct: - if field.Type().Elem() == reflect.TypeOf(time.Time{}) { - if strVal, ok := value.(string); ok { - if timeValue, err := time.Parse(time.RFC3339, strVal); err == nil { - field.Set(reflect.ValueOf(&timeValue)) - } else { - return errors.New("invalid value for time.Time") - } - } - } - default: - logrus.WithFields(logrus.Fields{"key": value}).Error("Unhandled default case for pointer type") - return errors.New("unhandled default case for pointer type") - } - return nil -} - -// Satisfies handles default setting, type conversion, and validation for the provided data -// against the destination struct. It takes in a map or any type, converts it, sets defaults, -// and validates the result. -func (inst *Tools) Satisfies(data any, dest any) error { - jsonData, err := json.Marshal(data) - if err != nil { - return err - } - - err = json.Unmarshal(jsonData, dest) - if err != nil { - return err - } - - if dataMap, ok := data.(map[string]any); ok { - err = inst.convertFields(dataMap, dest) - if err != nil { - return err - } - } - - if err = inst.setDefaultsAndConvert(dest); err != nil { - return err - } - - err = validate.Struct(dest) - if err != nil { - return err - } - - return nil -} diff --git a/internal/tools/tools.go b/internal/tools/tools.go deleted file mode 100644 index ca26a1b..0000000 --- a/internal/tools/tools.go +++ /dev/null @@ -1,84 +0,0 @@ -package tools - -import ( - "encoding/json" - "fmt" - "strconv" - "strings" -) - -type Tools struct{} - -// Split splits a string by the given separator rune -func (inst *Tools) Split(s string, sep rune) []string { - var parts []string - var part []rune - for _, c := range s { - if c == sep { - if len(part) > 0 { - parts = append(parts, string(part)) - part = nil - } - } else { - part = append(part, c) - } - } - if len(part) > 0 { - parts = append(parts, string(part)) - } - return parts -} - -// SplitOnce splits a string by the given separator rune once -func (inst *Tools) SplitOnce(s string, sep rune) []string { - parts := make([]string, 2) - i := 0 - var part []rune - for _, c := range s { - if c == sep && i == 0 { - parts[i] = string(part) - part = nil - i++ - } else { - part = append(part, c) - } - } - if i < 2 { - parts[i] = string(part) - } - return parts -} - -// ToString converts any value to a string -func (inst *Tools) ToString(val any) string { - return fmt.Sprintf("%v", val) -} - -// ToInt converts a string to an integer -func (inst *Tools) ToInt(s string) (int, error) { - return strconv.Atoi(s) -} - -// ToBool converts a string to a boolean -func (inst *Tools) ToBool(s string) (bool, error) { - return strconv.ParseBool(s) -} - -// Join concatenates elements of a slice into a single string with the given separator -func (inst *Tools) Join(arr []string, sep string) string { - return strings.Join(arr, sep) -} - -// StructToJSON converts a struct to JSON format -func (inst *Tools) StructToJSON(v any) (string, error) { - bytes, err := json.Marshal(v) - if err != nil { - return "", err - } - return string(bytes), nil -} - -// JSONToStruct converts JSON to a struct -func (inst *Tools) JSONToStruct(data string, v any) error { - return json.Unmarshal([]byte(data), v) -} diff --git a/types/filter.go b/types/filter.go deleted file mode 100644 index c46a05c..0000000 --- a/types/filter.go +++ /dev/null @@ -1,26 +0,0 @@ -package types - -// FilterPaginationLengthEnum defines pagination length options. -type FilterPaginationLengthEnum int - -// FilterSortByEnum defines sorting options. -type FilterSortByEnum string - -// Sort defines the structure for sorting data based on a field and order. -type Sort[T any] struct { - By FilterSortByEnum `json:"by,omitempty"` // Sort order: ASC or DESC - Field string `json:"field,omitempty"` // The field to sort by -} - -// Paginated defines pagination properties for data requests. -type Paginated struct { - Length FilterPaginationLengthEnum `json:"length,omitempty"` // Number of items per page - Page int `json:"page,omitempty"` // Current page number -} - -// DefaultFilter defines a filter structure with search, sort, and pagination options. -type DefaultFilter[T any] struct { - Search string `json:"search,omitempty"` // Search query string - Sort Sort[T] `json:"sort,omitempty"` // Sort parameters - Pagination Paginated `json:"pagination,omitempty"` // Pagination parameters -} diff --git a/types/message.go b/types/message.go deleted file mode 100644 index 9eae338..0000000 --- a/types/message.go +++ /dev/null @@ -1,8 +0,0 @@ -package types - -// DefaultMessage defines the default message structure for Gossiper -// Pattern is the type or category of the message, Data is the payload -type DefaultMessage struct { - Pattern string `json:"pattern"` - Data any `json:"data"` -}