fix: restricting search

This commit is contained in:
Samuel Tariku 2025-05-23 21:43:34 +03:00
parent 1be3ffdc3c
commit eafd68d3c2
15 changed files with 82 additions and 55 deletions

View File

@ -93,9 +93,19 @@ SELECT id,
suspended_at, suspended_at,
company_id company_id
FROM users FROM users
WHERE first_name ILIKE '%' || $1 || '%' WHERE (
first_name ILIKE '%' || $1 || '%'
OR last_name ILIKE '%' || $1 || '%' OR last_name ILIKE '%' || $1 || '%'
OR phone_number LIKE '%' || $1 || '%'; OR phone_number LIKE '%' || $1 || '%'
)
AND (
role = sqlc.narg('role')
OR sqlc.narg('role') IS NULL
)
AND (
company_id = sqlc.narg('company_id')
OR sqlc.narg('company_id') IS NULL
);
-- name: UpdateUser :exec -- name: UpdateUser :exec
UPDATE users UPDATE users
SET first_name = $1, SET first_name = $1,

View File

@ -29,7 +29,6 @@ WHERE user_id = $1;
-- name: GetCustomerWallet :one -- name: GetCustomerWallet :one
SELECT cw.id, SELECT cw.id,
cw.customer_id, cw.customer_id,
cw.company_id,
rw.id AS regular_id, rw.id AS regular_id,
rw.balance AS regular_balance, rw.balance AS regular_balance,
sw.id AS static_id, sw.id AS static_id,
@ -40,8 +39,7 @@ SELECT cw.id,
FROM customer_wallets cw FROM customer_wallets cw
JOIN wallets rw ON cw.regular_wallet_id = rw.id JOIN wallets rw ON cw.regular_wallet_id = rw.id
JOIN wallets sw ON cw.static_wallet_id = sw.id JOIN wallets sw ON cw.static_wallet_id = sw.id
WHERE cw.customer_id = $1 WHERE cw.customer_id = $1;
AND cw.company_id = $2;
-- name: GetAllBranchWallets :many -- name: GetAllBranchWallets :many
SELECT wallets.id, SELECT wallets.id,
wallets.balance, wallets.balance,

View File

