Merge remote-tracking branch 'refs/remotes/origin/main'
This commit is contained in:
commit
91c929c7a2
2
db/migrations/000006_enet_pulse.down.sql
Normal file
2
db/migrations/000006_enet_pulse.down.sql
Normal file
|
|
@ -0,0 +1,2 @@
|
||||||
|
DROP TABLE IF EXISTS enetpulse_sports;
|
||||||
|
DROP TABLE IF EXISTS enetpulse_tournament_templates;
|
||||||
23
db/migrations/000006_enet_pulse.up.sql
Normal file
23
db/migrations/000006_enet_pulse.up.sql
Normal file
|
|
@ -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
|
||||||
|
);
|
||||||
57
db/query/enet_pulse.sql
Normal file
57
db/query/enet_pulse.sql
Normal file
|
|
@ -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;
|
||||||
|
|
||||||
198
gen/db/enet_pulse.sql.go
Normal file
198
gen/db/enet_pulse.sql.go
Normal file
|
|
@ -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
|
||||||
|
}
|
||||||
|
|
@ -256,6 +256,30 @@ type DisabledOdd struct {
|
||||||
CreatedAt pgtype.Timestamp `json:"created_at"`
|
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 {
|
type Event struct {
|
||||||
ID int64 `json:"id"`
|
ID int64 `json:"id"`
|
||||||
SourceEventID string `json:"source_event_id"`
|
SourceEventID string `json:"source_event_id"`
|
||||||
|
|
|
||||||
|
|
@ -389,6 +389,13 @@ func (c *Config) loadEnv() error {
|
||||||
c.ADRO_SMS_HOST_URL = "https://api.afrosms.com"
|
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")
|
popOKClientID := os.Getenv("POPOK_CLIENT_ID")
|
||||||
|
|
||||||
popOKPlatform := os.Getenv("POPOK_PLATFORM")
|
popOKPlatform := os.Getenv("POPOK_PLATFORM")
|
||||||
|
|
|
||||||
|
|
@ -1,5 +1,7 @@
|
||||||
package domain
|
package domain
|
||||||
|
|
||||||
|
import "time"
|
||||||
|
|
||||||
type EnetPulseSport struct {
|
type EnetPulseSport struct {
|
||||||
ID string `json:"id"`
|
ID string `json:"id"`
|
||||||
Name string `json:"name"`
|
Name string `json:"name"`
|
||||||
|
|
@ -365,3 +367,45 @@ type TournamentOddsResponse struct {
|
||||||
TournamentID int64 `json:"objectFK"`
|
TournamentID int64 `json:"objectFK"`
|
||||||
Odds []PreMatchOutcome `json:"odds"` // reuse PreMatchOutcome struct from pre-match odds
|
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
|
||||||
|
}
|
||||||
|
|
|
||||||
|
|
@ -31,6 +31,20 @@ type GameEntity struct {
|
||||||
Category string `json:"category"`
|
Category string `json:"category"`
|
||||||
HasDemoMode bool `json:"hasDemoMode"`
|
HasDemoMode bool `json:"hasDemoMode"`
|
||||||
HasFreeBets bool `json:"hasFreeBets"`
|
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 {
|
type GameStartRequest struct {
|
||||||
|
|
|
||||||
|
|
@ -283,35 +283,36 @@ type PopokLaunchResponse struct {
|
||||||
|
|
||||||
type VirtualGameProvider struct {
|
type VirtualGameProvider struct {
|
||||||
// ID int64 `json:"id" db:"id"`
|
// ID int64 `json:"id" db:"id"`
|
||||||
ProviderID string `json:"provider_id" db:"provider_id"`
|
ProviderID string `json:"provider_id" db:"provider_id"`
|
||||||
ProviderName string `json:"provider_name" db:"provider_name"`
|
ProviderName string `json:"provider_name" db:"provider_name"`
|
||||||
LogoDark *string `json:"logo_dark,omitempty" db:"logo_dark"`
|
LogoDark *string `json:"logo_dark,omitempty" db:"logo_dark"`
|
||||||
LogoLight *string `json:"logo_light,omitempty" db:"logo_light"`
|
LogoLight *string `json:"logo_light,omitempty" db:"logo_light"`
|
||||||
Enabled bool `json:"enabled" db:"enabled"`
|
Enabled bool `json:"enabled" db:"enabled"`
|
||||||
CreatedAt time.Time `json:"created_at" db:"created_at"`
|
CreatedAt time.Time `json:"created_at" db:"created_at"`
|
||||||
UpdatedAt *time.Time `json:"updated_at,omitempty" db:"updated_at"`
|
UpdatedAt *time.Time `json:"updated_at,omitempty" db:"updated_at"`
|
||||||
}
|
}
|
||||||
|
|
||||||
// VirtualGameProviderPagination is used when returning paginated results
|
// VirtualGameProviderPagination is used when returning paginated results
|
||||||
type VirtualGameProviderPagination struct {
|
type VirtualGameProviderPagination struct {
|
||||||
Providers []VirtualGameProvider `json:"providers"`
|
Providers []VirtualGameProvider `json:"providers"`
|
||||||
TotalCount int64 `json:"total_count"`
|
TotalCount int64 `json:"total_count"`
|
||||||
Limit int32 `json:"limit"`
|
Limit int32 `json:"limit"`
|
||||||
Offset int32 `json:"offset"`
|
Offset int32 `json:"offset"`
|
||||||
}
|
}
|
||||||
|
|
||||||
type UnifiedGame struct {
|
type UnifiedGame struct {
|
||||||
GameID string `json:"gameId"`
|
GameID string `json:"gameId"`
|
||||||
ProviderID string `json:"providerId"`
|
ProviderID string `json:"providerId"`
|
||||||
Provider string `json:"provider"`
|
Provider string `json:"provider"`
|
||||||
Name string `json:"name"`
|
Name string `json:"name"`
|
||||||
Category string `json:"category,omitempty"`
|
Category string `json:"category,omitempty"`
|
||||||
DeviceType string `json:"deviceType,omitempty"`
|
DeviceType string `json:"deviceType,omitempty"`
|
||||||
Volatility string `json:"volatility,omitempty"`
|
Volatility string `json:"volatility,omitempty"`
|
||||||
RTP *float64 `json:"rtp,omitempty"`
|
RTP *float64 `json:"rtp,omitempty"`
|
||||||
HasDemo bool `json:"hasDemo"`
|
HasDemo bool `json:"hasDemo"`
|
||||||
HasFreeBets bool `json:"hasFreeBets"`
|
HasFreeBets bool `json:"hasFreeBets"`
|
||||||
Bets []float64 `json:"bets,omitempty"`
|
Bets []float64 `json:"bets,omitempty"`
|
||||||
Thumbnail string `json:"thumbnail,omitempty"`
|
Thumbnail string `json:"thumbnail,omitempty"`
|
||||||
Status int `json:"status,omitempty"`
|
Status int `json:"status,omitempty"`
|
||||||
|
DemoURL string `json:"demoUrl"`
|
||||||
}
|
}
|
||||||
|
|
|
||||||
141
internal/repository/enet_pulse.go
Normal file
141
internal/repository/enet_pulse.go
Normal file
|
|
@ -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},
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
@ -4,6 +4,7 @@ import (
|
||||||
"context"
|
"context"
|
||||||
"fmt"
|
"fmt"
|
||||||
"math"
|
"math"
|
||||||
|
"strconv"
|
||||||
|
|
||||||
dbgen "github.com/SamuelTariku/FortuneBet-Backend/gen/db"
|
dbgen "github.com/SamuelTariku/FortuneBet-Backend/gen/db"
|
||||||
"github.com/SamuelTariku/FortuneBet-Backend/internal/domain"
|
"github.com/SamuelTariku/FortuneBet-Backend/internal/domain"
|
||||||
|
|
|
||||||
|
|
@ -6,6 +6,7 @@ import (
|
||||||
"fmt"
|
"fmt"
|
||||||
"io"
|
"io"
|
||||||
"net/http"
|
"net/http"
|
||||||
|
"strconv"
|
||||||
"time"
|
"time"
|
||||||
|
|
||||||
"github.com/SamuelTariku/FortuneBet-Backend/internal/config"
|
"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(
|
url := fmt.Sprintf(
|
||||||
"http://eapi.enetpulse.com/sport/list/?username=%s&token=%s",
|
"http://eapi.enetpulse.com/sport/list/?username=%s&token=%s",
|
||||||
s.cfg.EnetPulseConfig.UserName, s.cfg.EnetPulseConfig.Token,
|
s.cfg.EnetPulseConfig.UserName, s.cfg.EnetPulseConfig.Token,
|
||||||
)
|
)
|
||||||
|
|
||||||
|
// 2️⃣ Create HTTP request with context
|
||||||
req, err := http.NewRequestWithContext(ctx, http.MethodGet, url, nil)
|
req, err := http.NewRequestWithContext(ctx, http.MethodGet, url, nil)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return fmt.Errorf("creating sport request: %w", err)
|
return fmt.Errorf("creating sport request: %w", err)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// 3️⃣ Execute request
|
||||||
resp, err := s.httpClient.Do(req)
|
resp, err := s.httpClient.Do(req)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return fmt.Errorf("requesting sports: %w", err)
|
return fmt.Errorf("requesting sports: %w", err)
|
||||||
}
|
}
|
||||||
defer resp.Body.Close()
|
defer resp.Body.Close()
|
||||||
|
|
||||||
|
// 4️⃣ Check response status
|
||||||
if resp.StatusCode != http.StatusOK {
|
if resp.StatusCode != http.StatusOK {
|
||||||
body, _ := io.ReadAll(resp.Body)
|
body, _ := io.ReadAll(resp.Body)
|
||||||
return fmt.Errorf("failed to fetch sports (status %d): %s",
|
return fmt.Errorf("failed to fetch sports (status %d): %s", resp.StatusCode, string(body))
|
||||||
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 {
|
if err := json.NewDecoder(resp.Body).Decode(&sportsResp); err != nil {
|
||||||
return fmt.Errorf("decoding sports response: %w", err)
|
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 {
|
for _, sport := range sportsResp.Sports {
|
||||||
// Insert/update in DB or cache
|
// Parse updates count
|
||||||
// e.g. s.store.SaveSport(ctx, sport)
|
updatesCount := 0
|
||||||
fmt.Printf("Fetched sport: ID=%s Name=%s UpdatedAt=%s\n",
|
if sport.N != "" {
|
||||||
sport.ID, sport.Name, sport.UT)
|
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
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -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) {
|
func (s *Service) FetchAndStoreAllVirtualGames(ctx context.Context, req domain.ProviderRequest, currency string) ([]domain.UnifiedGame, error) {
|
||||||
|
|
||||||
logger := s.mongoLogger.With(
|
logger := s.mongoLogger.With(
|
||||||
zap.String("service", "FetchAndStoreAllVirtualGames"),
|
zap.String("service", "FetchAndStoreAllVirtualGames"),
|
||||||
zap.Any("ProviderRequest", req),
|
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)
|
_, err := s.AddProviders(ctx, req)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, fmt.Errorf("failed to add providers to database: %w", err)
|
return nil, fmt.Errorf("failed to add providers to database: %w", err)
|
||||||
}
|
}
|
||||||
|
|
||||||
var allGames []domain.UnifiedGame
|
var allGames []domain.UnifiedGame
|
||||||
// --- 1. Get providers from external API ---
|
|
||||||
|
// --- 1. Existing providers (Veli Games) ---
|
||||||
providersRes, err := s.GetProviders(ctx, req)
|
providersRes, err := s.GetProviders(ctx, req)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
logger.Error("Failed to fetch provider", zap.Error(err))
|
logger.Error("Failed to fetch provider", zap.Error(err))
|
||||||
return nil, fmt.Errorf("failed to fetch providers: %w", 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 {
|
for _, p := range providersRes.Items {
|
||||||
// Violates foreign key if the provider isn't added
|
|
||||||
games, err := s.GetGames(ctx, domain.GameListRequest{
|
games, err := s.GetGames(ctx, domain.GameListRequest{
|
||||||
BrandID: s.cfg.VeliGames.BrandID,
|
BrandID: s.cfg.VeliGames.BrandID,
|
||||||
ProviderID: p.ProviderID,
|
ProviderID: p.ProviderID,
|
||||||
|
|
@ -185,20 +184,18 @@ func (s *Service) FetchAndStoreAllVirtualGames(ctx context.Context, req domain.P
|
||||||
|
|
||||||
for _, g := range games {
|
for _, g := range games {
|
||||||
unified := domain.UnifiedGame{
|
unified := domain.UnifiedGame{
|
||||||
GameID: g.GameID,
|
GameID: g.GameID,
|
||||||
ProviderID: g.ProviderID,
|
ProviderID: g.ProviderID,
|
||||||
Provider: p.ProviderName,
|
Provider: p.ProviderName,
|
||||||
Name: g.Name,
|
Name: g.Name,
|
||||||
Category: g.Category,
|
Category: g.Category,
|
||||||
DeviceType: g.DeviceType,
|
DeviceType: g.DeviceType,
|
||||||
// Volatility: g.Volatility,
|
|
||||||
// RTP: g.RTP,
|
|
||||||
HasDemo: g.HasDemoMode,
|
HasDemo: g.HasDemoMode,
|
||||||
HasFreeBets: g.HasFreeBets,
|
HasFreeBets: g.HasFreeBets,
|
||||||
}
|
}
|
||||||
allGames = append(allGames, unified)
|
allGames = append(allGames, unified)
|
||||||
|
|
||||||
// --- Save to DB ---
|
// Save to DB
|
||||||
_, err = s.repo.CreateVirtualGame(ctx, dbgen.CreateVirtualGameParams{
|
_, err = s.repo.CreateVirtualGame(ctx, dbgen.CreateVirtualGameParams{
|
||||||
GameID: g.GameID,
|
GameID: g.GameID,
|
||||||
ProviderID: g.ProviderID,
|
ProviderID: g.ProviderID,
|
||||||
|
|
@ -211,8 +208,6 @@ func (s *Service) FetchAndStoreAllVirtualGames(ctx context.Context, req domain.P
|
||||||
String: g.DeviceType,
|
String: g.DeviceType,
|
||||||
Valid: g.DeviceType != "",
|
Valid: g.DeviceType != "",
|
||||||
},
|
},
|
||||||
// Volatility: g.Volatility,
|
|
||||||
// RTP: g.RTP,
|
|
||||||
HasDemo: pgtype.Bool{
|
HasDemo: pgtype.Bool{
|
||||||
Bool: g.HasDemoMode,
|
Bool: g.HasDemoMode,
|
||||||
Valid: true,
|
Valid: true,
|
||||||
|
|
@ -221,18 +216,56 @@ func (s *Service) FetchAndStoreAllVirtualGames(ctx context.Context, req domain.P
|
||||||
Bool: g.HasFreeBets,
|
Bool: g.HasFreeBets,
|
||||||
Valid: true,
|
Valid: true,
|
||||||
},
|
},
|
||||||
// Bets: g.Bets,
|
|
||||||
// Thumbnail: g.Thumbnail,
|
|
||||||
// Status: g.Status,
|
|
||||||
})
|
})
|
||||||
|
|
||||||
if err != nil {
|
if err != nil {
|
||||||
logger.Error("failed to create virtual game", zap.Error(err))
|
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)
|
popokGames, err := s.virtualGameSvc.ListGames(ctx, currency)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
logger.Error("failed to fetch PopOk games", zap.Error(err))
|
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)
|
allGames = append(allGames, unified)
|
||||||
|
|
||||||
// --- Convert []float64 to []pgtype.Numeric ---
|
// Convert []float64 to []pgtype.Numeric
|
||||||
var betsNumeric []pgtype.Numeric
|
var betsNumeric []pgtype.Numeric
|
||||||
for _, bet := range g.Bets {
|
for _, bet := range g.Bets {
|
||||||
var num pgtype.Numeric
|
var num pgtype.Numeric
|
||||||
|
|
@ -260,9 +293,9 @@ func (s *Service) FetchAndStoreAllVirtualGames(ctx context.Context, req domain.P
|
||||||
betsNumeric = append(betsNumeric, num)
|
betsNumeric = append(betsNumeric, num)
|
||||||
}
|
}
|
||||||
|
|
||||||
// --- Save to DB ---
|
// Save to DB
|
||||||
_, err = s.repo.CreateVirtualGame(ctx, dbgen.CreateVirtualGameParams{
|
_, 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",
|
ProviderID: "popok",
|
||||||
Name: g.GameName,
|
Name: g.GameName,
|
||||||
Bets: betsNumeric,
|
Bets: betsNumeric,
|
||||||
|
|
@ -284,9 +317,8 @@ func (s *Service) FetchAndStoreAllVirtualGames(ctx context.Context, req domain.P
|
||||||
},
|
},
|
||||||
|
|
||||||
})
|
})
|
||||||
|
|
||||||
if err != nil {
|
if err != nil {
|
||||||
logger.Error("failed to create virtual game", zap.Error(err))
|
logger.Error("failed to create PopOK virtual game", zap.Error(err))
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -9,6 +9,7 @@ import (
|
||||||
)
|
)
|
||||||
|
|
||||||
type VeliVirtualGameService interface {
|
type VeliVirtualGameService interface {
|
||||||
|
GetAtlasVGames(ctx context.Context) ([]domain.AtlasGameEntity, error)
|
||||||
FetchAndStoreAllVirtualGames(ctx context.Context, req domain.ProviderRequest, currency string) ([]domain.UnifiedGame, error)
|
FetchAndStoreAllVirtualGames(ctx context.Context, req domain.ProviderRequest, currency string) ([]domain.UnifiedGame, error)
|
||||||
GetAllVirtualGames(ctx context.Context, params dbgen.GetAllVirtualGamesParams) ([]domain.UnifiedGame, error)
|
GetAllVirtualGames(ctx context.Context, params dbgen.GetAllVirtualGamesParams) ([]domain.UnifiedGame, error)
|
||||||
AddProviders(ctx context.Context, req domain.ProviderRequest) (*domain.ProviderResponse, error)
|
AddProviders(ctx context.Context, req domain.ProviderRequest) (*domain.ProviderResponse, error)
|
||||||
|
|
|
||||||
|
|
@ -2,9 +2,13 @@ package veli
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"context"
|
"context"
|
||||||
|
"encoding/json"
|
||||||
"errors"
|
"errors"
|
||||||
"fmt"
|
"fmt"
|
||||||
|
"io"
|
||||||
|
"net/http"
|
||||||
"strconv"
|
"strconv"
|
||||||
|
"time"
|
||||||
|
|
||||||
"github.com/SamuelTariku/FortuneBet-Backend/internal/config"
|
"github.com/SamuelTariku/FortuneBet-Backend/internal/config"
|
||||||
"github.com/SamuelTariku/FortuneBet-Backend/internal/domain"
|
"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) {
|
func (s *Service) GetProviders(ctx context.Context, req domain.ProviderRequest) (*domain.ProviderResponse, error) {
|
||||||
// Always mirror request body fields into sigParams
|
// Always mirror request body fields into sigParams
|
||||||
sigParams := map[string]any{
|
sigParams := map[string]any{
|
||||||
|
|
|
||||||
|
|
@ -9,6 +9,7 @@ import (
|
||||||
|
|
||||||
"github.com/SamuelTariku/FortuneBet-Backend/internal/domain"
|
"github.com/SamuelTariku/FortuneBet-Backend/internal/domain"
|
||||||
betSvc "github.com/SamuelTariku/FortuneBet-Backend/internal/services/bet"
|
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"
|
eventsvc "github.com/SamuelTariku/FortuneBet-Backend/internal/services/event"
|
||||||
oddssvc "github.com/SamuelTariku/FortuneBet-Backend/internal/services/odds"
|
oddssvc "github.com/SamuelTariku/FortuneBet-Backend/internal/services/odds"
|
||||||
"github.com/SamuelTariku/FortuneBet-Backend/internal/services/report"
|
"github.com/SamuelTariku/FortuneBet-Backend/internal/services/report"
|
||||||
|
|
@ -242,3 +243,51 @@ func ProcessBetCashback(ctx context.Context, betService *betSvc.Service) {
|
||||||
c.Start()
|
c.Start()
|
||||||
log.Println("Cron jobs started for bet cashbacks")
|
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")
|
||||||
|
}
|
||||||
|
|
|
||||||
|
|
@ -12,6 +12,34 @@ import (
|
||||||
"github.com/gofiber/fiber/v2"
|
"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
|
// InitAtlasGame godoc
|
||||||
// @Summary Start an Atlas virtual game session
|
// @Summary Start an Atlas virtual game session
|
||||||
// @Description Initializes a game session for the given player using Atlas virtual game provider
|
// @Description Initializes a game session for the given player using Atlas virtual game provider
|
||||||
|
|
|
||||||
|
|
@ -381,6 +381,7 @@ func (a *App) initAppRoutes() {
|
||||||
groupV1.Post("/veli/credit-balances", a.authMiddleware, h.GetCreditBalances)
|
groupV1.Post("/veli/credit-balances", a.authMiddleware, h.GetCreditBalances)
|
||||||
|
|
||||||
//Atlas Virtual Game Routes
|
//Atlas Virtual Game Routes
|
||||||
|
groupV1.Get("/atlas/games", a.authMiddleware, h.InitAtlasGame)
|
||||||
groupV1.Post("/atlas/init-game", a.authMiddleware, h.InitAtlasGame)
|
groupV1.Post("/atlas/init-game", a.authMiddleware, h.InitAtlasGame)
|
||||||
a.fiber.Post("/account", h.AtlasGetUserDataCallback)
|
a.fiber.Post("/account", h.AtlasGetUserDataCallback)
|
||||||
a.fiber.Post("/betwin", h.HandleAtlasBetWin)
|
a.fiber.Post("/betwin", h.HandleAtlasBetWin)
|
||||||
|
|
|
||||||
Loading…
Reference in New Issue
Block a user