From 423ae69fb0b91bb2ca3ea7a2e2b77344b5428e9a Mon Sep 17 00:00:00 2001 From: Yared Yemane Date: Thu, 25 Sep 2025 16:37:53 +0300 Subject: [PATCH 1/2] atlas orchestration + .env fixes --- internal/config/config.go | 7 ++ internal/domain/veli_games.go | 14 ++++ internal/domain/virtual_game.go | 47 ++++++----- .../virtualGame/veli/game_orchestration.go | 84 +++++++++++++------ internal/services/virtualGame/veli/port.go | 1 + internal/services/virtualGame/veli/service.go | 40 +++++++++ 6 files changed, 144 insertions(+), 49 deletions(-) diff --git a/internal/config/config.go b/internal/config/config.go index 15b9421..d96e816 100644 --- a/internal/config/config.go +++ b/internal/config/config.go @@ -380,6 +380,13 @@ func (c *Config) loadEnv() error { c.ADRO_SMS_HOST_URL = "https://api.afrosms.com" } + //Atlas + c.Atlas.BaseURL = os.Getenv("ATLAS_BASE_URL") + c.Atlas.CasinoID = os.Getenv("ATLAS_CASINO_ID") + c.Atlas.OperatorID = os.Getenv("ATLAS_OPERATOR_ID") + c.Atlas.PartnerID = os.Getenv("ATLAS_PARTNER_ID") + c.Atlas.SecretKey = os.Getenv("ATLAS_SECRET_KEY") + popOKClientID := os.Getenv("POPOK_CLIENT_ID") popOKPlatform := os.Getenv("POPOK_PLATFORM") diff --git a/internal/domain/veli_games.go b/internal/domain/veli_games.go index 2fad79a..db6ad6e 100644 --- a/internal/domain/veli_games.go +++ b/internal/domain/veli_games.go @@ -31,6 +31,20 @@ type GameEntity struct { Category string `json:"category"` HasDemoMode bool `json:"hasDemoMode"` HasFreeBets bool `json:"hasFreeBets"` + // Thumbnail string `json:"thumbnail"` // ✅ new field + // DemoURL string `json:"demoUrl"` // ✅ new field +} + +type AtlasGameEntity struct { + GameID string `json:"game_id"` + ProviderID string `json:"providerId"` + Name string `json:"name"` + DeviceType string `json:"deviceType"` + Category string `json:"type"` + HasDemoMode bool `json:"has_demo"` + HasFreeBets bool `json:"hasFreeBets"` + Thumbnail string `json:"thumbnail_img_url"` // ✅ new field + DemoURL string `json:"demo_url"` // ✅ new field } type GameStartRequest struct { diff --git a/internal/domain/virtual_game.go b/internal/domain/virtual_game.go index fe92d4c..9929c5d 100644 --- a/internal/domain/virtual_game.go +++ b/internal/domain/virtual_game.go @@ -283,35 +283,36 @@ type PopokLaunchResponse struct { type VirtualGameProvider struct { // ID int64 `json:"id" db:"id"` - ProviderID string `json:"provider_id" db:"provider_id"` - ProviderName string `json:"provider_name" db:"provider_name"` - LogoDark *string `json:"logo_dark,omitempty" db:"logo_dark"` - LogoLight *string `json:"logo_light,omitempty" db:"logo_light"` - Enabled bool `json:"enabled" db:"enabled"` - CreatedAt time.Time `json:"created_at" db:"created_at"` + ProviderID string `json:"provider_id" db:"provider_id"` + ProviderName string `json:"provider_name" db:"provider_name"` + LogoDark *string `json:"logo_dark,omitempty" db:"logo_dark"` + LogoLight *string `json:"logo_light,omitempty" db:"logo_light"` + Enabled bool `json:"enabled" db:"enabled"` + CreatedAt time.Time `json:"created_at" db:"created_at"` UpdatedAt *time.Time `json:"updated_at,omitempty" db:"updated_at"` } // VirtualGameProviderPagination is used when returning paginated results type VirtualGameProviderPagination struct { - Providers []VirtualGameProvider `json:"providers"` - TotalCount int64 `json:"total_count"` - Limit int32 `json:"limit"` - Offset int32 `json:"offset"` + Providers []VirtualGameProvider `json:"providers"` + TotalCount int64 `json:"total_count"` + Limit int32 `json:"limit"` + Offset int32 `json:"offset"` } type UnifiedGame struct { - GameID string `json:"gameId"` - ProviderID string `json:"providerId"` - Provider string `json:"provider"` - Name string `json:"name"` - Category string `json:"category,omitempty"` - DeviceType string `json:"deviceType,omitempty"` - Volatility string `json:"volatility,omitempty"` - RTP *float64 `json:"rtp,omitempty"` - HasDemo bool `json:"hasDemo"` - HasFreeBets bool `json:"hasFreeBets"` - Bets []float64 `json:"bets,omitempty"` - Thumbnail string `json:"thumbnail,omitempty"` - Status int `json:"status,omitempty"` + GameID string `json:"gameId"` + ProviderID string `json:"providerId"` + Provider string `json:"provider"` + Name string `json:"name"` + Category string `json:"category,omitempty"` + DeviceType string `json:"deviceType,omitempty"` + Volatility string `json:"volatility,omitempty"` + RTP *float64 `json:"rtp,omitempty"` + HasDemo bool `json:"hasDemo"` + HasFreeBets bool `json:"hasFreeBets"` + Bets []float64 `json:"bets,omitempty"` + Thumbnail string `json:"thumbnail,omitempty"` + Status int `json:"status,omitempty"` + DemoURL string `json:"demoUrl"` } diff --git a/internal/services/virtualGame/veli/game_orchestration.go b/internal/services/virtualGame/veli/game_orchestration.go index a371296..37ef8be 100644 --- a/internal/services/virtualGame/veli/game_orchestration.go +++ b/internal/services/virtualGame/veli/game_orchestration.go @@ -149,29 +149,28 @@ func (s *Service) GetAllVirtualGames(ctx context.Context, params dbgen.GetAllVir } func (s *Service) FetchAndStoreAllVirtualGames(ctx context.Context, req domain.ProviderRequest, currency string) ([]domain.UnifiedGame, error) { - logger := s.mongoLogger.With( zap.String("service", "FetchAndStoreAllVirtualGames"), zap.Any("ProviderRequest", req), ) - // This is necessary, since the provider is a foreign key + // This is necessary since the provider is a foreign key _, err := s.AddProviders(ctx, req) if err != nil { return nil, fmt.Errorf("failed to add providers to database: %w", err) } var allGames []domain.UnifiedGame - // --- 1. Get providers from external API --- + + // --- 1. Existing providers (Veli Games) --- providersRes, err := s.GetProviders(ctx, req) if err != nil { logger.Error("Failed to fetch provider", zap.Error(err)) return nil, fmt.Errorf("failed to fetch providers: %w", err) } - // --- 2. Fetch games for each provider --- + // --- 2. Fetch games for each provider (Veli Games) --- for _, p := range providersRes.Items { - // Violates foreign key if the provider isn't added games, err := s.GetGames(ctx, domain.GameListRequest{ BrandID: s.cfg.VeliGames.BrandID, ProviderID: p.ProviderID, @@ -185,20 +184,18 @@ func (s *Service) FetchAndStoreAllVirtualGames(ctx context.Context, req domain.P for _, g := range games { unified := domain.UnifiedGame{ - GameID: g.GameID, - ProviderID: g.ProviderID, - Provider: p.ProviderName, - Name: g.Name, - Category: g.Category, - DeviceType: g.DeviceType, - // Volatility: g.Volatility, - // RTP: g.RTP, + GameID: g.GameID, + ProviderID: g.ProviderID, + Provider: p.ProviderName, + Name: g.Name, + Category: g.Category, + DeviceType: g.DeviceType, HasDemo: g.HasDemoMode, HasFreeBets: g.HasFreeBets, } allGames = append(allGames, unified) - // --- Save to DB --- + // Save to DB _, err = s.repo.CreateVirtualGame(ctx, dbgen.CreateVirtualGameParams{ GameID: g.GameID, ProviderID: g.ProviderID, @@ -211,8 +208,6 @@ func (s *Service) FetchAndStoreAllVirtualGames(ctx context.Context, req domain.P String: g.DeviceType, Valid: g.DeviceType != "", }, - // Volatility: g.Volatility, - // RTP: g.RTP, HasDemo: pgtype.Bool{ Bool: g.HasDemoMode, Valid: true, @@ -221,18 +216,56 @@ func (s *Service) FetchAndStoreAllVirtualGames(ctx context.Context, req domain.P Bool: g.HasFreeBets, Valid: true, }, - // Bets: g.Bets, - // Thumbnail: g.Thumbnail, - // Status: g.Status, }) - if err != nil { logger.Error("failed to create virtual game", zap.Error(err)) } } } - // --- 3. Handle PopOK separately --- + // --- 3. Fetch Atlas-V games --- + atlasGames, err := s.GetAtlasVGames(ctx) + if err != nil { + logger.Error("failed to fetch Atlas-V games", zap.Error(err)) + } else { + for _, g := range atlasGames { + unified := domain.UnifiedGame{ + GameID: g.GameID, + ProviderID: "atlasv", + Provider: "Atlas-V Gaming", // "Atlas-V" + Name: g.Name, + Category: g.Category, // using Type as Category + Thumbnail: g.Thumbnail, + HasDemo: true, + DemoURL: g.DemoURL, + } + allGames = append(allGames, unified) + + // Save to DB + _, err = s.repo.CreateVirtualGame(ctx, dbgen.CreateVirtualGameParams{ + GameID: g.GameID, + ProviderID: "atlasv", + Name: g.Name, + Category: pgtype.Text{ + String: g.Category, + Valid: g.Category != "", + }, + Thumbnail: pgtype.Text{ + String: g.Thumbnail, + Valid: g.Thumbnail != "", + }, + HasDemo: pgtype.Bool{ + Bool: g.HasDemoMode, + Valid: true, + }, + }) + if err != nil { + logger.Error("failed to create Atlas-V virtual game", zap.Error(err)) + } + } + } + + // --- 4. Handle PopOK separately --- popokGames, err := s.virtualGameSvc.ListGames(ctx, currency) if err != nil { logger.Error("failed to fetch PopOk games", zap.Error(err)) @@ -252,7 +285,7 @@ func (s *Service) FetchAndStoreAllVirtualGames(ctx context.Context, req domain.P } allGames = append(allGames, unified) - // --- Convert []float64 to []pgtype.Numeric --- + // Convert []float64 to []pgtype.Numeric var betsNumeric []pgtype.Numeric for _, bet := range g.Bets { var num pgtype.Numeric @@ -260,9 +293,9 @@ func (s *Service) FetchAndStoreAllVirtualGames(ctx context.Context, req domain.P betsNumeric = append(betsNumeric, num) } - // --- Save to DB --- + // Save to DB _, err = s.repo.CreateVirtualGame(ctx, dbgen.CreateVirtualGameParams{ - GameID: fmt.Sprintf("%d", g.ID), //The id here needs to be clean for me to access + GameID: fmt.Sprintf("%d", g.ID), ProviderID: "popok", Name: g.GameName, Bets: betsNumeric, @@ -275,9 +308,8 @@ func (s *Service) FetchAndStoreAllVirtualGames(ctx context.Context, req domain.P Valid: true, }, }) - if err != nil { - logger.Error("failed to create virtual game", zap.Error(err)) + logger.Error("failed to create PopOK virtual game", zap.Error(err)) } } diff --git a/internal/services/virtualGame/veli/port.go b/internal/services/virtualGame/veli/port.go index edbffe2..5e50cba 100644 --- a/internal/services/virtualGame/veli/port.go +++ b/internal/services/virtualGame/veli/port.go @@ -9,6 +9,7 @@ import ( ) type VeliVirtualGameService interface { + GetAtlasVGames(ctx context.Context) ([]domain.AtlasGameEntity, error) FetchAndStoreAllVirtualGames(ctx context.Context, req domain.ProviderRequest, currency string) ([]domain.UnifiedGame, error) GetAllVirtualGames(ctx context.Context, params dbgen.GetAllVirtualGamesParams) ([]domain.UnifiedGame, error) AddProviders(ctx context.Context, req domain.ProviderRequest) (*domain.ProviderResponse, error) diff --git a/internal/services/virtualGame/veli/service.go b/internal/services/virtualGame/veli/service.go index 3f41633..cea093e 100644 --- a/internal/services/virtualGame/veli/service.go +++ b/internal/services/virtualGame/veli/service.go @@ -2,9 +2,13 @@ package veli import ( "context" + "encoding/json" "errors" "fmt" + "io" + "net/http" "strconv" + "time" "github.com/SamuelTariku/FortuneBet-Backend/internal/config" "github.com/SamuelTariku/FortuneBet-Backend/internal/domain" @@ -51,6 +55,42 @@ func New( } } +func (s *Service) GetAtlasVGames(ctx context.Context) ([]domain.AtlasGameEntity, error) { + // 1. Compose URL (could be configurable) + url := "https://atlas-v.com/partner/35fr5784dbgr4dfw234wsdsw" + + "?hash=b3596faa6185180e9b2ca01cb5a052d316511872×tamp=1700244963080" + + // 2. Create a dedicated HTTP client with timeout + client := &http.Client{Timeout: 15 * time.Second} + + // 3. Prepare request with context + req, err := http.NewRequestWithContext(ctx, http.MethodGet, url, nil) + if err != nil { + return nil, fmt.Errorf("creating request: %w", err) + } + + // 4. Execute request + resp, err := client.Do(req) + if err != nil { + return nil, fmt.Errorf("calling Atlas-V API: %w", err) + } + defer resp.Body.Close() + + // 5. Check response status + if resp.StatusCode != http.StatusOK { + body, _ := io.ReadAll(resp.Body) + return nil, fmt.Errorf("Atlas-V API error: status %d, body: %s", resp.StatusCode, body) + } + + // 6. Decode response into slice of GameEntity + var games []domain.AtlasGameEntity + if err := json.NewDecoder(resp.Body).Decode(&games); err != nil { + return nil, fmt.Errorf("decoding Atlas-V games: %w", err) + } + + return games, nil +} + func (s *Service) GetProviders(ctx context.Context, req domain.ProviderRequest) (*domain.ProviderResponse, error) { // Always mirror request body fields into sigParams sigParams := map[string]any{ From 6ceff2843eef535402ccc8d51e2d41e5a1ae1f3b Mon Sep 17 00:00:00 2001 From: Yared Yemane Date: Thu, 25 Sep 2025 20:58:25 +0300 Subject: [PATCH 2/2] enetpulse 1st phase repo and cron job implementation --- db/migrations/000006_enet_pulse.down.sql | 2 + db/migrations/000006_enet_pulse.up.sql | 23 +++ db/query/enet_pulse.sql | 57 +++++++ gen/db/auth.sql.go | 2 +- gen/db/bet.sql.go | 2 +- gen/db/bet_stat.sql.go | 2 +- gen/db/bonus.sql.go | 2 +- gen/db/branch.sql.go | 2 +- gen/db/cashier.sql.go | 2 +- gen/db/company.sql.go | 2 +- gen/db/copyfrom.go | 2 +- gen/db/db.go | 2 +- gen/db/direct_deposit.sql.go | 2 +- gen/db/disabled_odds.sql.go | 2 +- gen/db/enet_pulse.sql.go | 198 +++++++++++++++++++++++ gen/db/event_history.sql.go | 2 +- gen/db/events.sql.go | 164 +------------------ gen/db/events_stat.sql.go | 2 +- gen/db/flags.sql.go | 2 +- gen/db/institutions.sql.go | 2 +- gen/db/issue_reporting.sql.go | 2 +- gen/db/leagues.sql.go | 2 +- gen/db/location.sql.go | 2 +- gen/db/models.go | 26 ++- gen/db/monitor.sql.go | 2 +- gen/db/notification.sql.go | 2 +- gen/db/odd_history.sql.go | 2 +- gen/db/odds.sql.go | 2 +- gen/db/otp.sql.go | 2 +- gen/db/raffle.sql.go | 2 +- gen/db/referal.sql.go | 2 +- gen/db/report.sql.go | 2 +- gen/db/result.sql.go | 2 +- gen/db/result_log.sql.go | 2 +- gen/db/settings.sql.go | 2 +- gen/db/shop_transactions.sql.go | 2 +- gen/db/ticket.sql.go | 2 +- gen/db/transfer.sql.go | 2 +- gen/db/user.sql.go | 2 +- gen/db/virtual_games.sql.go | 2 +- gen/db/wallet.sql.go | 2 +- internal/domain/enet_pulse.go | 44 +++++ internal/repository/enet_pulse.go | 141 ++++++++++++++++ internal/repository/event.go | 7 +- internal/services/enet_pulse/service.go | 144 +++++++++++++++-- internal/web_server/cron.go | 49 ++++++ internal/web_server/handlers/atlas.go | 28 ++++ internal/web_server/routes.go | 1 + 48 files changed, 746 insertions(+), 208 deletions(-) create mode 100644 db/migrations/000006_enet_pulse.down.sql create mode 100644 db/migrations/000006_enet_pulse.up.sql create mode 100644 db/query/enet_pulse.sql create mode 100644 gen/db/enet_pulse.sql.go create mode 100644 internal/repository/enet_pulse.go diff --git a/db/migrations/000006_enet_pulse.down.sql b/db/migrations/000006_enet_pulse.down.sql new file mode 100644 index 0000000..10f25f6 --- /dev/null +++ b/db/migrations/000006_enet_pulse.down.sql @@ -0,0 +1,2 @@ +DROP TABLE IF EXISTS enetpulse_sports; +DROP TABLE IF EXISTS enetpulse_tournament_templates; \ No newline at end of file diff --git a/db/migrations/000006_enet_pulse.up.sql b/db/migrations/000006_enet_pulse.up.sql new file mode 100644 index 0000000..059db33 --- /dev/null +++ b/db/migrations/000006_enet_pulse.up.sql @@ -0,0 +1,23 @@ +CREATE TABLE IF NOT EXISTS enetpulse_sports ( + id BIGSERIAL PRIMARY KEY, + sport_id VARCHAR(50) NOT NULL UNIQUE, -- from API "id" + name VARCHAR(255) NOT NULL, -- from API "name" + updates_count INT DEFAULT 0, -- from API "n" + last_updated_at TIMESTAMPTZ, -- from API "ut" + status INT DEFAULT 1, -- optional status (active/inactive) + created_at TIMESTAMPTZ NOT NULL DEFAULT CURRENT_TIMESTAMP, + updated_at TIMESTAMPTZ +); + +CREATE TABLE IF NOT EXISTS enetpulse_tournament_templates ( + id BIGSERIAL PRIMARY KEY, + template_id VARCHAR(50) NOT NULL UNIQUE, -- from API "id" + name VARCHAR(255) NOT NULL, -- from API "name" + sport_fk VARCHAR(50) NOT NULL REFERENCES enetpulse_sports(sport_id) ON DELETE CASCADE, + gender VARCHAR(20) DEFAULT 'unknown', -- from API "gender" {male, female, mixed, unknown} + updates_count INT DEFAULT 0, -- from API "n" + last_updated_at TIMESTAMPTZ, -- from API "ut" + status INT DEFAULT 1, -- optional status (active/inactive) + created_at TIMESTAMPTZ NOT NULL DEFAULT CURRENT_TIMESTAMP, + updated_at TIMESTAMPTZ +); diff --git a/db/query/enet_pulse.sql b/db/query/enet_pulse.sql new file mode 100644 index 0000000..b9fdfa4 --- /dev/null +++ b/db/query/enet_pulse.sql @@ -0,0 +1,57 @@ +-- name: CreateEnetpulseSport :one +INSERT INTO enetpulse_sports ( + sport_id, + name, + updates_count, + last_updated_at, + status, + updated_at +) VALUES ( + $1, $2, $3, $4, $5, NOW() +) +RETURNING *; + +-- name: GetAllEnetpulseSports :many +SELECT + id, + sport_id, + name, + updates_count, + last_updated_at, + status, + created_at, + updated_at +FROM enetpulse_sports +ORDER BY name; + +-- name: CreateEnetpulseTournamentTemplate :one +INSERT INTO enetpulse_tournament_templates ( + template_id, + name, + sport_fk, + gender, + updates_count, + last_updated_at, + status, + updated_at +) VALUES ( + $1, $2, $3, $4, $5, $6, $7, NOW() +) +RETURNING *; + + +-- name: GetAllEnetpulseTournamentTemplates :many +SELECT + id, + template_id, + name, + sport_fk, + gender, + updates_count, + last_updated_at, + status, + created_at, + updated_at +FROM enetpulse_tournament_templates +ORDER BY name; + diff --git a/gen/db/auth.sql.go b/gen/db/auth.sql.go index 8dd2280..7d8d59d 100644 --- a/gen/db/auth.sql.go +++ b/gen/db/auth.sql.go @@ -1,6 +1,6 @@ // Code generated by sqlc. DO NOT EDIT. // versions: -// sqlc v1.30.0 +// sqlc v1.29.0 // source: auth.sql package dbgen diff --git a/gen/db/bet.sql.go b/gen/db/bet.sql.go index 573c4c2..ff64087 100644 --- a/gen/db/bet.sql.go +++ b/gen/db/bet.sql.go @@ -1,6 +1,6 @@ // Code generated by sqlc. DO NOT EDIT. // versions: -// sqlc v1.30.0 +// sqlc v1.29.0 // source: bet.sql package dbgen diff --git a/gen/db/bet_stat.sql.go b/gen/db/bet_stat.sql.go index 9a7b494..275ef07 100644 --- a/gen/db/bet_stat.sql.go +++ b/gen/db/bet_stat.sql.go @@ -1,6 +1,6 @@ // Code generated by sqlc. DO NOT EDIT. // versions: -// sqlc v1.30.0 +// sqlc v1.29.0 // source: bet_stat.sql package dbgen diff --git a/gen/db/bonus.sql.go b/gen/db/bonus.sql.go index 1a5d8e9..7c6f168 100644 --- a/gen/db/bonus.sql.go +++ b/gen/db/bonus.sql.go @@ -1,6 +1,6 @@ // Code generated by sqlc. DO NOT EDIT. // versions: -// sqlc v1.30.0 +// sqlc v1.29.0 // source: bonus.sql package dbgen diff --git a/gen/db/branch.sql.go b/gen/db/branch.sql.go index 89d2959..a9a57b8 100644 --- a/gen/db/branch.sql.go +++ b/gen/db/branch.sql.go @@ -1,6 +1,6 @@ // Code generated by sqlc. DO NOT EDIT. // versions: -// sqlc v1.30.0 +// sqlc v1.29.0 // source: branch.sql package dbgen diff --git a/gen/db/cashier.sql.go b/gen/db/cashier.sql.go index 55e69d2..fc4a7f8 100644 --- a/gen/db/cashier.sql.go +++ b/gen/db/cashier.sql.go @@ -1,6 +1,6 @@ // Code generated by sqlc. DO NOT EDIT. // versions: -// sqlc v1.30.0 +// sqlc v1.29.0 // source: cashier.sql package dbgen diff --git a/gen/db/company.sql.go b/gen/db/company.sql.go index 18bc509..506eaca 100644 --- a/gen/db/company.sql.go +++ b/gen/db/company.sql.go @@ -1,6 +1,6 @@ // Code generated by sqlc. DO NOT EDIT. // versions: -// sqlc v1.30.0 +// sqlc v1.29.0 // source: company.sql package dbgen diff --git a/gen/db/copyfrom.go b/gen/db/copyfrom.go index f7a4793..1212253 100644 --- a/gen/db/copyfrom.go +++ b/gen/db/copyfrom.go @@ -1,6 +1,6 @@ // Code generated by sqlc. DO NOT EDIT. // versions: -// sqlc v1.30.0 +// sqlc v1.29.0 // source: copyfrom.go package dbgen diff --git a/gen/db/db.go b/gen/db/db.go index 8134784..84de07c 100644 --- a/gen/db/db.go +++ b/gen/db/db.go @@ -1,6 +1,6 @@ // Code generated by sqlc. DO NOT EDIT. // versions: -// sqlc v1.30.0 +// sqlc v1.29.0 package dbgen diff --git a/gen/db/direct_deposit.sql.go b/gen/db/direct_deposit.sql.go index ff5a3b2..be02750 100644 --- a/gen/db/direct_deposit.sql.go +++ b/gen/db/direct_deposit.sql.go @@ -1,6 +1,6 @@ // Code generated by sqlc. DO NOT EDIT. // versions: -// sqlc v1.30.0 +// sqlc v1.29.0 // source: direct_deposit.sql package dbgen diff --git a/gen/db/disabled_odds.sql.go b/gen/db/disabled_odds.sql.go index 58913cf..b9cc744 100644 --- a/gen/db/disabled_odds.sql.go +++ b/gen/db/disabled_odds.sql.go @@ -1,6 +1,6 @@ // Code generated by sqlc. DO NOT EDIT. // versions: -// sqlc v1.30.0 +// sqlc v1.29.0 // source: disabled_odds.sql package dbgen diff --git a/gen/db/enet_pulse.sql.go b/gen/db/enet_pulse.sql.go new file mode 100644 index 0000000..e935fac --- /dev/null +++ b/gen/db/enet_pulse.sql.go @@ -0,0 +1,198 @@ +// Code generated by sqlc. DO NOT EDIT. +// versions: +// sqlc v1.29.0 +// source: enet_pulse.sql + +package dbgen + +import ( + "context" + + "github.com/jackc/pgx/v5/pgtype" +) + +const CreateEnetpulseSport = `-- name: CreateEnetpulseSport :one +INSERT INTO enetpulse_sports ( + sport_id, + name, + updates_count, + last_updated_at, + status, + updated_at +) VALUES ( + $1, $2, $3, $4, $5, NOW() +) +RETURNING id, sport_id, name, updates_count, last_updated_at, status, created_at, updated_at +` + +type CreateEnetpulseSportParams struct { + SportID string `json:"sport_id"` + Name string `json:"name"` + UpdatesCount pgtype.Int4 `json:"updates_count"` + LastUpdatedAt pgtype.Timestamptz `json:"last_updated_at"` + Status pgtype.Int4 `json:"status"` +} + +func (q *Queries) CreateEnetpulseSport(ctx context.Context, arg CreateEnetpulseSportParams) (EnetpulseSport, error) { + row := q.db.QueryRow(ctx, CreateEnetpulseSport, + arg.SportID, + arg.Name, + arg.UpdatesCount, + arg.LastUpdatedAt, + arg.Status, + ) + var i EnetpulseSport + err := row.Scan( + &i.ID, + &i.SportID, + &i.Name, + &i.UpdatesCount, + &i.LastUpdatedAt, + &i.Status, + &i.CreatedAt, + &i.UpdatedAt, + ) + return i, err +} + +const CreateEnetpulseTournamentTemplate = `-- name: CreateEnetpulseTournamentTemplate :one +INSERT INTO enetpulse_tournament_templates ( + template_id, + name, + sport_fk, + gender, + updates_count, + last_updated_at, + status, + updated_at +) VALUES ( + $1, $2, $3, $4, $5, $6, $7, NOW() +) +RETURNING id, template_id, name, sport_fk, gender, updates_count, last_updated_at, status, created_at, updated_at +` + +type CreateEnetpulseTournamentTemplateParams struct { + TemplateID string `json:"template_id"` + Name string `json:"name"` + SportFk string `json:"sport_fk"` + Gender pgtype.Text `json:"gender"` + UpdatesCount pgtype.Int4 `json:"updates_count"` + LastUpdatedAt pgtype.Timestamptz `json:"last_updated_at"` + Status pgtype.Int4 `json:"status"` +} + +func (q *Queries) CreateEnetpulseTournamentTemplate(ctx context.Context, arg CreateEnetpulseTournamentTemplateParams) (EnetpulseTournamentTemplate, error) { + row := q.db.QueryRow(ctx, CreateEnetpulseTournamentTemplate, + arg.TemplateID, + arg.Name, + arg.SportFk, + arg.Gender, + arg.UpdatesCount, + arg.LastUpdatedAt, + arg.Status, + ) + var i EnetpulseTournamentTemplate + err := row.Scan( + &i.ID, + &i.TemplateID, + &i.Name, + &i.SportFk, + &i.Gender, + &i.UpdatesCount, + &i.LastUpdatedAt, + &i.Status, + &i.CreatedAt, + &i.UpdatedAt, + ) + return i, err +} + +const GetAllEnetpulseSports = `-- name: GetAllEnetpulseSports :many +SELECT + id, + sport_id, + name, + updates_count, + last_updated_at, + status, + created_at, + updated_at +FROM enetpulse_sports +ORDER BY name +` + +func (q *Queries) GetAllEnetpulseSports(ctx context.Context) ([]EnetpulseSport, error) { + rows, err := q.db.Query(ctx, GetAllEnetpulseSports) + if err != nil { + return nil, err + } + defer rows.Close() + var items []EnetpulseSport + for rows.Next() { + var i EnetpulseSport + if err := rows.Scan( + &i.ID, + &i.SportID, + &i.Name, + &i.UpdatesCount, + &i.LastUpdatedAt, + &i.Status, + &i.CreatedAt, + &i.UpdatedAt, + ); err != nil { + return nil, err + } + items = append(items, i) + } + if err := rows.Err(); err != nil { + return nil, err + } + return items, nil +} + +const GetAllEnetpulseTournamentTemplates = `-- name: GetAllEnetpulseTournamentTemplates :many +SELECT + id, + template_id, + name, + sport_fk, + gender, + updates_count, + last_updated_at, + status, + created_at, + updated_at +FROM enetpulse_tournament_templates +ORDER BY name +` + +func (q *Queries) GetAllEnetpulseTournamentTemplates(ctx context.Context) ([]EnetpulseTournamentTemplate, error) { + rows, err := q.db.Query(ctx, GetAllEnetpulseTournamentTemplates) + if err != nil { + return nil, err + } + defer rows.Close() + var items []EnetpulseTournamentTemplate + for rows.Next() { + var i EnetpulseTournamentTemplate + if err := rows.Scan( + &i.ID, + &i.TemplateID, + &i.Name, + &i.SportFk, + &i.Gender, + &i.UpdatesCount, + &i.LastUpdatedAt, + &i.Status, + &i.CreatedAt, + &i.UpdatedAt, + ); err != nil { + return nil, err + } + items = append(items, i) + } + if err := rows.Err(); err != nil { + return nil, err + } + return items, nil +} diff --git a/gen/db/event_history.sql.go b/gen/db/event_history.sql.go index 35946cd..a4f1c2e 100644 --- a/gen/db/event_history.sql.go +++ b/gen/db/event_history.sql.go @@ -1,6 +1,6 @@ // Code generated by sqlc. DO NOT EDIT. // versions: -// sqlc v1.30.0 +// sqlc v1.29.0 // source: event_history.sql package dbgen diff --git a/gen/db/events.sql.go b/gen/db/events.sql.go index 0729560..fc793b1 100644 --- a/gen/db/events.sql.go +++ b/gen/db/events.sql.go @@ -1,6 +1,6 @@ // Code generated by sqlc. DO NOT EDIT. // versions: -// sqlc v1.30.0 +// sqlc v1.29.0 // source: events.sql package dbgen @@ -513,166 +513,6 @@ func (q *Queries) GetEventsWithSettings(ctx context.Context, arg GetEventsWithSe return items, nil } -const GetExpiredEvents = `-- name: GetExpiredEvents :many -SELECT id, sport_id, match_name, home_team, away_team, home_team_id, away_team_id, home_kit_image, away_kit_image, league_id, league_name, start_time, score, match_minute, timer_status, added_time, match_period, is_live, status, fetched_at, source, default_is_active, default_is_featured, default_winning_upper_limit, is_monitored, league_cc -FROM event_with_country -WHERE start_time < now() - and ( - status = $1 - OR $1 IS NULL - ) -ORDER BY start_time ASC -` - -func (q *Queries) GetExpiredEvents(ctx context.Context, status pgtype.Text) ([]EventWithCountry, error) { - rows, err := q.db.Query(ctx, GetExpiredEvents, status) - if err != nil { - return nil, err - } - defer rows.Close() - var items []EventWithCountry - for rows.Next() { - var i EventWithCountry - if err := rows.Scan( - &i.ID, - &i.SportID, - &i.MatchName, - &i.HomeTeam, - &i.AwayTeam, - &i.HomeTeamID, - &i.AwayTeamID, - &i.HomeKitImage, - &i.AwayKitImage, - &i.LeagueID, - &i.LeagueName, - &i.StartTime, - &i.Score, - &i.MatchMinute, - &i.TimerStatus, - &i.AddedTime, - &i.MatchPeriod, - &i.IsLive, - &i.Status, - &i.FetchedAt, - &i.Source, - &i.DefaultIsActive, - &i.DefaultIsFeatured, - &i.DefaultWinningUpperLimit, - &i.IsMonitored, - &i.LeagueCc, - ); err != nil { - return nil, err - } - items = append(items, i) - } - if err := rows.Err(); err != nil { - return nil, err - } - return items, nil -} - -const GetPaginatedUpcomingEvents = `-- name: GetPaginatedUpcomingEvents :many -SELECT id, sport_id, match_name, home_team, away_team, home_team_id, away_team_id, home_kit_image, away_kit_image, league_id, league_name, start_time, score, match_minute, timer_status, added_time, match_period, is_live, status, fetched_at, source, default_is_active, default_is_featured, default_winning_upper_limit, is_monitored, league_cc -FROM event_with_country -WHERE start_time > now() - AND is_live = false - AND status = 'upcoming' - AND ( - league_id = $1 - OR $1 IS NULL - ) - AND ( - sport_id = $2 - OR $2 IS NULL - ) - AND ( - match_name ILIKE '%' || $3 || '%' - OR league_name ILIKE '%' || $3 || '%' - OR $3 IS NULL - ) - AND ( - start_time < $4 - OR $4 IS NULL - ) - AND ( - start_time > $5 - OR $5 IS NULL - ) - AND ( - league_cc = $6 - OR $6 IS NULL - ) -ORDER BY start_time ASC -LIMIT $8 OFFSET $7 -` - -type GetPaginatedUpcomingEventsParams struct { - LeagueID pgtype.Int8 `json:"league_id"` - SportID pgtype.Int4 `json:"sport_id"` - Query pgtype.Text `json:"query"` - LastStartTime pgtype.Timestamp `json:"last_start_time"` - FirstStartTime pgtype.Timestamp `json:"first_start_time"` - CountryCode pgtype.Text `json:"country_code"` - Offset pgtype.Int4 `json:"offset"` - Limit pgtype.Int4 `json:"limit"` -} - -func (q *Queries) GetPaginatedUpcomingEvents(ctx context.Context, arg GetPaginatedUpcomingEventsParams) ([]EventWithCountry, error) { - rows, err := q.db.Query(ctx, GetPaginatedUpcomingEvents, - arg.LeagueID, - arg.SportID, - arg.Query, - arg.LastStartTime, - arg.FirstStartTime, - arg.CountryCode, - arg.Offset, - arg.Limit, - ) - if err != nil { - return nil, err - } - defer rows.Close() - var items []EventWithCountry - for rows.Next() { - var i EventWithCountry - if err := rows.Scan( - &i.ID, - &i.SportID, - &i.MatchName, - &i.HomeTeam, - &i.AwayTeam, - &i.HomeTeamID, - &i.AwayTeamID, - &i.HomeKitImage, - &i.AwayKitImage, - &i.LeagueID, - &i.LeagueName, - &i.StartTime, - &i.Score, - &i.MatchMinute, - &i.TimerStatus, - &i.AddedTime, - &i.MatchPeriod, - &i.IsLive, - &i.Status, - &i.FetchedAt, - &i.Source, - &i.DefaultIsActive, - &i.DefaultIsFeatured, - &i.DefaultWinningUpperLimit, - &i.IsMonitored, - &i.LeagueCc, - ); err != nil { - return nil, err - } - items = append(items, i) - } - if err := rows.Err(); err != nil { - return nil, err - } - return items, nil -} - const GetSportAndLeagueIDs = `-- name: GetSportAndLeagueIDs :one SELECT sport_id, league_id FROM events WHERE id = $1 @@ -683,7 +523,7 @@ type GetSportAndLeagueIDsRow struct { LeagueID int64 `json:"league_id"` } -func (q *Queries) GetSportAndLeagueIDs(ctx context.Context, id string) (GetSportAndLeagueIDsRow, error) { +func (q *Queries) GetSportAndLeagueIDs(ctx context.Context, id int64) (GetSportAndLeagueIDsRow, error) { row := q.db.QueryRow(ctx, GetSportAndLeagueIDs, id) var i GetSportAndLeagueIDsRow err := row.Scan(&i.SportID, &i.LeagueID) diff --git a/gen/db/events_stat.sql.go b/gen/db/events_stat.sql.go index 615e2fa..677fa2a 100644 --- a/gen/db/events_stat.sql.go +++ b/gen/db/events_stat.sql.go @@ -1,6 +1,6 @@ // Code generated by sqlc. DO NOT EDIT. // versions: -// sqlc v1.30.0 +// sqlc v1.29.0 // source: events_stat.sql package dbgen diff --git a/gen/db/flags.sql.go b/gen/db/flags.sql.go index 4b82cac..653543f 100644 --- a/gen/db/flags.sql.go +++ b/gen/db/flags.sql.go @@ -1,6 +1,6 @@ // Code generated by sqlc. DO NOT EDIT. // versions: -// sqlc v1.30.0 +// sqlc v1.29.0 // source: flags.sql package dbgen diff --git a/gen/db/institutions.sql.go b/gen/db/institutions.sql.go index 61ca108..324ac3e 100644 --- a/gen/db/institutions.sql.go +++ b/gen/db/institutions.sql.go @@ -1,6 +1,6 @@ // Code generated by sqlc. DO NOT EDIT. // versions: -// sqlc v1.30.0 +// sqlc v1.29.0 // source: institutions.sql package dbgen diff --git a/gen/db/issue_reporting.sql.go b/gen/db/issue_reporting.sql.go index e35fba1..7fcb4af 100644 --- a/gen/db/issue_reporting.sql.go +++ b/gen/db/issue_reporting.sql.go @@ -1,6 +1,6 @@ // Code generated by sqlc. DO NOT EDIT. // versions: -// sqlc v1.30.0 +// sqlc v1.29.0 // source: issue_reporting.sql package dbgen diff --git a/gen/db/leagues.sql.go b/gen/db/leagues.sql.go index 0aaad2c..1d2800b 100644 --- a/gen/db/leagues.sql.go +++ b/gen/db/leagues.sql.go @@ -1,6 +1,6 @@ // Code generated by sqlc. DO NOT EDIT. // versions: -// sqlc v1.30.0 +// sqlc v1.29.0 // source: leagues.sql package dbgen diff --git a/gen/db/location.sql.go b/gen/db/location.sql.go index 254c73a..008aa61 100644 --- a/gen/db/location.sql.go +++ b/gen/db/location.sql.go @@ -1,6 +1,6 @@ // Code generated by sqlc. DO NOT EDIT. // versions: -// sqlc v1.30.0 +// sqlc v1.29.0 // source: location.sql package dbgen diff --git a/gen/db/models.go b/gen/db/models.go index 7a3d25f..0319e8f 100644 --- a/gen/db/models.go +++ b/gen/db/models.go @@ -1,6 +1,6 @@ // Code generated by sqlc. DO NOT EDIT. // versions: -// sqlc v1.30.0 +// sqlc v1.29.0 package dbgen @@ -244,6 +244,30 @@ type DisabledOdd struct { CreatedAt pgtype.Timestamp `json:"created_at"` } +type EnetpulseSport struct { + ID int64 `json:"id"` + SportID string `json:"sport_id"` + Name string `json:"name"` + UpdatesCount pgtype.Int4 `json:"updates_count"` + LastUpdatedAt pgtype.Timestamptz `json:"last_updated_at"` + Status pgtype.Int4 `json:"status"` + CreatedAt pgtype.Timestamptz `json:"created_at"` + UpdatedAt pgtype.Timestamptz `json:"updated_at"` +} + +type EnetpulseTournamentTemplate struct { + ID int64 `json:"id"` + TemplateID string `json:"template_id"` + Name string `json:"name"` + SportFk string `json:"sport_fk"` + Gender pgtype.Text `json:"gender"` + UpdatesCount pgtype.Int4 `json:"updates_count"` + LastUpdatedAt pgtype.Timestamptz `json:"last_updated_at"` + Status pgtype.Int4 `json:"status"` + CreatedAt pgtype.Timestamptz `json:"created_at"` + UpdatedAt pgtype.Timestamptz `json:"updated_at"` +} + type Event struct { ID int64 `json:"id"` SourceEventID string `json:"source_event_id"` diff --git a/gen/db/monitor.sql.go b/gen/db/monitor.sql.go index b5f248f..a9a7ecb 100644 --- a/gen/db/monitor.sql.go +++ b/gen/db/monitor.sql.go @@ -1,6 +1,6 @@ // Code generated by sqlc. DO NOT EDIT. // versions: -// sqlc v1.30.0 +// sqlc v1.29.0 // source: monitor.sql package dbgen diff --git a/gen/db/notification.sql.go b/gen/db/notification.sql.go index 9ce7e42..ba9882b 100644 --- a/gen/db/notification.sql.go +++ b/gen/db/notification.sql.go @@ -1,6 +1,6 @@ // Code generated by sqlc. DO NOT EDIT. // versions: -// sqlc v1.30.0 +// sqlc v1.29.0 // source: notification.sql package dbgen diff --git a/gen/db/odd_history.sql.go b/gen/db/odd_history.sql.go index dd834c5..3fe7dd9 100644 --- a/gen/db/odd_history.sql.go +++ b/gen/db/odd_history.sql.go @@ -1,6 +1,6 @@ // Code generated by sqlc. DO NOT EDIT. // versions: -// sqlc v1.30.0 +// sqlc v1.29.0 // source: odd_history.sql package dbgen diff --git a/gen/db/odds.sql.go b/gen/db/odds.sql.go index 1f181e4..ac9974c 100644 --- a/gen/db/odds.sql.go +++ b/gen/db/odds.sql.go @@ -1,6 +1,6 @@ // Code generated by sqlc. DO NOT EDIT. // versions: -// sqlc v1.30.0 +// sqlc v1.29.0 // source: odds.sql package dbgen diff --git a/gen/db/otp.sql.go b/gen/db/otp.sql.go index c96aaaa..7dba175 100644 --- a/gen/db/otp.sql.go +++ b/gen/db/otp.sql.go @@ -1,6 +1,6 @@ // Code generated by sqlc. DO NOT EDIT. // versions: -// sqlc v1.30.0 +// sqlc v1.29.0 // source: otp.sql package dbgen diff --git a/gen/db/raffle.sql.go b/gen/db/raffle.sql.go index a4888f9..a7a364e 100644 --- a/gen/db/raffle.sql.go +++ b/gen/db/raffle.sql.go @@ -1,6 +1,6 @@ // Code generated by sqlc. DO NOT EDIT. // versions: -// sqlc v1.30.0 +// sqlc v1.29.0 // source: raffle.sql package dbgen diff --git a/gen/db/referal.sql.go b/gen/db/referal.sql.go index 99d8bb2..caaa01a 100644 --- a/gen/db/referal.sql.go +++ b/gen/db/referal.sql.go @@ -1,6 +1,6 @@ // Code generated by sqlc. DO NOT EDIT. // versions: -// sqlc v1.30.0 +// sqlc v1.29.0 // source: referal.sql package dbgen diff --git a/gen/db/report.sql.go b/gen/db/report.sql.go index d6193c1..1a1ccde 100644 --- a/gen/db/report.sql.go +++ b/gen/db/report.sql.go @@ -1,6 +1,6 @@ // Code generated by sqlc. DO NOT EDIT. // versions: -// sqlc v1.30.0 +// sqlc v1.29.0 // source: report.sql package dbgen diff --git a/gen/db/result.sql.go b/gen/db/result.sql.go index 899561b..bff7b1e 100644 --- a/gen/db/result.sql.go +++ b/gen/db/result.sql.go @@ -1,6 +1,6 @@ // Code generated by sqlc. DO NOT EDIT. // versions: -// sqlc v1.30.0 +// sqlc v1.29.0 // source: result.sql package dbgen diff --git a/gen/db/result_log.sql.go b/gen/db/result_log.sql.go index 3f11e16..468795e 100644 --- a/gen/db/result_log.sql.go +++ b/gen/db/result_log.sql.go @@ -1,6 +1,6 @@ // Code generated by sqlc. DO NOT EDIT. // versions: -// sqlc v1.30.0 +// sqlc v1.29.0 // source: result_log.sql package dbgen diff --git a/gen/db/settings.sql.go b/gen/db/settings.sql.go index 76eb504..96ea916 100644 --- a/gen/db/settings.sql.go +++ b/gen/db/settings.sql.go @@ -1,6 +1,6 @@ // Code generated by sqlc. DO NOT EDIT. // versions: -// sqlc v1.30.0 +// sqlc v1.29.0 // source: settings.sql package dbgen diff --git a/gen/db/shop_transactions.sql.go b/gen/db/shop_transactions.sql.go index 7664dbb..bcd884e 100644 --- a/gen/db/shop_transactions.sql.go +++ b/gen/db/shop_transactions.sql.go @@ -1,6 +1,6 @@ // Code generated by sqlc. DO NOT EDIT. // versions: -// sqlc v1.30.0 +// sqlc v1.29.0 // source: shop_transactions.sql package dbgen diff --git a/gen/db/ticket.sql.go b/gen/db/ticket.sql.go index 45603ba..bc9bb5f 100644 --- a/gen/db/ticket.sql.go +++ b/gen/db/ticket.sql.go @@ -1,6 +1,6 @@ // Code generated by sqlc. DO NOT EDIT. // versions: -// sqlc v1.30.0 +// sqlc v1.29.0 // source: ticket.sql package dbgen diff --git a/gen/db/transfer.sql.go b/gen/db/transfer.sql.go index fe25cbe..b2a1066 100644 --- a/gen/db/transfer.sql.go +++ b/gen/db/transfer.sql.go @@ -1,6 +1,6 @@ // Code generated by sqlc. DO NOT EDIT. // versions: -// sqlc v1.30.0 +// sqlc v1.29.0 // source: transfer.sql package dbgen diff --git a/gen/db/user.sql.go b/gen/db/user.sql.go index 9f9cd95..999f169 100644 --- a/gen/db/user.sql.go +++ b/gen/db/user.sql.go @@ -1,6 +1,6 @@ // Code generated by sqlc. DO NOT EDIT. // versions: -// sqlc v1.30.0 +// sqlc v1.29.0 // source: user.sql package dbgen diff --git a/gen/db/virtual_games.sql.go b/gen/db/virtual_games.sql.go index b98f602..5a2809a 100644 --- a/gen/db/virtual_games.sql.go +++ b/gen/db/virtual_games.sql.go @@ -1,6 +1,6 @@ // Code generated by sqlc. DO NOT EDIT. // versions: -// sqlc v1.30.0 +// sqlc v1.29.0 // source: virtual_games.sql package dbgen diff --git a/gen/db/wallet.sql.go b/gen/db/wallet.sql.go index ccb2d37..fcde631 100644 --- a/gen/db/wallet.sql.go +++ b/gen/db/wallet.sql.go @@ -1,6 +1,6 @@ // Code generated by sqlc. DO NOT EDIT. // versions: -// sqlc v1.30.0 +// sqlc v1.29.0 // source: wallet.sql package dbgen diff --git a/internal/domain/enet_pulse.go b/internal/domain/enet_pulse.go index a7a9135..bde9e5f 100644 --- a/internal/domain/enet_pulse.go +++ b/internal/domain/enet_pulse.go @@ -1,5 +1,7 @@ package domain +import "time" + type EnetPulseSport struct { ID string `json:"id"` Name string `json:"name"` @@ -365,3 +367,45 @@ type TournamentOddsResponse struct { TournamentID int64 `json:"objectFK"` Odds []PreMatchOutcome `json:"odds"` // reuse PreMatchOutcome struct from pre-match odds } + +type CreateEnetpulseSport struct { + SportID string `json:"sport_id"` // from API "id" + Name string `json:"name"` // from API "name" + UpdatesCount int `json:"updates_count,omitempty"` // from API "n" + LastUpdatedAt time.Time `json:"last_updated_at"` // from API "ut" + Status int `json:"status,omitempty"` // optional, default 1 +} + +type EnetpulseSport struct { + ID int64 `json:"id"` // DB primary key + SportID string `json:"sport_id"` // from API "id" + Name string `json:"name"` // from API "name" + UpdatesCount int `json:"updates_count"` // from API "n" + LastUpdatedAt time.Time `json:"last_updated_at"` + Status int `json:"status"` // active/inactive + CreatedAt time.Time `json:"created_at"` + UpdatedAt time.Time `json:"updated_at"` +} + +type EnetpulseTournamentTemplate struct { + ID int64 `json:"id"` + TemplateID string `json:"template_id"` // from API "id" + Name string `json:"name"` // from API "name" + SportFK string `json:"sport_fk"` // related sport id + Gender string `json:"gender"` // male, female, mixed, unknown + UpdatesCount int `json:"updates_count"` // from API "n" + LastUpdatedAt time.Time `json:"last_updated_at"` + Status int `json:"status"` // optional + CreatedAt time.Time `json:"created_at"` + UpdatedAt time.Time `json:"updated_at"` +} + +type CreateEnetpulseTournamentTemplate struct { + TemplateID string `json:"templateId"` // from API "id" + Name string `json:"name"` // from API "name" + SportFK int64 `json:"sportFK"` // foreign key to sport + Gender string `json:"gender"` // male, female, mixed, unknown + UpdatesCount int `json:"updatesCount"` // from API "n" + LastUpdatedAt time.Time `json:"lastUpdatedAt"` // from API "ut" + Status int `json:"status"` // optional, e.g., active/inactive +} diff --git a/internal/repository/enet_pulse.go b/internal/repository/enet_pulse.go new file mode 100644 index 0000000..3b484ad --- /dev/null +++ b/internal/repository/enet_pulse.go @@ -0,0 +1,141 @@ +package repository + +import ( + "context" + "fmt" + + dbgen "github.com/SamuelTariku/FortuneBet-Backend/gen/db" + "github.com/SamuelTariku/FortuneBet-Backend/internal/domain" + "github.com/jackc/pgx/v5/pgtype" +) + +func (s *Store) CreateEnetpulseSport(ctx context.Context, sport domain.CreateEnetpulseSport) (domain.EnetpulseSport, error) { + // Convert domain model to DB model if needed + dbSport, err := s.queries.CreateEnetpulseSport(ctx, ConvertCreateEnetpulseSport(sport)) + if err != nil { + return domain.EnetpulseSport{}, err + } + return ConvertDBEnetpulseSport(dbSport), nil +} + +// Fetch all sports +func (s *Store) GetAllEnetpulseSports(ctx context.Context) ([]domain.EnetpulseSport, error) { + dbSports, err := s.queries.GetAllEnetpulseSports(ctx) + if err != nil { + return nil, err + } + + var sports []domain.EnetpulseSport + for _, dbSport := range dbSports { + sports = append(sports, ConvertDBEnetpulseSport(dbSport)) + } + + return sports, nil +} + +func (s *Store) CreateEnetpulseTournamentTemplate( + ctx context.Context, + template domain.CreateEnetpulseTournamentTemplate, +) (domain.EnetpulseTournamentTemplate, error) { + // Convert domain model to DB model if needed + dbTemplate, err := s.queries.CreateEnetpulseTournamentTemplate( + ctx, + ConvertCreateEnetpulseTournamentTemplate(template), + ) + if err != nil { + return domain.EnetpulseTournamentTemplate{}, err + } + return ConvertDBEnetpulseTournamentTemplate(dbTemplate), nil +} + +// Fetch all tournament templates +func (s *Store) GetAllEnetpulseTournamentTemplates(ctx context.Context) ([]domain.EnetpulseTournamentTemplate, error) { + dbTemplates, err := s.queries.GetAllEnetpulseTournamentTemplates(ctx) + if err != nil { + return nil, err + } + + var templates []domain.EnetpulseTournamentTemplate + for _, dbTemplate := range dbTemplates { + templates = append(templates, ConvertDBEnetpulseTournamentTemplate(dbTemplate)) + } + + return templates, nil +} + +func ConvertCreateEnetpulseSport(s domain.CreateEnetpulseSport) dbgen.CreateEnetpulseSportParams { + return dbgen.CreateEnetpulseSportParams{ + SportID: s.SportID, + Name: s.Name, + UpdatesCount: pgtype.Int4{Int32: int32(s.UpdatesCount), Valid: true}, // SQLC might use int32 + LastUpdatedAt: pgtype.Timestamptz{Time: s.LastUpdatedAt, Valid: true}, + Status: pgtype.Int4{Int32: int32(s.Status), Valid: true}, + // UpdatedAt: nil, // SQLC will default NOW() if nil + } +} + +func ConvertDBEnetpulseSport(db dbgen.EnetpulseSport) domain.EnetpulseSport { + return domain.EnetpulseSport{ + ID: db.ID, + SportID: db.SportID, + Name: db.Name, + UpdatesCount: func() int { + if db.UpdatesCount.Valid { + return int(db.UpdatesCount.Int32) + } + return 0 // or another default value if needed + }(), // cast from int32 + LastUpdatedAt: db.LastUpdatedAt.Time, + Status: func() int { + if db.Status.Valid { + return int(db.Status.Int32) + } + return 0 // or another default value if needed + }(), // cast from int32 + CreatedAt: db.CreatedAt.Time, + UpdatedAt: db.UpdatedAt.Time, + } +} + +func ConvertDBEnetpulseTournamentTemplate(db dbgen.EnetpulseTournamentTemplate) domain.EnetpulseTournamentTemplate { + return domain.EnetpulseTournamentTemplate{ + ID: db.ID, + TemplateID: db.TemplateID, + Name: db.Name, + SportFK: db.SportFk, + Gender: func() string { + if db.Gender.Valid { + return db.Gender.String + } + return "" + }(), + UpdatesCount: func() int { + if db.UpdatesCount.Valid { + return int(db.UpdatesCount.Int32) + } + return 0 + }(), + LastUpdatedAt: db.LastUpdatedAt.Time, + Status: func() int { + if db.Status.Valid { + return int(db.Status.Int32) + } + return 0 + }(), + CreatedAt: db.CreatedAt.Time, + UpdatedAt: db.UpdatedAt.Time, + } +} + +func ConvertCreateEnetpulseTournamentTemplate( + t domain.CreateEnetpulseTournamentTemplate, +) dbgen.CreateEnetpulseTournamentTemplateParams { + return dbgen.CreateEnetpulseTournamentTemplateParams{ + TemplateID: t.TemplateID, + SportFk: fmt.Sprintf("%d", t.SportFK), + Gender: pgtype.Text{String: t.Gender, Valid: t.Gender != ""}, + UpdatesCount: pgtype.Int4{Int32: int32(t.UpdatesCount), Valid: true}, + LastUpdatedAt: pgtype.Timestamptz{Time: t.LastUpdatedAt, Valid: true}, + Status: pgtype.Int4{Int32: int32(t.Status), Valid: true}, + } +} diff --git a/internal/repository/event.go b/internal/repository/event.go index 5458e6e..b273576 100644 --- a/internal/repository/event.go +++ b/internal/repository/event.go @@ -4,6 +4,7 @@ import ( "context" "fmt" "math" + "strconv" dbgen "github.com/SamuelTariku/FortuneBet-Backend/gen/db" "github.com/SamuelTariku/FortuneBet-Backend/internal/domain" @@ -294,7 +295,11 @@ func (s *Store) DeleteEvent(ctx context.Context, eventID int64) error { } func (s *Store) GetSportAndLeagueIDs(ctx context.Context, eventID string) ([]int64, error) { - sportAndLeagueIDs, err := s.queries.GetSportAndLeagueIDs(ctx, eventID) + id, err := strconv.ParseInt(eventID, 10, 64) + if err != nil { + return nil, fmt.Errorf("invalid eventID: %v", err) + } + sportAndLeagueIDs, err := s.queries.GetSportAndLeagueIDs(ctx, id) if err != nil { return nil, err } diff --git a/internal/services/enet_pulse/service.go b/internal/services/enet_pulse/service.go index fc589fc..0e6de9c 100644 --- a/internal/services/enet_pulse/service.go +++ b/internal/services/enet_pulse/service.go @@ -6,6 +6,7 @@ import ( "fmt" "io" "net/http" + "strconv" "time" "github.com/SamuelTariku/FortuneBet-Backend/internal/config" @@ -33,42 +34,167 @@ func New(cfg config.Config, store *repository.Store) *Service { } } -func (s *Service) FetchSports(ctx context.Context) error { +func (s *Service) FetchAndStoreSports(ctx context.Context) error { + // 1️⃣ Compose URL with credentials url := fmt.Sprintf( "http://eapi.enetpulse.com/sport/list/?username=%s&token=%s", s.cfg.EnetPulseConfig.UserName, s.cfg.EnetPulseConfig.Token, ) + // 2️⃣ Create HTTP request with context req, err := http.NewRequestWithContext(ctx, http.MethodGet, url, nil) if err != nil { return fmt.Errorf("creating sport request: %w", err) } + // 3️⃣ Execute request resp, err := s.httpClient.Do(req) if err != nil { return fmt.Errorf("requesting sports: %w", err) } defer resp.Body.Close() + // 4️⃣ Check response status if resp.StatusCode != http.StatusOK { body, _ := io.ReadAll(resp.Body) - return fmt.Errorf("failed to fetch sports (status %d): %s", - resp.StatusCode, string(body)) + return fmt.Errorf("failed to fetch sports (status %d): %s", resp.StatusCode, string(body)) } - var sportsResp domain.SportsResponse + // 5️⃣ Decode JSON response + var sportsResp struct { + Sports map[string]struct { + ID string `json:"id"` + N string `json:"n"` // updates count + Name string `json:"name"` + UT string `json:"ut"` // timestamp string + } `json:"sports"` + } if err := json.NewDecoder(resp.Body).Decode(&sportsResp); err != nil { return fmt.Errorf("decoding sports response: %w", err) } - // Example: save sports to store or process + // 6️⃣ Iterate and store each sport for _, sport := range sportsResp.Sports { - // Insert/update in DB or cache - // e.g. s.store.SaveSport(ctx, sport) - fmt.Printf("Fetched sport: ID=%s Name=%s UpdatedAt=%s\n", - sport.ID, sport.Name, sport.UT) + // Parse updates count + updatesCount := 0 + if sport.N != "" { + if n, err := strconv.Atoi(sport.N); err == nil { + updatesCount = n + } + } + + // Parse timestamp + lastUpdatedAt, err := time.Parse(time.RFC3339, sport.UT) + if err != nil { + // Fallback to zero time if parsing fails + lastUpdatedAt = time.Time{} + } + + // Build domain object + createSport := domain.CreateEnetpulseSport{ + SportID: sport.ID, + Name: sport.Name, + UpdatesCount: updatesCount, + LastUpdatedAt: lastUpdatedAt, + Status: 1, // default active + } + + // Insert or update in DB + if _, err := s.store.CreateEnetpulseSport(ctx, createSport); err != nil { + // Log error but continue + // s.logger.Error("failed to store sport", zap.String("sport_id", sport.ID), zap.Error(err)) + continue + } } + // s.logger.Info("Successfully fetched and stored sports", zap.Int("count", len(sportsResp.Sports))) + return nil +} + +func (s *Service) FetchAndStoreTournamentTemplates(ctx context.Context) error { + // 1️⃣ Fetch all sports from the database + sports, err := s.store.GetAllEnetpulseSports(ctx) + if err != nil { + return fmt.Errorf("failed to fetch sports from DB: %w", err) + } + + for _, sport := range sports { + // 2️⃣ Compose URL for each sport using its sportID + url := fmt.Sprintf( + "http://eapi.enetpulse.com/tournament_template/list/?sportFK=%s&username=%s&token=%s", + sport.SportID, + s.cfg.EnetPulseConfig.UserName, + s.cfg.EnetPulseConfig.Token, + ) + + // 3️⃣ Create HTTP request + req, err := http.NewRequestWithContext(ctx, http.MethodGet, url, nil) + if err != nil { + return fmt.Errorf("creating tournament template request for sport %s: %w", sport.SportID, err) + } + + // 4️⃣ Execute request + resp, err := s.httpClient.Do(req) + if err != nil { + return fmt.Errorf("requesting tournament templates for sport %s: %w", sport.SportID, err) + } + defer resp.Body.Close() + + if resp.StatusCode != http.StatusOK { + body, _ := io.ReadAll(resp.Body) + return fmt.Errorf("failed to fetch tournament templates for sport %s (status %d): %s", + sport.SportID, resp.StatusCode, string(body)) + } + + // 5️⃣ Decode JSON response + var templatesResp struct { + TournamentTemplates map[string]struct { + ID string `json:"id"` + Name string `json:"name"` + SportFK string `json:"sportFK"` + Gender string `json:"gender"` + N string `json:"n"` // updates count + UT string `json:"ut"` // timestamp + } `json:"tournament_templates"` + } + if err := json.NewDecoder(resp.Body).Decode(&templatesResp); err != nil { + return fmt.Errorf("decoding tournament templates for sport %s: %w", sport.SportID, err) + } + + // 6️⃣ Iterate and store each tournament template + for _, tmpl := range templatesResp.TournamentTemplates { + updatesCount := 0 + if tmpl.N != "" { + if n, err := strconv.Atoi(tmpl.N); err == nil { + updatesCount = n + } + } + + lastUpdatedAt, err := time.Parse(time.RFC3339, tmpl.UT) + if err != nil { + lastUpdatedAt = time.Time{} + } + + createTemplate := domain.CreateEnetpulseTournamentTemplate{ + TemplateID: tmpl.ID, + Name: tmpl.Name, + SportFK: sport.ID, // use DB sport ID + Gender: tmpl.Gender, + UpdatesCount: updatesCount, + LastUpdatedAt: lastUpdatedAt, + Status: 1, // default active + } + + // Insert into DB + if _, err := s.store.CreateEnetpulseTournamentTemplate(ctx, createTemplate); err != nil { + // Log error but continue + // s.logger.Error("failed to store tournament template", zap.String("template_id", tmpl.ID), zap.Error(err)) + continue + } + } + } + + // s.logger.Info("Successfully fetched and stored all tournament templates") return nil } diff --git a/internal/web_server/cron.go b/internal/web_server/cron.go index 2e5c8dc..eadce96 100644 --- a/internal/web_server/cron.go +++ b/internal/web_server/cron.go @@ -9,6 +9,7 @@ import ( "github.com/SamuelTariku/FortuneBet-Backend/internal/domain" betSvc "github.com/SamuelTariku/FortuneBet-Backend/internal/services/bet" + enetpulse "github.com/SamuelTariku/FortuneBet-Backend/internal/services/enet_pulse" eventsvc "github.com/SamuelTariku/FortuneBet-Backend/internal/services/event" oddssvc "github.com/SamuelTariku/FortuneBet-Backend/internal/services/odds" "github.com/SamuelTariku/FortuneBet-Backend/internal/services/report" @@ -242,3 +243,51 @@ func ProcessBetCashback(ctx context.Context, betService *betSvc.Service) { c.Start() log.Println("Cron jobs started for bet cashbacks") } + +func StartEnetPulseCron(enetPulseSvc *enetpulse.Service, mongoLogger *zap.Logger) { + c := cron.New(cron.WithSeconds()) + + schedule := []struct { + spec string + task func() + }{ + { + spec: "0 * * * * *", // Every minute + task: func() { + mongoLogger.Info("Began fetching and storing sports cron task") + if err := enetPulseSvc.FetchAndStoreSports(context.Background()); err != nil { + mongoLogger.Error("Failed to fetch and store sports", + zap.Error(err), + ) + } else { + mongoLogger.Info("Completed fetching and storing sports without errors") + } + + mongoLogger.Info("Began fetching and storing tournament templates cron task") + if err := enetPulseSvc.FetchAndStoreTournamentTemplates(context.Background()); err != nil { + mongoLogger.Error("Failed to fetch and store tournament templates", + zap.Error(err), + ) + } else { + mongoLogger.Info("Completed fetching and storing tournament templates without errors") + } + }, + }, + } + + for _, job := range schedule { + // Run the task immediately at startup + job.task() + + // Schedule the task + if _, err := c.AddFunc(job.spec, job.task); err != nil { + mongoLogger.Error("Failed to schedule EnetPulse cron job", + zap.Error(err), + ) + } + } + + c.Start() + log.Println("EnetPulse cron jobs started for fetching and storing sports and tournament templates") + mongoLogger.Info("EnetPulse cron jobs started for fetching and storing sports and tournament templates") +} diff --git a/internal/web_server/handlers/atlas.go b/internal/web_server/handlers/atlas.go index 7708336..9690578 100644 --- a/internal/web_server/handlers/atlas.go +++ b/internal/web_server/handlers/atlas.go @@ -12,6 +12,34 @@ import ( "github.com/gofiber/fiber/v2" ) +// GetAtlasVGames godoc +// @Summary List Atlas virtual games +// @Description Retrieves available Atlas virtual games from the provider +// @Tags Virtual Games - Atlas +// @Produce json +// @Success 200 {object} domain.Response{data=[]domain.AtlasGameEntity} +// @Failure 502 {object} domain.ErrorResponse +// @Router /api/v1/atlas/games [get] +func (h *Handler) GetAtlasVGames(c *fiber.Ctx) error { + // Call the service + games, err := h.veliVirtualGameSvc.GetAtlasVGames(c.Context()) + if err != nil { + log.Println("GetAtlasVGames error:", err) + return c.Status(fiber.StatusBadGateway).JSON(domain.ErrorResponse{ + Message: "Failed to fetch Atlas virtual games", + Error: err.Error(), + }) + } + + // Return the list of games + return c.Status(fiber.StatusOK).JSON(domain.Response{ + Message: "Atlas virtual games retrieved successfully", + Data: games, + StatusCode: fiber.StatusOK, + Success: true, + }) +} + // InitAtlasGame godoc // @Summary Start an Atlas virtual game session // @Description Initializes a game session for the given player using Atlas virtual game provider diff --git a/internal/web_server/routes.go b/internal/web_server/routes.go index 29c49a9..b5c1ed0 100644 --- a/internal/web_server/routes.go +++ b/internal/web_server/routes.go @@ -381,6 +381,7 @@ func (a *App) initAppRoutes() { groupV1.Post("/veli/credit-balances", a.authMiddleware, h.GetCreditBalances) //Atlas Virtual Game Routes + groupV1.Get("/atlas/games", a.authMiddleware, h.InitAtlasGame) groupV1.Post("/atlas/init-game", a.authMiddleware, h.InitAtlasGame) a.fiber.Post("/account", h.AtlasGetUserDataCallback) a.fiber.Post("/betwin", h.HandleAtlasBetWin)