@ -168,7 +168,6 @@ type Company struct {
type CustomerWallet struct { type CustomerWallet struct {
ID int64 `json:"id"` ID int64 `json:"id"`
CustomerID int64 `json:"customer_id"` CustomerID int64 `json:"customer_id"`
CompanyID int64 `json:"company_id"`
RegularWalletID int64 `json:"regular_wallet_id"` RegularWalletID int64 `json:"regular_wallet_id"`
StaticWalletID int64 `json:"static_wallet_id"` StaticWalletID int64 `json:"static_wallet_id"`
CreatedAt pgtype.Timestamp `json:"created_at"` CreatedAt pgtype.Timestamp `json:"created_at"`

View File

@ -427,11 +427,27 @@ SELECT id,
suspended_at, suspended_at,
company_id company_id
FROM users FROM users
WHERE first_name ILIKE '%' || $1 || '%' WHERE (
first_name ILIKE '%' || $1 || '%'
OR last_name ILIKE '%' || $1 || '%' OR last_name ILIKE '%' || $1 || '%'
OR phone_number LIKE '%' || $1 || '%' OR phone_number LIKE '%' || $1 || '%'
)
AND (
role = $2
OR $2 IS NULL
)
AND (
company_id = $3
OR $3 IS NULL
)
` `
type SearchUserByNameOrPhoneParams struct {
Column1 pgtype.Text `json:"column_1"`
Role pgtype.Text `json:"role"`
CompanyID pgtype.Int8 `json:"company_id"`
}
type SearchUserByNameOrPhoneRow struct { type SearchUserByNameOrPhoneRow struct {
ID int64 `json:"id"` ID int64 `json:"id"`
FirstName string `json:"first_name"` FirstName string `json:"first_name"`
@ -448,8 +464,8 @@ type SearchUserByNameOrPhoneRow struct {
CompanyID pgtype.Int8 `json:"company_id"` CompanyID pgtype.Int8 `json:"company_id"`
} }
func (q *Queries) SearchUserByNameOrPhone(ctx context.Context, dollar_1 pgtype.Text) ([]SearchUserByNameOrPhoneRow, error) { func (q *Queries) SearchUserByNameOrPhone(ctx context.Context, arg SearchUserByNameOrPhoneParams) ([]SearchUserByNameOrPhoneRow, error) {
rows, err := q.db.Query(ctx, SearchUserByNameOrPhone, dollar_1) rows, err := q.db.Query(ctx, SearchUserByNameOrPhone, arg.Column1, arg.Role, arg.CompanyID)
if err != nil { if err != nil {
return nil, err return nil, err
} }

View File

@ -14,33 +14,25 @@ import (
const CreateCustomerWallet = `-- name: CreateCustomerWallet :one const CreateCustomerWallet = `-- name: CreateCustomerWallet :one
INSERT INTO customer_wallets ( INSERT INTO customer_wallets (
customer_id, customer_id,
company_id,
regular_wallet_id, regular_wallet_id,
static_wallet_id static_wallet_id
) )
VALUES ($1, $2, $3, $4) VALUES ($1, $2, $3)
RETURNING id, customer_id, company_id, regular_wallet_id, static_wallet_id, created_at, updated_at RETURNING id, customer_id, regular_wallet_id, static_wallet_id, created_at, updated_at
` `
type CreateCustomerWalletParams struct { type CreateCustomerWalletParams struct {
CustomerID int64 `json:"customer_id"` CustomerID int64 `json:"customer_id"`
CompanyID int64 `json:"company_id"`
RegularWalletID int64 `json:"regular_wallet_id"` RegularWalletID int64 `json:"regular_wallet_id"`
StaticWalletID int64 `json:"static_wallet_id"` StaticWalletID int64 `json:"static_wallet_id"`
} }
func (q *Queries) CreateCustomerWallet(ctx context.Context, arg CreateCustomerWalletParams) (CustomerWallet, error) { func (q *Queries) CreateCustomerWallet(ctx context.Context, arg CreateCustomerWalletParams) (CustomerWallet, error) {
row := q.db.QueryRow(ctx, CreateCustomerWallet, row := q.db.QueryRow(ctx, CreateCustomerWallet, arg.CustomerID, arg.RegularWalletID, arg.StaticWalletID)
arg.CustomerID,
arg.CompanyID,
arg.RegularWalletID,
arg.StaticWalletID,
)
var i CustomerWallet var i CustomerWallet
err := row.Scan( err := row.Scan(
&i.ID, &i.ID,
&i.CustomerID, &i.CustomerID,
&i.CompanyID,
&i.RegularWalletID, &i.RegularWalletID,
&i.StaticWalletID, &i.StaticWalletID,
&i.CreatedAt, &i.CreatedAt,
@ -190,7 +182,6 @@ func (q *Queries) GetAllWallets(ctx context.Context) ([]Wallet, error) {
const GetCustomerWallet = `-- name: GetCustomerWallet :one const GetCustomerWallet = `-- name: GetCustomerWallet :one
SELECT cw.id, SELECT cw.id,
cw.customer_id, cw.customer_id,
cw.company_id,
rw.id AS regular_id, rw.id AS regular_id,
rw.balance AS regular_balance, rw.balance AS regular_balance,
sw.id AS static_id, sw.id AS static_id,
@ -202,18 +193,11 @@ FROM customer_wallets cw
JOIN wallets rw ON cw.regular_wallet_id = rw.id JOIN wallets rw ON cw.regular_wallet_id = rw.id
JOIN wallets sw ON cw.static_wallet_id = sw.id JOIN wallets sw ON cw.static_wallet_id = sw.id
WHERE cw.customer_id = $1 WHERE cw.customer_id = $1
AND cw.company_id = $2
` `
type GetCustomerWalletParams struct {
CustomerID int64 `json:"customer_id"`
CompanyID int64 `json:"company_id"`
}
type GetCustomerWalletRow struct { type GetCustomerWalletRow struct {
ID int64 `json:"id"` ID int64 `json:"id"`
CustomerID int64 `json:"customer_id"` CustomerID int64 `json:"customer_id"`
CompanyID int64 `json:"company_id"`
RegularID int64 `json:"regular_id"` RegularID int64 `json:"regular_id"`
RegularBalance int64 `json:"regular_balance"` RegularBalance int64 `json:"regular_balance"`
StaticID int64 `json:"static_id"` StaticID int64 `json:"static_id"`
@ -223,13 +207,12 @@ type GetCustomerWalletRow struct {
CreatedAt pgtype.Timestamp `json:"created_at"` CreatedAt pgtype.Timestamp `json:"created_at"`
} }
func (q *Queries) GetCustomerWallet(ctx context.Context, arg GetCustomerWalletParams) (GetCustomerWalletRow, error) { func (q *Queries) GetCustomerWallet(ctx context.Context, customerID int64) (GetCustomerWalletRow, error) {
row := q.db.QueryRow(ctx, GetCustomerWallet, arg.CustomerID, arg.CompanyID) row := q.db.QueryRow(ctx, GetCustomerWallet, customerID)
var i GetCustomerWalletRow var i GetCustomerWalletRow
err := row.Scan( err := row.Scan(
&i.ID, &i.ID,
&i.CustomerID, &i.CustomerID,
&i.CompanyID,
&i.RegularID, &i.RegularID,
&i.RegularBalance, &i.RegularBalance,
&i.StaticID, &i.StaticID,

View File

@ -202,11 +202,28 @@ func (s *Store) GetCashiersByBranch(ctx context.Context, branchID int64) ([]doma
return userList, nil return userList, nil
} }
func (s *Store) SearchUserByNameOrPhone(ctx context.Context, searchString string) ([]domain.User, error) { func (s *Store) SearchUserByNameOrPhone(ctx context.Context, searchString string, role *domain.Role, companyID domain.ValidInt64) ([]domain.User, error) {
users, err := s.queries.SearchUserByNameOrPhone(ctx, pgtype.Text{
query := dbgen.SearchUserByNameOrPhoneParams{
Column1: pgtype.Text{
String: searchString, String: searchString,
Valid: true, Valid: true,
}) },
CompanyID: pgtype.Int8{
Int64: companyID.Value,
Valid: companyID.Valid,
},
}
if role != nil {
query.Role = pgtype.Text{
String: string(*role),
Valid: true,
}
}
users, err := s.queries.SearchUserByNameOrPhone(ctx, query)
if err != nil { if err != nil {
return nil, err return nil, err
} }

View File

@ -114,11 +114,8 @@ func (s *Store) GetWalletsByUser(ctx context.Context, userID int64) ([]domain.Wa
return result, nil return result, nil
} }
func (s *Store) GetCustomerWallet(ctx context.Context, customerID int64, companyID int64) (domain.GetCustomerWallet, error) { func (s *Store) GetCustomerWallet(ctx context.Context, customerID int64) (domain.GetCustomerWallet, error) {
customerWallet, err := s.queries.GetCustomerWallet(ctx, dbgen.GetCustomerWalletParams{ customerWallet, err := s.queries.GetCustomerWallet(ctx, customerID)
CustomerID: customerID,
CompanyID: companyID,
})
if err != nil { if err != nil {
return domain.GetCustomerWallet{}, err return domain.GetCustomerWallet{}, err

View File

@ -23,6 +23,7 @@ var (
ErrNoEventsAvailable = errors.New("Not enough events available with the given filters") ErrNoEventsAvailable = errors.New("Not enough events available with the given filters")
ErrGenerateRandomOutcome = errors.New("Failed to generate any random outcome for events") ErrGenerateRandomOutcome = errors.New("Failed to generate any random outcome for events")
ErrOutcomesNotCompleted = errors.New("Some bet outcomes are still pending") ErrOutcomesNotCompleted = errors.New("Some bet outcomes are still pending")
ErrEventHasBeenRemoved = errors.New("Event has been removed")
) )
type Service struct { type Service struct {
@ -75,7 +76,7 @@ func (s *Service) GenerateBetOutcome(ctx context.Context, eventID int64, marketI
event, err := s.eventSvc.GetUpcomingEventByID(ctx, eventIDStr) event, err := s.eventSvc.GetUpcomingEventByID(ctx, eventIDStr)
if err != nil { if err != nil {
return domain.CreateBetOutcome{}, err return domain.CreateBetOutcome{}, ErrEventHasBeenRemoved
} }
currentTime := time.Now() currentTime := time.Now()

View File

@ -98,7 +98,6 @@ func (s *ServiceImpl) FetchNonLiveOdds(ctx context.Context) error {
s.logger.Error("Error while inserting ice hockey odd") s.logger.Error("Error while inserting ice hockey odd")
errs = append(errs, err) errs = append(errs, err)
} }
} }
// result := oddsData.Results[0] // result := oddsData.Results[0]

View File

@ -21,7 +21,7 @@ type UserStore interface {
CheckPhoneEmailExist(ctx context.Context, phoneNum, email string) (bool, bool, error) CheckPhoneEmailExist(ctx context.Context, phoneNum, email string) (bool, bool, error)
GetUserByEmail(ctx context.Context, email string) (domain.User, error) GetUserByEmail(ctx context.Context, email string) (domain.User, error)
GetUserByPhone(ctx context.Context, phoneNum string) (domain.User, error) GetUserByPhone(ctx context.Context, phoneNum string) (domain.User, error)
SearchUserByNameOrPhone(ctx context.Context, searchString string) ([]domain.User, error) SearchUserByNameOrPhone(ctx context.Context, searchString string, role *domain.Role, companyID domain.ValidInt64) ([]domain.User, error)
UpdatePassword(ctx context.Context, identifier string, password []byte, usedOtpId int64) error // identifier verified email or phone UpdatePassword(ctx context.Context, identifier string, password []byte, usedOtpId int64) error // identifier verified email or phone
} }
type SmsGateway interface { type SmsGateway interface {

View File

@ -6,9 +6,9 @@ import (
"github.com/SamuelTariku/FortuneBet-Backend/internal/domain" "github.com/SamuelTariku/FortuneBet-Backend/internal/domain"
) )
func (s *Service) SearchUserByNameOrPhone(ctx context.Context, searchString string) ([]domain.User, error) { func (s *Service) SearchUserByNameOrPhone(ctx context.Context, searchString string, role *domain.Role, companyID domain.ValidInt64) ([]domain.User, error) {
// Search user // Search user
return s.userStore.SearchUserByNameOrPhone(ctx, searchString) return s.userStore.SearchUserByNameOrPhone(ctx, searchString, role, companyID)
} }
func (s *Service) UpdateUser(ctx context.Context, user domain.UpdateUserReq) error { func (s *Service) UpdateUser(ctx context.Context, user domain.UpdateUserReq) error {

View File

@ -12,7 +12,7 @@ type WalletStore interface {
GetWalletByID(ctx context.Context, id int64) (domain.Wallet, error) GetWalletByID(ctx context.Context, id int64) (domain.Wallet, error)
GetAllWallets(ctx context.Context) ([]domain.Wallet, error) GetAllWallets(ctx context.Context) ([]domain.Wallet, error)
GetWalletsByUser(ctx context.Context, id int64) ([]domain.Wallet, error) GetWalletsByUser(ctx context.Context, id int64) ([]domain.Wallet, error)
GetCustomerWallet(ctx context.Context, customerID int64, companyID int64) (domain.GetCustomerWallet, error) GetCustomerWallet(ctx context.Context, customerID int64) (domain.GetCustomerWallet, error)
GetAllBranchWallets(ctx context.Context) ([]domain.BranchWallet, error) GetAllBranchWallets(ctx context.Context) ([]domain.BranchWallet, error)
UpdateBalance(ctx context.Context, id int64, balance domain.Currency) error UpdateBalance(ctx context.Context, id int64, balance domain.Currency) error
UpdateWalletActive(ctx context.Context, id int64, isActive bool) error UpdateWalletActive(ctx context.Context, id int64, isActive bool) error

View File

@ -56,8 +56,8 @@ func (s *Service) GetWalletsByUser(ctx context.Context, id int64) ([]domain.Wall
return s.walletStore.GetWalletsByUser(ctx, id) return s.walletStore.GetWalletsByUser(ctx, id)
} }
func (s *Service) GetCustomerWallet(ctx context.Context, customerID int64, companyID int64) (domain.GetCustomerWallet, error) { func (s *Service) GetCustomerWallet(ctx context.Context, customerID int64) (domain.GetCustomerWallet, error) {
return s.walletStore.GetCustomerWallet(ctx, customerID, companyID) return s.walletStore.GetCustomerWallet(ctx, customerID)
} }
func (s *Service) GetAllBranchWallets(ctx context.Context) ([]domain.BranchWallet, error) { func (s *Service) GetAllBranchWallets(ctx context.Context) ([]domain.BranchWallet, error) {

View File

@ -6,6 +6,7 @@ import (
"github.com/SamuelTariku/FortuneBet-Backend/internal/domain" "github.com/SamuelTariku/FortuneBet-Backend/internal/domain"
"github.com/SamuelTariku/FortuneBet-Backend/internal/services/bet" "github.com/SamuelTariku/FortuneBet-Backend/internal/services/bet"
"github.com/SamuelTariku/FortuneBet-Backend/internal/services/wallet"
"github.com/SamuelTariku/FortuneBet-Backend/internal/web_server/response" "github.com/SamuelTariku/FortuneBet-Backend/internal/web_server/response"
"github.com/gofiber/fiber/v2" "github.com/gofiber/fiber/v2"
) )
@ -42,6 +43,10 @@ func (h *Handler) CreateBet(c *fiber.Ctx) error {
if err != nil { if err != nil {
h.logger.Error("PlaceBet failed", "error", err) h.logger.Error("PlaceBet failed", "error", err)
switch err {
case bet.ErrEventHasBeenRemoved, bet.ErrEventHasNotEnded, bet.ErrRawOddInvalid, wallet.ErrBalanceInsufficient:
return fiber.NewError(fiber.StatusBadGateway, err.Error())
}
return fiber.NewError(fiber.StatusInternalServerError, "Unable to create bet") return fiber.NewError(fiber.StatusInternalServerError, "Unable to create bet")
} }

View File

@ -381,7 +381,8 @@ func getMedium(email, phoneNumber string) (domain.OtpMedium, error) {
} }
type SearchUserByNameOrPhoneReq struct { type SearchUserByNameOrPhoneReq struct {
SearchString string SearchString string `json:"query"`
Role *domain.Role `json:"role,omitempty"`
} }
// SearchUserByNameOrPhone godoc // SearchUserByNameOrPhone godoc
@ -409,7 +410,8 @@ func (h *Handler) SearchUserByNameOrPhone(c *fiber.Ctx) error {
response.WriteJSON(c, fiber.StatusBadRequest, "Invalid request", valErrs, nil) response.WriteJSON(c, fiber.StatusBadRequest, "Invalid request", valErrs, nil)
return nil return nil
} }
users, err := h.userSvc.SearchUserByNameOrPhone(c.Context(), req.SearchString) companyID := c.Locals("company_id").(domain.ValidInt64)
users, err := h.userSvc.SearchUserByNameOrPhone(c.Context(), req.SearchString, req.Role, companyID)
if err != nil { if err != nil {
h.logger.Error("SearchUserByNameOrPhone failed", "error", err) h.logger.Error("SearchUserByNameOrPhone failed", "error", err)
return c.Status(fiber.StatusInternalServerError).JSON(fiber.Map{ return c.Status(fiber.StatusInternalServerError).JSON(fiber.Map{