fix: global setting, company override settings, bet and ticket fixes for multi tenant

This commit is contained in:
Samuel Tariku 2025-08-25 07:23:55 +03:00
parent 8ca4758917
commit aaf14fedcf
51 changed files with 1897 additions and 1071 deletions

View File

@ -292,7 +292,7 @@ CREATE TABLE events (
default_is_featured BOOLEAN NOT NULL DEFAULT false,
default_winning_upper_limit INT NOT NULL,
is_monitored BOOLEAN NOT NULL DEFAULT FALSE,
UNIQUE(source_event_id, source)
UNIQUE(id, source)
);
CREATE TABLE event_history (
id BIGSERIAL PRIMARY KEY,
@ -321,8 +321,6 @@ CREATE TABLE odds_market (
default_is_active BOOLEAN NOT NULL DEFAULT true,
fetched_at TIMESTAMP DEFAULT now(),
expires_at TIMESTAMP NOT NULL,
UNIQUE (market_id, name, handicap),
UNIQUE (event_id, market_id, name, handicap),
UNIQUE (event_id, market_id)
);
CREATE TABLE odd_history (
@ -408,12 +406,21 @@ CREATE TABLE teams (
bet365_id BIGINT,
img_url TEXT
);
CREATE TABLE IF NOT EXISTS settings (
CREATE TABLE IF NOT EXISTS global_settings (
key TEXT PRIMARY KEY,
value TEXT NOT NULL,
created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP,
updated_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP
);
-- Tenant/Company-specific overrides
CREATE TABLE IF NOT EXISTS company_settings (
company_id BIGINT NOT NULL REFERENCES companies(id) ON DELETE CASCADE,
key TEXT NOT NULL,
value TEXT NOT NULL,
created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP,
updated_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP,
PRIMARY KEY (company_id, key)
);
CREATE TABLE bonus (
multiplier REAL NOT NULL,
id BIGSERIAL PRIMARY KEY,
@ -430,11 +437,11 @@ CREATE TABLE flags (
CHECK (
(
bet_id IS NOT NULL
AND odd_id IS NULL
AND odds_market_id IS NULL
)
OR (
bet_id IS NULL
AND odd_id IS NOT NULL
AND odds_market_id IS NOT NULL
)
)
);
@ -579,7 +586,7 @@ SELECT l.*,
COALESCE(cls.is_featured, l.default_is_featured) AS is_featured,
cls.updated_at
FROM leagues l
JOIN company_league_settings cls ON leagues.id = cls.league_id;
JOIN company_league_settings cls ON l.id = cls.league_id;
CREATE VIEW event_with_settings AS
SELECT e.*,
ces.company_id,
@ -592,15 +599,23 @@ SELECT e.*,
ces.updated_at,
l.country_code as league_cc
FROM events e
JOIN company_event_settings ces ON events.id = ces.event_id
JOIN leagues l ON leagues.id = e.league_id;
JOIN company_event_settings ces ON e.id = ces.event_id
JOIN leagues l ON l.id = e.league_id;
CREATE VIEW event_with_country AS
SELECT events.*,
leagues.country_code as league_cc
FROM events
LEFT JOIN leagues ON leagues.id = league_id;
LEFT JOIN leagues ON leagues.id = events.league_id;
CREATE VIEW odds_market_with_settings AS
SELECT o.*,
SELECT o.id,
o.event_id,
o.market_type,
o.market_name,
o.market_category,
o.market_id,
o.default_is_active,
o.fetched_at,
o.expires_at,
cos.company_id,
COALESCE(cos.is_active, o.default_is_active) AS is_active,
COALESCE(cos.custom_raw_odds, o.raw_odds) AS raw_odds,
@ -654,4 +669,10 @@ ADD CONSTRAINT fk_companies_admin FOREIGN KEY (admin_id) REFERENCES users(id),
ADD CONSTRAINT fk_companies_wallet FOREIGN KEY (wallet_id) REFERENCES wallets(id) ON DELETE CASCADE;
ALTER TABLE company_league_settings
ADD CONSTRAINT fk_league_settings_company FOREIGN KEY (company_id) REFERENCES companies(id) ON DELETE CASCADE,
ADD CONSTRAINT fk_league_settings_league FOREIGN KEY (league_id) REFERENCES league_id(id) ON DELETE CASCADE;
ADD CONSTRAINT fk_league_settings_league FOREIGN KEY (league_id) REFERENCES leagues(id) ON DELETE CASCADE;
ALTER TABLE company_event_settings
ADD CONSTRAINT fk_event_settings_company FOREIGN KEY (company_id) REFERENCES companies(id) ON DELETE CASCADE,
ADD CONSTRAINT fk_event_settings_event FOREIGN KEY (event_id) REFERENCES events(id) ON DELETE CASCADE;
ALTER TABLE company_odd_settings
ADD CONSTRAINT fk_odds_settings_company FOREIGN KEY (company_id) REFERENCES companies(id) ON DELETE CASCADE,
ADD CONSTRAINT fk_odds_settings_odds_market FOREIGN KEY (odds_market_id) REFERENCES odds_market(id) ON DELETE CASCADE;

View File

@ -1,6 +1,6 @@
-- Settings Initial Data
INSERT INTO settings (key, value)
VALUES ('sms_provider', '30'),
INSERT INTO global_settings (key, value)
VALUES ('sms_provider', 'afro_message'),
('max_number_of_outcomes', '30'),
('bet_amount_limit', '10000000'),
('daily_ticket_limit', '50'),

View File

@ -6,9 +6,10 @@ INSERT INTO bets (
user_id,
is_shop_bet,
outcomes_hash,
fast_code
fast_code,
company_id
)
VALUES ($1, $2, $3, $4, $5, $6, $7)
VALUES ($1, $2, $3, $4, $5, $6, $7, $8)
RETURNING *;
-- name: CreateBetOutcome :copyfrom
INSERT INTO bet_outcomes (
@ -52,6 +53,10 @@ wHERE (
is_shop_bet = sqlc.narg('is_shop_bet')
OR sqlc.narg('is_shop_bet') IS NULL
)
AND (
company_id = sqlc.narg('company_id')
OR sqlc.narg('company_id') IS NULL
)
AND (
cashed_out = sqlc.narg('cashed_out')
OR sqlc.narg('cashed_out') IS NULL

View File

@ -1,12 +1,41 @@
-- name: GetSettings :many
-- name: GetGlobalSettings :many
SELECT *
FROM settings;
-- name: GetSetting :one
FROM global_settings;
-- name: GetGlobalSetting :one
SELECT *
FROM settings
FROM global_settings
WHERE key = $1;
-- name: UpdateSetting :exec
UPDATE settings
-- name: UpdateGlobalSetting :exec
UPDATE global_settings
SET value = $2,
updated_at = CURRENT_TIMESTAMP
WHERE key = $1;
-- name: InsertCompanySetting :exec
INSERT INTO company_settings (company_id, key, value)
VALUES ($1, $2, $3) ON CONFLICT (company_id, key) DO
UPDATE
SET value = EXCLUDED.value;
-- name: GetAllCompanySettings :many
SELECT *
FROM company_settings;
-- name: GetCompanySetting :many
SELECT *
FROM company_settings
WHERE company_id = $1;
-- name: GetCompanySettingsByKey :many
SELECT *
FROM company_settings
WHERE key = $1;
-- name: GetOverrideSettings :many
SELECT gs.*,
COALESCE(cs.value, gs.value) AS value
FROM global_settings gs
LEFT JOIN company_settings cs ON cs.key = gs.key
AND cs.company_id = $1;
-- name: DeleteCompanySetting :exec
DELETE FROM company_settings
WHERE company_id = $1
AND key = $2;
-- name: DeleteAllCompanySetting :exec
DELETE FROM company_settings
WHERE company_id = $1;

View File

@ -1,6 +1,6 @@
-- name: CreateTicket :one
INSERT INTO tickets (amount, total_odds, ip)
VALUES ($1, $2, $3)
INSERT INTO tickets (amount, total_odds, ip, company_id)
VALUES ($1, $2, $3, $4)
RETURNING *;
-- name: CreateTicketOutcome :copyfrom
INSERT INTO ticket_outcomes (
@ -33,7 +33,11 @@ VALUES (
);
-- name: GetAllTickets :many
SELECT *
FROM ticket_with_outcomes;
FROM ticket_with_outcomes
WHERE (
company_id = sqlc.narg('company_id')
OR sqlc.narg('company_id') IS NULL
);
-- name: GetTicketByID :one
SELECT *
FROM ticket_with_outcomes
@ -60,6 +64,7 @@ where created_at < now() - interval '1 day';
Delete from ticket_outcomes
where ticket_id = $1;
-- name: GetAllTicketsInRange :one
SELECT COUNT(*) as total_tickets, COALESCE(SUM(amount), 0) as total_amount
SELECT COUNT(*) as total_tickets,
COALESCE(SUM(amount), 0) as total_amount
FROM tickets
WHERE created_at BETWEEN $1 AND $2;

View File

@ -19,9 +19,10 @@ INSERT INTO bets (
user_id,
is_shop_bet,
outcomes_hash,
fast_code
fast_code,
company_id
)
VALUES ($1, $2, $3, $4, $5, $6, $7)
VALUES ($1, $2, $3, $4, $5, $6, $7, $8)
RETURNING id, company_id, amount, total_odds, status, user_id, is_shop_bet, cashed_out, outcomes_hash, fast_code, processed, created_at, updated_at
`
@ -33,6 +34,7 @@ type CreateBetParams struct {
IsShopBet bool `json:"is_shop_bet"`
OutcomesHash string `json:"outcomes_hash"`
FastCode string `json:"fast_code"`
CompanyID int64 `json:"company_id"`
}
func (q *Queries) CreateBet(ctx context.Context, arg CreateBetParams) (Bet, error) {
@ -44,6 +46,7 @@ func (q *Queries) CreateBet(ctx context.Context, arg CreateBetParams) (Bet, erro
arg.IsShopBet,
arg.OutcomesHash,
arg.FastCode,
arg.CompanyID,
)
var i Bet
err := row.Scan(
@ -112,27 +115,32 @@ wHERE (
OR $2 IS NULL
)
AND (
cashed_out = $3
company_id = $3
OR $3 IS NULL
)
AND (
full_name ILIKE '%' || $4 || '%'
OR phone_number ILIKE '%' || $4 || '%'
cashed_out = $4
OR $4 IS NULL
)
AND (
created_at > $5
full_name ILIKE '%' || $5 || '%'
OR phone_number ILIKE '%' || $5 || '%'
OR $5 IS NULL
)
AND (
created_at < $6
created_at > $6
OR $6 IS NULL
)
AND (
created_at < $7
OR $7 IS NULL
)
`
type GetAllBetsParams struct {
UserID pgtype.Int8 `json:"user_id"`
IsShopBet pgtype.Bool `json:"is_shop_bet"`
CompanyID pgtype.Int8 `json:"company_id"`
CashedOut pgtype.Bool `json:"cashed_out"`
Query pgtype.Text `json:"query"`
CreatedBefore pgtype.Timestamp `json:"created_before"`
@ -143,6 +151,7 @@ func (q *Queries) GetAllBets(ctx context.Context, arg GetAllBetsParams) ([]BetWi
rows, err := q.db.Query(ctx, GetAllBets,
arg.UserID,
arg.IsShopBet,
arg.CompanyID,
arg.CashedOut,
arg.Query,
arg.CreatedBefore,

View File

@ -1,167 +0,0 @@
// Code generated by sqlc. DO NOT EDIT.
// versions:
// sqlc v1.29.0
// source: custom_odds.sql
package dbgen
import (
"context"
"github.com/jackc/pgx/v5/pgtype"
)
const DeleteCustomOddByEventID = `-- name: DeleteCustomOddByEventID :exec
DELETE FROM custom_odds_market
WHERE event_id = $1
`
func (q *Queries) DeleteCustomOddByEventID(ctx context.Context, eventID string) error {
_, err := q.db.Exec(ctx, DeleteCustomOddByEventID, eventID)
return err
}
const DeleteCustomOddsByID = `-- name: DeleteCustomOddsByID :exec
DELETE FROM custom_odds_market
WHERE id = $1
`
func (q *Queries) DeleteCustomOddsByID(ctx context.Context, id int64) error {
_, err := q.db.Exec(ctx, DeleteCustomOddsByID, id)
return err
}
const DeleteCustomOddsByOddID = `-- name: DeleteCustomOddsByOddID :exec
DELETE FROM custom_odds_market
WHERE odds_market_id = $1
AND company_id = $2
`
type DeleteCustomOddsByOddIDParams struct {
OddsMarketID int64 `json:"odds_market_id"`
CompanyID int64 `json:"company_id"`
}
func (q *Queries) DeleteCustomOddsByOddID(ctx context.Context, arg DeleteCustomOddsByOddIDParams) error {
_, err := q.db.Exec(ctx, DeleteCustomOddsByOddID, arg.OddsMarketID, arg.CompanyID)
return err
}
const GetAllCustomOdds = `-- name: GetAllCustomOdds :many
SELECT id, company_id, odds_market_id, event_id, raw_odds, created_at
FROM custom_odds_market
WHERE (
company_id = $1
OR $1 IS NULL
)
`
func (q *Queries) GetAllCustomOdds(ctx context.Context, companyID pgtype.Int8) ([]CustomOddsMarket, error) {
rows, err := q.db.Query(ctx, GetAllCustomOdds, companyID)
if err != nil {
return nil, err
}
defer rows.Close()
var items []CustomOddsMarket
for rows.Next() {
var i CustomOddsMarket
if err := rows.Scan(
&i.ID,
&i.CompanyID,
&i.OddsMarketID,
&i.EventID,
&i.RawOdds,
&i.CreatedAt,
); err != nil {
return nil, err
}
items = append(items, i)
}
if err := rows.Err(); err != nil {
return nil, err
}
return items, nil
}
const GetCustomOddByID = `-- name: GetCustomOddByID :one
SELECT id, company_id, odds_market_id, event_id, raw_odds, created_at
FROM custom_odds_market
WHERE id = $1
`
func (q *Queries) GetCustomOddByID(ctx context.Context, id int64) (CustomOddsMarket, error) {
row := q.db.QueryRow(ctx, GetCustomOddByID, id)
var i CustomOddsMarket
err := row.Scan(
&i.ID,
&i.CompanyID,
&i.OddsMarketID,
&i.EventID,
&i.RawOdds,
&i.CreatedAt,
)
return i, err
}
const GetCustomOddByOddID = `-- name: GetCustomOddByOddID :one
SELECT id, company_id, odds_market_id, event_id, raw_odds, created_at
FROM custom_odds_market
WHERE odds_market_id = $1
AND company_id = $2
`
type GetCustomOddByOddIDParams struct {
OddsMarketID int64 `json:"odds_market_id"`
CompanyID int64 `json:"company_id"`
}
func (q *Queries) GetCustomOddByOddID(ctx context.Context, arg GetCustomOddByOddIDParams) (CustomOddsMarket, error) {
row := q.db.QueryRow(ctx, GetCustomOddByOddID, arg.OddsMarketID, arg.CompanyID)
var i CustomOddsMarket
err := row.Scan(
&i.ID,
&i.CompanyID,
&i.OddsMarketID,
&i.EventID,
&i.RawOdds,
&i.CreatedAt,
)
return i, err
}
const InsertCustomOddsMarket = `-- name: InsertCustomOddsMarket :one
INSERT INTO custom_odds_market (
odds_market_id,
company_id,
event_id,
raw_odds
)
VALUES ($1, $2, $3, $4)
RETURNING id, company_id, odds_market_id, event_id, raw_odds, created_at
`
type InsertCustomOddsMarketParams struct {
OddsMarketID int64 `json:"odds_market_id"`
CompanyID int64 `json:"company_id"`
EventID string `json:"event_id"`
RawOdds []byte `json:"raw_odds"`
}
func (q *Queries) InsertCustomOddsMarket(ctx context.Context, arg InsertCustomOddsMarketParams) (CustomOddsMarket, error) {
row := q.db.QueryRow(ctx, InsertCustomOddsMarket,
arg.OddsMarketID,
arg.CompanyID,
arg.EventID,
arg.RawOdds,
)
var i CustomOddsMarket
err := row.Scan(
&i.ID,
&i.CompanyID,
&i.OddsMarketID,
&i.EventID,
&i.RawOdds,
&i.CreatedAt,
)
return i, err
}

View File

@ -240,6 +240,14 @@ type CompanyOddSetting struct {
UpdatedAt pgtype.Timestamp `json:"updated_at"`
}
type CompanySetting struct {
CompanyID int64 `json:"company_id"`
Key string `json:"key"`
Value string `json:"value"`
CreatedAt pgtype.Timestamp `json:"created_at"`
UpdatedAt pgtype.Timestamp `json:"updated_at"`
}
type CustomerWallet struct {
ID int64 `json:"id"`
CustomerID int64 `json:"customer_id"`
@ -412,6 +420,13 @@ type Flag struct {
Resolved pgtype.Bool `json:"resolved"`
}
type GlobalSetting struct {
Key string `json:"key"`
Value string `json:"value"`
CreatedAt pgtype.Timestamp `json:"created_at"`
UpdatedAt pgtype.Timestamp `json:"updated_at"`
}
type League struct {
ID int64 `json:"id"`
Name string `json:"name"`
@ -502,7 +517,6 @@ type OddsMarketWithSetting struct {
MarketName string `json:"market_name"`
MarketCategory string `json:"market_category"`
MarketID string `json:"market_id"`
RawOdds []byte `json:"raw_odds"`
DefaultIsActive bool `json:"default_is_active"`
FetchedAt pgtype.Timestamp `json:"fetched_at"`
ExpiresAt pgtype.Timestamp `json:"expires_at"`
@ -604,13 +618,6 @@ type ResultLog struct {
UpdatedAt pgtype.Timestamp `json:"updated_at"`
}
type Setting struct {
Key string `json:"key"`
Value string `json:"value"`
CreatedAt pgtype.Timestamp `json:"created_at"`
UpdatedAt pgtype.Timestamp `json:"updated_at"`
}
type ShopBet struct {
ID int64 `json:"id"`
ShopTransactionID int64 `json:"shop_transaction_id"`

View File

@ -68,7 +68,7 @@ func (q *Queries) GetAllOdds(ctx context.Context, arg GetAllOddsParams) ([]OddsM
}
const GetAllOddsWithSettings = `-- name: GetAllOddsWithSettings :many
SELECT id, event_id, market_type, market_name, market_category, market_id, odds_market_with_settings.raw_odds, default_is_active, fetched_at, expires_at, company_id, is_active, odds_market_with_settings.raw_odds, updated_at
SELECT id, event_id, market_type, market_name, market_category, market_id, default_is_active, fetched_at, expires_at, company_id, is_active, raw_odds, updated_at
FROM odds_market_with_settings
WHERE company_id = $1
LIMIT $3 OFFSET $2
@ -96,7 +96,6 @@ func (q *Queries) GetAllOddsWithSettings(ctx context.Context, arg GetAllOddsWith
&i.MarketName,
&i.MarketCategory,
&i.MarketID,
&i.RawOdds,
&i.DefaultIsActive,
&i.FetchedAt,
&i.ExpiresAt,
@ -220,7 +219,7 @@ func (q *Queries) GetOddsByMarketID(ctx context.Context, arg GetOddsByMarketIDPa
}
const GetOddsWithSettingsByEventID = `-- name: GetOddsWithSettingsByEventID :many
SELECT id, event_id, market_type, market_name, market_category, market_id, odds_market_with_settings.raw_odds, default_is_active, fetched_at, expires_at, company_id, is_active, odds_market_with_settings.raw_odds, updated_at
SELECT id, event_id, market_type, market_name, market_category, market_id, default_is_active, fetched_at, expires_at, company_id, is_active, raw_odds, updated_at
FROM odds_market_with_settings
WHERE event_id = $1
AND company_id = $2
@ -255,7 +254,6 @@ func (q *Queries) GetOddsWithSettingsByEventID(ctx context.Context, arg GetOddsW
&i.MarketName,
&i.MarketCategory,
&i.MarketID,
&i.RawOdds,
&i.DefaultIsActive,
&i.FetchedAt,
&i.ExpiresAt,
@ -275,7 +273,7 @@ func (q *Queries) GetOddsWithSettingsByEventID(ctx context.Context, arg GetOddsW
}
const GetOddsWithSettingsByMarketID = `-- name: GetOddsWithSettingsByMarketID :one
SELECT id, event_id, market_type, market_name, market_category, market_id, odds_market_with_settings.raw_odds, default_is_active, fetched_at, expires_at, company_id, is_active, odds_market_with_settings.raw_odds, updated_at
SELECT id, event_id, market_type, market_name, market_category, market_id, default_is_active, fetched_at, expires_at, company_id, is_active, raw_odds, updated_at
FROM odds_market_with_settings
WHERE market_id = $1
AND event_id = $2
@ -298,7 +296,6 @@ func (q *Queries) GetOddsWithSettingsByMarketID(ctx context.Context, arg GetOdds
&i.MarketName,
&i.MarketCategory,
&i.MarketID,
&i.RawOdds,
&i.DefaultIsActive,
&i.FetchedAt,
&i.ExpiresAt,

View File

@ -7,17 +7,140 @@ package dbgen
import (
"context"
"github.com/jackc/pgx/v5/pgtype"
)
const GetSetting = `-- name: GetSetting :one
SELECT key, value, created_at, updated_at
FROM settings
const DeleteAllCompanySetting = `-- name: DeleteAllCompanySetting :exec
DELETE FROM company_settings
WHERE company_id = $1
`
func (q *Queries) DeleteAllCompanySetting(ctx context.Context, companyID int64) error {
_, err := q.db.Exec(ctx, DeleteAllCompanySetting, companyID)
return err
}
const DeleteCompanySetting = `-- name: DeleteCompanySetting :exec
DELETE FROM company_settings
WHERE company_id = $1
AND key = $2
`
type DeleteCompanySettingParams struct {
CompanyID int64 `json:"company_id"`
Key string `json:"key"`
}
func (q *Queries) DeleteCompanySetting(ctx context.Context, arg DeleteCompanySettingParams) error {
_, err := q.db.Exec(ctx, DeleteCompanySetting, arg.CompanyID, arg.Key)
return err
}
const GetAllCompanySettings = `-- name: GetAllCompanySettings :many
SELECT company_id, key, value, created_at, updated_at
FROM company_settings
`
func (q *Queries) GetAllCompanySettings(ctx context.Context) ([]CompanySetting, error) {
rows, err := q.db.Query(ctx, GetAllCompanySettings)
if err != nil {
return nil, err
}
defer rows.Close()
var items []CompanySetting
for rows.Next() {
var i CompanySetting
if err := rows.Scan(
&i.CompanyID,
&i.Key,
&i.Value,
&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 GetCompanySetting = `-- name: GetCompanySetting :many
SELECT company_id, key, value, created_at, updated_at
FROM company_settings
WHERE company_id = $1
`
func (q *Queries) GetCompanySetting(ctx context.Context, companyID int64) ([]CompanySetting, error) {
rows, err := q.db.Query(ctx, GetCompanySetting, companyID)
if err != nil {
return nil, err
}
defer rows.Close()
var items []CompanySetting
for rows.Next() {
var i CompanySetting
if err := rows.Scan(
&i.CompanyID,
&i.Key,
&i.Value,
&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 GetCompanySettingsByKey = `-- name: GetCompanySettingsByKey :many
SELECT company_id, key, value, created_at, updated_at
FROM company_settings
WHERE key = $1
`
func (q *Queries) GetSetting(ctx context.Context, key string) (Setting, error) {
row := q.db.QueryRow(ctx, GetSetting, key)
var i Setting
func (q *Queries) GetCompanySettingsByKey(ctx context.Context, key string) ([]CompanySetting, error) {
rows, err := q.db.Query(ctx, GetCompanySettingsByKey, key)
if err != nil {
return nil, err
}
defer rows.Close()
var items []CompanySetting
for rows.Next() {
var i CompanySetting
if err := rows.Scan(
&i.CompanyID,
&i.Key,
&i.Value,
&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 GetGlobalSetting = `-- name: GetGlobalSetting :one
SELECT key, value, created_at, updated_at
FROM global_settings
WHERE key = $1
`
func (q *Queries) GetGlobalSetting(ctx context.Context, key string) (GlobalSetting, error) {
row := q.db.QueryRow(ctx, GetGlobalSetting, key)
var i GlobalSetting
err := row.Scan(
&i.Key,
&i.Value,
@ -27,20 +150,20 @@ func (q *Queries) GetSetting(ctx context.Context, key string) (Setting, error) {
return i, err
}
const GetSettings = `-- name: GetSettings :many
const GetGlobalSettings = `-- name: GetGlobalSettings :many
SELECT key, value, created_at, updated_at
FROM settings
FROM global_settings
`
func (q *Queries) GetSettings(ctx context.Context) ([]Setting, error) {
rows, err := q.db.Query(ctx, GetSettings)
func (q *Queries) GetGlobalSettings(ctx context.Context) ([]GlobalSetting, error) {
rows, err := q.db.Query(ctx, GetGlobalSettings)
if err != nil {
return nil, err
}
defer rows.Close()
var items []Setting
var items []GlobalSetting
for rows.Next() {
var i Setting
var i GlobalSetting
if err := rows.Scan(
&i.Key,
&i.Value,
@ -57,19 +180,79 @@ func (q *Queries) GetSettings(ctx context.Context) ([]Setting, error) {
return items, nil
}
const UpdateSetting = `-- name: UpdateSetting :exec
UPDATE settings
const GetOverrideSettings = `-- name: GetOverrideSettings :many
SELECT gs.key, gs.value, gs.created_at, gs.updated_at,
COALESCE(cs.value, gs.value) AS value
FROM global_settings gs
LEFT JOIN company_settings cs ON cs.key = gs.key
AND cs.company_id = $1
`
type GetOverrideSettingsRow struct {
Key string `json:"key"`
Value string `json:"value"`
CreatedAt pgtype.Timestamp `json:"created_at"`
UpdatedAt pgtype.Timestamp `json:"updated_at"`
Value_2 string `json:"value_2"`
}
func (q *Queries) GetOverrideSettings(ctx context.Context, companyID int64) ([]GetOverrideSettingsRow, error) {
rows, err := q.db.Query(ctx, GetOverrideSettings, companyID)
if err != nil {
return nil, err
}
defer rows.Close()
var items []GetOverrideSettingsRow
for rows.Next() {
var i GetOverrideSettingsRow
if err := rows.Scan(
&i.Key,
&i.Value,
&i.CreatedAt,
&i.UpdatedAt,
&i.Value_2,
); err != nil {
return nil, err
}
items = append(items, i)
}
if err := rows.Err(); err != nil {
return nil, err
}
return items, nil
}
const InsertCompanySetting = `-- name: InsertCompanySetting :exec
INSERT INTO company_settings (company_id, key, value)
VALUES ($1, $2, $3) ON CONFLICT (company_id, key) DO
UPDATE
SET value = EXCLUDED.value
`
type InsertCompanySettingParams struct {
CompanyID int64 `json:"company_id"`
Key string `json:"key"`
Value string `json:"value"`
}
func (q *Queries) InsertCompanySetting(ctx context.Context, arg InsertCompanySettingParams) error {
_, err := q.db.Exec(ctx, InsertCompanySetting, arg.CompanyID, arg.Key, arg.Value)
return err
}
const UpdateGlobalSetting = `-- name: UpdateGlobalSetting :exec
UPDATE global_settings
SET value = $2,
updated_at = CURRENT_TIMESTAMP
WHERE key = $1
`
type UpdateSettingParams struct {
type UpdateGlobalSettingParams struct {
Key string `json:"key"`
Value string `json:"value"`
}
func (q *Queries) UpdateSetting(ctx context.Context, arg UpdateSettingParams) error {
_, err := q.db.Exec(ctx, UpdateSetting, arg.Key, arg.Value)
func (q *Queries) UpdateGlobalSetting(ctx context.Context, arg UpdateGlobalSettingParams) error {
_, err := q.db.Exec(ctx, UpdateGlobalSetting, arg.Key, arg.Value)
return err
}

View File

@ -25,8 +25,8 @@ func (q *Queries) CountTicketByIP(ctx context.Context, ip string) (int64, error)
}
const CreateTicket = `-- name: CreateTicket :one
INSERT INTO tickets (amount, total_odds, ip)
VALUES ($1, $2, $3)
INSERT INTO tickets (amount, total_odds, ip, company_id)
VALUES ($1, $2, $3, $4)
RETURNING id, company_id, amount, total_odds, ip, created_at, updated_at
`
@ -34,10 +34,16 @@ type CreateTicketParams struct {
Amount int64 `json:"amount"`
TotalOdds float32 `json:"total_odds"`
Ip string `json:"ip"`
CompanyID int64 `json:"company_id"`
}
func (q *Queries) CreateTicket(ctx context.Context, arg CreateTicketParams) (Ticket, error) {
row := q.db.QueryRow(ctx, CreateTicket, arg.Amount, arg.TotalOdds, arg.Ip)
row := q.db.QueryRow(ctx, CreateTicket,
arg.Amount,
arg.TotalOdds,
arg.Ip,
arg.CompanyID,
)
var i Ticket
err := row.Scan(
&i.ID,
@ -99,10 +105,14 @@ func (q *Queries) DeleteTicketOutcome(ctx context.Context, ticketID int64) error
const GetAllTickets = `-- name: GetAllTickets :many
SELECT id, company_id, amount, total_odds, ip, created_at, updated_at, outcomes
FROM ticket_with_outcomes
WHERE (
company_id = $1
OR $1 IS NULL
)
`
func (q *Queries) GetAllTickets(ctx context.Context) ([]TicketWithOutcome, error) {
rows, err := q.db.Query(ctx, GetAllTickets)
func (q *Queries) GetAllTickets(ctx context.Context, companyID pgtype.Int8) ([]TicketWithOutcome, error) {
rows, err := q.db.Query(ctx, GetAllTickets, companyID)
if err != nil {
return nil, err
}
@ -131,7 +141,8 @@ func (q *Queries) GetAllTickets(ctx context.Context) ([]TicketWithOutcome, error
}
const GetAllTicketsInRange = `-- name: GetAllTicketsInRange :one
SELECT COUNT(*) as total_tickets, COALESCE(SUM(amount), 0) as total_amount
SELECT COUNT(*) as total_tickets,
COALESCE(SUM(amount), 0) as total_amount
FROM tickets
WHERE created_at BETWEEN $1 AND $2
`

View File

@ -2,6 +2,9 @@ package domain
import (
"time"
dbgen "github.com/SamuelTariku/FortuneBet-Backend/gen/db"
"github.com/jackc/pgx/v5/pgtype"
)
// The Odd ID here is not the odd id from our database
@ -49,6 +52,7 @@ type Bet struct {
TotalOdds float32
Status OutcomeStatus
UserID int64
CompanyID int64
IsShopBet bool
CashedOut bool
FastCode string
@ -57,6 +61,7 @@ type Bet struct {
type BetFilter struct {
UserID ValidInt64
CompanyID ValidInt64
CashedOut ValidBool
IsShopBet ValidBool
Query ValidString
@ -81,6 +86,7 @@ type GetBet struct {
FullName string
PhoneNumber string
UserID int64
CompanyID int64
IsShopBet bool
CashedOut bool
Outcomes []BetOutcome
@ -93,6 +99,7 @@ type CreateBet struct {
TotalOdds float32
Status OutcomeStatus
UserID int64
CompanyID int64
IsShopBet bool
OutcomesHash string
FastCode string
@ -133,6 +140,7 @@ type CreateBetRes struct {
TotalOdds float32 `json:"total_odds" example:"4.22"`
Status OutcomeStatus `json:"status" example:"1"`
UserID int64 `json:"user_id" example:"2"`
CompanyID int64 `json:"company_id" example:"1"`
IsShopBet bool `json:"is_shop_bet" example:"false"`
CreatedNumber int64 `json:"created_number" example:"2"`
FastCode string `json:"fast_code"`
@ -145,19 +153,21 @@ type BetRes struct {
Status OutcomeStatus `json:"status" example:"1"`
Fullname string `json:"full_name" example:"John Smith"`
UserID int64 `json:"user_id" example:"2"`
CompanyID int64 `json:"company_id" example:"1"`
IsShopBet bool `json:"is_shop_bet" example:"false"`
CashedOut bool `json:"cashed_out" example:"false"`
CreatedAt time.Time `json:"created_at" example:"2025-04-08T12:00:00Z"`
FastCode string `json:"fast_code"`
}
func ConvertCreateBet(bet Bet, createdNumber int64) CreateBetRes {
func ConvertCreateBetRes(bet Bet, createdNumber int64) CreateBetRes {
return CreateBetRes{
ID: bet.ID,
Amount: bet.Amount.Float32(),
TotalOdds: bet.TotalOdds,
Status: bet.Status,
UserID: bet.UserID,
CompanyID: bet.CompanyID,
CreatedNumber: createdNumber,
IsShopBet: bet.IsShopBet,
FastCode: bet.FastCode,
@ -172,6 +182,7 @@ func ConvertBet(bet GetBet) BetRes {
Status: bet.Status,
Fullname: bet.FullName,
UserID: bet.UserID,
CompanyID: bet.CompanyID,
Outcomes: bet.Outcomes,
IsShopBet: bet.IsShopBet,
CashedOut: bet.CashedOut,
@ -179,3 +190,106 @@ func ConvertBet(bet GetBet) BetRes {
FastCode: bet.FastCode,
}
}
func ConvertDBBet(bet dbgen.Bet) Bet {
return Bet{
ID: bet.ID,
Amount: Currency(bet.Amount),
TotalOdds: bet.TotalOdds,
Status: OutcomeStatus(bet.Status),
UserID: bet.UserID,
CompanyID: bet.CompanyID,
IsShopBet: bet.IsShopBet,
CashedOut: bet.CashedOut,
FastCode: bet.FastCode,
CreatedAt: bet.CreatedAt.Time,
}
}
func ConvertDBBetOutcomes(outcome dbgen.BetOutcome) BetOutcome {
return BetOutcome{
ID: outcome.ID,
BetID: outcome.BetID,
SportID: outcome.SportID,
EventID: outcome.EventID,
OddID: outcome.OddID,
HomeTeamName: outcome.HomeTeamName,
AwayTeamName: outcome.AwayTeamName,
MarketID: outcome.MarketID,
MarketName: outcome.MarketName,
Odd: outcome.Odd,
OddName: outcome.OddName,
OddHeader: outcome.OddHeader,
OddHandicap: outcome.OddHandicap,
Status: OutcomeStatus(outcome.Status),
Expires: outcome.Expires.Time,
}
}
func ConvertDBBetWithOutcomes(bet dbgen.BetWithOutcome) GetBet {
var outcomes []BetOutcome = make([]BetOutcome, 0, len(bet.Outcomes))
for _, outcome := range bet.Outcomes {
outcomes = append(outcomes, ConvertDBBetOutcomes(outcome))
}
return GetBet{
ID: bet.ID,
Amount: Currency(bet.Amount),
TotalOdds: bet.TotalOdds,
Status: OutcomeStatus(bet.Status),
FullName: bet.FullName.(string),
PhoneNumber: bet.PhoneNumber.String,
UserID: bet.UserID,
IsShopBet: bet.IsShopBet,
CashedOut: bet.CashedOut,
Outcomes: outcomes,
FastCode: bet.FastCode,
CreatedAt: bet.CreatedAt.Time,
}
}
func ConvertDBFlag(flag dbgen.Flag) Flag {
return Flag{
ID: flag.ID,
BetID: flag.BetID.Int64,
OddID: flag.OddsMarketID.Int64,
Reason: flag.Reason.String,
FlaggedAt: flag.FlaggedAt.Time,
Resolved: flag.Resolved.Bool,
}
}
func ConvertDBCreateBetOutcome(betOutcome CreateBetOutcome) dbgen.CreateBetOutcomeParams {
return dbgen.CreateBetOutcomeParams{
BetID: betOutcome.BetID,
EventID: betOutcome.EventID,
SportID: betOutcome.SportID,
OddID: betOutcome.OddID,
HomeTeamName: betOutcome.HomeTeamName,
AwayTeamName: betOutcome.AwayTeamName,
MarketID: betOutcome.MarketID,
MarketName: betOutcome.MarketName,
Odd: betOutcome.Odd,
OddName: betOutcome.OddName,
OddHeader: betOutcome.OddHeader,
OddHandicap: betOutcome.OddHandicap,
Expires: pgtype.Timestamp{
Time: betOutcome.Expires,
Valid: true,
},
}
}
func ConvertCreateBet(bet CreateBet) dbgen.CreateBetParams {
return dbgen.CreateBetParams{
Amount: int64(bet.Amount),
TotalOdds: bet.TotalOdds,
Status: int32(bet.Status),
UserID: bet.UserID,
CompanyID: bet.CompanyID,
IsShopBet: bet.IsShopBet,
OutcomesHash: bet.OutcomesHash,
FastCode: bet.FastCode,
}
}

View File

@ -21,14 +21,14 @@ var SuccessResZapFields = []zap.Field{
zap.Time("timestamp", time.Now()),
}
var BadRequestLogger = MongoDBLogger.With(
zap.Int("status_code", fiber.StatusBadRequest),
zap.Time("timestamp", time.Now()))
// var BadRequestLogger = MongoDBLogger.With(
// zap.Int("status_code", fiber.StatusBadRequest),
// zap.Time("timestamp", time.Now()))
var InternalServerErrorLogger = MongoDBLogger.With(
zap.Int("status_code", fiber.StatusInternalServerError),
zap.Time("timestamp", time.Now()))
// var InternalServerErrorLogger = MongoDBLogger.With(
// zap.Int("status_code", fiber.StatusInternalServerError),
// zap.Time("timestamp", time.Now()))
var SuccessResLogger = MongoDBLogger.With(
zap.Int("status_code", fiber.StatusOK),
zap.Time("timestamp", time.Now()))
// var SuccessResLogger = MongoDBLogger.With(
// zap.Int("status_code", fiber.StatusOK),
// zap.Time("timestamp", time.Now()))

View File

@ -3,28 +3,26 @@ package domain
import (
"errors"
"fmt"
"math"
"time"
)
type Currency int64
// ToCurrency converts a float32 to Currency
// ToCurrency converts a float32 (like 12.34) into Currency (stored in cents).
func ToCurrency(f float32) Currency {
return Currency((f * 100) + 0.5)
cents := math.Round(float64(f) * 100) // avoid float32 precision issues
return Currency(int64(cents))
}
// Float32 converts a Currency to float32
// Float32 converts a Currency back into float32 (like 12.34).
func (m Currency) Float32() float32 {
x := float32(m)
x = x / 100
return x
return float32(m) / 100
}
// String returns a formatted Currency value
// String returns a formatted Currency value for display.
func (m Currency) String() string {
x := float32(m)
x = x / 100
return fmt.Sprintf("$%.2f", x)
return fmt.Sprintf("$%.2f", m.Float32())
}
type IntCurrency string

View File

@ -1,69 +1,69 @@
package domain
import (
"encoding/json"
"time"
// import (
// "encoding/json"
// "time"
dbgen "github.com/SamuelTariku/FortuneBet-Backend/gen/db"
)
// dbgen "github.com/SamuelTariku/FortuneBet-Backend/gen/db"
// )
type CustomOdd struct {
ID int64
OddID int64
CompanyID int64
EventID string
RawOdds []json.RawMessage
CreatedAt time.Time
}
// type CustomOdd struct {
// ID int64
// OddID int64
// CompanyID int64
// EventID string
// RawOdds []json.RawMessage
// CreatedAt time.Time
// }
type CreateCustomOdd struct {
OddID int64
CompanyID int64
EventID string
RawOdds []json.RawMessage
}
// type CreateCustomOdd struct {
// OddID int64
// CompanyID int64
// EventID string
// RawOdds []json.RawMessage
// }
type CustomOddFilter struct {
CompanyID ValidInt64
}
// type CustomOddFilter struct {
// CompanyID ValidInt64
// }
func ConvertCreateCustomOdd(odd CreateCustomOdd) (dbgen.InsertCustomOddParams, error) {
rawOddsBytes, err := json.Marshal(odd.RawOdds)
// func ConvertCreateCustomOdd(odd CreateCustomOdd) (dbgen.InsertCustomOddParams, error) {
// rawOddsBytes, err := json.Marshal(odd.RawOdds)
if err != nil {
return dbgen.InsertCustomOddParams{}, err
}
return dbgen.InsertCustomOddParams{
OddID: odd.OddID,
CompanyID: odd.CompanyID,
EventID: odd.EventID,
RawOdds: rawOddsBytes,
}, nil
}
// if err != nil {
// return dbgen.InsertCustomOddParams{}, err
// }
// return dbgen.InsertCustomOddParams{
// OddID: odd.OddID,
// CompanyID: odd.CompanyID,
// EventID: odd.EventID,
// RawOdds: rawOddsBytes,
// }, nil
// }
func ConvertDBCustomOdd(dbCustomOdd dbgen.CustomOdd) (CustomOdd, error) {
var rawOdds []json.RawMessage
if err := json.Unmarshal(dbCustomOdd.RawOdds, &rawOdds); err != nil {
return CustomOdd{}, err
}
return CustomOdd{
ID: dbCustomOdd.ID,
OddID: dbCustomOdd.OddID,
CompanyID: dbCustomOdd.CompanyID,
EventID: dbCustomOdd.EventID,
RawOdds: rawOdds,
CreatedAt: dbCustomOdd.CreatedAt.Time,
}, nil
}
// func ConvertDBCustomOdd(dbCustomOdd dbgen.CustomOdd) (CustomOdd, error) {
// var rawOdds []json.RawMessage
// if err := json.Unmarshal(dbCustomOdd.RawOdds, &rawOdds); err != nil {
// return CustomOdd{}, err
// }
// return CustomOdd{
// ID: dbCustomOdd.ID,
// OddID: dbCustomOdd.OddID,
// CompanyID: dbCustomOdd.CompanyID,
// EventID: dbCustomOdd.EventID,
// RawOdds: rawOdds,
// CreatedAt: dbCustomOdd.CreatedAt.Time,
// }, nil
// }
func ConvertDbCustomOdds(list []dbgen.CustomOdd) ([]CustomOdd, error) {
result := make([]CustomOdd, 0, len(list))
for _, item := range list {
convertedItem, err := ConvertDBCustomOdd(item)
if err != nil {
return nil, err
}
result = append(result, convertedItem)
}
return result, nil
}
// func ConvertDbCustomOdds(list []dbgen.CustomOdd) ([]CustomOdd, error) {
// result := make([]CustomOdd, 0, len(list))
// for _, item := range list {
// convertedItem, err := ConvertDBCustomOdd(item)
// if err != nil {
// return nil, err
// }
// result = append(result, convertedItem)
// }
// return result, nil
// }

View File

@ -8,7 +8,7 @@ import (
type DisabledOdd struct {
ID int64
OddID int64
OddMarketID int64
RawOddID int64
EventID string
CompanyID int64
@ -16,7 +16,7 @@ type DisabledOdd struct {
}
type CreateDisabledOdd struct {
OddID int64
OddMarketID int64
RawOddID int64
EventID string
CompanyID int64
@ -24,7 +24,7 @@ type CreateDisabledOdd struct {
func ConvertCreateDisabledOdd(odd CreateDisabledOdd) dbgen.InsertDisabledOddsParams {
return dbgen.InsertDisabledOddsParams{
OddID: odd.OddID,
OddsMarketID: odd.OddMarketID,
EventID: odd.EventID,
CompanyID: odd.CompanyID,
RawOddID: odd.RawOddID,
@ -34,7 +34,7 @@ func ConvertCreateDisabledOdd(odd CreateDisabledOdd) dbgen.InsertDisabledOddsPar
func ConvertDBDisabledOdd(dbDisabledOdd dbgen.DisabledOdd) DisabledOdd {
return DisabledOdd{
ID: dbDisabledOdd.ID,
OddID: dbDisabledOdd.OddID,
OddMarketID: dbDisabledOdd.OddsMarketID,
RawOddID: dbDisabledOdd.RawOddID,
EventID: dbDisabledOdd.EventID,
CompanyID: dbDisabledOdd.CompanyID,

View File

@ -23,7 +23,7 @@ type Result struct {
FullTimeScore string
HalfTimeScore string
SS string
Scores map[string]Score
Scores map[string]ScoreResultResponse
CreatedAt time.Time
UpdatedAt time.Time
}

View File

@ -9,3 +9,12 @@ const (
RoleCustomer Role = "customer"
RoleCashier Role = "cashier"
)
func (r Role) IsValid() bool {
switch r {
case RoleSuperAdmin, RoleAdmin, RoleBranchManager, RoleCustomer, RoleCashier:
return true
default:
return false
}
}

View File

@ -0,0 +1,447 @@
package domain
import (
"errors"
"fmt"
"strconv"
"strings"
"time"
dbgen "github.com/SamuelTariku/FortuneBet-Backend/gen/db"
"go.uber.org/zap"
)
var (
ErrSettingNotFound = errors.New("cannot find setting in list")
)
type SettingList struct {
SMSProvider SMSProvider `json:"sms_provider"`
MaxNumberOfOutcomes int64 `json:"max_number_of_outcomes"`
BetAmountLimit Currency `json:"bet_amount_limit"`
DailyTicketPerIP int64 `json:"daily_ticket_limit"`
TotalWinningLimit Currency `json:"total_winning_limit"`
AmountForBetReferral Currency `json:"amount_for_bet_referral"`
CashbackAmountCap Currency `json:"cashback_amount_cap"`
}
type SettingListRes struct {
SMSProvider SMSProvider `json:"sms_provider"`
MaxNumberOfOutcomes int64 `json:"max_number_of_outcomes"`
BetAmountLimit float32 `json:"bet_amount_limit"`
DailyTicketPerIP int64 `json:"daily_ticket_limit"`
TotalWinningLimit float32 `json:"total_winning_limit"`
AmountForBetReferral float32 `json:"amount_for_bet_referral"`
CashbackAmountCap float32 `json:"cashback_amount_cap"`
}
type SaveSettingListReq struct {
SMSProvider *string `json:"sms_provider,omitempty"`
MaxNumberOfOutcomes *int64 `json:"max_number_of_outcomes,omitempty"`
BetAmountLimit *float32 `json:"bet_amount_limit,omitempty"`
DailyTicketPerIP *int64 `json:"daily_ticket_limit,omitempty"`
TotalWinningLimit *float32 `json:"total_winning_limit,omitempty"`
AmountForBetReferral *float32 `json:"amount_for_bet_referral,omitempty"`
CashbackAmountCap *float32 `json:"cashback_amount_cap,omitempty"`
}
func ConvertSaveSettingListReq(settings SaveSettingListReq) ValidSettingList {
return ValidSettingList{
SMSProvider: ConvertStringPtr(settings.SMSProvider),
MaxNumberOfOutcomes: ConvertInt64Ptr(settings.MaxNumberOfOutcomes),
BetAmountLimit: ConvertFloat32PtrToCurrency(settings.BetAmountLimit),
DailyTicketPerIP: ConvertInt64Ptr(settings.DailyTicketPerIP),
TotalWinningLimit: ConvertFloat32PtrToCurrency(settings.TotalWinningLimit),
AmountForBetReferral: ConvertFloat32PtrToCurrency(settings.AmountForBetReferral),
CashbackAmountCap: ConvertFloat32PtrToCurrency(settings.CashbackAmountCap),
}
}
type ValidSettingList struct {
SMSProvider ValidString
MaxNumberOfOutcomes ValidInt64
BetAmountLimit ValidCurrency
DailyTicketPerIP ValidInt64
TotalWinningLimit ValidCurrency
AmountForBetReferral ValidCurrency
CashbackAmountCap ValidCurrency
}
// Always make sure to run the validation before converting this
func (vsl *ValidSettingList) ToSettingList() SettingList {
return SettingList{
SMSProvider: SMSProvider(vsl.SMSProvider.Value),
MaxNumberOfOutcomes: vsl.MaxNumberOfOutcomes.Value,
BetAmountLimit: Currency(vsl.BetAmountLimit.Value),
DailyTicketPerIP: vsl.DailyTicketPerIP.Value,
TotalWinningLimit: Currency(vsl.TotalWinningLimit.Value),
AmountForBetReferral: Currency(vsl.AmountForBetReferral.Value),
CashbackAmountCap: Currency(vsl.CashbackAmountCap.Value),
}
}
// Custom Validations for non-generic types
func (vsl *ValidSettingList) CustomValidationSettings() error {
if !SMSProvider(vsl.SMSProvider.Value).IsValid() {
return fmt.Errorf("sms provider invalid")
}
return nil
}
func (vsl *ValidSettingList) GetInt64SettingsMap() map[string]*ValidInt64 {
return map[string]*ValidInt64{
"max_number_of_outcomes": &vsl.MaxNumberOfOutcomes,
"daily_ticket_limit": &vsl.DailyTicketPerIP,
}
}
func (vsl *ValidSettingList) GetCurrencySettingsMap() map[string]*ValidCurrency {
return map[string]*ValidCurrency{
"bet_amount_limit": &vsl.BetAmountLimit,
"total_winnings_limit": &vsl.TotalWinningLimit,
"amount_for_bet_referral": &vsl.AmountForBetReferral,
"cashback_amount_cap": &vsl.CashbackAmountCap,
}
}
func (vsl *ValidSettingList) GetStringSettingsMap() map[string]*ValidString {
return map[string]*ValidString{
"sms_provider": &vsl.SMSProvider,
}
}
func (vsl *ValidSettingList) GetBoolSettingsMap() map[string]*ValidBool {
return map[string]*ValidBool{}
}
func (vsl *ValidSettingList) GetFloat32SettingsMap() map[string]*ValidFloat32 {
return map[string]*ValidFloat32{}
}
func (vsl *ValidSettingList) GetTimeSettingsMap() map[string]*ValidTime {
return map[string]*ValidTime{}
}
func (vsl *ValidSettingList) GetTotalSettings() int {
return len(vsl.GetInt64SettingsMap()) +
len(vsl.GetCurrencySettingsMap()) +
len(vsl.GetStringSettingsMap()) +
len(vsl.GetBoolSettingsMap()) +
len(vsl.GetFloat32SettingsMap()) +
len(vsl.GetTimeSettingsMap())
}
func (vsl *ValidSettingList) GetAllValid() map[string]*bool {
settingValid := make(map[string]*bool)
for key, setting := range vsl.GetInt64SettingsMap() {
settingValid[key] = &(*setting).Valid
}
for key, setting := range vsl.GetCurrencySettingsMap() {
settingValid[key] = &(*setting).Valid
}
for key, setting := range vsl.GetStringSettingsMap() {
settingValid[key] = &(*setting).Valid
}
for key, setting := range vsl.GetBoolSettingsMap() {
settingValid[key] = &(*setting).Valid
}
for key, setting := range vsl.GetFloat32SettingsMap() {
settingValid[key] = &(*setting).Valid
}
for key, setting := range vsl.GetTimeSettingsMap() {
settingValid[key] = &(*setting).Valid
}
return settingValid
}
func setValidSetting[T any](settings map[string]*T, searchKey string, setVal T) error {
for key, setting := range settings {
if key == searchKey {
*setting = setVal
}
return nil
}
return ErrSettingNotFound
}
func (vsl *ValidSettingList) SetInt64Setting(searchKey string, searchVal string) error {
value, err := strconv.ParseInt(searchVal, 10, 64)
if err != nil {
return err
}
return setValidSetting(vsl.GetInt64SettingsMap(), searchKey, ValidInt64{Value: value, Valid: true})
}
func (vsl *ValidSettingList) SetCurrencySetting(searchKey string, searchVal string) error {
value, err := strconv.ParseInt(searchVal, 10, 64)
if err != nil {
return err
}
return setValidSetting(vsl.GetCurrencySettingsMap(), searchKey, ValidCurrency{Value: Currency(value), Valid: true})
}
func (vsl *ValidSettingList) SetStringSetting(searchKey string, searchVal string) error {
return setValidSetting(vsl.GetStringSettingsMap(), searchKey, ValidString{Value: searchVal, Valid: true})
}
func (vsl *ValidSettingList) SetBoolSetting(searchKey string, searchVal string) error {
value, err := strconv.ParseBool(searchVal)
if err != nil {
return err
}
return setValidSetting(vsl.GetBoolSettingsMap(), searchKey, ValidBool{Value: value, Valid: true})
}
func (vsl *ValidSettingList) SetFloat32Setting(searchKey string, searchVal string) error {
value, err := strconv.ParseFloat(searchVal, 32)
if err != nil {
return err
}
return setValidSetting(vsl.GetFloat32SettingsMap(), searchKey, ValidFloat32{Value: float32(value), Valid: true})
}
func (vsl *ValidSettingList) SetTimeSetting(searchKey string, searchVal string) error {
value, err := time.Parse(time.RFC3339, searchVal)
if err != nil {
return err
}
return setValidSetting(vsl.GetTimeSettingsMap(), searchKey, ValidTime{Value: value, Valid: true})
}
func (vsl *ValidSettingList) SetSetting(searchKey string, searchVal string) error {
setters := []func(string, string) error{
vsl.SetInt64Setting,
vsl.SetCurrencySetting,
vsl.SetStringSetting,
vsl.SetBoolSetting,
vsl.SetFloat32Setting,
vsl.SetTimeSetting,
}
for _, setter := range setters {
if err := setter(searchKey, searchVal); err != nil {
if err == ErrSettingNotFound {
continue // not this setter, try the next
}
return fmt.Errorf("error while processing setting %q: %w", searchKey, err)
}
return nil // successfully set
}
// If we get here, none of the setters matched
return ErrSettingNotFound
}
func convertValidSettings[T any](
settings map[string]*T,
isValid func(*T) bool,
toString func(*T) string,
) []Setting {
result := make([]Setting, 0, len(settings))
for key, s := range settings {
if isValid(s) {
result = append(result, Setting{
Key: key,
Value: toString(s),
})
}
}
return result
}
func (vsl *ValidSettingList) ConvertInt64Settings() []Setting {
return convertValidSettings(
vsl.GetInt64SettingsMap(),
func(s *ValidInt64) bool { return s.Valid },
func(s *ValidInt64) string { return strconv.FormatInt(s.Value, 10) },
)
}
func (vsl *ValidSettingList) ConvertCurrencySettings() []Setting {
return convertValidSettings(
vsl.GetCurrencySettingsMap(),
func(s *ValidCurrency) bool { return s.Valid },
func(s *ValidCurrency) string { return strconv.FormatInt(int64(s.Value), 10) },
)
}
func (vsl *ValidSettingList) ConvertStringSettings() []Setting {
return convertValidSettings(
vsl.GetStringSettingsMap(),
func(s *ValidString) bool { return s.Valid },
func(s *ValidString) string { return s.Value },
)
}
func (vsl *ValidSettingList) ConvertBoolSettings() []Setting {
return convertValidSettings(
vsl.GetBoolSettingsMap(),
func(s *ValidBool) bool { return s.Valid },
func(s *ValidBool) string { return strconv.FormatBool(s.Value) },
)
}
func (vsl *ValidSettingList) ConvertFloat32Settings() []Setting {
return convertValidSettings(
vsl.GetFloat32SettingsMap(),
func(s *ValidFloat32) bool { return s.Valid },
func(s *ValidFloat32) string { return strconv.FormatFloat(float64(s.Value), 'f', -1, 32) },
)
}
func (vsl *ValidSettingList) ConvertTimeSettings() []Setting {
return convertValidSettings(
vsl.GetTimeSettingsMap(),
func(s *ValidTime) bool { return s.Valid },
func(s *ValidTime) string { return s.Value.Format(time.RFC3339) },
)
}
func validateSettings[T any](
settings map[string]*T,
customValidator func(*T) bool,
) error {
var errs []string
for key, s := range settings {
if !customValidator(s) {
errs = append(errs, fmt.Sprintf("%v is invalid", key))
}
}
if len(errs) > 0 {
return fmt.Errorf(strings.Join(errs, "; "))
}
return nil
}
func (vsl *ValidSettingList) ValidateInt64Settings() error {
return validateSettings(vsl.GetInt64SettingsMap(),
func(s *ValidInt64) bool {
return s.Valid
},
)
}
func (vsl *ValidSettingList) ValidateCurrencySettings() error {
return validateSettings(vsl.GetCurrencySettingsMap(),
func(s *ValidCurrency) bool {
return s.Valid
},
)
}
func (vsl *ValidSettingList) ValidateStringSettings() error {
return validateSettings(vsl.GetStringSettingsMap(),
func(s *ValidString) bool {
return s.Valid
},
)
}
func (vsl *ValidSettingList) ValidateBoolSettings() error {
return validateSettings(vsl.GetBoolSettingsMap(),
func(s *ValidBool) bool {
return s.Valid
},
)
}
func (vsl *ValidSettingList) ValidateFloat32Settings() error {
return validateSettings(vsl.GetFloat32SettingsMap(),
func(s *ValidFloat32) bool {
return s.Valid
},
)
}
func (vsl *ValidSettingList) ValidateTimeSettings() error {
return validateSettings(vsl.GetTimeSettingsMap(),
func(s *ValidTime) bool {
return s.Valid
},
)
}
func (vsl *ValidSettingList) ValidateAllSettings() error {
var errs []string
validators := []func() error{
vsl.ValidateInt64Settings,
vsl.ValidateCurrencySettings,
vsl.ValidateStringSettings,
vsl.ValidateBoolSettings,
vsl.ValidateFloat32Settings,
vsl.ValidateTimeSettings,
vsl.CustomValidationSettings,
}
for _, validator := range validators {
if err := validator(); err != nil {
errs = append(errs, err.Error())
}
}
if len(errs) > 0 {
return fmt.Errorf(strings.Join(errs, "; "))
}
return nil
}
func (vsl *ValidSettingList) ConvertAllSettings() []Setting {
totalCap := vsl.GetTotalSettings()
all := make([]Setting, 0, totalCap)
all = append(all, vsl.ConvertInt64Settings()...)
all = append(all, vsl.ConvertCurrencySettings()...)
all = append(all, vsl.ConvertStringSettings()...)
all = append(all, vsl.ConvertBoolSettings()...)
all = append(all, vsl.ConvertFloat32Settings()...)
all = append(all, vsl.ConvertTimeSettings()...)
return all
}
func ConvertDBGlobalSettingList(settings []dbgen.GlobalSetting) (SettingList, error) {
var dbSettingList ValidSettingList
for _, setting := range settings {
if err := dbSettingList.SetSetting(setting.Key, setting.Value); err != nil {
if err == ErrSettingNotFound {
MongoDBLogger.Warn("unknown setting found on database", zap.String("setting", setting.Key))
}
}
}
if err := dbSettingList.ValidateAllSettings(); err != nil {
fmt.Printf("setting validation error: %v \n")
MongoDBLogger.Warn("setting validation error", zap.Error(err))
return SettingList{}, err
}
settingList := dbSettingList.ToSettingList()
return settingList, nil
}
func ConvertDBOverrideSettingList(settings []dbgen.GetOverrideSettingsRow) (SettingList, error) {
var dbSettingList ValidSettingList
for _, setting := range settings {
if err := dbSettingList.SetSetting(setting.Key, setting.Value); err != nil {
if err == ErrSettingNotFound {
MongoDBLogger.Warn("unknown setting found on database", zap.String("setting", setting.Key))
}
}
}
if err := dbSettingList.ValidateAllSettings(); err != nil {
fmt.Printf("setting validation error: %v \n")
MongoDBLogger.Warn("setting validation error", zap.Error(err))
return SettingList{}, err
}
settingList := dbSettingList.ToSettingList()
return settingList, nil
}

View File

@ -1,8 +1,9 @@
package domain
import (
"strconv"
"time"
dbgen "github.com/SamuelTariku/FortuneBet-Backend/gen/db"
)
type Setting struct {
@ -16,160 +17,20 @@ type SettingRes struct {
Value string `json:"value"`
UpdatedAt time.Time `json:"updated_at"`
}
type UpdateSettingListReq struct {
SMSProvider *string `json:"sms_provider,omitempty"`
MaxNumberOfOutcomes *int64 `json:"max_number_of_outcomes,omitempty"`
BetAmountLimit *float32 `json:"bet_amount_limit,omitempty"`
DailyTicketPerIP *int64 `json:"daily_ticket_limit,omitempty"`
TotalWinningLimit *float32 `json:"total_winning_limit,omitempty"`
AmountForBetReferral *float32 `json:"amount_for_bet_referral,omitempty"`
CashbackAmountCap *float32 `json:"cashback_amount_cap,omitempty"`
type CompanySetting struct {
Key string
Value string
CompanyID int64
UpdatedAt time.Time
CreatedAt time.Time
}
type SettingList struct {
SMSProvider SMSProvider `json:"sms_provider"`
MaxNumberOfOutcomes int64 `json:"max_number_of_outcomes"`
BetAmountLimit Currency `json:"bet_amount_limit"`
DailyTicketPerIP int64 `json:"daily_ticket_limit"`
TotalWinningLimit Currency `json:"total_winning_limit"`
AmountForBetReferral Currency `json:"amount_for_bet_referral"`
CashbackAmountCap Currency `json:"cashback_amount_cap"`
}
type SettingListRes struct {
SMSProvider SMSProvider `json:"sms_provider"`
MaxNumberOfOutcomes int64 `json:"max_number_of_outcomes"`
BetAmountLimit float32 `json:"bet_amount_limit"`
DailyTicketPerIP int64 `json:"daily_ticket_limit"`
TotalWinningLimit float32 `json:"total_winning_limit"`
AmountForBetReferral float32 `json:"amount_for_bet_referral"`
CashbackAmountCap float32 `json:"cashback_amount_cap"`
}
type ValidSettingList struct {
SMSProvider ValidString
MaxNumberOfOutcomes ValidInt64
BetAmountLimit ValidInt64
DailyTicketPerIP ValidInt64
TotalWinningLimit ValidInt64
AmountForBetReferral ValidInt64
CashbackAmountCap ValidInt64
}
func ConvertInt64SettingsMap(dbSettingList *ValidSettingList) map[string]*ValidInt64 {
return map[string]*ValidInt64{
"max_number_of_outcomes": &dbSettingList.MaxNumberOfOutcomes,
"bet_amount_limit": &dbSettingList.BetAmountLimit,
"daily_ticket_limit": &dbSettingList.DailyTicketPerIP,
"total_winnings_limit": &dbSettingList.TotalWinningLimit,
"amount_for_bet_referral": &dbSettingList.AmountForBetReferral,
"cashback_amount_cap": &dbSettingList.CashbackAmountCap,
}
}
func ConvertStringSettingsMap(dbSettingList *ValidSettingList) map[string]*ValidString {
return map[string]*ValidString{
"sms_provider": &dbSettingList.SMSProvider,
}
}
func ConvertBoolSettingsMap(dbSettingList *ValidSettingList) map[string]*ValidBool {
return map[string]*ValidBool{}
}
func ConvertFloat32SettingsMap(dbSettingList *ValidSettingList) map[string]*ValidFloat32 {
return map[string]*ValidFloat32{}
}
func ConvertTimeSettingsMap(dbSettingList *ValidSettingList) map[string]*ValidTime {
return map[string]*ValidTime{}
}
func ValidateSettingList(dbSettingList ValidSettingList) SettingList {
// TODO: Add validation here
return SettingList{
SMSProvider: SMSProvider(dbSettingList.SMSProvider.Value),
MaxNumberOfOutcomes: dbSettingList.MaxNumberOfOutcomes.Value,
BetAmountLimit: Currency(dbSettingList.BetAmountLimit.Value),
DailyTicketPerIP: dbSettingList.DailyTicketPerIP.Value,
TotalWinningLimit: Currency(dbSettingList.TotalWinningLimit.Value),
AmountForBetReferral: Currency(dbSettingList.AmountForBetReferral.Value),
CashbackAmountCap: Currency(dbSettingList.CashbackAmountCap.Value),
}
}
func ConvertValidSettingList(settingList ValidSettingList) []Setting {
var convertedSettings []Setting
// if settingList.AmountForBetReferral.Valid {
// newValue := strconv.FormatInt(settingList.AmountForBetReferral.Value, 10)
// settings = append(settings, Setting{
// Key: "amount_for_bet_referral",
// Value: newValue,
// })
// }
int64SettingsMap := ConvertInt64SettingsMap(&settingList)
stringSettingsMap := ConvertStringSettingsMap(&settingList)
boolSettingsMap := ConvertBoolSettingsMap(&settingList)
float32SettingsMap := ConvertFloat32SettingsMap(&settingList)
timeSettingsMap := ConvertTimeSettingsMap(&settingList)
for key, settingPtr := range int64SettingsMap {
setting := *settingPtr
if setting.Valid {
stringVal := strconv.FormatInt(setting.Value, 10)
convertedSettings = append(convertedSettings, Setting{
Key: key,
Value: stringVal,
})
}
}
for key, settingPtr := range stringSettingsMap {
setting := *settingPtr
if setting.Valid {
convertedSettings = append(convertedSettings, Setting{
Key: key,
Value: setting.Value,
})
}
}
for key, settingPtr := range boolSettingsMap {
setting := *settingPtr
if setting.Valid {
stringVal := strconv.FormatBool(setting.Value)
convertedSettings = append(convertedSettings, Setting{
Key: key,
Value: stringVal,
})
}
}
for key, settingPtr := range float32SettingsMap {
setting := *settingPtr
if setting.Valid {
stringVal := strconv.FormatFloat(float64(setting.Value), 'E', -1, 64)
convertedSettings = append(convertedSettings, Setting{
Key: key,
Value: stringVal,
})
}
}
for key, settingPtr := range timeSettingsMap {
setting := *settingPtr
if setting.Valid {
var stringVal string = setting.Value.Format("2006-01-02 15:04:05")
convertedSettings = append(convertedSettings, Setting{
Key: key,
Value: stringVal,
})
}
}
return convertedSettings
type CompanySettingRes struct {
Key string `json:"key"`
Value string `json:"value"`
CompanyID int64 `json:"company_id"`
UpdatedAt time.Time `json:"updated_at"`
CreatedAt time.Time `json:"created_at"`
}
func ConvertSetting(setting Setting) SettingRes {
@ -180,14 +41,20 @@ func ConvertSetting(setting Setting) SettingRes {
}
}
func ConvertUpdateSettingListReq(settings UpdateSettingListReq) ValidSettingList {
return ValidSettingList{
SMSProvider: ConvertStringPtr(settings.SMSProvider),
MaxNumberOfOutcomes: ConvertInt64Ptr(settings.MaxNumberOfOutcomes),
BetAmountLimit: ConvertCurrencyFloatPtr(settings.BetAmountLimit),
DailyTicketPerIP: ConvertInt64Ptr(settings.DailyTicketPerIP),
TotalWinningLimit: ConvertCurrencyFloatPtr(settings.TotalWinningLimit),
AmountForBetReferral: ConvertCurrencyFloatPtr(settings.AmountForBetReferral),
CashbackAmountCap: ConvertCurrencyFloatPtr(settings.CashbackAmountCap),
func ConvertCompanySetting(companySetting dbgen.CompanySetting) CompanySetting {
return CompanySetting{
Key: companySetting.Key,
Value: companySetting.Value,
CompanyID: companySetting.CompanyID,
UpdatedAt: companySetting.UpdatedAt.Time,
CreatedAt: companySetting.CreatedAt.Time,
}
}
func ConvertCompanySettings(settings []dbgen.CompanySetting) []CompanySetting {
result := make([]CompanySetting, 0, len(settings))
for _, setting := range settings {
result = append(result, ConvertCompanySetting(setting))
}
return result
}

View File

@ -39,6 +39,7 @@ type Ticket struct {
ID int64
Amount Currency
TotalOdds float32
CompanyID int64
}
type GetTicket struct {
@ -46,12 +47,14 @@ type GetTicket struct {
Amount Currency
TotalOdds float32
Outcomes []TicketOutcome
CompanyID int64
}
type CreateTicket struct {
Amount Currency
TotalOdds float32
IP string
CompanyID int64
}
type CreateTicketOutcomeReq struct {
@ -80,4 +83,9 @@ type TicketRes struct {
Outcomes []TicketOutcome `json:"outcomes"`
Amount float32 `json:"amount" example:"100.0"`
TotalOdds float32 `json:"total_odds" example:"4.22"`
CompanyID int64 `json:"company_id" example:"1"`
}
type TicketFilter struct {
CompanyID ValidInt64
}

View File

@ -35,10 +35,7 @@ type UserFilter struct {
CreatedBefore ValidTime
CreatedAfter ValidTime
}
type ValidRole struct {
Value Role
Valid bool
}
type RegisterUserReq struct {
FirstName string

View File

@ -9,38 +9,12 @@ import (
"github.com/jackc/pgx/v5/pgtype"
)
// Valid Int64
type ValidInt64 struct {
Value int64
Valid bool
}
type ValidInt struct {
Value int
Valid bool
}
type ValidInt32 struct {
Value int32
Valid bool
}
type ValidFloat32 struct {
Value float32
Valid bool
}
type ValidString struct {
Value string
Valid bool
}
type ValidTime struct {
Value time.Time
Valid bool
}
type ValidBool struct {
Value bool
Valid bool
}
// ValidInt64 → pgtype.Int8
func (v ValidInt64) ToPG() pgtype.Int8 {
return pgtype.Int8{
Int64: v.Value,
@ -48,126 +22,6 @@ func (v ValidInt64) ToPG() pgtype.Int8 {
}
}
// ValidInt32 → pgtype.Int4
func (v ValidInt32) ToPG() pgtype.Int4 {
return pgtype.Int4{
Int32: v.Value,
Valid: v.Valid,
}
}
// ValidInt → pgtype.Int4 (Go int mapped to int32 for pg compatibility)
func (v ValidInt) ToPG() pgtype.Int4 {
return pgtype.Int4{
Int32: int32(v.Value),
Valid: v.Valid,
}
}
// ValidFloat32 → pgtype.Float4
func (v ValidFloat32) ToPG() pgtype.Float4 {
return pgtype.Float4{
Float32: v.Value,
Valid: v.Valid,
}
}
// ValidString → pgtype.Text
func (v ValidString) ToPG() pgtype.Text {
return pgtype.Text{
String: v.Value,
Valid: v.Valid,
}
}
// ValidTime → pgtype.Timestamp
func (v ValidTime) ToPG() pgtype.Timestamp {
return pgtype.Timestamp{
Time: v.Value,
Valid: v.Valid,
}
}
// ValidBool → pgtype.Bool
func (v ValidBool) ToPG() pgtype.Bool {
return pgtype.Bool{
Bool: v.Value,
Valid: v.Valid,
}
}
func ConvertInt64Ptr(value *int64) ValidInt64 {
if value == nil {
return ValidInt64{}
}
return ValidInt64{
Value: *value,
Valid: true,
}
}
func ConvertInt32Ptr(value *int32) ValidInt32 {
if value == nil {
return ValidInt32{}
}
return ValidInt32{
Value: *value,
Valid: true,
}
}
func ConvertIntPtr(value *int) ValidInt {
if value == nil {
return ValidInt{}
}
return ValidInt{
Value: *value,
Valid: true,
}
}
func ConvertStringPtr(value *string) ValidString {
if value == nil {
return ValidString{}
}
return ValidString{
Value: *value,
Valid: true,
}
}
func ConvertFloat32Ptr(value *float32) ValidFloat32 {
if value == nil {
return ValidFloat32{}
}
return ValidFloat32{
Value: *value,
Valid: true,
}
}
func ConvertCurrencyFloatPtr(value *float32) ValidInt64 {
if value == nil {
return ValidInt64{}
}
convertedCurrency := ToCurrency(*value)
return ValidInt64{
Value: int64(convertedCurrency),
Valid: true,
}
}
func ConvertBoolPtr(value *bool) ValidBool {
if value == nil {
return ValidBool{}
}
return ValidBool{
Value: *value,
Valid: true,
}
}
func (n *ValidInt64) UnmarshalJSON(data []byte) error {
var s string
if err := json.Unmarshal(data, &s); err == nil {
@ -191,3 +45,201 @@ func (n *ValidInt64) UnmarshalJSON(data []byte) error {
return fmt.Errorf("invalid int64 value: %s", string(data))
}
func ConvertInt64Ptr(value *int64) ValidInt64 {
if value == nil {
return ValidInt64{}
}
return ValidInt64{
Value: *value,
Valid: true,
}
}
// Valid Int
type ValidInt struct {
Value int
Valid bool
}
func (v ValidInt) ToPG() pgtype.Int4 {
return pgtype.Int4{
Int32: int32(v.Value),
Valid: v.Valid,
}
}
func ConvertIntPtr(value *int) ValidInt {
if value == nil {
return ValidInt{}
}
return ValidInt{
Value: *value,
Valid: true,
}
}
// Valid Int32
type ValidInt32 struct {
Value int32
Valid bool
}
func (v ValidInt32) ToPG() pgtype.Int4 {
return pgtype.Int4{
Int32: v.Value,
Valid: v.Valid,
}
}
func ConvertInt32Ptr(value *int32) ValidInt32 {
if value == nil {
return ValidInt32{}
}
return ValidInt32{
Value: *value,
Valid: true,
}
}
// Valid Float32
type ValidFloat32 struct {
Value float32
Valid bool
}
func (v ValidFloat32) ToPG() pgtype.Float4 {
return pgtype.Float4{
Float32: v.Value,
Valid: v.Valid,
}
}
func ConvertFloat32Ptr(value *float32) ValidFloat32 {
if value == nil {
return ValidFloat32{}
}
return ValidFloat32{
Value: *value,
Valid: true,
}
}
// Valid String
type ValidString struct {
Value string
Valid bool
}
func (v ValidString) ToPG() pgtype.Text {
return pgtype.Text{
String: v.Value,
Valid: v.Valid,
}
}
func ConvertStringPtr(value *string) ValidString {
if value == nil {
return ValidString{}
}
return ValidString{
Value: *value,
Valid: true,
}
}
// Valid Time
type ValidTime struct {
Value time.Time
Valid bool
}
func (v ValidTime) ToPG() pgtype.Timestamp {
return pgtype.Timestamp{
Time: v.Value,
Valid: v.Valid,
}
}
// Valid Bool
type ValidBool struct {
Value bool
Valid bool
}
func (v ValidBool) ToPG() pgtype.Bool {
return pgtype.Bool{
Bool: v.Value,
Valid: v.Valid,
}
}
func ConvertBoolPtr(value *bool) ValidBool {
if value == nil {
return ValidBool{}
}
return ValidBool{
Value: *value,
Valid: true,
}
}
// Valid Currency
type ValidCurrency struct {
Value Currency
Valid bool
}
func (v ValidCurrency) ToPG() pgtype.Int8 {
return pgtype.Int8{
Int64: int64(v.Value),
Valid: v.Valid,
}
}
func ConvertCurrencyPtr(value *Currency) ValidCurrency {
if value == nil {
return ValidCurrency{}
}
return ValidCurrency{
Value: *value,
Valid: true,
}
}
func ConvertFloat32PtrToCurrency(value *float32) ValidCurrency {
if value == nil {
return ValidCurrency{}
}
converted := ToCurrency(*value)
return ValidCurrency{
Value: converted,
Valid: true,
}
}
// Valid Role
type ValidRole struct {
Value Role
Valid bool
}
func (v ValidRole) ToPG() pgtype.Text {
return pgtype.Text{
String: string(v.Value),
Valid: v.Valid,
}
}
func ConvertRolePtr(value *Role) ValidRole {
if value == nil {
return ValidRole{}
}
return ValidRole{
Value: *value,
Valid: true,
}
}

View File

@ -19,115 +19,14 @@ var (
mongoLogger *zap.Logger
)
func convertDBBet(bet dbgen.Bet) domain.Bet {
return domain.Bet{
ID: bet.ID,
Amount: domain.Currency(bet.Amount),
TotalOdds: bet.TotalOdds,
Status: domain.OutcomeStatus(bet.Status),
UserID: bet.UserID,
IsShopBet: bet.IsShopBet,
CashedOut: bet.CashedOut,
FastCode: bet.FastCode,
CreatedAt: bet.CreatedAt.Time,
}
}
func convertDBBetOutcomes(outcome dbgen.BetOutcome) domain.BetOutcome {
return domain.BetOutcome{
ID: outcome.ID,
BetID: outcome.BetID,
SportID: outcome.SportID,
EventID: outcome.EventID,
OddID: outcome.OddID,
HomeTeamName: outcome.HomeTeamName,
AwayTeamName: outcome.AwayTeamName,
MarketID: outcome.MarketID,
MarketName: outcome.MarketName,
Odd: outcome.Odd,
OddName: outcome.OddName,
OddHeader: outcome.OddHeader,
OddHandicap: outcome.OddHandicap,
Status: domain.OutcomeStatus(outcome.Status),
Expires: outcome.Expires.Time,
}
}
func convertDBBetWithOutcomes(bet dbgen.BetWithOutcome) domain.GetBet {
var outcomes []domain.BetOutcome = make([]domain.BetOutcome, 0, len(bet.Outcomes))
for _, outcome := range bet.Outcomes {
outcomes = append(outcomes, convertDBBetOutcomes(outcome))
}
return domain.GetBet{
ID: bet.ID,
Amount: domain.Currency(bet.Amount),
TotalOdds: bet.TotalOdds,
Status: domain.OutcomeStatus(bet.Status),
FullName: bet.FullName.(string),
PhoneNumber: bet.PhoneNumber.String,
UserID: bet.UserID,
IsShopBet: bet.IsShopBet,
CashedOut: bet.CashedOut,
Outcomes: outcomes,
FastCode: bet.FastCode,
CreatedAt: bet.CreatedAt.Time,
}
}
func convertDBFlag(flag dbgen.Flag) domain.Flag {
return domain.Flag{
ID: flag.ID,
BetID: flag.BetID.Int64,
OddID: flag.OddsMarketID.Int64,
Reason: flag.Reason.String,
FlaggedAt: flag.FlaggedAt.Time,
Resolved: flag.Resolved.Bool,
}
}
func convertDBCreateBetOutcome(betOutcome domain.CreateBetOutcome) dbgen.CreateBetOutcomeParams {
return dbgen.CreateBetOutcomeParams{
BetID: betOutcome.BetID,
EventID: betOutcome.EventID,
SportID: betOutcome.SportID,
OddID: betOutcome.OddID,
HomeTeamName: betOutcome.HomeTeamName,
AwayTeamName: betOutcome.AwayTeamName,
MarketID: betOutcome.MarketID,
MarketName: betOutcome.MarketName,
Odd: betOutcome.Odd,
OddName: betOutcome.OddName,
OddHeader: betOutcome.OddHeader,
OddHandicap: betOutcome.OddHandicap,
Expires: pgtype.Timestamp{
Time: betOutcome.Expires,
Valid: true,
},
}
}
func convertCreateBet(bet domain.CreateBet) dbgen.CreateBetParams {
return dbgen.CreateBetParams{
Amount: int64(bet.Amount),
TotalOdds: bet.TotalOdds,
Status: int32(bet.Status),
UserID: bet.UserID,
IsShopBet: bet.IsShopBet,
OutcomesHash: bet.OutcomesHash,
FastCode: bet.FastCode,
}
}
func (s *Store) CreateBet(ctx context.Context, bet domain.CreateBet) (domain.Bet, error) {
newBet, err := s.queries.CreateBet(ctx, convertCreateBet(bet))
newBet, err := s.queries.CreateBet(ctx, domain.ConvertCreateBet(bet))
if err != nil {
fmt.Println("We are here")
logger.Error("Failed to create bet", slog.String("error", err.Error()), slog.Any("bet", bet))
return domain.Bet{}, err
}
return convertDBBet(newBet), err
return domain.ConvertDBBet(newBet), err
}
@ -135,7 +34,7 @@ func (s *Store) CreateBetOutcome(ctx context.Context, outcomes []domain.CreateBe
var dbParams []dbgen.CreateBetOutcomeParams = make([]dbgen.CreateBetOutcomeParams, 0, len(outcomes))
for _, outcome := range outcomes {
dbParams = append(dbParams, convertDBCreateBetOutcome(outcome))
dbParams = append(dbParams, domain.ConvertDBCreateBetOutcome(outcome))
}
rows, err := s.queries.CreateBetOutcome(ctx, dbParams)
@ -177,7 +76,7 @@ func (s *Store) CreateFlag(ctx context.Context, flag domain.CreateFlagReq) (doma
return domain.Flag{}, err
}
return convertDBFlag(f), nil
return domain.ConvertDBFlag(f), nil
}
func (s *Store) GetBetByID(ctx context.Context, id int64) (domain.GetBet, error) {
@ -190,35 +89,18 @@ func (s *Store) GetBetByID(ctx context.Context, id int64) (domain.GetBet, error)
return domain.GetBet{}, err
}
return convertDBBetWithOutcomes(bet), nil
return domain.ConvertDBBetWithOutcomes(bet), nil
}
func (s *Store) GetAllBets(ctx context.Context, filter domain.BetFilter) ([]domain.GetBet, error) {
bets, err := s.queries.GetAllBets(ctx, dbgen.GetAllBetsParams{
UserID: pgtype.Int8{
Int64: filter.UserID.Value,
Valid: filter.UserID.Valid,
},
CashedOut: pgtype.Bool{
Bool: filter.CashedOut.Value,
Valid: filter.CashedOut.Valid,
},
IsShopBet: pgtype.Bool{
Bool: filter.IsShopBet.Value,
Valid: filter.IsShopBet.Valid,
},
Query: pgtype.Text{
String: filter.Query.Value,
Valid: filter.Query.Valid,
},
CreatedBefore: pgtype.Timestamp{
Time: filter.CreatedBefore.Value,
Valid: filter.CreatedBefore.Valid,
},
CreatedAfter: pgtype.Timestamp{
Time: filter.CreatedAfter.Value,
Valid: filter.CreatedAfter.Valid,
},
UserID: filter.UserID.ToPG(),
CompanyID: filter.CompanyID.ToPG(),
CashedOut: filter.CashedOut.ToPG(),
IsShopBet: filter.IsShopBet.ToPG(),
Query: filter.Query.ToPG(),
CreatedBefore: filter.CreatedBefore.ToPG(),
CreatedAfter: filter.CreatedAfter.ToPG(),
})
if err != nil {
domain.MongoDBLogger.Error("failed to get all bets",
@ -230,7 +112,7 @@ func (s *Store) GetAllBets(ctx context.Context, filter domain.BetFilter) ([]doma
var result []domain.GetBet = make([]domain.GetBet, 0, len(bets))
for _, bet := range bets {
result = append(result, convertDBBetWithOutcomes(bet))
result = append(result, domain.ConvertDBBetWithOutcomes(bet))
}
return result, nil
@ -245,7 +127,7 @@ func (s *Store) GetBetByUserID(ctx context.Context, UserID int64) ([]domain.GetB
var result []domain.GetBet = make([]domain.GetBet, 0, len(bets))
for _, bet := range bets {
result = append(result, convertDBBetWithOutcomes(bet))
result = append(result, domain.ConvertDBBetWithOutcomes(bet))
}
return result, nil
@ -258,7 +140,7 @@ func (s *Store) GetBetByFastCode(ctx context.Context, fastcode string) (domain.G
return domain.GetBet{}, err
}
return convertDBBetWithOutcomes(bet), nil
return domain.ConvertDBBetWithOutcomes(bet), nil
}
func (s *Store) GetBetsForCashback(ctx context.Context) ([]domain.GetBet, error) {
@ -270,7 +152,7 @@ func (s *Store) GetBetsForCashback(ctx context.Context) ([]domain.GetBet, error)
}
for _, bet := range bets {
cashbackBet := convertDBBetWithOutcomes(bet)
cashbackBet := domain.ConvertDBBetWithOutcomes(bet)
res = append(res, cashbackBet)
}
@ -361,7 +243,7 @@ func (s *Store) GetBetOutcomeByEventID(ctx context.Context, eventID int64, is_fi
var result []domain.BetOutcome = make([]domain.BetOutcome, 0, len(outcomes))
for _, outcome := range outcomes {
result = append(result, convertDBBetOutcomes(outcome))
result = append(result, domain.ConvertDBBetOutcomes(outcome))
}
return result, nil
}
@ -378,7 +260,7 @@ func (s *Store) GetBetOutcomeByBetID(ctx context.Context, betID int64) ([]domain
var result []domain.BetOutcome = make([]domain.BetOutcome, 0, len(outcomes))
for _, outcome := range outcomes {
result = append(result, convertDBBetOutcomes(outcome))
result = append(result, domain.ConvertDBBetOutcomes(outcome))
}
return result, nil
}
@ -397,7 +279,7 @@ func (s *Store) UpdateBetOutcomeStatus(ctx context.Context, id int64, status dom
return domain.BetOutcome{}, err
}
res := convertDBBetOutcomes(update)
res := domain.ConvertDBBetOutcomes(update)
return res, nil
}
@ -415,7 +297,7 @@ func (s *Store) UpdateBetOutcomeStatusByBetID(ctx context.Context, id int64, sta
return domain.BetOutcome{}, err
}
res := convertDBBetOutcomes(update)
res := domain.ConvertDBBetOutcomes(update)
return res, nil
}
@ -436,7 +318,7 @@ func (s *Store) UpdateBetOutcomeStatusForEvent(ctx context.Context, eventID int6
var result []domain.BetOutcome = make([]domain.BetOutcome, 0, len(outcomes))
for _, outcome := range outcomes {
result = append(result, convertDBBetOutcomes(outcome))
result = append(result, domain.ConvertDBBetOutcomes(outcome))
}
return result, nil
}

View File

@ -2,116 +2,24 @@ package repository
import (
"context"
"fmt"
"strconv"
"time"
dbgen "github.com/SamuelTariku/FortuneBet-Backend/gen/db"
"github.com/SamuelTariku/FortuneBet-Backend/internal/domain"
"go.uber.org/zap"
)
func GetDBSettingList(settings []dbgen.Setting) (domain.SettingList, error) {
var dbSettingList domain.ValidSettingList
var int64SettingsMap = domain.ConvertInt64SettingsMap(&dbSettingList)
var stringSettingsMap = domain.ConvertStringSettingsMap(&dbSettingList)
var boolSettingsMap = domain.ConvertBoolSettingsMap(&dbSettingList)
var float32SettingsMap = domain.ConvertFloat32SettingsMap(&dbSettingList)
var timeSettingsMap = domain.ConvertTimeSettingsMap(&dbSettingList)
for _, setting := range settings {
isSettingUnknown := true
for key, dbSetting := range int64SettingsMap {
if setting.Key == key {
value, err := strconv.ParseInt(setting.Value, 10, 64)
if err != nil {
return domain.SettingList{}, err
}
*dbSetting = domain.ValidInt64{
Value: value,
Valid: true,
}
isSettingUnknown = false
}
}
for key, dbSetting := range stringSettingsMap {
if setting.Key == key {
*dbSetting = domain.ValidString{
Value: setting.Value,
Valid: true,
}
isSettingUnknown = false
}
}
for key, dbSetting := range boolSettingsMap {
if setting.Key == key {
value, err := strconv.ParseBool(setting.Value)
if err != nil {
return domain.SettingList{}, err
}
*dbSetting = domain.ValidBool{
Value: value,
Valid: true,
}
isSettingUnknown = false
}
}
for key, dbSetting := range float32SettingsMap {
if setting.Key == key {
value, err := strconv.ParseFloat(setting.Value, 32)
if err != nil {
return domain.SettingList{}, err
}
*dbSetting = domain.ValidFloat32{
Value: float32(value),
Valid: true,
}
isSettingUnknown = false
}
}
for key, dbSetting := range timeSettingsMap {
if setting.Key == key {
value, err := time.Parse(time.RFC3339, setting.Value)
if err != nil {
return domain.SettingList{}, err
}
*dbSetting = domain.ValidTime{
Value: value,
Valid: true,
}
isSettingUnknown = false
}
}
if isSettingUnknown {
domain.MongoDBLogger.Warn("unknown setting found on database", zap.String("setting", setting.Key))
}
}
for key, dbSetting := range int64SettingsMap {
if !dbSetting.Valid {
fmt.Printf("setting value not found on database: %v \n", key)
domain.MongoDBLogger.Warn("setting value not found on database", zap.String("setting", key))
}
}
return domain.ValidateSettingList(dbSettingList), nil
}
func (s *Store) GetSettingList(ctx context.Context) (domain.SettingList, error) {
settings, err := s.queries.GetSettings(ctx)
func (s *Store) GetGlobalSettingList(ctx context.Context) (domain.SettingList, error) {
settings, err := s.queries.GetGlobalSettings(ctx)
if err != nil {
domain.MongoDBLogger.Error("failed to get all settings", zap.Error(err))
return domain.SettingList{}, err
}
return GetDBSettingList(settings)
return domain.ConvertDBGlobalSettingList(settings)
}
func (s *Store) GetSettings(ctx context.Context) ([]domain.Setting, error) {
settings, err := s.queries.GetSettings(ctx)
func (s *Store) GetGlobalSettings(ctx context.Context) ([]domain.Setting, error) {
settings, err := s.queries.GetGlobalSettings(ctx)
if err != nil {
domain.MongoDBLogger.Error("failed to get all settings", zap.Error(err))
@ -129,8 +37,8 @@ func (s *Store) GetSettings(ctx context.Context) ([]domain.Setting, error) {
return result, nil
}
func (s *Store) GetSetting(ctx context.Context, key string) (domain.Setting, error) {
dbSetting, err := s.queries.GetSetting(ctx, key)
func (s *Store) GetGlobalSetting(ctx context.Context, key string) (domain.Setting, error) {
dbSetting, err := s.queries.GetGlobalSetting(ctx, key)
if err != nil {
domain.MongoDBLogger.Error("failed to get all settings", zap.Error(err))
@ -145,14 +53,18 @@ func (s *Store) GetSetting(ctx context.Context, key string) (domain.Setting, err
return result, nil
}
func (s *Store) UpdateSetting(ctx context.Context, key, value string) error {
err := s.queries.UpdateSetting(ctx, dbgen.UpdateSettingParams{
func (s *Store) UpdateGlobalSetting(ctx context.Context, key, value string) error {
err := s.queries.UpdateGlobalSetting(ctx, dbgen.UpdateGlobalSettingParams{
Key: key,
Value: value,
})
if err != nil {
domain.MongoDBLogger.Error("failed to update setting", zap.String("key", key), zap.String("value", value), zap.Error(err))
domain.MongoDBLogger.Error("failed to update setting",
zap.String("key", key),
zap.String("value", value),
zap.Error(err),
)
return err
}
@ -161,11 +73,11 @@ func (s *Store) UpdateSetting(ctx context.Context, key, value string) error {
}
func (s *Store) UpdateSettingList(ctx context.Context, settingList domain.ValidSettingList) error {
convertedSettings := domain.ConvertValidSettingList(settingList)
func (s *Store) UpdateGlobalSettingList(ctx context.Context, settingList domain.ValidSettingList) error {
convertedSettings := settingList.ConvertAllSettings()
for _, setting := range convertedSettings {
err := s.UpdateSetting(ctx, setting.Key, setting.Value)
err := s.UpdateGlobalSetting(ctx, setting.Key, setting.Value)
if err != nil {
domain.MongoDBLogger.Warn("failed to update setting list", zap.String("key", setting.Key), zap.Error(err))
return err
@ -173,3 +85,89 @@ func (s *Store) UpdateSettingList(ctx context.Context, settingList domain.ValidS
}
return nil
}
func (s *Store) InsertCompanySetting(ctx context.Context, key, value string, companyID int64) error {
err := s.queries.InsertCompanySetting(ctx, dbgen.InsertCompanySettingParams{
CompanyID: companyID,
Key: key,
Value: value,
})
if err != nil {
return err
}
return nil
}
func (s *Store) InsertCompanySettingList(ctx context.Context, settingList domain.ValidSettingList, companyID int64) error {
convertedSettings := settingList.ConvertAllSettings()
for _, setting := range convertedSettings {
err := s.InsertCompanySetting(ctx, setting.Key, setting.Value, companyID)
if err != nil {
domain.MongoDBLogger.Warn("failed to update setting list", zap.String("key", setting.Key), zap.Error(err))
return err
}
}
return nil
}
func (s *Store) GetAllCompanySettings(ctx context.Context) ([]domain.CompanySetting, error) {
settings, err := s.queries.GetAllCompanySettings(ctx)
if err != nil {
return nil, err
}
return domain.ConvertCompanySettings(settings), nil
}
func (s *Store) GetCompanySettingsByKey(ctx context.Context, key string) ([]domain.CompanySetting, error) {
settings, err := s.queries.GetCompanySettingsByKey(ctx, key)
if err != nil {
return nil, err
}
return domain.ConvertCompanySettings(settings), nil
}
func (s *Store) GetOverrideSettings(ctx context.Context, companyID int64) ([]domain.Setting, error) {
settings, err := s.queries.GetOverrideSettings(ctx, companyID)
if err != nil {
return nil, err
}
result := make([]domain.Setting, 0, len(settings))
for _, setting := range settings {
result = append(result, domain.Setting{
Key: setting.Key,
Value: setting.Value,
UpdatedAt: setting.UpdatedAt.Time,
})
}
return result, nil
}
func (s *Store) GetOverrideSettingsList(ctx context.Context, companyID int64) (domain.SettingList, error) {
settings, err := s.queries.GetOverrideSettings(ctx, companyID)
if err != nil {
return domain.SettingList{}, err
}
return domain.ConvertDBOverrideSettingList(settings)
}
func (s *Store) DeleteCompanySetting(ctx context.Context, companyID int64, key string) error {
return s.queries.DeleteCompanySetting(ctx, dbgen.DeleteCompanySettingParams{
CompanyID: companyID,
Key: key,
})
}
func (s *Store) DeleteAllCompanySetting(ctx context.Context, companyID int64,) error {
return s.queries.DeleteAllCompanySetting(ctx, companyID)
}

View File

@ -24,7 +24,7 @@ func convertDBShopBetDetail(bet dbgen.ShopBetDetail) domain.ShopBetDetail {
var outcomes []domain.BetOutcome = make([]domain.BetOutcome, 0, len(bet.Outcomes))
for _, outcome := range bet.Outcomes {
outcomes = append(outcomes, convertDBBetOutcomes(outcome))
outcomes = append(outcomes, domain.ConvertDBBetOutcomes(outcome))
}
return domain.ShopBetDetail{
ID: bet.ID,

View File

@ -13,6 +13,7 @@ func convertDBTicket(ticket dbgen.Ticket) domain.Ticket {
ID: ticket.ID,
Amount: domain.Currency(ticket.Amount),
TotalOdds: ticket.TotalOdds,
CompanyID: ticket.CompanyID,
}
}
@ -71,6 +72,7 @@ func convertCreateTicket(ticket domain.CreateTicket) dbgen.CreateTicketParams {
Amount: int64(ticket.Amount),
TotalOdds: ticket.TotalOdds,
Ip: ticket.IP,
CompanyID: ticket.CompanyID,
}
}
@ -110,8 +112,8 @@ func (s *Store) GetTicketByID(ctx context.Context, id int64) (domain.GetTicket,
return convertDBTicketOutcomes(ticket), nil
}
func (s *Store) GetAllTickets(ctx context.Context) ([]domain.GetTicket, error) {
tickets, err := s.queries.GetAllTickets(ctx)
func (s *Store) GetAllTickets(ctx context.Context, filter domain.TicketFilter) ([]domain.GetTicket, error) {
tickets, err := s.queries.GetAllTickets(ctx, filter.CompanyID.ToPG())
if err != nil {
return nil, err
}

View File

@ -215,8 +215,8 @@ func (s *Service) GenerateBetOutcome(ctx context.Context, eventID int64, marketI
return newOutcome, nil
}
func (s *Service) PlaceBet(ctx context.Context, req domain.CreateBetReq, userID int64, role domain.Role, companyID domain.ValidInt64) (domain.CreateBetRes, error) {
settingsList, err := s.settingSvc.GetSettingList(ctx)
func (s *Service) PlaceBet(ctx context.Context, req domain.CreateBetReq, userID int64, role domain.Role, companyID int64) (domain.CreateBetRes, error) {
settingsList, err := s.settingSvc.GetOverrideSettingsList(ctx, companyID)
if err != nil {
return domain.CreateBetRes{}, err
@ -297,6 +297,7 @@ func (s *Service) PlaceBet(ctx context.Context, req domain.CreateBetReq, userID
OutcomesHash: outcomesHash,
FastCode: fastCode,
UserID: userID,
CompanyID: companyID,
}
switch role {
@ -347,7 +348,7 @@ func (s *Service) PlaceBet(ctx context.Context, req domain.CreateBetReq, userID
return domain.CreateBetRes{}, err
}
if companyID.Valid && branch.CompanyID == companyID.Value {
if branch.CompanyID == companyID {
s.mongoLogger.Warn("unauthorized company",
zap.Int64("branch_id", *req.BranchID),
zap.Error(err),
@ -477,7 +478,7 @@ func (s *Service) PlaceBet(ctx context.Context, req domain.CreateBetReq, userID
}
}
res := domain.ConvertCreateBet(bet, rows)
res := domain.ConvertCreateBetRes(bet, rows)
return res, nil
}
@ -714,7 +715,7 @@ func (s *Service) GenerateRandomBetOutcomes(ctx context.Context, eventID string,
return newOdds, totalOdds, nil
}
func (s *Service) PlaceRandomBet(ctx context.Context, userID, branchID int64, leagueID domain.ValidInt64, sportID domain.ValidInt32, firstStartTime, lastStartTime domain.ValidTime) (domain.CreateBetRes, error) {
func (s *Service) PlaceRandomBet(ctx context.Context, userID, branchID, companyID int64, leagueID domain.ValidInt64, sportID domain.ValidInt32, firstStartTime, lastStartTime domain.ValidTime) (domain.CreateBetRes, error) {
// Get a unexpired event id
@ -816,6 +817,7 @@ func (s *Service) PlaceRandomBet(ctx context.Context, userID, branchID int64, le
TotalOdds: totalOdds,
Status: domain.OUTCOME_STATUS_PENDING,
UserID: userID,
CompanyID: companyID,
IsShopBet: true,
FastCode: fastCode,
}
@ -842,7 +844,7 @@ func (s *Service) PlaceRandomBet(ctx context.Context, userID, branchID int64, le
return domain.CreateBetRes{}, err
}
res := domain.ConvertCreateBet(bet, rows)
res := domain.ConvertCreateBetRes(bet, rows)
s.mongoLogger.Info("Random bets placed successfully",
zap.Int64("userID", userID),
@ -1310,7 +1312,7 @@ func (s *Service) SetBetToRemoved(ctx context.Context, id int64) error {
}
func (s *Service) ProcessBetCashback(ctx context.Context) error {
settingsList, err := s.settingSvc.GetSettingList(ctx)
bets, err := s.betStore.GetBetsForCashback(ctx)
if err != nil {
s.mongoLogger.Error("failed to fetch bets",
@ -1319,6 +1321,7 @@ func (s *Service) ProcessBetCashback(ctx context.Context) error {
return err
}
for _, bet := range bets {
shouldProcess := true
loseCount := 0
@ -1359,6 +1362,8 @@ func (s *Service) ProcessBetCashback(ctx context.Context) error {
)
continue
}
settingsList, err := s.settingSvc.GetOverrideSettingsList(ctx, bet.CompanyID)
cashbackAmount := math.Min(float64(settingsList.CashbackAmountCap.Float32()), float64(calculateCashbackAmount(bet.Amount.Float32(), bet.TotalOdds)))
_, err = s.walletSvc.AddToWallet(ctx, wallets.StaticID, domain.ToCurrency(float32(cashbackAmount)), domain.ValidInt64{}, domain.TRANSFER_DIRECT,

View File

@ -15,13 +15,25 @@ var (
ErrSMSProviderNotFound = errors.New("SMS Provider Not Found")
)
func (s *Service) SendSMS(ctx context.Context, receiverPhone, message string) error {
// If the company id is valid, it is a company based notification else its a global notification (by the super admin)
func (s *Service) SendSMS(ctx context.Context, receiverPhone, message string, companyID domain.ValidInt64) error {
settingsList, err := s.settingSvc.GetSettingList(ctx)
var settingsList domain.SettingList
var err error
if companyID.Valid {
settingsList, err = s.settingSvc.GetOverrideSettingsList(ctx, companyID.Value)
if err != nil {
// TODO: Send a log about the error
return err
}
} else {
settingsList, err = s.settingSvc.GetGlobalSettingList(ctx)
if err != nil {
// TODO: Send a log about the error
return err
}
}
switch settingsList.SMSProvider {
case domain.AfroMessage:

View File

@ -340,7 +340,7 @@ func (s *Service) SendNotificationSMS(ctx context.Context, recipientID int64, me
if user.PhoneNumber == "" {
return fmt.Errorf("Phone Number is invalid")
}
err = s.messengerSvc.SendSMS(ctx, user.PhoneNumber, message)
err = s.messengerSvc.SendSMS(ctx, user.PhoneNumber, message, user.CompanyID)
if err != nil {
return err
}

View File

@ -15,7 +15,7 @@ type Service interface {
GetPrematchOddsByUpcomingID(ctx context.Context, upcomingID string) ([]domain.OddMarket, error)
GetPaginatedPrematchOddsByUpcomingID(ctx context.Context, upcomingID string, limit domain.ValidInt64, offset domain.ValidInt64) ([]domain.OddMarket, error)
GetALLPrematchOdds(ctx context.Context) ([]domain.OddMarket, error)
GetRawOddsByMarketID(ctx context.Context, marketID string, upcomingID string) (domain.RawOddsByMarketID, error)
// GetRawOddsByMarketID(ctx context.Context, marketID string, upcomingID string) (domain.OddMarket, error)
DeleteOddsForEvent(ctx context.Context, eventID string) error
// Odd History

View File

@ -9,7 +9,7 @@ import (
type ReferralStore interface {
GenerateReferralCode() (string, error)
CreateReferral(ctx context.Context, userID int64) error
ProcessReferral(ctx context.Context, referredID, referralCode string) error
ProcessReferral(ctx context.Context, referredPhone, referralCode string, companyID int64) error
ProcessDepositBonus(ctx context.Context, userID string, amount float64) error
GetReferralStats(ctx context.Context, userID string) (*domain.ReferralStats, error)
CreateReferralSettings(ctx context.Context, req domain.ReferralSettingsReq) error

View File

@ -107,7 +107,7 @@ func (s *Service) CreateReferral(ctx context.Context, userID int64) error {
return nil
}
func (s *Service) ProcessReferral(ctx context.Context, referredPhone, referralCode string) error {
func (s *Service) ProcessReferral(ctx context.Context, referredPhone, referralCode string, companyID int64) error {
s.logger.Info("Processing referral", "referredPhone", referredPhone, "referralCode", referralCode)
referral, err := s.repo.GetReferralByCode(ctx, referralCode)
@ -121,7 +121,10 @@ func (s *Service) ProcessReferral(ctx context.Context, referredPhone, referralCo
return ErrInvalidReferral
}
user, err := s.store.GetUserByPhone(ctx, referredPhone)
user, err := s.store.GetUserByPhone(ctx, referredPhone, domain.ValidInt64{
Value: companyID,
Valid: true,
})
if err != nil {
if errors.Is(err, domain.ErrUserNotFound) {
s.logger.Warn("User not found for referral", "referredPhone", referredPhone)

View File

@ -7,9 +7,18 @@ import (
)
type SettingStore interface {
GetSettingList(ctx context.Context) (domain.SettingList, error)
GetSettings(ctx context.Context) ([]domain.Setting, error)
GetSetting(ctx context.Context, key string) (domain.Setting, error)
UpdateSetting(ctx context.Context, key, value string) error
UpdateSettingList(ctx context.Context, settingList domain.ValidSettingList) error
GetGlobalSettingList(ctx context.Context) (domain.SettingList, error)
GetGlobalSettings(ctx context.Context) ([]domain.Setting, error)
GetGlobalSetting(ctx context.Context, key string) (domain.Setting, error)
UpdateGlobalSetting(ctx context.Context, key, value string) error
UpdateGlobalSettingList(ctx context.Context, settingList domain.ValidSettingList) error
InsertCompanySetting(ctx context.Context, key, value string, companyID int64) error
InsertCompanySettingList(ctx context.Context, settingList domain.ValidSettingList, companyID int64) error
GetAllCompanySettings(ctx context.Context) ([]domain.CompanySetting, error)
GetCompanySettingsByKey(ctx context.Context, key string) ([]domain.CompanySetting, error)
GetOverrideSettings(ctx context.Context, companyID int64) ([]domain.Setting, error)
GetOverrideSettingsList(ctx context.Context, companyID int64) (domain.SettingList, error)
DeleteCompanySetting(ctx context.Context, companyID int64, key string) error
DeleteAllCompanySetting(ctx context.Context, companyID int64) error
}

View File

@ -16,21 +16,48 @@ func NewService(settingStore SettingStore) *Service {
}
}
func (s *Service) GetSettingList(ctx context.Context) (domain.SettingList, error) {
return s.settingStore.GetSettingList(ctx)
func (s *Service) GetGlobalSettingList(ctx context.Context) (domain.SettingList, error) {
return s.settingStore.GetGlobalSettingList(ctx)
}
func (s *Service) GetSettings(ctx context.Context) ([]domain.Setting, error) {
return s.settingStore.GetSettings(ctx)
func (s *Service) GetGlobalSettings(ctx context.Context) ([]domain.Setting, error) {
return s.settingStore.GetGlobalSettings(ctx)
}
func (s *Service) GetSetting(ctx context.Context, key string) (domain.Setting, error) {
return s.settingStore.GetSetting(ctx, key)
func (s *Service) GetGlobalSetting(ctx context.Context, key string) (domain.Setting, error) {
return s.settingStore.GetGlobalSetting(ctx, key)
}
func (s *Service) UpdateSetting(ctx context.Context, key, value string) error {
return s.settingStore.UpdateSetting(ctx, key, value)
func (s *Service) UpdateGlobalSetting(ctx context.Context, key, value string) error {
return s.settingStore.UpdateGlobalSetting(ctx, key, value)
}
func (s *Service) UpdateSettingList(ctx context.Context, settingList domain.ValidSettingList) error {
return s.settingStore.UpdateSettingList(ctx, settingList)
func (s *Service) UpdateGlobalSettingList(ctx context.Context, settingList domain.ValidSettingList) error {
return s.settingStore.UpdateGlobalSettingList(ctx, settingList)
}
func (s *Service) InsertCompanySetting(ctx context.Context, key, value string, companyID int64) error {
return s.settingStore.InsertCompanySetting(ctx, key, value, companyID)
}
func (s *Service) InsertCompanySettingList(ctx context.Context, settingList domain.ValidSettingList, companyID int64) error {
return s.settingStore.InsertCompanySettingList(ctx, settingList, companyID)
}
func (s *Service) GetAllCompanySettings(ctx context.Context) ([]domain.CompanySetting, error) {
return s.settingStore.GetAllCompanySettings(ctx)
}
func (s *Service) GetCompanySettingsByKey(ctx context.Context, key string) ([]domain.CompanySetting, error) {
return s.settingStore.GetCompanySettingsByKey(ctx, key)
}
func (s *Service) GetOverrideSettings(ctx context.Context, companyID int64) ([]domain.Setting, error) {
return s.settingStore.GetOverrideSettings(ctx, companyID)
}
func (s *Service) GetOverrideSettingsList(ctx context.Context, companyID int64) (domain.SettingList, error) {
return s.settingStore.GetOverrideSettingsList(ctx, companyID)
}
func (s *Service) DeleteCompanySetting(ctx context.Context, companyID int64, key string) error {
return s.settingStore.DeleteCompanySetting(ctx, companyID, key)
}
func (s *Service) DeleteAllCompanySetting(ctx context.Context, companyID int64) error {
return s.settingStore.DeleteAllCompanySetting(ctx, companyID)
}

View File

@ -10,7 +10,7 @@ type TicketStore interface {
CreateTicket(ctx context.Context, ticket domain.CreateTicket) (domain.Ticket, error)
CreateTicketOutcome(ctx context.Context, outcomes []domain.CreateTicketOutcome) (int64, error)
GetTicketByID(ctx context.Context, id int64) (domain.GetTicket, error)
GetAllTickets(ctx context.Context) ([]domain.GetTicket, error)
GetAllTickets(ctx context.Context, filter domain.TicketFilter) ([]domain.GetTicket, error)
CountTicketByIP(ctx context.Context, IP string) (int64, error)
UpdateTicketOutcomeStatus(ctx context.Context, id int64, status domain.OutcomeStatus) error
DeleteOldTickets(ctx context.Context) error

View File

@ -80,7 +80,7 @@ func (s *Service) GenerateTicketOutcome(ctx context.Context, settings domain.Set
return domain.CreateTicketOutcome{}, ErrTicketHasExpired
}
odds, err := s.prematchSvc.GetRawOddsByMarketID(ctx, marketIDStr, eventIDStr)
odds, err := s.prematchSvc.GetOddsByMarketID(ctx, marketIDStr, eventIDStr)
if err != nil {
s.mongoLogger.Error("failed to get raw odds by market ID",
@ -158,8 +158,8 @@ func (s *Service) GenerateTicketOutcome(ctx context.Context, settings domain.Set
}
func (s *Service) CreateTicket(ctx context.Context, req domain.CreateTicketReq, clientIP string) (domain.Ticket, int64, error) {
settingsList, err := s.settingSvc.GetSettingList(ctx)
func (s *Service) CreateTicket(ctx context.Context, req domain.CreateTicketReq, clientIP string, companyID int64) (domain.Ticket, int64, error) {
settingsList, err := s.settingSvc.GetOverrideSettingsList(ctx, companyID)
// Check to see if the number of outcomes is above a set limit
if len(req.Outcomes) > int(settingsList.MaxNumberOfOutcomes) {
@ -221,6 +221,7 @@ func (s *Service) CreateTicket(ctx context.Context, req domain.CreateTicketReq,
Amount: domain.ToCurrency(req.Amount),
TotalOdds: totalOdds,
IP: clientIP,
CompanyID: companyID,
})
if err != nil {
s.mongoLogger.Error("Error Creating Ticket", zap.Float32("Total Odds", totalOdds), zap.Float32("amount", req.Amount))
@ -261,8 +262,8 @@ func (s *Service) CreateTicketOutcome(ctx context.Context, outcomes []domain.Cre
func (s *Service) GetTicketByID(ctx context.Context, id int64) (domain.GetTicket, error) {
return s.ticketStore.GetTicketByID(ctx, id)
}
func (s *Service) GetAllTickets(ctx context.Context) ([]domain.GetTicket, error) {
return s.ticketStore.GetAllTickets(ctx)
func (s *Service) GetAllTickets(ctx context.Context, filter domain.TicketFilter) ([]domain.GetTicket, error) {
return s.ticketStore.GetAllTickets(ctx, filter)
}
func (s *Service) CountTicketByIP(ctx context.Context, IP string) (int64, error) {

View File

@ -155,7 +155,7 @@ func (s *Service) GetBranchByRole(ctx context.Context, branchID *int64, role dom
// Check if the user has access to the company
if role != domain.RoleSuperAdmin {
if !userCompanyID.Valid || userCompanyID.Value != branch.CompanyID {
if userCompanyID.Valid && userCompanyID.Value != branch.CompanyID {
return nil, nil, ErrUnauthorizedCompanyID
}
}

View File

@ -52,7 +52,7 @@ func (s *Service) CreateShopBet(ctx context.Context, userID int64, role domain.R
newBet, err := s.betSvc.PlaceBet(ctx, domain.CreateBetReq{
Outcomes: req.Outcomes,
Amount: req.Amount,
}, userID, role, userCompanyID)
}, userID, role, *companyID)
if err != nil {
return domain.ShopBet{}, err

View File

@ -261,7 +261,7 @@ func (s *service) ProcessWin(ctx context.Context, req domain.WinRequest) (*domai
// --- 1. Validate PlayerID ---
playerIDInt64, err := strconv.ParseInt(req.PlayerID, 10, 64)
if err != nil {
z`` return nil, fmt.Errorf("BAD_REQUEST: invalid PlayerID %s", req.PlayerID)
return nil, fmt.Errorf("BAD_REQUEST: invalid PlayerID %s", req.PlayerID)
}
// --- 2. Get player wallets ---

View File

@ -42,7 +42,7 @@ type loginCustomerRes struct {
func (h *Handler) LoginCustomer(c *fiber.Ctx) error {
companyID := c.Locals("company_id").(domain.ValidInt64)
if !companyID.Valid {
domain.BadRequestLogger.Error("invalid company id")
h.BadRequestLogger().Error("invalid company id")
return fiber.NewError(fiber.StatusBadRequest, "invalid company id")
}
var req loginCustomerReq
@ -163,7 +163,7 @@ type loginAdminRes struct {
func (h *Handler) LoginAdmin(c *fiber.Ctx) error {
companyID := c.Locals("company_id").(domain.ValidInt64)
if !companyID.Valid {
domain.BadRequestLogger.Error("invalid company id")
h.BadRequestLogger().Error("invalid company id")
return fiber.NewError(fiber.StatusBadRequest, "invalid company id")
}
var req loginAdminReq

View File

@ -23,11 +23,16 @@ import (
// @Success 200 {object} domain.BetRes
// @Failure 400 {object} response.APIResponse
// @Failure 500 {object} response.APIResponse
// @Router /api/v1/sport/bet [post]
// @Router /api/v1/{tenant_slug}/sport/bet [post]
func (h *Handler) CreateBet(c *fiber.Ctx) error {
companyID := c.Locals("company_id").(domain.ValidInt64)
if !companyID.Valid {
h.BadRequestLogger().Error("invalid company id")
return fiber.NewError(fiber.StatusBadRequest, "invalid company id")
}
userID := c.Locals("user_id").(int64)
role := c.Locals("role").(domain.Role)
companyID := c.Locals("company_id").(domain.ValidInt64)
var req domain.CreateBetReq
if err := c.BodyParser(&req); err != nil {
@ -39,7 +44,7 @@ func (h *Handler) CreateBet(c *fiber.Ctx) error {
return fiber.NewError(fiber.StatusBadRequest, "Invalid request body:"+err.Error())
}
res, err := h.CreateBetInternal(c, req, userID, role, companyID)
res, err := h.CreateBetInternal(c, req, userID, role, companyID.Value)
if err != nil {
h.mongoLoggerSvc.Error("Failed to create bet",
zap.Int("status_code", fiber.StatusInternalServerError),
@ -70,11 +75,15 @@ func (h *Handler) CreateBet(c *fiber.Ctx) error {
// @Success 200 {object} domain.BetRes
// @Failure 400 {object} response.APIResponse
// @Failure 500 {object} response.APIResponse
// @Router /api/v1/sport/bet/fastcode [post]
// @Router /api/v1/{tenant_slug}/sport/bet/fastcode [post]
func (h *Handler) CreateBetWithFastCode(c *fiber.Ctx) error {
companyID := c.Locals("company_id").(domain.ValidInt64)
if !companyID.Valid {
h.BadRequestLogger().Error("invalid company id")
return fiber.NewError(fiber.StatusBadRequest, "invalid company id")
}
userID := c.Locals("user_id").(int64)
role := c.Locals("role").(domain.Role)
companyID := c.Locals("company_id").(domain.ValidInt64)
var req domain.CreateBetWithFastCodeReq
@ -136,7 +145,7 @@ func (h *Handler) CreateBetWithFastCode(c *fiber.Ctx) error {
BranchID: req.BranchID,
}
res, err := h.CreateBetInternal(c, newReq, userID, role, companyID)
res, err := h.CreateBetInternal(c, newReq, userID, role, companyID.Value)
if err != nil {
h.mongoLoggerSvc.Error("Failed to create bet",
zap.Int("status_code", fiber.StatusInternalServerError),
@ -152,7 +161,7 @@ func (h *Handler) CreateBetWithFastCode(c *fiber.Ctx) error {
wallet, err := h.walletSvc.GetCustomerWallet(c.Context(), bet.UserID)
// amount added for fast code owner can be fetched from settings in db
settingList, err := h.settingSvc.GetSettingList(c.Context())
settingList, err := h.settingSvc.GetOverrideSettingsList(c.Context(), companyID.Value)
amount := settingList.AmountForBetReferral
_, err = h.walletSvc.AddToWallet(c.Context(), wallet.StaticID, amount, domain.ValidInt64{},
@ -177,7 +186,7 @@ func (h *Handler) CreateBetWithFastCode(c *fiber.Ctx) error {
return response.WriteJSON(c, fiber.StatusOK, "Bet Created", res, nil)
}
func (h *Handler) CreateBetInternal(c *fiber.Ctx, req domain.CreateBetReq, userID int64, role domain.Role, companyID domain.ValidInt64) (domain.CreateBetRes, error) {
func (h *Handler) CreateBetInternal(c *fiber.Ctx, req domain.CreateBetReq, userID int64, role domain.Role, companyID int64) (domain.CreateBetRes, error) {
valErrs, ok := h.validator.Validate(c, req)
if !ok {
h.mongoLoggerSvc.Error("CreateBet validation failed",
@ -195,7 +204,7 @@ func (h *Handler) CreateBetInternal(c *fiber.Ctx, req domain.CreateBetReq, userI
h.mongoLoggerSvc.Info("PlaceBet failed",
zap.Int("status_code", fiber.StatusBadRequest),
zap.Int64("userID", userID),
zap.Int64("companyID", companyID.Value),
zap.Int64("companyID", companyID),
zap.String("role", string(role)),
zap.Error(err),
zap.Time("timestamp", time.Now()),
@ -206,7 +215,7 @@ func (h *Handler) CreateBetInternal(c *fiber.Ctx, req domain.CreateBetReq, userI
h.mongoLoggerSvc.Error("PlaceBet failed",
zap.Int("status_code", fiber.StatusInternalServerError),
zap.Int64("userID", userID),
zap.Int64("companyID", companyID.Value),
zap.Int64("companyID", companyID),
zap.String("role", string(role)),
zap.Error(err),
zap.Time("timestamp", time.Now()),
@ -228,11 +237,17 @@ func (h *Handler) CreateBetInternal(c *fiber.Ctx, req domain.CreateBetReq, userI
// @Success 200 {object} domain.BetRes
// @Failure 400 {object} response.APIResponse
// @Failure 500 {object} response.APIResponse
// @Router /api/v1/sport/random/bet [post]
// @Router /api/v1/{tenant_slug}/sport/random/bet [post]
func (h *Handler) RandomBet(c *fiber.Ctx) error {
companyID := c.Locals("company_id").(domain.ValidInt64)
if !companyID.Valid {
h.BadRequestLogger().Error("invalid company id")
return fiber.NewError(fiber.StatusBadRequest, "invalid company id")
}
userID := c.Locals("user_id").(int64)
leagueIDQuery, err := strconv.Atoi(c.Query("league_id"))
leagueIDQuery, err := strconv.ParseInt(c.Query("league_id"), 10, 64)
if err != nil {
h.mongoLoggerSvc.Info("invalid league id",
zap.Int("status_code", fiber.StatusBadRequest),
@ -255,8 +270,8 @@ func (h *Handler) RandomBet(c *fiber.Ctx) error {
firstStartTimeQuery := c.Query("first_start_time")
lastStartTimeQuery := c.Query("last_start_time")
leagueID := domain.ValidInt32{
Value: int32(leagueIDQuery),
leagueID := domain.ValidInt64{
Value: leagueIDQuery,
Valid: leagueIDQuery != 0,
}
sportID := domain.ValidInt32{
@ -326,7 +341,7 @@ func (h *Handler) RandomBet(c *fiber.Ctx) error {
var res domain.CreateBetRes
for i := 0; i < int(req.NumberOfBets); i++ {
res, err = h.betSvc.PlaceRandomBet(c.Context(), userID, req.BranchID, leagueID, sportID, firstStartTime, lastStartTime)
res, err = h.betSvc.PlaceRandomBet(c.Context(), userID, req.BranchID, companyID.Value, leagueID, sportID, firstStartTime, lastStartTime)
if err != nil {
switch err {
case bet.ErrNoEventsAvailable:
@ -362,8 +377,13 @@ func (h *Handler) RandomBet(c *fiber.Ctx) error {
// @Success 200 {array} domain.BetRes
// @Failure 400 {object} response.APIResponse
// @Failure 500 {object} response.APIResponse
// @Router /api/v1/sport/bet [get]
// @Router /api/v1/{tenant_slug}/sport/bet [get]
func (h *Handler) GetAllBet(c *fiber.Ctx) error {
companyID := c.Locals("company_id").(domain.ValidInt64)
if !companyID.Valid {
h.BadRequestLogger().Error("invalid company id")
return fiber.NewError(fiber.StatusBadRequest, "invalid company id")
}
role := c.Locals("role").(domain.Role)
// companyID := c.Locals("company_id").(domain.ValidInt64)
// branchID := c.Locals("branch_id").(domain.ValidInt64)
@ -432,6 +452,7 @@ func (h *Handler) GetAllBet(c *fiber.Ctx) error {
}
bets, err := h.betSvc.GetAllBets(c.Context(), domain.BetFilter{
CompanyID: companyID,
IsShopBet: isShopBet,
Query: searchString,
CreatedBefore: createdBefore,
@ -464,8 +485,13 @@ func (h *Handler) GetAllBet(c *fiber.Ctx) error {
// @Success 200 {object} domain.BetRes
// @Failure 400 {object} response.APIResponse
// @Failure 500 {object} response.APIResponse
// @Router /api/v1/sport/bet/{id} [get]
// @Router /api/v1/{tenant_slug}/sport/bet/{id} [get]
func (h *Handler) GetBetByID(c *fiber.Ctx) error {
companyID := c.Locals("company_id").(domain.ValidInt64)
if !companyID.Valid {
h.BadRequestLogger().Error("invalid company id")
return fiber.NewError(fiber.StatusBadRequest, "invalid company id")
}
betID := c.Params("id")
id, err := strconv.ParseInt(betID, 10, 64)
if err != nil {
@ -489,6 +515,16 @@ func (h *Handler) GetBetByID(c *fiber.Ctx) error {
return fiber.NewError(fiber.StatusNotFound, "Failed to retrieve bet")
}
if bet.CompanyID != companyID.Value {
h.mongoLoggerSvc.Warn("User Attempt to access another company bet",
zap.Int64("betID", id),
zap.Int("status_code", fiber.StatusNotFound),
zap.Error(err),
zap.Time("timestamp", time.Now()),
)
return fiber.NewError(fiber.StatusNotFound, "Failed to retrieve bet")
}
res := domain.ConvertBet(bet)
// h.mongoLoggerSvc.Info("Bet retrieved successfully",
@ -510,8 +546,14 @@ func (h *Handler) GetBetByID(c *fiber.Ctx) error {
// @Success 200 {object} domain.BetRes
// @Failure 400 {object} response.APIResponse
// @Failure 500 {object} response.APIResponse
// @Router /api/v1/sport/bet/fastcode/{fast_code} [get]
// @Router /api/v1/{tenant_slug}/sport/bet/fastcode/{fast_code} [get]
func (h *Handler) GetBetByFastCode(c *fiber.Ctx) error {
companyID := c.Locals("company_id").(domain.ValidInt64)
if !companyID.Valid {
h.BadRequestLogger().Error("invalid company id")
return fiber.NewError(fiber.StatusBadRequest, "invalid company id")
}
fastCode := c.Params("fast_code")
bet, err := h.betSvc.GetBetByFastCode(c.Context(), fastCode)
@ -525,6 +567,15 @@ func (h *Handler) GetBetByFastCode(c *fiber.Ctx) error {
return fiber.NewError(fiber.StatusNotFound, "Failed to find bet by fast code")
}
if bet.CompanyID != companyID.Value {
h.mongoLoggerSvc.Warn("User Attempt to access another company bet",
zap.String("fast_code", fastCode),
zap.Int("status_code", fiber.StatusNotFound),
zap.Error(err),
zap.Time("timestamp", time.Now()),
)
return fiber.NewError(fiber.StatusNotFound, "Failed to retrieve bet")
}
res := domain.ConvertBet(bet)
// h.mongoLoggerSvc.Info("Bet retrieved successfully",
@ -551,8 +602,14 @@ type UpdateCashOutReq struct {
// @Success 200 {object} response.APIResponse
// @Failure 400 {object} response.APIResponse
// @Failure 500 {object} response.APIResponse
// @Router /api/v1/sport/bet/{id} [patch]
// @Router /api/v1/{tenant_slug}/sport/bet/{id} [patch]
func (h *Handler) UpdateCashOut(c *fiber.Ctx) error {
companyID := c.Locals("company_id").(domain.ValidInt64)
if !companyID.Valid {
h.BadRequestLogger().Error("invalid company id")
return fiber.NewError(fiber.StatusBadRequest, "invalid company id")
}
type UpdateCashOutReq struct {
CashedOut bool `json:"cashed_out" validate:"required" example:"true"`
}
@ -588,6 +645,27 @@ func (h *Handler) UpdateCashOut(c *fiber.Ctx) error {
return fiber.NewError(fiber.StatusBadRequest, errMsg)
}
bet, err := h.betSvc.GetBetByID(c.Context(), id)
if err != nil {
h.mongoLoggerSvc.Info("Failed to get bet",
zap.Int64("betID", id),
zap.Int("status_code", fiber.StatusNotFound),
zap.Error(err),
zap.Time("timestamp", time.Now()),
)
return fiber.NewError(fiber.StatusNotFound, "Failed to retrieve bet")
}
if bet.CompanyID != companyID.Value {
h.mongoLoggerSvc.Warn("User Attempt to access another company bet",
zap.Int64("betID", id),
zap.Int("status_code", fiber.StatusNotFound),
zap.Error(err),
zap.Time("timestamp", time.Now()),
)
return fiber.NewError(fiber.StatusNotFound, "Failed to retrieve bet")
}
err = h.betSvc.UpdateCashOut(c.Context(), id, req.CashedOut)
if err != nil {
h.mongoLoggerSvc.Error("Failed to update cash out bet",
@ -618,8 +696,14 @@ func (h *Handler) UpdateCashOut(c *fiber.Ctx) error {
// @Success 200 {object} response.APIResponse
// @Failure 400 {object} response.APIResponse
// @Failure 500 {object} response.APIResponse
// @Router /api/v1/sport/bet/{id} [delete]
// @Router /api/v1/{tenant_slug}/sport/bet/{id} [delete]
func (h *Handler) DeleteBet(c *fiber.Ctx) error {
companyID := c.Locals("company_id").(domain.ValidInt64)
if !companyID.Valid {
h.BadRequestLogger().Error("invalid company id")
return fiber.NewError(fiber.StatusBadRequest, "invalid company id")
}
betID := c.Params("id")
id, err := strconv.ParseInt(betID, 10, 64)
if err != nil {
@ -632,6 +716,28 @@ func (h *Handler) DeleteBet(c *fiber.Ctx) error {
return fiber.NewError(fiber.StatusBadRequest, "Invalid bet ID")
}
// This is to make sure that you can remove a bet only from the right route
bet, err := h.betSvc.GetBetByID(c.Context(), id)
if err != nil {
h.mongoLoggerSvc.Info("Failed to get bet",
zap.Int64("betID", id),
zap.Int("status_code", fiber.StatusNotFound),
zap.Error(err),
zap.Time("timestamp", time.Now()),
)
return fiber.NewError(fiber.StatusNotFound, "Failed to retrieve bet")
}
if bet.CompanyID != companyID.Value {
h.mongoLoggerSvc.Warn("User Attempt to access another company bet",
zap.Int64("betID", id),
zap.Int("status_code", fiber.StatusNotFound),
zap.Error(err),
zap.Time("timestamp", time.Now()),
)
return fiber.NewError(fiber.StatusNotFound, "Failed to retrieve bet")
}
err = h.betSvc.SetBetToRemoved(c.Context(), id)
if err != nil {
h.mongoLoggerSvc.Error("Failed to delete bet by ID",

View File

@ -0,0 +1,25 @@
package handlers
import (
"time"
"github.com/gofiber/fiber/v2"
"go.uber.org/zap"
)
func (h *Handler) BadRequestLogger() *zap.Logger {
return h.mongoLoggerSvc.With(
zap.Int("status_code", fiber.StatusBadRequest),
zap.Time("timestamp", time.Now()))
}
func (h *Handler) InternalServerErrorLogger() *zap.Logger {
return h.mongoLoggerSvc.With(
zap.Int("status_code", fiber.StatusInternalServerError),
zap.Time("timestamp", time.Now()))
}
func (h *Handler) SuccessResLogger() *zap.Logger {
return h.mongoLoggerSvc.With(
zap.Int("status_code", fiber.StatusOK),
zap.Time("timestamp", time.Now()),
)
}

View File

@ -43,7 +43,7 @@ func (h *Handler) GetAllUpcomingEvents(c *fiber.Ctx) error {
if leagueIDQuery != "" {
leagueIDInt, err := strconv.ParseInt(leagueIDQuery, 10, 64)
if err != nil {
domain.BadRequestLogger.Error("invalid league id",
h.BadRequestLogger().Error("invalid league id",
zap.String("league_id", leagueIDQuery),
zap.Error(err),
)
@ -59,7 +59,7 @@ func (h *Handler) GetAllUpcomingEvents(c *fiber.Ctx) error {
if sportIDQuery != "" {
sportIDint, err := strconv.Atoi(sportIDQuery)
if err != nil {
domain.BadRequestLogger.Info("invalid sport id",
h.BadRequestLogger().Info("invalid sport id",
zap.String("sportID", sportIDQuery),
zap.Error(err),
)
@ -82,7 +82,7 @@ func (h *Handler) GetAllUpcomingEvents(c *fiber.Ctx) error {
if firstStartTimeQuery != "" {
firstStartTimeParsed, err := time.Parse(time.RFC3339, firstStartTimeQuery)
if err != nil {
domain.BadRequestLogger.Info("invalid start_time format",
h.BadRequestLogger().Info("invalid start_time format",
zap.String("first_start_time", firstStartTimeQuery),
zap.Error(err),
)
@ -99,7 +99,7 @@ func (h *Handler) GetAllUpcomingEvents(c *fiber.Ctx) error {
if lastStartTimeQuery != "" {
lastStartTimeParsed, err := time.Parse(time.RFC3339, lastStartTimeQuery)
if err != nil {
domain.BadRequestLogger.Info("invalid last_start_time format",
h.BadRequestLogger().Info("invalid last_start_time format",
zap.String("last_start_time", lastStartTimeQuery),
zap.Error(err),
)
@ -122,7 +122,7 @@ func (h *Handler) GetAllUpcomingEvents(c *fiber.Ctx) error {
if isFeaturedQuery != "" {
isFeaturedParsed, err := strconv.ParseBool(isFeaturedQuery)
if err != nil {
domain.BadRequestLogger.Error("Failed to parse isFeatured",
h.BadRequestLogger().Error("Failed to parse isFeatured",
zap.String("is_featured", isFeaturedQuery),
zap.Error(err),
)
@ -150,7 +150,7 @@ func (h *Handler) GetAllUpcomingEvents(c *fiber.Ctx) error {
// fmt.Printf("League ID: %v", leagueID)
if err != nil {
domain.InternalServerErrorLogger.Error("Failed to retrieve all upcoming events",
h.InternalServerErrorLogger().Error("Failed to retrieve all upcoming events",
zap.Error(err),
)
return fiber.NewError(fiber.StatusInternalServerError, err.Error())
@ -180,7 +180,7 @@ func (h *Handler) GetAllUpcomingEvents(c *fiber.Ctx) error {
func (h *Handler) GetTenantUpcomingEvents(c *fiber.Ctx) error {
companyID := c.Locals("company_id").(domain.ValidInt64)
if !companyID.Valid {
domain.BadRequestLogger.Error("invalid company id")
h.BadRequestLogger().Error("invalid company id")
return fiber.NewError(fiber.StatusBadRequest, "invalid company id")
}
@ -200,7 +200,7 @@ func (h *Handler) GetTenantUpcomingEvents(c *fiber.Ctx) error {
if leagueIDQuery != "" {
leagueIDInt, err := strconv.ParseInt(leagueIDQuery, 10, 64)
if err != nil {
domain.BadRequestLogger.Error("invalid league id",
h.BadRequestLogger().Error("invalid league id",
zap.String("league_id", leagueIDQuery),
zap.Error(err),
)
@ -216,7 +216,7 @@ func (h *Handler) GetTenantUpcomingEvents(c *fiber.Ctx) error {
if sportIDQuery != "" {
sportIDint, err := strconv.Atoi(sportIDQuery)
if err != nil {
domain.BadRequestLogger.Info("invalid sport id",
h.BadRequestLogger().Info("invalid sport id",
zap.String("sportID", sportIDQuery),
zap.Error(err),
)
@ -239,7 +239,7 @@ func (h *Handler) GetTenantUpcomingEvents(c *fiber.Ctx) error {
if firstStartTimeQuery != "" {
firstStartTimeParsed, err := time.Parse(time.RFC3339, firstStartTimeQuery)
if err != nil {
domain.BadRequestLogger.Info("invalid start_time format",
h.BadRequestLogger().Info("invalid start_time format",
zap.String("first_start_time", firstStartTimeQuery),
zap.Error(err),
)
@ -256,7 +256,7 @@ func (h *Handler) GetTenantUpcomingEvents(c *fiber.Ctx) error {
if lastStartTimeQuery != "" {
lastStartTimeParsed, err := time.Parse(time.RFC3339, lastStartTimeQuery)
if err != nil {
domain.BadRequestLogger.Info("invalid last_start_time format",
h.BadRequestLogger().Info("invalid last_start_time format",
zap.String("last_start_time", lastStartTimeQuery),
zap.Error(err),
)
@ -279,7 +279,7 @@ func (h *Handler) GetTenantUpcomingEvents(c *fiber.Ctx) error {
if isFeaturedQuery != "" {
isFeaturedParsed, err := strconv.ParseBool(isFeaturedQuery)
if err != nil {
domain.BadRequestLogger.Error("Failed to parse isFeatured",
h.BadRequestLogger().Error("Failed to parse isFeatured",
zap.String("is_featured", isFeaturedQuery),
zap.Error(err),
)
@ -307,7 +307,7 @@ func (h *Handler) GetTenantUpcomingEvents(c *fiber.Ctx) error {
// fmt.Printf("League ID: %v", leagueID)
if err != nil {
domain.InternalServerErrorLogger.Error("Failed to retrieve all upcoming events",
h.InternalServerErrorLogger().Error("Failed to retrieve all upcoming events",
zap.Error(err),
)
return fiber.NewError(fiber.StatusInternalServerError, err.Error())
@ -343,7 +343,7 @@ type TopLeague struct {
func (h *Handler) GetTopLeagues(c *fiber.Ctx) error {
companyID := c.Locals("company_id").(domain.ValidInt64)
if !companyID.Valid {
domain.BadRequestLogger.Error("invalid company id")
h.BadRequestLogger().Error("invalid company id")
return fiber.NewError(fiber.StatusBadRequest, "invalid company id")
}
@ -355,7 +355,7 @@ func (h *Handler) GetTopLeagues(c *fiber.Ctx) error {
})
if err != nil {
domain.InternalServerErrorLogger.Error("Error while fetching top leagues",
h.InternalServerErrorLogger().Error("Error while fetching top leagues",
zap.Int64("company_id", companyID.Value),
zap.Error(err),
)
@ -372,7 +372,7 @@ func (h *Handler) GetTopLeagues(c *fiber.Ctx) error {
},
})
if err != nil {
domain.InternalServerErrorLogger.Warn("Error while fetching events for top league",
h.InternalServerErrorLogger().Warn("Error while fetching events for top league",
zap.Int64("LeagueID", league.ID),
zap.Int64("company_id", companyID.Value),
zap.Error(err),
@ -408,13 +408,13 @@ func (h *Handler) GetUpcomingEventByID(c *fiber.Ctx) error {
id := c.Params("id")
if id == "" {
domain.BadRequestLogger.Info("Failed to parse event id", zap.String("id", id))
h.BadRequestLogger().Info("Failed to parse event id", zap.String("id", id))
return fiber.NewError(fiber.StatusBadRequest, "Missing id")
}
event, err := h.eventSvc.GetUpcomingEventByID(c.Context(), id)
if err != nil {
domain.InternalServerErrorLogger.Error("Failed to get upcoming event by id",
h.InternalServerErrorLogger().Error("Failed to get upcoming event by id",
zap.String("eventID", id),
zap.Error(err),
)
@ -440,19 +440,19 @@ func (h *Handler) GetUpcomingEventByID(c *fiber.Ctx) error {
func (h *Handler) GetTenantEventByID(c *fiber.Ctx) error {
companyID := c.Locals("company_id").(domain.ValidInt64)
if !companyID.Valid {
domain.BadRequestLogger.Error("invalid company id")
h.BadRequestLogger().Error("invalid company id")
return fiber.NewError(fiber.StatusBadRequest, "invalid company id")
}
id := c.Params("id")
if id == "" {
domain.BadRequestLogger.Info("Failed to parse event id", zap.String("id", id))
h.BadRequestLogger().Info("Failed to parse event id", zap.String("id", id))
return fiber.NewError(fiber.StatusBadRequest, "Missing id")
}
event, err := h.eventSvc.GetEventWithSettingByID(c.Context(), id, companyID.Value)
if err != nil {
domain.InternalServerErrorLogger.Error("Failed to get upcoming event by id",
h.InternalServerErrorLogger().Error("Failed to get upcoming event by id",
zap.String("eventID", id),
zap.Error(err),
)
@ -484,7 +484,7 @@ func (h *Handler) SetEventStatusToRemoved(c *fiber.Ctx) error {
err := h.eventSvc.UpdateEventStatus(c.Context(), eventID, domain.STATUS_REMOVED)
if err != nil {
domain.InternalServerErrorLogger.Error("Failed to update event status",
h.InternalServerErrorLogger().Error("Failed to update event status",
zap.String("EventID", eventID),
zap.Error(err),
)
@ -515,7 +515,7 @@ type UpdateEventSettingsReq struct {
func (h *Handler) UpdateEventSettings(c *fiber.Ctx) error {
companyID := c.Locals("company_id").(domain.ValidInt64)
if !companyID.Valid {
domain.BadRequestLogger.Error("invalid company id")
h.BadRequestLogger().Error("invalid company id")
return fiber.NewError(fiber.StatusBadRequest, "invalid company id")
}
@ -523,7 +523,7 @@ func (h *Handler) UpdateEventSettings(c *fiber.Ctx) error {
var req UpdateEventSettingsReq
if err := c.BodyParser(&req); err != nil {
domain.BadRequestLogger.Info("Failed to parse user id",
h.BadRequestLogger().Info("Failed to parse user id",
zap.String("eventID", eventID),
zap.Error(err),
)
@ -543,7 +543,7 @@ func (h *Handler) UpdateEventSettings(c *fiber.Ctx) error {
for field, msg := range valErrs {
errMsg += fmt.Sprintf("%s: %s; ", field, msg)
}
domain.BadRequestLogger.Error("Failed to update event featured",
h.BadRequestLogger().Error("Failed to update event featured",
append(logFields, zap.String("errMsg", errMsg))...,
)
return fiber.NewError(fiber.StatusBadRequest, errMsg)
@ -557,7 +557,7 @@ func (h *Handler) UpdateEventSettings(c *fiber.Ctx) error {
})
if err != nil {
domain.InternalServerErrorLogger.Error("Failed to update event featured", append(logFields, zap.Error(err))...)
h.InternalServerErrorLogger().Error("Failed to update event featured", append(logFields, zap.Error(err))...)
return fiber.NewError(fiber.StatusInternalServerError, err.Error())
}

View File

@ -50,7 +50,7 @@ func (h *Handler) GetAllLeagues(c *fiber.Ctx) error {
if sportIDQuery != "" {
sportIDint, err := strconv.Atoi(sportIDQuery)
if err != nil {
domain.BadRequestLogger.Info("invalid sport id",
h.BadRequestLogger().Info("invalid sport id",
zap.String("sport_id", sportIDQuery),
zap.Error(err),
)
@ -89,7 +89,7 @@ func (h *Handler) GetAllLeagues(c *fiber.Ctx) error {
if err != nil {
fmt.Printf("Error fetching league %v \n", err)
domain.InternalServerErrorLogger.Error("Failed to get all leagues", append(queryLogFields, zap.Error(err))...,
h.InternalServerErrorLogger().Error("Failed to get all leagues", append(queryLogFields, zap.Error(err))...,
)
return fiber.NewError(fiber.StatusInternalServerError, "Failed to get leagues:"+err.Error())
}
@ -109,7 +109,7 @@ func (h *Handler) GetAllLeagues(c *fiber.Ctx) error {
func (h *Handler) GetAllLeaguesForTenant(c *fiber.Ctx) error {
companyID := c.Locals("company_id").(domain.ValidInt64)
if !companyID.Valid {
domain.BadRequestLogger.Error("invalid company id")
h.BadRequestLogger().Error("invalid company id")
return fiber.NewError(fiber.StatusBadRequest, "invalid company id")
}
@ -142,7 +142,7 @@ func (h *Handler) GetAllLeaguesForTenant(c *fiber.Ctx) error {
if sportIDQuery != "" {
sportIDint, err := strconv.Atoi(sportIDQuery)
if err != nil {
domain.BadRequestLogger.Info("invalid sport id",
h.BadRequestLogger().Info("invalid sport id",
zap.String("sport_id", sportIDQuery),
zap.Error(err),
)
@ -183,7 +183,7 @@ func (h *Handler) GetAllLeaguesForTenant(c *fiber.Ctx) error {
if err != nil {
fmt.Printf("Error fetching league %v \n", err)
domain.InternalServerErrorLogger.Error("Failed to get all leagues", append(queryLogFields, zap.Error(err))...)
h.InternalServerErrorLogger().Error("Failed to get all leagues", append(queryLogFields, zap.Error(err))...)
return fiber.NewError(fiber.StatusInternalServerError, "Failed to get leagues:"+err.Error())
}
return response.WriteJSON(c, fiber.StatusOK, "All leagues retrieved", leagues, nil)
@ -228,7 +228,7 @@ func (h *Handler) SetLeagueActive(c *fiber.Ctx) error {
var req SetLeagueActiveReq
if err := c.BodyParser(&req); err != nil {
domain.InternalServerErrorLogger.Error("SetLeagueReq failed to parse request body",
h.InternalServerErrorLogger().Error("SetLeagueReq failed to parse request body",
append(queryLogFields, zap.Error(err))...,
)
return fiber.NewError(fiber.StatusBadRequest, "Failed to parse request:"+err.Error())
@ -241,7 +241,7 @@ func (h *Handler) SetLeagueActive(c *fiber.Ctx) error {
for field, msg := range valErrs {
errMsg += fmt.Sprintf("%s: %s; ", field, msg)
}
domain.BadRequestLogger.Info("Failed to validate SetLeagueActiveReq", append(queryLogFields, zap.Error(err))...)
h.BadRequestLogger().Info("Failed to validate SetLeagueActiveReq", append(queryLogFields, zap.Error(err))...)
return fiber.NewError(fiber.StatusBadRequest, errMsg)
}
@ -253,11 +253,11 @@ func (h *Handler) SetLeagueActive(c *fiber.Ctx) error {
Valid: true,
},
}); err != nil {
domain.InternalServerErrorLogger.Error("Failed to update league active", append(queryLogFields, zap.Error(err))...)
h.InternalServerErrorLogger().Error("Failed to update league active", append(queryLogFields, zap.Error(err))...)
return fiber.NewError(fiber.StatusInternalServerError, "Failed to update league:"+err.Error())
}
domain.SuccessResLogger.Info("League Active has been successfully updated", queryLogFields...)
h.SuccessResLogger().Info("League Active has been successfully updated", queryLogFields...)
return response.WriteJSON(c, fiber.StatusOK, "League updated successfully", nil, nil)
}
@ -300,7 +300,7 @@ func (h *Handler) SetLeagueFeatured(c *fiber.Ctx) error {
}
var req SetLeagueAsFeatured
if err := c.BodyParser(&req); err != nil {
domain.BadRequestLogger.Info("SetLeagueFeaturedReq failed to parse request body", append(queryLogFields, zap.Error(err))...)
h.BadRequestLogger().Info("SetLeagueFeaturedReq failed to parse request body", append(queryLogFields, zap.Error(err))...)
return fiber.NewError(fiber.StatusBadRequest, "Failed to parse request body:"+err.Error())
}
@ -311,7 +311,7 @@ func (h *Handler) SetLeagueFeatured(c *fiber.Ctx) error {
for field, msg := range valErrs {
errMsg += fmt.Sprintf("%s: %s; ", field, msg)
}
domain.BadRequestLogger.Info("Failed to validate SetLeagueFeaturedReq", append(queryLogFields, zap.Error(err))...)
h.BadRequestLogger().Info("Failed to validate SetLeagueFeaturedReq", append(queryLogFields, zap.Error(err))...)
return fiber.NewError(fiber.StatusBadRequest, errMsg)
}
@ -324,10 +324,10 @@ func (h *Handler) SetLeagueFeatured(c *fiber.Ctx) error {
},
})
if err != nil {
domain.InternalServerErrorLogger.Error("Failed to update league", append(queryLogFields, zap.Error(err))...)
h.InternalServerErrorLogger().Error("Failed to update league", append(queryLogFields, zap.Error(err))...)
return fiber.NewError(fiber.StatusInternalServerError, "Failed to update league:"+err.Error())
}
domain.SuccessResLogger.Info("League Featured has been successfully updated", queryLogFields...)
h.SuccessResLogger().Info("League Featured has been successfully updated", queryLogFields...)
return response.WriteJSON(c, fiber.StatusOK, "League updated successfully", nil, nil)
}

View File

@ -21,13 +21,13 @@ import (
func (h *Handler) GetAllOdds(c *fiber.Ctx) error {
limit, err := strconv.Atoi(c.Query("limit", "10")) // Default limit is 10
if err != nil || limit <= 0 {
domain.BadRequestLogger.Info("Invalid limit value", zap.Error(err))
h.BadRequestLogger().Info("Invalid limit value", zap.Error(err))
return fiber.NewError(fiber.StatusBadRequest, "Invalid limit value")
}
offset, err := strconv.Atoi(c.Query("offset", "0")) // Default offset is 0
if err != nil || offset < 0 {
domain.BadRequestLogger.Info("Invalid offset value", zap.Error(err))
h.BadRequestLogger().Info("Invalid offset value", zap.Error(err))
return fiber.NewError(fiber.StatusBadRequest, err.Error())
}
@ -42,7 +42,7 @@ func (h *Handler) GetAllOdds(c *fiber.Ctx) error {
},
})
if err != nil {
domain.InternalServerErrorLogger.Error("Failed to retrieve all odds",
h.InternalServerErrorLogger().Error("Failed to retrieve all odds",
zap.Int("limit", limit),
zap.Int("offset", offset),
zap.Error(err),
@ -65,19 +65,19 @@ func (h *Handler) GetAllOdds(c *fiber.Ctx) error {
func (h *Handler) GetAllTenantOdds(c *fiber.Ctx) error {
companyID := c.Locals("company_id").(domain.ValidInt64)
if !companyID.Valid {
domain.BadRequestLogger.Error("invalid company id")
h.BadRequestLogger().Error("invalid company id")
return fiber.NewError(fiber.StatusBadRequest, "invalid company id")
}
limit, err := strconv.Atoi(c.Query("limit", "10")) // Default limit is 10
if err != nil || limit <= 0 {
domain.BadRequestLogger.Info("Invalid limit value", zap.Error(err))
h.BadRequestLogger().Info("Invalid limit value", zap.Error(err))
return fiber.NewError(fiber.StatusBadRequest, "Invalid limit value")
}
offset, err := strconv.Atoi(c.Query("offset", "0")) // Default offset is 0
if err != nil || offset < 0 {
domain.BadRequestLogger.Info("Invalid offset value", zap.Error(err))
h.BadRequestLogger().Info("Invalid offset value", zap.Error(err))
return fiber.NewError(fiber.StatusBadRequest, err.Error())
}
@ -92,7 +92,7 @@ func (h *Handler) GetAllTenantOdds(c *fiber.Ctx) error {
},
})
if err != nil {
domain.InternalServerErrorLogger.Error("Failed to retrieve all odds",
h.InternalServerErrorLogger().Error("Failed to retrieve all odds",
zap.Int("limit", limit),
zap.Int("offset", offset),
zap.Error(err),
@ -123,20 +123,20 @@ func (h *Handler) GetOddsByMarketID(c *fiber.Ctx) error {
marketID := c.Params("market_id")
if marketID == "" {
domain.BadRequestLogger.Info("Missing market_id", logFields...)
h.BadRequestLogger().Info("Missing market_id", logFields...)
return fiber.NewError(fiber.StatusBadRequest, "Missing market_id")
}
upcomingID := c.Params("upcoming_id")
if upcomingID == "" {
domain.BadRequestLogger.Info("Missing upcoming_id", logFields...)
h.BadRequestLogger().Info("Missing upcoming_id", logFields...)
return fiber.NewError(fiber.StatusBadRequest, "Missing upcoming_id")
}
rawOdds, err := h.prematchSvc.GetOddsByMarketID(c.Context(), marketID, upcomingID)
if err != nil {
// Lets turn this into a warn because this is constantly going off
domain.InternalServerErrorLogger.Warn("Failed to get raw odds by market ID", append(logFields, zap.Error(err))...)
h.InternalServerErrorLogger().Warn("Failed to get raw odds by market ID", append(logFields, zap.Error(err))...)
return fiber.NewError(fiber.StatusInternalServerError, err.Error())
}
@ -159,7 +159,7 @@ func (h *Handler) GetOddsByMarketID(c *fiber.Ctx) error {
func (h *Handler) GetTenantOddsByMarketID(c *fiber.Ctx) error {
companyID := c.Locals("company_id").(domain.ValidInt64)
if !companyID.Valid {
domain.BadRequestLogger.Error("invalid company id")
h.BadRequestLogger().Error("invalid company id")
return fiber.NewError(fiber.StatusBadRequest, "invalid company id")
}
logFields := []zap.Field{
@ -170,13 +170,13 @@ func (h *Handler) GetTenantOddsByMarketID(c *fiber.Ctx) error {
marketID := c.Params("market_id")
if marketID == "" {
domain.BadRequestLogger.Info("Missing market_id", logFields...)
h.BadRequestLogger().Info("Missing market_id", logFields...)
return fiber.NewError(fiber.StatusBadRequest, "Missing market_id")
}
upcomingID := c.Params("upcoming_id")
if upcomingID == "" {
domain.BadRequestLogger.Info("Missing upcoming_id", logFields...)
h.BadRequestLogger().Info("Missing upcoming_id", logFields...)
return fiber.NewError(fiber.StatusBadRequest, "Missing upcoming_id")
}
@ -184,7 +184,7 @@ func (h *Handler) GetTenantOddsByMarketID(c *fiber.Ctx) error {
if err != nil {
// Lets turn this into a warn because this is constantly going off
domain.InternalServerErrorLogger.Warn("Failed to get raw odds by market ID", append(logFields, zap.Error(err))...)
h.InternalServerErrorLogger().Warn("Failed to get raw odds by market ID", append(logFields, zap.Error(err))...)
return fiber.NewError(fiber.StatusInternalServerError, err.Error())
}
@ -214,28 +214,28 @@ func (h *Handler) GetOddsByUpcomingID(c *fiber.Ctx) error {
upcomingID := c.Params("upcoming_id")
if upcomingID == "" {
domain.BadRequestLogger.Info("Missing upcoming_id", logFields...)
h.BadRequestLogger().Info("Missing upcoming_id", logFields...)
return fiber.NewError(fiber.StatusBadRequest, "Missing upcoming_id")
}
limit, err := strconv.Atoi(c.Query("limit", "10")) // Default limit is 10
if err != nil || limit <= 0 {
logFields = append(logFields, zap.Error(err))
domain.BadRequestLogger.Info("Invalid limit value", logFields...)
h.BadRequestLogger().Info("Invalid limit value", logFields...)
return fiber.NewError(fiber.StatusBadRequest, "Invalid limit value")
}
offset, err := strconv.Atoi(c.Query("offset", "0")) // Default offset is 0
if err != nil || offset < 0 {
logFields = append(logFields, zap.Error(err))
domain.BadRequestLogger.Info("Invalid offset value", logFields...)
h.BadRequestLogger().Info("Invalid offset value", logFields...)
return fiber.NewError(fiber.StatusBadRequest, err.Error())
}
odds, err := h.prematchSvc.GetOddsByEventID(c.Context(), upcomingID, domain.OddMarketWithEventFilter{})
if err != nil {
logFields = append(logFields, zap.Error(err))
domain.InternalServerErrorLogger.Error("Failed to retrieve odds", append(logFields, zap.Error(err))...)
h.InternalServerErrorLogger().Error("Failed to retrieve odds", append(logFields, zap.Error(err))...)
return fiber.NewError(fiber.StatusInternalServerError, "Failed to retrieve odds"+err.Error())
}
@ -259,7 +259,7 @@ func (h *Handler) GetOddsByUpcomingID(c *fiber.Ctx) error {
func (h *Handler) GetTenantOddsByUpcomingID(c *fiber.Ctx) error {
companyID := c.Locals("company_id").(domain.ValidInt64)
if !companyID.Valid {
domain.BadRequestLogger.Error("invalid company id")
h.BadRequestLogger().Error("invalid company id")
return fiber.NewError(fiber.StatusBadRequest, "invalid company id")
}
@ -272,28 +272,28 @@ func (h *Handler) GetTenantOddsByUpcomingID(c *fiber.Ctx) error {
upcomingID := c.Params("upcoming_id")
if upcomingID == "" {
domain.BadRequestLogger.Info("Missing upcoming_id", logFields...)
h.BadRequestLogger().Info("Missing upcoming_id", logFields...)
return fiber.NewError(fiber.StatusBadRequest, "Missing upcoming_id")
}
limit, err := strconv.Atoi(c.Query("limit", "10")) // Default limit is 10
if err != nil || limit <= 0 {
logFields = append(logFields, zap.Error(err))
domain.BadRequestLogger.Info("Invalid limit value", logFields...)
h.BadRequestLogger().Info("Invalid limit value", logFields...)
return fiber.NewError(fiber.StatusBadRequest, "Invalid limit value")
}
offset, err := strconv.Atoi(c.Query("offset", "0")) // Default offset is 0
if err != nil || offset < 0 {
logFields = append(logFields, zap.Error(err))
domain.BadRequestLogger.Info("Invalid offset value", logFields...)
h.BadRequestLogger().Info("Invalid offset value", logFields...)
return fiber.NewError(fiber.StatusBadRequest, err.Error())
}
odds, err := h.prematchSvc.GetOddsWithSettingsByEventID(c.Context(), upcomingID, companyID.Value, domain.OddMarketFilter{})
if err != nil {
logFields = append(logFields, zap.Error(err))
domain.InternalServerErrorLogger.Error("Failed to retrieve odds", append(logFields, zap.Error(err))...)
h.InternalServerErrorLogger().Error("Failed to retrieve odds", append(logFields, zap.Error(err))...)
return fiber.NewError(fiber.StatusInternalServerError, "Failed to retrieve odds"+err.Error())
}

View File

@ -10,8 +10,8 @@ import (
"go.uber.org/zap"
)
func (h *Handler) GetSettingList(c *fiber.Ctx) error {
settingsList, err := h.settingSvc.GetSettingList(c.Context())
func (h *Handler) GetGlobalSettingList(c *fiber.Ctx) error {
settingsList, err := h.settingSvc.GetGlobalSettingList(c.Context())
if err != nil {
h.mongoLoggerSvc.Error("Failed to fetch settings",
@ -25,7 +25,7 @@ func (h *Handler) GetSettingList(c *fiber.Ctx) error {
return response.WriteJSON(c, fiber.StatusOK, "All Settings retrieved successfully", settingsList, nil)
}
func (h *Handler) GetSettingByKey(c *fiber.Ctx) error {
func (h *Handler) GetGlobalSettingByKey(c *fiber.Ctx) error {
settingKey := c.Params("key")
if settingKey == "" {
h.mongoLoggerSvc.Info("empty setting key",
@ -35,7 +35,7 @@ func (h *Handler) GetSettingByKey(c *fiber.Ctx) error {
return fiber.NewError(fiber.StatusBadRequest, "setting key must be passed")
}
setting, err := h.settingSvc.GetSetting(c.Context(), settingKey)
setting, err := h.settingSvc.GetGlobalSetting(c.Context(), settingKey)
if err != nil {
h.mongoLoggerSvc.Info("invalid setting key",
@ -52,12 +52,12 @@ func (h *Handler) GetSettingByKey(c *fiber.Ctx) error {
}
func (h *Handler) UpdateSettingList(c *fiber.Ctx) error {
func (h *Handler) UpdateGlobalSettingList(c *fiber.Ctx) error {
var req domain.UpdateSettingListReq
var req domain.SaveSettingListReq
if err := c.BodyParser(&req); err != nil {
h.mongoLoggerSvc.Info("Failed to parse UpdateSettingListReq",
h.mongoLoggerSvc.Info("Failed to parse SaveSettingListReq",
zap.Int("status_code", fiber.StatusBadRequest),
zap.Error(err),
zap.Time("timestamp", time.Now()),
@ -70,26 +70,82 @@ func (h *Handler) UpdateSettingList(c *fiber.Ctx) error {
for field, msg := range valErrs {
errMsg += fmt.Sprintf("%s: %s; ", field, msg)
}
h.mongoLoggerSvc.Info("Failed to validate UpdateSettingListReq",
h.mongoLoggerSvc.Info("Failed to validate SaveSettingListReq",
zap.Int("status_code", fiber.StatusBadRequest),
zap.String("errMsg", errMsg),
zap.Time("timestamp", time.Now()),
)
return fiber.NewError(fiber.StatusBadRequest, errMsg)
}
settingList := domain.ConvertUpdateSettingListReq(req)
err := h.settingSvc.UpdateSettingList(c.Context(), settingList)
settingList := domain.ConvertSaveSettingListReq(req)
err := h.settingSvc.UpdateGlobalSettingList(c.Context(), settingList)
if err != nil {
h.mongoLoggerSvc.Info("failed to update setting",
h.mongoLoggerSvc.Info("failed to save setting",
zap.Any("setting_list", settingList),
zap.Int("status_code", fiber.StatusInternalServerError),
zap.Time("timestamp", time.Now()),
)
return fiber.NewError(fiber.StatusInternalServerError, "failed to update setting")
return fiber.NewError(fiber.StatusInternalServerError, "failed to save setting")
}
settingsList, err := h.settingSvc.GetSettingList(c.Context())
settingsList, err := h.settingSvc.GetGlobalSettingList(c.Context())
if err != nil {
h.mongoLoggerSvc.Error("Failed to fetch settings",
zap.Int("status_code", fiber.StatusInternalServerError),
zap.Error(err),
zap.Time("timestamp", time.Now()),
)
return fiber.NewError(fiber.StatusInternalServerError, "Failed to get setting list:"+err.Error())
}
return response.WriteJSON(c, fiber.StatusOK, "setting updated", settingsList, nil)
}
func (h *Handler) SaveCompanySettingList(c *fiber.Ctx) error {
companyID := c.Locals("company_id").(domain.ValidInt64)
if !companyID.Valid {
h.BadRequestLogger().Error("invalid company id")
return fiber.NewError(fiber.StatusBadRequest, "invalid company id")
}
var req domain.SaveSettingListReq
if err := c.BodyParser(&req); err != nil {
h.mongoLoggerSvc.Info("Failed to parse SaveSettingListReq",
zap.Int("status_code", fiber.StatusBadRequest),
zap.Error(err),
zap.Time("timestamp", time.Now()),
)
return fiber.NewError(fiber.StatusBadRequest, "Invalid request")
}
valErrs, ok := h.validator.Validate(c, req)
if !ok {
var errMsg string
for field, msg := range valErrs {
errMsg += fmt.Sprintf("%s: %s; ", field, msg)
}
h.mongoLoggerSvc.Info("Failed to validate SaveSettingListReq",
zap.Int("status_code", fiber.StatusBadRequest),
zap.String("errMsg", errMsg),
zap.Time("timestamp", time.Now()),
)
return fiber.NewError(fiber.StatusBadRequest, errMsg)
}
settingList := domain.ConvertSaveSettingListReq(req)
err := h.settingSvc.InsertCompanySettingList(c.Context(), settingList, companyID.Value)
if err != nil {
h.mongoLoggerSvc.Info("failed to save setting",
zap.Any("setting_list", settingList),
zap.Int("status_code", fiber.StatusInternalServerError),
zap.Time("timestamp", time.Now()),
)
return fiber.NewError(fiber.StatusInternalServerError, "failed to save setting")
}
settingsList, err := h.settingSvc.GetOverrideSettingsList(c.Context(), companyID.Value)
if err != nil {
h.mongoLoggerSvc.Error("Failed to fetch settings",
@ -103,3 +159,76 @@ func (h *Handler) UpdateSettingList(c *fiber.Ctx) error {
return response.WriteJSON(c, fiber.StatusOK, "setting updated", settingsList, nil)
}
func (h *Handler) GetCompanySettingList(c *fiber.Ctx) error {
companyID := c.Locals("company_id").(domain.ValidInt64)
if !companyID.Valid {
h.BadRequestLogger().Error("invalid company id")
return fiber.NewError(fiber.StatusBadRequest, "invalid company id")
}
settingsList, err := h.settingSvc.GetOverrideSettingsList(c.Context(), companyID.Value)
if err != nil {
h.mongoLoggerSvc.Error("Failed to fetch settings",
zap.Int("status_code", fiber.StatusInternalServerError),
zap.Error(err),
zap.Time("timestamp", time.Now()),
)
return fiber.NewError(fiber.StatusInternalServerError, "Failed to get setting list:"+err.Error())
}
return response.WriteJSON(c, fiber.StatusOK, "All Settings retrieved successfully", settingsList, nil)
}
// /api/v1/{tenant_slug}/settings/{key}
func (h *Handler) DeleteCompanySetting(c *fiber.Ctx) error {
companyID := c.Locals("company_id").(domain.ValidInt64)
if !companyID.Valid {
h.BadRequestLogger().Error("invalid company id")
return fiber.NewError(fiber.StatusBadRequest, "invalid company id")
}
settingKey := c.Params("key")
if settingKey == "" {
h.mongoLoggerSvc.Info("empty setting key",
zap.Int("status_code", fiber.StatusBadRequest),
zap.Time("timestamp", time.Now()),
)
return fiber.NewError(fiber.StatusBadRequest, "setting key must be passed")
}
err := h.settingSvc.DeleteCompanySetting(c.Context(), companyID.Value, settingKey)
if err != nil {
h.mongoLoggerSvc.Error("Failed to delete company override settings",
zap.Int("status_code", fiber.StatusInternalServerError),
zap.Error(err),
zap.Time("timestamp", time.Now()),
)
return fiber.NewError(fiber.StatusInternalServerError, "Failed to delete company override settings:"+err.Error())
}
return response.WriteJSON(c, fiber.StatusOK, "setting deleted", nil, nil)
}
func (h *Handler) DeleteAllCompanySetting(c *fiber.Ctx) error {
companyID := c.Locals("company_id").(domain.ValidInt64)
if !companyID.Valid {
h.BadRequestLogger().Error("invalid company id")
return fiber.NewError(fiber.StatusBadRequest, "invalid company id")
}
err := h.settingSvc.DeleteAllCompanySetting(c.Context(), companyID.Value)
if err != nil {
h.mongoLoggerSvc.Error("Failed to delete company override settings",
zap.Int("status_code", fiber.StatusInternalServerError),
zap.Error(err),
zap.Time("timestamp", time.Now()),
)
return fiber.NewError(fiber.StatusInternalServerError, "Failed to delete company override settings:"+err.Error())
}
return response.WriteJSON(c, fiber.StatusOK, "setting deleted", nil, nil)
}

View File

@ -21,8 +21,13 @@ import (
// @Success 200 {object} domain.CreateTicketRes
// @Failure 400 {object} response.APIResponse
// @Failure 500 {object} response.APIResponse
// @Router /api/v1/ticket [post]
// @Router /api/v1/{tenant_slug}/ticket [post]
func (h *Handler) CreateTicket(c *fiber.Ctx) error {
companyID := c.Locals("company_id").(domain.ValidInt64)
if !companyID.Valid {
h.BadRequestLogger().Error("invalid company id")
return fiber.NewError(fiber.StatusBadRequest, "invalid company id")
}
var req domain.CreateTicketReq
if err := c.BodyParser(&req); err != nil {
h.mongoLoggerSvc.Info("Failed to parse CreateTicket request",
@ -47,7 +52,7 @@ func (h *Handler) CreateTicket(c *fiber.Ctx) error {
return fiber.NewError(fiber.StatusBadRequest, errMsg)
}
newTicket, rows, err := h.ticketSvc.CreateTicket(c.Context(), req, c.IP())
newTicket, rows, err := h.ticketSvc.CreateTicket(c.Context(), req, c.IP(), companyID.Value)
if err != nil {
@ -85,8 +90,13 @@ func (h *Handler) CreateTicket(c *fiber.Ctx) error {
// @Success 200 {object} domain.TicketRes
// @Failure 400 {object} response.APIResponse
// @Failure 500 {object} response.APIResponse
// @Router /api/v1/ticket/{id} [get]
// @Router /api/v1/{tenant_slug}/ticket/{id} [get]
func (h *Handler) GetTicketByID(c *fiber.Ctx) error {
companyID := c.Locals("company_id").(domain.ValidInt64)
if !companyID.Valid {
h.BadRequestLogger().Error("invalid company id")
return fiber.NewError(fiber.StatusBadRequest, "invalid company id")
}
ticketID := c.Params("id")
id, err := strconv.ParseInt(ticketID, 10, 64)
if err != nil {
@ -110,11 +120,22 @@ func (h *Handler) GetTicketByID(c *fiber.Ctx) error {
return fiber.NewError(fiber.StatusNotFound, "Failed to retrieve ticket")
}
if ticket.CompanyID != companyID.Value {
h.mongoLoggerSvc.Warn("User attempt to access another company ticket",
zap.Int64("ticketID", id),
zap.Int("status_code", fiber.StatusNotFound),
zap.Error(err),
zap.Time("timestamp", time.Now()),
)
return fiber.NewError(fiber.StatusNotFound, "Failed to retrieve ticket")
}
res := domain.TicketRes{
ID: ticket.ID,
Outcomes: ticket.Outcomes,
Amount: ticket.Amount.Float32(),
TotalOdds: ticket.TotalOdds,
CompanyID: ticket.CompanyID,
}
return response.WriteJSON(c, fiber.StatusOK, "Ticket retrieved successfully", res, nil)
}
@ -128,10 +149,18 @@ func (h *Handler) GetTicketByID(c *fiber.Ctx) error {
// @Success 200 {array} domain.TicketRes
// @Failure 400 {object} response.APIResponse
// @Failure 500 {object} response.APIResponse
// @Router /api/v1/ticket [get]
// @Router /api/v1/{tenant_slug}/ticket [get]
func (h *Handler) GetAllTickets(c *fiber.Ctx) error {
companyID := c.Locals("company_id").(domain.ValidInt64)
if !companyID.Valid {
h.BadRequestLogger().Error("invalid company id")
return fiber.NewError(fiber.StatusBadRequest, "invalid company id")
}
tickets, err := h.ticketSvc.GetAllTickets(c.Context(), domain.TicketFilter{
CompanyID: companyID,
})
tickets, err := h.ticketSvc.GetAllTickets(c.Context())
if err != nil {
h.mongoLoggerSvc.Error("Failed to get tickets",
zap.Int("status_code", fiber.StatusInternalServerError),

View File

@ -37,7 +37,7 @@ type CheckPhoneEmailExistRes struct {
func (h *Handler) CheckPhoneEmailExist(c *fiber.Ctx) error {
companyID := c.Locals("company_id").(domain.ValidInt64)
if !companyID.Valid {
domain.BadRequestLogger.Error("invalid company id")
h.BadRequestLogger().Error("invalid company id")
return fiber.NewError(fiber.StatusBadRequest, "invalid company id")
}
var req CheckPhoneEmailExistReq
@ -95,7 +95,7 @@ type RegisterCodeReq struct {
func (h *Handler) SendRegisterCode(c *fiber.Ctx) error {
companyID := c.Locals("company_id").(domain.ValidInt64)
if !companyID.Valid {
domain.BadRequestLogger.Error("invalid company id")
h.BadRequestLogger().Error("invalid company id")
return fiber.NewError(fiber.StatusBadRequest, "invalid company id")
}
var req RegisterCodeReq
@ -166,7 +166,7 @@ type RegisterUserReq struct {
func (h *Handler) RegisterUser(c *fiber.Ctx) error {
companyID := c.Locals("company_id").(domain.ValidInt64)
if !companyID.Valid {
domain.BadRequestLogger.Error("invalid company id")
h.BadRequestLogger().Error("invalid company id")
return fiber.NewError(fiber.StatusBadRequest, "invalid company id")
}
var req RegisterUserReq
@ -248,7 +248,7 @@ func (h *Handler) RegisterUser(c *fiber.Ctx) error {
}
if req.ReferalCode != "" {
err = h.referralSvc.ProcessReferral(c.Context(), req.PhoneNumber, req.ReferalCode)
err = h.referralSvc.ProcessReferral(c.Context(), req.PhoneNumber, req.ReferalCode, companyID.Value)
if err != nil {
h.mongoLoggerSvc.Error("Failed to process referral during registration",
zap.String("phone", req.PhoneNumber),
@ -297,7 +297,7 @@ type ResetCodeReq struct {
func (h *Handler) SendResetCode(c *fiber.Ctx) error {
companyID := c.Locals("company_id").(domain.ValidInt64)
if !companyID.Valid {
domain.BadRequestLogger.Error("invalid company id")
h.BadRequestLogger().Error("invalid company id")
return fiber.NewError(fiber.StatusBadRequest, "invalid company id")
}
var req ResetCodeReq

View File

@ -234,8 +234,8 @@ func (a *App) initAppRoutes() {
groupV1.Get("/branchCashier", a.authMiddleware, h.GetBranchForCashier)
// Branch Operation
groupV1.Get("/supportedOperation", a.authMiddleware, h.GetAllSupportedOperations)
groupV1.Post("/supportedOperation", a.authMiddleware, h.CreateSupportedOperation)
groupV1.Get("/supportedOperation", a.authMiddleware, a.SuperAdminOnly, h.GetAllSupportedOperations)
groupV1.Post("/supportedOperation", a.authMiddleware, a.SuperAdminOnly, h.CreateSupportedOperation)
groupV1.Post("/operation", a.authMiddleware, h.CreateBranchOperation)
groupV1.Get("/branch/:id/operation", a.authMiddleware, h.GetBranchOperations)
@ -252,20 +252,20 @@ func (a *App) initAppRoutes() {
groupV1.Get("/admin-company", a.authMiddleware, h.GetCompanyForAdmin)
// Ticket Routes
groupV1.Post("/ticket", h.CreateTicket)
groupV1.Get("/ticket", h.GetAllTickets)
groupV1.Get("/ticket/:id", h.GetTicketByID)
tenant.Post("/ticket", h.CreateTicket)
tenant.Get("/ticket", h.GetAllTickets)
tenant.Get("/ticket/:id", h.GetTicketByID)
// Bet Routes
groupV1.Post("/sport/bet", a.authMiddleware, h.CreateBet)
groupV1.Post("/sport/bet/fastcode", a.authMiddleware, h.CreateBetWithFastCode)
groupV1.Get("/sport/bet/fastcode/:fast_code", h.GetBetByFastCode)
groupV1.Get("/sport/bet", a.authMiddleware, h.GetAllBet)
groupV1.Get("/sport/bet/:id", h.GetBetByID)
groupV1.Patch("/sport/bet/:id", a.authMiddleware, h.UpdateCashOut)
groupV1.Delete("/sport/bet/:id", a.authMiddleware, h.DeleteBet)
tenantAuth.Post("/sport/bet", h.CreateBet)
tenantAuth.Post("/sport/bet/fastcode", h.CreateBetWithFastCode)
tenant.Get("/sport/bet/fastcode/:fast_code", h.GetBetByFastCode)
tenantAuth.Get("/sport/bet", h.GetAllBet)
tenantAuth.Get("/sport/bet/:id", h.GetBetByID)
tenantAuth.Patch("/sport/bet/:id", h.UpdateCashOut)
tenantAuth.Delete("/sport/bet/:id", h.DeleteBet)
groupV1.Post("/sport/random/bet", a.authMiddleware, h.RandomBet)
tenantAuth.Post("/sport/random/bet", h.RandomBet)
// Wallet
groupV1.Get("/wallet", h.GetAllWallets)
@ -326,10 +326,11 @@ func (a *App) initAppRoutes() {
groupV1.Post("/shop/cashout", a.authMiddleware, a.CompanyOnly, h.CashoutByCashoutID)
groupV1.Post("/shop/deposit", a.authMiddleware, a.CompanyOnly, h.DepositForCustomer)
// groupV1.Get("/shop/deposit", a.authMiddleware, a.CompanyOnly, h.DepositForCustomer)
groupV1.Get("/shop/transaction", a.authMiddleware, h.GetAllTransactions)
groupV1.Get("/shop/transaction/:id", a.authMiddleware, h.GetTransactionByID)
groupV1.Get("/shop/transaction/:id/bet", a.authMiddleware, h.GetShopBetByTransactionID)
groupV1.Put("/shop/transaction/:id", a.authMiddleware, h.UpdateTransactionVerified)
groupV1.Get("/shop/transaction", a.authMiddleware, a.CompanyOnly, h.GetAllTransactions)
groupV1.Get("/shop/transaction/:id", a.authMiddleware, a.CompanyOnly, h.GetTransactionByID)
groupV1.Get("/shop/transaction/:id/bet", a.authMiddleware, a.CompanyOnly, h.GetShopBetByTransactionID)
groupV1.Put("/shop/transaction/:id", a.authMiddleware, a.CompanyOnly, h.UpdateTransactionVerified)
// Notification Routes
groupV1.Get("/ws/connect", a.WebsocketAuthMiddleware, h.ConnectSocket)
@ -362,8 +363,13 @@ func (a *App) initAppRoutes() {
groupV1.Delete("/issues/:issue_id", a.authMiddleware, a.OnlyAdminAndAbove, h.DeleteIssue)
// Settings
groupV1.Get("/settings", a.authMiddleware, h.GetSettingList)
groupV1.Get("/settings/:key", a.authMiddleware, h.GetSettingByKey)
groupV1.Put("/settings", a.authMiddleware, h.UpdateSettingList)
groupV1.Get("/settings", a.authMiddleware, a.SuperAdminOnly, h.GetGlobalSettingList)
groupV1.Get("/settings/:key", a.authMiddleware, a.SuperAdminOnly, h.GetGlobalSettingByKey)
groupV1.Put("/settings", a.authMiddleware, a.SuperAdminOnly, h.UpdateGlobalSettingList)
tenantAuth.Post("/settings", h.SaveCompanySettingList)
tenantAuth.Get("/settings", h.GetCompanySettingList)
tenantAuth.Delete("/settings/:key", h.DeleteCompanySetting)
tenantAuth.Delete("/settings", h.DeleteAllCompanySetting)
}