integration fixes

This commit is contained in:
Samuel Tariku 2025-04-26 14:48:45 +03:00
parent fcd926223a
commit 208a2d74be
45 changed files with 2905 additions and 405 deletions

View File

@ -10,8 +10,9 @@ CREATE TABLE IF NOT EXISTS users (
phone_verified BOOLEAN NOT NULL DEFAULT FALSE,
created_at TIMESTAMPTZ NOT NULL DEFAULT CURRENT_TIMESTAMP,
updated_at TIMESTAMPTZ,
--
suspended_at TIMESTAMPTZ NULL, -- this can be NULL if the user is not suspended
company_id BIGINT,
suspended_at TIMESTAMPTZ NULL,
-- this can be NULL if the user is not suspended
suspended BOOLEAN NOT NULL DEFAULT FALSE,
CHECK (
email IS NOT NULL
@ -53,6 +54,7 @@ CREATE TABLE IF NOT EXISTS bets (
created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP,
updated_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP,
is_shop_bet BOOLEAN NOT NULL,
UNIQUE(cashout_id),
CHECK (
user_id IS NOT NULL
OR branch_id IS NOT NULL
@ -148,6 +150,7 @@ CREATE TABLE IF NOT EXISTS transactions (
branch_id BIGINT NOT NULL,
cashier_id BIGINT NOT NULL,
bet_id BIGINT NOT NULL,
number_of_outcomes BIGINT NOT NULL,
type BIGINT NOT NULL,
payment_option BIGINT NOT NULL,
full_name VARCHAR(255) NOT NULL,

View File

@ -1,5 +1,4 @@
CREATE TYPE ReferralStatus AS ENUM ('PENDING', 'COMPLETED', 'EXPIRED', 'CANCELLED');
CREATE TABLE IF NOT EXISTS referral_settings (
id BIGSERIAL PRIMARY KEY,
referral_reward_amount DECIMAL(15, 2) NOT NULL DEFAULT 0.00,
@ -17,7 +16,6 @@ CREATE TABLE IF NOT EXISTS referral_settings (
AND cashback_percentage <= 100
)
);
CREATE TABLE IF NOT EXISTS referrals (
id BIGSERIAL PRIMARY KEY,
referral_code VARCHAR(10) NOT NULL UNIQUE,
@ -29,25 +27,20 @@ CREATE TABLE IF NOT EXISTS referrals (
created_at TIMESTAMPTZ NOT NULL DEFAULT CURRENT_TIMESTAMP,
updated_at TIMESTAMPTZ NOT NULL DEFAULT CURRENT_TIMESTAMP,
expires_at TIMESTAMPTZ NOT NULL,
FOREIGN KEY (referrer_id) REFERENCES users (id),
FOREIGN KEY (referred_id) REFERENCES users (id),
-- FOREIGN KEY (referrer_id) REFERENCES users (id),
-- FOREIGN KEY (referred_id) REFERENCES users (id),
CONSTRAINT reward_amount_positive CHECK (reward_amount >= 0),
CONSTRAINT cashback_amount_positive CHECK (cashback_amount >= 0)
);
CREATE INDEX idx_referrals_referral_code ON referrals (referral_code);
CREATE INDEX idx_referrals_referrer_id ON referrals (referrer_id);
CREATE INDEX idx_referrals_status ON referrals (status);
ALTER TABLE users
ADD COLUMN IF NOT EXISTS referral_code VARCHAR(10) UNIQUE,
ADD COLUMN IF NOT EXISTS referred_by VARCHAR(10);
ADD COLUMN IF NOT EXISTS referred_by VARCHAR(10);
-- Modify wallet table to track bonus money separately
ALTER TABLE wallets
ADD COLUMN IF NOT EXISTS bonus_balance DECIMAL(15, 2) NOT NULL DEFAULT 0.00,
ADD COLUMN IF NOT EXISTS cash_balance DECIMAL(15, 2) NOT NULL DEFAULT 0.00,
ADD CONSTRAINT bonus_balance_positive CHECK (bonus_balance >= 0),
ADD CONSTRAINT cash_balance_positive CHECK (cash_balance >= 0);
ADD COLUMN IF NOT EXISTS cash_balance DECIMAL(15, 2) NOT NULL DEFAULT 0.00,
ADD CONSTRAINT bonus_balance_positive CHECK (bonus_balance >= 0),
ADD CONSTRAINT cash_balance_positive CHECK (cash_balance >= 0);

View File

@ -1,15 +1,19 @@
-- name: GetUserByEmailPhone :one
SELECT * FROM users
WHERE email = $1 OR phone_number = $2;
SELECT *
FROM users
WHERE email = $1
OR phone_number = $2;
-- name: CreateRefreshToken :exec
INSERT INTO refresh_tokens (user_id, token, expires_at, created_at, revoked)
VALUES ($1, $2, $3, $4, $5);
-- name: GetRefreshToken :one
SELECT * FROM refresh_tokens
SELECT *
FROM refresh_tokens
WHERE token = $1;
-- name: GetRefreshTokenByUserID :one
SELECT *
FROM refresh_tokens
WHERE user_id = $1;
-- name: RevokeRefreshToken :exec
UPDATE refresh_tokens
SET revoked = TRUE

View File

@ -13,6 +13,10 @@ FROM companies;
SELECT *
FROM companies
WHERE id = $1;
-- name: SearchCompanyByName :many
SELECT *
FROM companies
WHERE name ILIKE '%' || $1 || '%';
-- name: UpdateCompany :one
UPDATE companies
SET name = $1,

View File

@ -9,9 +9,24 @@ INSERT INTO users (
email_verified,
phone_verified,
created_at,
updated_at
updated_at,
suspended,
company_id
)
VALUES (
$1,
$2,
$3,
$4,
$5,
$6,
$7,
$8,
$9,
$10,
$11,
$12
)
VALUES ($1, $2, $3, $4, $5, $6, $7, $8, $9, $10)
RETURNING id,
first_name,
last_name,
@ -21,7 +36,9 @@ RETURNING id,
email_verified,
phone_verified,
created_at,
updated_at;
updated_at,
suspended,
company_id;
-- name: GetUserByID :one
SELECT *
FROM users
@ -36,8 +53,31 @@ SELECT id,
email_verified,
phone_verified,
created_at,
updated_at
FROM users;
updated_at,
suspended,
suspended_at,
company_id
FROM users
wHERE (
role = $1
OR $1 IS NULL
)
AND (
company_id = $2
OR $2 IS NULL
)
LIMIT $3 OFFSET $4;
-- name: GetTotalUsers :one
SELECT COUNT(*)
FROM users
wHERE (
role = $1
OR $1 IS NULL
)
AND (
company_id = $2
OR $2 IS NULL
);
-- name: SearchUserByNameOrPhone :many
SELECT id,
first_name,
@ -48,7 +88,10 @@ SELECT id,
email_verified,
phone_verified,
created_at,
updated_at
updated_at,
suspended,
suspended_at,
company_id
FROM users
WHERE first_name ILIKE '%' || $1 || '%'
OR last_name ILIKE '%' || $1 || '%'
@ -62,6 +105,12 @@ SET first_name = $1,
role = $5,
updated_at = $6
WHERE id = $7;
-- name: SuspendUser :exec
UPDATE users
SET suspended = $1,
suspended_at = $2,
updated_at = CURRENT_TIMESTAMP
WHERE id = $3;
-- name: DeleteUser :exec
DELETE FROM users
WHERE id = $1;
@ -88,7 +137,10 @@ SELECT id,
email_verified,
phone_verified,
created_at,
updated_at
updated_at,
suspended,
suspended_at,
company_id
FROM users
WHERE email = $1;
-- name: GetUserByPhone :one
@ -101,7 +153,10 @@ SELECT id,
email_verified,
phone_verified,
created_at,
updated_at
updated_at,
suspended,
suspended_at,
company_id
FROM users
WHERE phone_number = $1;
-- name: UpdatePassword :exec

View File

@ -24,6 +24,111 @@ const docTemplate = `{
"host": "{{.Host}}",
"basePath": "{{.BasePath}}",
"paths": {
"/admin": {
"get": {
"description": "Get all Admins",
"consumes": [
"application/json"
],
"produces": [
"application/json"
],
"tags": [
"admin"
],
"summary": "Get all Admins",
"parameters": [
{
"type": "integer",
"description": "Page number",
"name": "page",
"in": "query"
},
{
"type": "integer",
"description": "Page size",
"name": "page_size",
"in": "query"
}
],
"responses": {
"200": {
"description": "OK",
"schema": {
"$ref": "#/definitions/response.APIResponse"
}
},
"400": {
"description": "Bad Request",
"schema": {
"$ref": "#/definitions/response.APIResponse"
}
},
"401": {
"description": "Unauthorized",
"schema": {
"$ref": "#/definitions/response.APIResponse"
}
},
"500": {
"description": "Internal Server Error",
"schema": {
"$ref": "#/definitions/response.APIResponse"
}
}
}
},
"post": {
"description": "Create Admin",
"consumes": [
"application/json"
],
"produces": [
"application/json"
],
"tags": [
"admin"
],
"summary": "Create Admin",
"parameters": [
{
"description": "Create admin",
"name": "manger",
"in": "body",
"required": true,
"schema": {
"$ref": "#/definitions/handlers.CreateAdminReq"
}
}
],
"responses": {
"200": {
"description": "OK",
"schema": {
"$ref": "#/definitions/response.APIResponse"
}
},
"400": {
"description": "Bad Request",
"schema": {
"$ref": "#/definitions/response.APIResponse"
}
},
"401": {
"description": "Unauthorized",
"schema": {
"$ref": "#/definitions/response.APIResponse"
}
},
"500": {
"description": "Internal Server Error",
"schema": {
"$ref": "#/definitions/response.APIResponse"
}
}
}
}
},
"/auth/login": {
"post": {
"description": "Login customer",
@ -955,6 +1060,13 @@ const docTemplate = `{
],
"summary": "Update cashier",
"parameters": [
{
"type": "integer",
"description": "Cashier ID",
"name": "id",
"in": "path",
"required": true
},
{
"description": "Update cashier",
"name": "cashier",
@ -1361,7 +1473,7 @@ const docTemplate = `{
}
},
"post": {
"description": "Create Managers",
"description": "Create Manager",
"consumes": [
"application/json"
],
@ -1371,7 +1483,7 @@ const docTemplate = `{
"tags": [
"manager"
],
"summary": "Create Managers",
"summary": "Create Manager",
"parameters": [
{
"description": "Create manager",
@ -1803,6 +1915,147 @@ const docTemplate = `{
}
}
},
"/referral/settings": {
"get": {
"security": [
{
"Bearer": []
}
],
"description": "Retrieves current referral settings (admin only)",
"consumes": [
"application/json"
],
"produces": [
"application/json"
],
"tags": [
"referral"
],
"summary": "Get referral settings",
"responses": {
"200": {
"description": "OK",
"schema": {
"$ref": "#/definitions/domain.ReferralSettings"
}
},
"401": {
"description": "Unauthorized",
"schema": {
"$ref": "#/definitions/response.APIResponse"
}
},
"403": {
"description": "Forbidden",
"schema": {
"$ref": "#/definitions/response.APIResponse"
}
},
"500": {
"description": "Internal Server Error",
"schema": {
"$ref": "#/definitions/response.APIResponse"
}
}
}
},
"put": {
"security": [
{
"Bearer": []
}
],
"description": "Updates referral settings (admin only)",
"consumes": [
"application/json"
],
"produces": [
"application/json"
],
"tags": [
"referral"
],
"summary": "Update referral settings",
"parameters": [
{
"description": "Referral settings",
"name": "settings",
"in": "body",
"required": true,
"schema": {
"$ref": "#/definitions/domain.ReferralSettings"
}
}
],
"responses": {
"200": {
"description": "OK",
"schema": {
"$ref": "#/definitions/response.APIResponse"
}
},
"401": {
"description": "Unauthorized",
"schema": {
"$ref": "#/definitions/response.APIResponse"
}
},
"403": {
"description": "Forbidden",
"schema": {
"$ref": "#/definitions/response.APIResponse"
}
},
"500": {
"description": "Internal Server Error",
"schema": {
"$ref": "#/definitions/response.APIResponse"
}
}
}
}
},
"/referral/stats": {
"get": {
"security": [
{
"Bearer": []
}
],
"description": "Retrieves referral statistics for the authenticated user",
"consumes": [
"application/json"
],
"produces": [
"application/json"
],
"tags": [
"referral"
],
"summary": "Get referral statistics",
"responses": {
"200": {
"description": "OK",
"schema": {
"$ref": "#/definitions/domain.ReferralStats"
}
},
"401": {
"description": "Unauthorized",
"schema": {
"$ref": "#/definitions/response.APIResponse"
}
},
"500": {
"description": "Internal Server Error",
"schema": {
"$ref": "#/definitions/response.APIResponse"
}
}
}
}
},
"/search/branch": {
"get": {
"description": "Search branches by name or location",
@ -1850,6 +2103,44 @@ const docTemplate = `{
}
}
},
"/search/company": {
"get": {
"description": "Gets all companies",
"consumes": [
"application/json"
],
"produces": [
"application/json"
],
"tags": [
"company"
],
"summary": "Gets all companies",
"responses": {
"200": {
"description": "OK",
"schema": {
"type": "array",
"items": {
"$ref": "#/definitions/handlers.CompanyRes"
}
}
},
"400": {
"description": "Bad Request",
"schema": {
"$ref": "#/definitions/response.APIResponse"
}
},
"500": {
"description": "Internal Server Error",
"schema": {
"$ref": "#/definitions/response.APIResponse"
}
}
}
}
},
"/supportedOperation": {
"get": {
"description": "Gets all supported operations",
@ -2184,7 +2475,7 @@ const docTemplate = `{
}
},
"patch": {
"description": "Updates the cashed out field",
"description": "Updates the verified status of a transaction",
"consumes": [
"application/json"
],
@ -2194,7 +2485,7 @@ const docTemplate = `{
"tags": [
"transaction"
],
"summary": "Updates the cashed out field",
"summary": "Updates the verified field of a transaction",
"parameters": [
{
"type": "integer",
@ -2205,7 +2496,7 @@ const docTemplate = `{
},
{
"description": "Updates Transaction Verification",
"name": "updateCashOut",
"name": "updateVerified",
"in": "body",
"required": true,
"schema": {
@ -2689,6 +2980,56 @@ const docTemplate = `{
}
}
},
"/user/single/{id}": {
"get": {
"description": "Get a single user by id",
"consumes": [
"application/json"
],
"produces": [
"application/json"
],
"tags": [
"user"
],
"summary": "Get user by id",
"parameters": [
{
"type": "integer",
"description": "User ID",
"name": "id",
"in": "path",
"required": true
}
],
"responses": {
"200": {
"description": "OK",
"schema": {
"$ref": "#/definitions/response.APIResponse"
}
},
"400": {
"description": "Bad Request",
"schema": {
"$ref": "#/definitions/response.APIResponse"
}
},
"401": {
"description": "Unauthorized",
"schema": {
"$ref": "#/definitions/response.APIResponse"
}
},
"500": {
"description": "Internal Server Error",
"schema": {
"$ref": "#/definitions/response.APIResponse"
}
}
}
}
},
"/user/wallet": {
"get": {
"security": [
@ -2738,6 +3079,109 @@ const docTemplate = `{
}
}
},
"/virtual-game/callback": {
"post": {
"description": "Processes callbacks from PopOK for game events",
"consumes": [
"application/json"
],
"produces": [
"application/json"
],
"tags": [
"virtual-game"
],
"summary": "Handle PopOK game callback",
"parameters": [
{
"description": "Callback data",
"name": "callback",
"in": "body",
"required": true,
"schema": {
"$ref": "#/definitions/domain.PopOKCallback"
}
}
],
"responses": {
"200": {
"description": "OK",
"schema": {
"$ref": "#/definitions/response.APIResponse"
}
},
"400": {
"description": "Bad Request",
"schema": {
"$ref": "#/definitions/response.APIResponse"
}
},
"500": {
"description": "Internal Server Error",
"schema": {
"$ref": "#/definitions/response.APIResponse"
}
}
}
}
},
"/virtual-game/launch": {
"post": {
"security": [
{
"Bearer": []
}
],
"description": "Generates a URL to launch a PopOK game",
"consumes": [
"application/json"
],
"produces": [
"application/json"
],
"tags": [
"virtual-game"
],
"summary": "Launch a PopOK virtual game",
"parameters": [
{
"description": "Game launch details",
"name": "launch",
"in": "body",
"required": true,
"schema": {
"$ref": "#/definitions/handlers.launchVirtualGameReq"
}
}
],
"responses": {
"200": {
"description": "OK",
"schema": {
"$ref": "#/definitions/handlers.launchVirtualGameRes"
}
},
"400": {
"description": "Bad Request",
"schema": {
"$ref": "#/definitions/response.APIResponse"
}
},
"401": {
"description": "Unauthorized",
"schema": {
"$ref": "#/definitions/response.APIResponse"
}
},
"500": {
"description": "Internal Server Error",
"schema": {
"$ref": "#/definitions/response.APIResponse"
}
}
}
}
},
"/wallet": {
"get": {
"description": "Retrieve all wallets",
@ -3019,6 +3463,34 @@ const docTemplate = `{
"BANK"
]
},
"domain.PopOKCallback": {
"type": "object",
"properties": {
"amount": {
"type": "number"
},
"currency": {
"type": "string"
},
"session_id": {
"type": "string"
},
"signature": {
"description": "HMAC-SHA256 signature for verification",
"type": "string"
},
"timestamp": {
"type": "integer"
},
"transaction_id": {
"type": "string"
},
"type": {
"description": "BET, WIN, REFUND, JACKPOT_WIN",
"type": "string"
}
}
},
"domain.RawOddsByMarketID": {
"type": "object",
"properties": {
@ -3040,6 +3512,58 @@ const docTemplate = `{
}
}
},
"domain.ReferralSettings": {
"type": "object",
"properties": {
"betReferralBonusPercentage": {
"type": "number"
},
"cashbackPercentage": {
"type": "number"
},
"createdAt": {
"type": "string"
},
"expiresAfterDays": {
"type": "integer"
},
"id": {
"type": "integer"
},
"maxReferrals": {
"type": "integer"
},
"referralRewardAmount": {
"type": "number"
},
"updatedAt": {
"type": "string"
},
"updatedBy": {
"type": "string"
},
"version": {
"type": "integer"
}
}
},
"domain.ReferralStats": {
"type": "object",
"properties": {
"completedReferrals": {
"type": "integer"
},
"pendingRewards": {
"type": "number"
},
"totalReferrals": {
"type": "integer"
},
"totalRewardEarned": {
"type": "number"
}
}
},
"domain.Role": {
"type": "string",
"enum": [
@ -3327,6 +3851,9 @@ const docTemplate = `{
},
"handlers.CheckPhoneEmailExistReq": {
"type": "object",
"required": [
"phone_number"
],
"properties": {
"email": {
"type": "string",
@ -3370,11 +3897,39 @@ const docTemplate = `{
}
}
},
"handlers.CreateAdminReq": {
"type": "object",
"properties": {
"company_id": {
"type": "integer",
"example": 1
},
"email": {
"type": "string",
"example": "john.doe@example.com"
},
"first_name": {
"type": "string",
"example": "John"
},
"last_name": {
"type": "string",
"example": "Doe"
},
"password": {
"type": "string",
"example": "password123"
},
"phone_number": {
"type": "string",
"example": "1234567890"
}
}
},
"handlers.CreateBetOutcomeReq": {
"type": "object",
"properties": {
"event_id": {
"description": "BetID int64 ` + "`" + `json:\"bet_id\" example:\"1\"` + "`" + `",
"type": "integer",
"example": 1
},
@ -3399,10 +3954,6 @@ const docTemplate = `{
"type": "string",
"example": "John"
},
"is_shop_bet": {
"type": "boolean",
"example": false
},
"outcomes": {
"type": "array",
"items": {
@ -3497,6 +4048,10 @@ const docTemplate = `{
"phone_number": {
"type": "string",
"example": "1234567890"
},
"suspended": {
"type": "boolean",
"example": false
}
}
},
@ -3516,6 +4071,10 @@ const docTemplate = `{
"handlers.CreateManagerReq": {
"type": "object",
"properties": {
"company_id": {
"type": "integer",
"example": 1
},
"email": {
"type": "string",
"example": "john.doe@example.com"
@ -3737,7 +4296,6 @@ const docTemplate = `{
"example": "Doe"
},
"otp": {
"description": "Role string",
"type": "string",
"example": "123456"
},
@ -3770,18 +4328,27 @@ const docTemplate = `{
},
"handlers.ResetPasswordReq": {
"type": "object",
"required": [
"otp",
"password"
],
"properties": {
"email": {
"type": "string"
"type": "string",
"example": "john.doe@example.com"
},
"otp": {
"type": "string"
"type": "string",
"example": "123456"
},
"password": {
"type": "string"
"type": "string",
"minLength": 8,
"example": "newpassword123"
},
"phoneNumber": {
"type": "string"
"phone_number": {
"type": "string",
"example": "1234567890"
}
}
},
@ -3872,6 +4439,10 @@ const docTemplate = `{
"type": "integer",
"example": 1
},
"number_of_outcomes": {
"type": "integer",
"example": 1
},
"payment_option": {
"allOf": [
{
@ -3952,17 +4523,25 @@ const docTemplate = `{
},
"handlers.UpdateTransactionVerifiedReq": {
"type": "object",
"required": [
"verified"
],
"properties": {
"verified": {
"type": "boolean"
"type": "boolean",
"example": true
}
}
},
"handlers.UpdateWalletActiveReq": {
"type": "object",
"required": [
"is_active"
],
"properties": {
"isActive": {
"type": "boolean"
"is_active": {
"type": "boolean",
"example": true
}
}
},
@ -3984,6 +4563,9 @@ const docTemplate = `{
"id": {
"type": "integer"
},
"last_login": {
"type": "string"
},
"last_name": {
"type": "string"
},
@ -4046,8 +4628,45 @@ const docTemplate = `{
}
}
},
"handlers.launchVirtualGameReq": {
"type": "object",
"required": [
"currency",
"game_id",
"mode"
],
"properties": {
"currency": {
"type": "string",
"example": "USD"
},
"game_id": {
"type": "string",
"example": "crash_001"
},
"mode": {
"type": "string",
"enum": [
"REAL",
"DEMO"
],
"example": "REAL"
}
}
},
"handlers.launchVirtualGameRes": {
"type": "object",
"properties": {
"launch_url": {
"type": "string"
}
}
},
"handlers.loginCustomerReq": {
"type": "object",
"required": [
"password"
],
"properties": {
"email": {
"type": "string",
@ -4079,20 +4698,30 @@ const docTemplate = `{
},
"handlers.logoutReq": {
"type": "object",
"required": [
"refresh_token"
],
"properties": {
"refresh_token": {
"type": "string"
"type": "string",
"example": "\u003crefresh-token\u003e"
}
}
},
"handlers.refreshToken": {
"type": "object",
"required": [
"access_token",
"refresh_token"
],
"properties": {
"access_token": {
"type": "string"
"type": "string",
"example": "\u003cjwt-token\u003e"
},
"refresh_token": {
"type": "string"
"type": "string",
"example": "\u003crefresh-token\u003e"
}
}
},

View File

@ -16,6 +16,111 @@
"version": "1.0"
},
"paths": {
"/admin": {
"get": {
"description": "Get all Admins",
"consumes": [
"application/json"
],
"produces": [
"application/json"
],
"tags": [
"admin"
],
"summary": "Get all Admins",
"parameters": [
{
"type": "integer",
"description": "Page number",
"name": "page",
"in": "query"
},
{
"type": "integer",
"description": "Page size",
"name": "page_size",
"in": "query"
}
],
"responses": {
"200": {
"description": "OK",
"schema": {
"$ref": "#/definitions/response.APIResponse"
}
},
"400": {
"description": "Bad Request",
"schema": {
"$ref": "#/definitions/response.APIResponse"
}
},
"401": {
"description": "Unauthorized",
"schema": {
"$ref": "#/definitions/response.APIResponse"
}
},
"500": {
"description": "Internal Server Error",
"schema": {
"$ref": "#/definitions/response.APIResponse"
}
}
}
},
"post": {
"description": "Create Admin",
"consumes": [
"application/json"
],
"produces": [
"application/json"
],
"tags": [
"admin"
],
"summary": "Create Admin",
"parameters": [
{
"description": "Create admin",
"name": "manger",
"in": "body",
"required": true,
"schema": {
"$ref": "#/definitions/handlers.CreateAdminReq"
}
}
],
"responses": {
"200": {
"description": "OK",
"schema": {
"$ref": "#/definitions/response.APIResponse"
}
},
"400": {
"description": "Bad Request",
"schema": {
"$ref": "#/definitions/response.APIResponse"
}
},
"401": {
"description": "Unauthorized",
"schema": {
"$ref": "#/definitions/response.APIResponse"
}
},
"500": {
"description": "Internal Server Error",
"schema": {
"$ref": "#/definitions/response.APIResponse"
}
}
}
}
},
"/auth/login": {
"post": {
"description": "Login customer",
@ -947,6 +1052,13 @@
],
"summary": "Update cashier",
"parameters": [
{
"type": "integer",
"description": "Cashier ID",
"name": "id",
"in": "path",
"required": true
},
{
"description": "Update cashier",
"name": "cashier",
@ -1353,7 +1465,7 @@
}
},
"post": {
"description": "Create Managers",
"description": "Create Manager",
"consumes": [
"application/json"
],
@ -1363,7 +1475,7 @@
"tags": [
"manager"
],
"summary": "Create Managers",
"summary": "Create Manager",
"parameters": [
{
"description": "Create manager",
@ -1795,6 +1907,147 @@
}
}
},
"/referral/settings": {
"get": {
"security": [
{
"Bearer": []
}
],
"description": "Retrieves current referral settings (admin only)",
"consumes": [
"application/json"
],
"produces": [
"application/json"
],
"tags": [
"referral"
],
"summary": "Get referral settings",
"responses": {
"200": {
"description": "OK",
"schema": {
"$ref": "#/definitions/domain.ReferralSettings"
}
},
"401": {
"description": "Unauthorized",
"schema": {
"$ref": "#/definitions/response.APIResponse"
}
},
"403": {
"description": "Forbidden",
"schema": {
"$ref": "#/definitions/response.APIResponse"
}
},
"500": {
"description": "Internal Server Error",
"schema": {
"$ref": "#/definitions/response.APIResponse"
}
}
}
},
"put": {
"security": [
{
"Bearer": []
}
],
"description": "Updates referral settings (admin only)",
"consumes": [
"application/json"
],
"produces": [
"application/json"
],
"tags": [
"referral"
],
"summary": "Update referral settings",
"parameters": [
{
"description": "Referral settings",
"name": "settings",
"in": "body",
"required": true,
"schema": {
"$ref": "#/definitions/domain.ReferralSettings"
}
}
],
"responses": {
"200": {
"description": "OK",
"schema": {
"$ref": "#/definitions/response.APIResponse"
}
},
"401": {
"description": "Unauthorized",
"schema": {
"$ref": "#/definitions/response.APIResponse"
}
},
"403": {
"description": "Forbidden",
"schema": {
"$ref": "#/definitions/response.APIResponse"
}
},
"500": {
"description": "Internal Server Error",
"schema": {
"$ref": "#/definitions/response.APIResponse"
}
}
}
}
},
"/referral/stats": {
"get": {
"security": [
{
"Bearer": []
}
],
"description": "Retrieves referral statistics for the authenticated user",
"consumes": [
"application/json"
],
"produces": [
"application/json"
],
"tags": [
"referral"
],
"summary": "Get referral statistics",
"responses": {
"200": {
"description": "OK",
"schema": {
"$ref": "#/definitions/domain.ReferralStats"
}
},
"401": {
"description": "Unauthorized",
"schema": {
"$ref": "#/definitions/response.APIResponse"
}
},
"500": {
"description": "Internal Server Error",
"schema": {
"$ref": "#/definitions/response.APIResponse"
}
}
}
}
},
"/search/branch": {
"get": {
"description": "Search branches by name or location",
@ -1842,6 +2095,44 @@
}
}
},
"/search/company": {
"get": {
"description": "Gets all companies",
"consumes": [
"application/json"
],
"produces": [
"application/json"
],
"tags": [
"company"
],
"summary": "Gets all companies",
"responses": {
"200": {
"description": "OK",
"schema": {
"type": "array",
"items": {
"$ref": "#/definitions/handlers.CompanyRes"
}
}
},
"400": {
"description": "Bad Request",
"schema": {
"$ref": "#/definitions/response.APIResponse"
}
},
"500": {
"description": "Internal Server Error",
"schema": {
"$ref": "#/definitions/response.APIResponse"
}
}
}
}
},
"/supportedOperation": {
"get": {
"description": "Gets all supported operations",
@ -2176,7 +2467,7 @@
}
},
"patch": {
"description": "Updates the cashed out field",
"description": "Updates the verified status of a transaction",
"consumes": [
"application/json"
],
@ -2186,7 +2477,7 @@
"tags": [
"transaction"
],
"summary": "Updates the cashed out field",
"summary": "Updates the verified field of a transaction",
"parameters": [
{
"type": "integer",
@ -2197,7 +2488,7 @@
},
{
"description": "Updates Transaction Verification",
"name": "updateCashOut",
"name": "updateVerified",
"in": "body",
"required": true,
"schema": {
@ -2681,6 +2972,56 @@
}
}
},
"/user/single/{id}": {
"get": {
"description": "Get a single user by id",
"consumes": [
"application/json"
],
"produces": [
"application/json"
],
"tags": [
"user"
],
"summary": "Get user by id",
"parameters": [
{
"type": "integer",
"description": "User ID",
"name": "id",
"in": "path",
"required": true
}
],
"responses": {
"200": {
"description": "OK",
"schema": {
"$ref": "#/definitions/response.APIResponse"
}
},
"400": {
"description": "Bad Request",
"schema": {
"$ref": "#/definitions/response.APIResponse"
}
},
"401": {
"description": "Unauthorized",
"schema": {
"$ref": "#/definitions/response.APIResponse"
}
},
"500": {
"description": "Internal Server Error",
"schema": {
"$ref": "#/definitions/response.APIResponse"
}
}
}
}
},
"/user/wallet": {
"get": {
"security": [
@ -2730,6 +3071,109 @@
}
}
},
"/virtual-game/callback": {
"post": {
"description": "Processes callbacks from PopOK for game events",
"consumes": [
"application/json"
],
"produces": [
"application/json"
],
"tags": [
"virtual-game"
],
"summary": "Handle PopOK game callback",
"parameters": [
{
"description": "Callback data",
"name": "callback",
"in": "body",
"required": true,
"schema": {
"$ref": "#/definitions/domain.PopOKCallback"
}
}
],
"responses": {
"200": {
"description": "OK",
"schema": {
"$ref": "#/definitions/response.APIResponse"
}
},
"400": {
"description": "Bad Request",
"schema": {
"$ref": "#/definitions/response.APIResponse"
}
},
"500": {
"description": "Internal Server Error",
"schema": {
"$ref": "#/definitions/response.APIResponse"
}
}
}
}
},
"/virtual-game/launch": {
"post": {
"security": [
{
"Bearer": []
}
],
"description": "Generates a URL to launch a PopOK game",
"consumes": [
"application/json"
],
"produces": [
"application/json"
],
"tags": [
"virtual-game"
],
"summary": "Launch a PopOK virtual game",
"parameters": [
{
"description": "Game launch details",
"name": "launch",
"in": "body",
"required": true,
"schema": {
"$ref": "#/definitions/handlers.launchVirtualGameReq"
}
}
],
"responses": {
"200": {
"description": "OK",
"schema": {
"$ref": "#/definitions/handlers.launchVirtualGameRes"
}
},
"400": {
"description": "Bad Request",
"schema": {
"$ref": "#/definitions/response.APIResponse"
}
},
"401": {
"description": "Unauthorized",
"schema": {
"$ref": "#/definitions/response.APIResponse"
}
},
"500": {
"description": "Internal Server Error",
"schema": {
"$ref": "#/definitions/response.APIResponse"
}
}
}
}
},
"/wallet": {
"get": {
"description": "Retrieve all wallets",
@ -3011,6 +3455,34 @@
"BANK"
]
},
"domain.PopOKCallback": {
"type": "object",
"properties": {
"amount": {
"type": "number"
},
"currency": {
"type": "string"
},
"session_id": {
"type": "string"
},
"signature": {
"description": "HMAC-SHA256 signature for verification",
"type": "string"
},
"timestamp": {
"type": "integer"
},
"transaction_id": {
"type": "string"
},
"type": {
"description": "BET, WIN, REFUND, JACKPOT_WIN",
"type": "string"
}
}
},
"domain.RawOddsByMarketID": {
"type": "object",
"properties": {
@ -3032,6 +3504,58 @@
}
}
},
"domain.ReferralSettings": {
"type": "object",
"properties": {
"betReferralBonusPercentage": {
"type": "number"
},
"cashbackPercentage": {
"type": "number"
},
"createdAt": {
"type": "string"
},
"expiresAfterDays": {
"type": "integer"
},
"id": {
"type": "integer"
},
"maxReferrals": {
"type": "integer"
},
"referralRewardAmount": {
"type": "number"
},
"updatedAt": {
"type": "string"
},
"updatedBy": {
"type": "string"
},
"version": {
"type": "integer"
}
}
},
"domain.ReferralStats": {
"type": "object",
"properties": {
"completedReferrals": {
"type": "integer"
},
"pendingRewards": {
"type": "number"
},
"totalReferrals": {
"type": "integer"
},
"totalRewardEarned": {
"type": "number"
}
}
},
"domain.Role": {
"type": "string",
"enum": [
@ -3319,6 +3843,9 @@
},
"handlers.CheckPhoneEmailExistReq": {
"type": "object",
"required": [
"phone_number"
],
"properties": {
"email": {
"type": "string",
@ -3362,11 +3889,39 @@
}
}
},
"handlers.CreateAdminReq": {
"type": "object",
"properties": {
"company_id": {
"type": "integer",
"example": 1
},
"email": {
"type": "string",
"example": "john.doe@example.com"
},
"first_name": {
"type": "string",
"example": "John"
},
"last_name": {
"type": "string",
"example": "Doe"
},
"password": {
"type": "string",
"example": "password123"
},
"phone_number": {
"type": "string",
"example": "1234567890"
}
}
},
"handlers.CreateBetOutcomeReq": {
"type": "object",
"properties": {
"event_id": {
"description": "BetID int64 `json:\"bet_id\" example:\"1\"`",
"type": "integer",
"example": 1
},
@ -3391,10 +3946,6 @@
"type": "string",
"example": "John"
},
"is_shop_bet": {
"type": "boolean",
"example": false
},
"outcomes": {
"type": "array",
"items": {
@ -3489,6 +4040,10 @@
"phone_number": {
"type": "string",
"example": "1234567890"
},
"suspended": {
"type": "boolean",
"example": false
}
}
},
@ -3508,6 +4063,10 @@
"handlers.CreateManagerReq": {
"type": "object",
"properties": {
"company_id": {
"type": "integer",
"example": 1
},
"email": {
"type": "string",
"example": "john.doe@example.com"
@ -3729,7 +4288,6 @@
"example": "Doe"
},
"otp": {
"description": "Role string",
"type": "string",
"example": "123456"
},
@ -3762,18 +4320,27 @@
},
"handlers.ResetPasswordReq": {
"type": "object",
"required": [
"otp",
"password"
],
"properties": {
"email": {
"type": "string"
"type": "string",
"example": "john.doe@example.com"
},
"otp": {
"type": "string"
"type": "string",
"example": "123456"
},
"password": {
"type": "string"
"type": "string",
"minLength": 8,
"example": "newpassword123"
},
"phoneNumber": {
"type": "string"
"phone_number": {
"type": "string",
"example": "1234567890"
}
}
},
@ -3864,6 +4431,10 @@
"type": "integer",
"example": 1
},
"number_of_outcomes": {
"type": "integer",
"example": 1
},
"payment_option": {
"allOf": [
{
@ -3944,17 +4515,25 @@
},
"handlers.UpdateTransactionVerifiedReq": {
"type": "object",
"required": [
"verified"
],
"properties": {
"verified": {
"type": "boolean"
"type": "boolean",
"example": true
}
}
},
"handlers.UpdateWalletActiveReq": {
"type": "object",
"required": [
"is_active"
],
"properties": {
"isActive": {
"type": "boolean"
"is_active": {
"type": "boolean",
"example": true
}
}
},
@ -3976,6 +4555,9 @@
"id": {
"type": "integer"
},
"last_login": {
"type": "string"
},
"last_name": {
"type": "string"
},
@ -4038,8 +4620,45 @@
}
}
},
"handlers.launchVirtualGameReq": {
"type": "object",
"required": [
"currency",
"game_id",
"mode"
],
"properties": {
"currency": {
"type": "string",
"example": "USD"
},
"game_id": {
"type": "string",
"example": "crash_001"
},
"mode": {
"type": "string",
"enum": [
"REAL",
"DEMO"
],
"example": "REAL"
}
}
},
"handlers.launchVirtualGameRes": {
"type": "object",
"properties": {
"launch_url": {
"type": "string"
}
}
},
"handlers.loginCustomerReq": {
"type": "object",
"required": [
"password"
],
"properties": {
"email": {
"type": "string",
@ -4071,20 +4690,30 @@
},
"handlers.logoutReq": {
"type": "object",
"required": [
"refresh_token"
],
"properties": {
"refresh_token": {
"type": "string"
"type": "string",
"example": "\u003crefresh-token\u003e"
}
}
},
"handlers.refreshToken": {
"type": "object",
"required": [
"access_token",
"refresh_token"
],
"properties": {
"access_token": {
"type": "string"
"type": "string",
"example": "\u003cjwt-token\u003e"
},
"refresh_token": {
"type": "string"
"type": "string",
"example": "\u003crefresh-token\u003e"
}
}
},

View File

@ -103,6 +103,25 @@ definitions:
- TELEBIRR_TRANSACTION
- ARIFPAY_TRANSACTION
- BANK
domain.PopOKCallback:
properties:
amount:
type: number
currency:
type: string
session_id:
type: string
signature:
description: HMAC-SHA256 signature for verification
type: string
timestamp:
type: integer
transaction_id:
type: string
type:
description: BET, WIN, REFUND, JACKPOT_WIN
type: string
type: object
domain.RawOddsByMarketID:
properties:
fetched_at:
@ -117,6 +136,40 @@ definitions:
items: {}
type: array
type: object
domain.ReferralSettings:
properties:
betReferralBonusPercentage:
type: number
cashbackPercentage:
type: number
createdAt:
type: string
expiresAfterDays:
type: integer
id:
type: integer
maxReferrals:
type: integer
referralRewardAmount:
type: number
updatedAt:
type: string
updatedBy:
type: string
version:
type: integer
type: object
domain.ReferralStats:
properties:
completedReferrals:
type: integer
pendingRewards:
type: number
totalReferrals:
type: integer
totalRewardEarned:
type: number
type: object
domain.Role:
enum:
- super_admin
@ -331,6 +384,8 @@ definitions:
phone_number:
example: "1234567890"
type: string
required:
- phone_number
type: object
handlers.CheckPhoneEmailExistRes:
properties:
@ -354,10 +409,30 @@ definitions:
example: 1
type: integer
type: object
handlers.CreateAdminReq:
properties:
company_id:
example: 1
type: integer
email:
example: john.doe@example.com
type: string
first_name:
example: John
type: string
last_name:
example: Doe
type: string
password:
example: password123
type: string
phone_number:
example: "1234567890"
type: string
type: object
handlers.CreateBetOutcomeReq:
properties:
event_id:
description: BetID int64 `json:"bet_id" example:"1"`
example: 1
type: integer
market_id:
@ -375,9 +450,6 @@ definitions:
full_name:
example: John
type: string
is_shop_bet:
example: false
type: boolean
outcomes:
items:
$ref: '#/definitions/handlers.CreateBetOutcomeReq'
@ -444,6 +516,9 @@ definitions:
phone_number:
example: "1234567890"
type: string
suspended:
example: false
type: boolean
type: object
handlers.CreateCompanyReq:
properties:
@ -456,6 +531,9 @@ definitions:
type: object
handlers.CreateManagerReq:
properties:
company_id:
example: 1
type: integer
email:
example: john.doe@example.com
type: string
@ -611,7 +689,6 @@ definitions:
example: Doe
type: string
otp:
description: Role string
example: "123456"
type: string
password:
@ -636,13 +713,21 @@ definitions:
handlers.ResetPasswordReq:
properties:
email:
example: john.doe@example.com
type: string
otp:
example: "123456"
type: string
password:
example: newpassword123
minLength: 8
type: string
phoneNumber:
phone_number:
example: "1234567890"
type: string
required:
- otp
- password
type: object
handlers.SearchUserByNameOrPhoneReq:
properties:
@ -705,6 +790,9 @@ definitions:
id:
example: 1
type: integer
number_of_outcomes:
example: 1
type: integer
payment_option:
allOf:
- $ref: '#/definitions/domain.PaymentOption'
@ -762,12 +850,18 @@ definitions:
handlers.UpdateTransactionVerifiedReq:
properties:
verified:
example: true
type: boolean
required:
- verified
type: object
handlers.UpdateWalletActiveReq:
properties:
isActive:
is_active:
example: true
type: boolean
required:
- is_active
type: object
handlers.UserProfileRes:
properties:
@ -781,6 +875,8 @@ definitions:
type: string
id:
type: integer
last_login:
type: string
last_name:
type: string
phone_number:
@ -824,6 +920,30 @@ definitions:
example: 1
type: integer
type: object
handlers.launchVirtualGameReq:
properties:
currency:
example: USD
type: string
game_id:
example: crash_001
type: string
mode:
enum:
- REAL
- DEMO
example: REAL
type: string
required:
- currency
- game_id
- mode
type: object
handlers.launchVirtualGameRes:
properties:
launch_url:
type: string
type: object
handlers.loginCustomerReq:
properties:
email:
@ -835,6 +955,8 @@ definitions:
phone_number:
example: "1234567890"
type: string
required:
- password
type: object
handlers.loginCustomerRes:
properties:
@ -848,14 +970,22 @@ definitions:
handlers.logoutReq:
properties:
refresh_token:
example: <refresh-token>
type: string
required:
- refresh_token
type: object
handlers.refreshToken:
properties:
access_token:
example: <jwt-token>
type: string
refresh_token:
example: <refresh-token>
type: string
required:
- access_token
- refresh_token
type: object
handlers.updateUserReq:
properties:
@ -905,6 +1035,75 @@ info:
title: FortuneBet API
version: "1.0"
paths:
/admin:
get:
consumes:
- application/json
description: Get all Admins
parameters:
- description: Page number
in: query
name: page
type: integer
- description: Page size
in: query
name: page_size
type: integer
produces:
- application/json
responses:
"200":
description: OK
schema:
$ref: '#/definitions/response.APIResponse'
"400":
description: Bad Request
schema:
$ref: '#/definitions/response.APIResponse'
"401":
description: Unauthorized
schema:
$ref: '#/definitions/response.APIResponse'
"500":
description: Internal Server Error
schema:
$ref: '#/definitions/response.APIResponse'
summary: Get all Admins
tags:
- admin
post:
consumes:
- application/json
description: Create Admin
parameters:
- description: Create admin
in: body
name: manger
required: true
schema:
$ref: '#/definitions/handlers.CreateAdminReq'
produces:
- application/json
responses:
"200":
description: OK
schema:
$ref: '#/definitions/response.APIResponse'
"400":
description: Bad Request
schema:
$ref: '#/definitions/response.APIResponse'
"401":
description: Unauthorized
schema:
$ref: '#/definitions/response.APIResponse'
"500":
description: Internal Server Error
schema:
$ref: '#/definitions/response.APIResponse'
summary: Create Admin
tags:
- admin
/auth/login:
post:
consumes:
@ -1516,6 +1715,11 @@ paths:
- application/json
description: Update cashier
parameters:
- description: Cashier ID
in: path
name: id
required: true
type: integer
- description: Update cashier
in: body
name: cashier
@ -1790,7 +1994,7 @@ paths:
post:
consumes:
- application/json
description: Create Managers
description: Create Manager
parameters:
- description: Create manager
in: body
@ -1817,7 +2021,7 @@ paths:
description: Internal Server Error
schema:
$ref: '#/definitions/response.APIResponse'
summary: Create Managers
summary: Create Manager
tags:
- manager
/managers/{id}:
@ -2079,6 +2283,95 @@ paths:
summary: Retrieve raw odds by Market ID
tags:
- prematch
/referral/settings:
get:
consumes:
- application/json
description: Retrieves current referral settings (admin only)
produces:
- application/json
responses:
"200":
description: OK
schema:
$ref: '#/definitions/domain.ReferralSettings'
"401":
description: Unauthorized
schema:
$ref: '#/definitions/response.APIResponse'
"403":
description: Forbidden
schema:
$ref: '#/definitions/response.APIResponse'
"500":
description: Internal Server Error
schema:
$ref: '#/definitions/response.APIResponse'
security:
- Bearer: []
summary: Get referral settings
tags:
- referral
put:
consumes:
- application/json
description: Updates referral settings (admin only)
parameters:
- description: Referral settings
in: body
name: settings
required: true
schema:
$ref: '#/definitions/domain.ReferralSettings'
produces:
- application/json
responses:
"200":
description: OK
schema:
$ref: '#/definitions/response.APIResponse'
"401":
description: Unauthorized
schema:
$ref: '#/definitions/response.APIResponse'
"403":
description: Forbidden
schema:
$ref: '#/definitions/response.APIResponse'
"500":
description: Internal Server Error
schema:
$ref: '#/definitions/response.APIResponse'
security:
- Bearer: []
summary: Update referral settings
tags:
- referral
/referral/stats:
get:
consumes:
- application/json
description: Retrieves referral statistics for the authenticated user
produces:
- application/json
responses:
"200":
description: OK
schema:
$ref: '#/definitions/domain.ReferralStats'
"401":
description: Unauthorized
schema:
$ref: '#/definitions/response.APIResponse'
"500":
description: Internal Server Error
schema:
$ref: '#/definitions/response.APIResponse'
security:
- Bearer: []
summary: Get referral statistics
tags:
- referral
/search/branch:
get:
consumes:
@ -2110,6 +2403,31 @@ paths:
summary: Search branches
tags:
- branch
/search/company:
get:
consumes:
- application/json
description: Gets all companies
produces:
- application/json
responses:
"200":
description: OK
schema:
items:
$ref: '#/definitions/handlers.CompanyRes'
type: array
"400":
description: Bad Request
schema:
$ref: '#/definitions/response.APIResponse'
"500":
description: Internal Server Error
schema:
$ref: '#/definitions/response.APIResponse'
summary: Gets all companies
tags:
- company
/supportedOperation:
get:
consumes:
@ -2333,7 +2651,7 @@ paths:
patch:
consumes:
- application/json
description: Updates the cashed out field
description: Updates the verified status of a transaction
parameters:
- description: Transaction ID
in: path
@ -2342,7 +2660,7 @@ paths:
type: integer
- description: Updates Transaction Verification
in: body
name: updateCashOut
name: updateVerified
required: true
schema:
$ref: '#/definitions/handlers.UpdateTransactionVerifiedReq'
@ -2361,7 +2679,7 @@ paths:
description: Internal Server Error
schema:
$ref: '#/definitions/response.APIResponse'
summary: Updates the cashed out field
summary: Updates the verified field of a transaction
tags:
- transaction
/transfer/refill/:id:
@ -2659,6 +2977,39 @@ paths:
summary: Send reset code
tags:
- user
/user/single/{id}:
get:
consumes:
- application/json
description: Get a single user by id
parameters:
- description: User ID
in: path
name: id
required: true
type: integer
produces:
- application/json
responses:
"200":
description: OK
schema:
$ref: '#/definitions/response.APIResponse'
"400":
description: Bad Request
schema:
$ref: '#/definitions/response.APIResponse'
"401":
description: Unauthorized
schema:
$ref: '#/definitions/response.APIResponse'
"500":
description: Internal Server Error
schema:
$ref: '#/definitions/response.APIResponse'
summary: Get user by id
tags:
- user
/user/wallet:
get:
consumes:
@ -2690,6 +3041,72 @@ paths:
summary: Get customer wallet
tags:
- wallet
/virtual-game/callback:
post:
consumes:
- application/json
description: Processes callbacks from PopOK for game events
parameters:
- description: Callback data
in: body
name: callback
required: true
schema:
$ref: '#/definitions/domain.PopOKCallback'
produces:
- application/json
responses:
"200":
description: OK
schema:
$ref: '#/definitions/response.APIResponse'
"400":
description: Bad Request
schema:
$ref: '#/definitions/response.APIResponse'
"500":
description: Internal Server Error
schema:
$ref: '#/definitions/response.APIResponse'
summary: Handle PopOK game callback
tags:
- virtual-game
/virtual-game/launch:
post:
consumes:
- application/json
description: Generates a URL to launch a PopOK game
parameters:
- description: Game launch details
in: body
name: launch
required: true
schema:
$ref: '#/definitions/handlers.launchVirtualGameReq'
produces:
- application/json
responses:
"200":
description: OK
schema:
$ref: '#/definitions/handlers.launchVirtualGameRes'
"400":
description: Bad Request
schema:
$ref: '#/definitions/response.APIResponse'
"401":
description: Unauthorized
schema:
$ref: '#/definitions/response.APIResponse'
"500":
description: Internal Server Error
schema:
$ref: '#/definitions/response.APIResponse'
security:
- Bearer: []
summary: Launch a PopOK virtual game
tags:
- virtual-game
/wallet:
get:
consumes:

View File

@ -36,7 +36,8 @@ func (q *Queries) CreateRefreshToken(ctx context.Context, arg CreateRefreshToken
}
const GetRefreshToken = `-- name: GetRefreshToken :one
SELECT id, user_id, token, expires_at, created_at, revoked FROM refresh_tokens
SELECT id, user_id, token, expires_at, created_at, revoked
FROM refresh_tokens
WHERE token = $1
`
@ -54,9 +55,31 @@ func (q *Queries) GetRefreshToken(ctx context.Context, token string) (RefreshTok
return i, err
}
const GetRefreshTokenByUserID = `-- name: GetRefreshTokenByUserID :one
SELECT id, user_id, token, expires_at, created_at, revoked
FROM refresh_tokens
WHERE user_id = $1
`
func (q *Queries) GetRefreshTokenByUserID(ctx context.Context, userID int64) (RefreshToken, error) {
row := q.db.QueryRow(ctx, GetRefreshTokenByUserID, userID)
var i RefreshToken
err := row.Scan(
&i.ID,
&i.UserID,
&i.Token,
&i.ExpiresAt,
&i.CreatedAt,
&i.Revoked,
)
return i, err
}
const GetUserByEmailPhone = `-- name: GetUserByEmailPhone :one
SELECT id, first_name, last_name, email, phone_number, role, password, email_verified, phone_verified, created_at, updated_at, suspended_at, suspended, referral_code, referred_by FROM users
WHERE email = $1 OR phone_number = $2
SELECT id, first_name, last_name, email, phone_number, role, password, email_verified, phone_verified, created_at, updated_at, company_id, suspended_at, suspended, referral_code, referred_by
FROM users
WHERE email = $1
OR phone_number = $2
`
type GetUserByEmailPhoneParams struct {
@ -79,6 +102,7 @@ func (q *Queries) GetUserByEmailPhone(ctx context.Context, arg GetUserByEmailPho
&i.PhoneVerified,
&i.CreatedAt,
&i.UpdatedAt,
&i.CompanyID,
&i.SuspendedAt,
&i.Suspended,
&i.ReferralCode,

View File

@ -191,7 +191,7 @@ func (q *Queries) GetAllBranches(ctx context.Context) ([]BranchDetail, error) {
}
const GetAllCashiers = `-- name: GetAllCashiers :many
SELECT users.id, users.first_name, users.last_name, users.email, users.phone_number, users.role, users.password, users.email_verified, users.phone_verified, users.created_at, users.updated_at, users.suspended_at, users.suspended, users.referral_code, users.referred_by
SELECT users.id, users.first_name, users.last_name, users.email, users.phone_number, users.role, users.password, users.email_verified, users.phone_verified, users.created_at, users.updated_at, users.company_id, users.suspended_at, users.suspended, users.referral_code, users.referred_by
FROM branch_cashiers
JOIN users ON branch_cashiers.user_id = users.id
`
@ -217,6 +217,7 @@ func (q *Queries) GetAllCashiers(ctx context.Context) ([]User, error) {
&i.PhoneVerified,
&i.CreatedAt,
&i.UpdatedAt,
&i.CompanyID,
&i.SuspendedAt,
&i.Suspended,
&i.ReferralCode,
@ -430,7 +431,7 @@ func (q *Queries) GetBranchOperations(ctx context.Context, branchID int64) ([]Ge
}
const GetCashiersByBranch = `-- name: GetCashiersByBranch :many
SELECT users.id, users.first_name, users.last_name, users.email, users.phone_number, users.role, users.password, users.email_verified, users.phone_verified, users.created_at, users.updated_at, users.suspended_at, users.suspended, users.referral_code, users.referred_by
SELECT users.id, users.first_name, users.last_name, users.email, users.phone_number, users.role, users.password, users.email_verified, users.phone_verified, users.created_at, users.updated_at, users.company_id, users.suspended_at, users.suspended, users.referral_code, users.referred_by
FROM branch_cashiers
JOIN users ON branch_cashiers.user_id = users.id
WHERE branch_cashiers.branch_id = $1
@ -457,6 +458,7 @@ func (q *Queries) GetCashiersByBranch(ctx context.Context, branchID int64) ([]Us
&i.PhoneVerified,
&i.CreatedAt,
&i.UpdatedAt,
&i.CompanyID,
&i.SuspendedAt,
&i.Suspended,
&i.ReferralCode,

View File

@ -7,6 +7,8 @@ package dbgen
import (
"context"
"github.com/jackc/pgx/v5/pgtype"
)
const CreateCompany = `-- name: CreateCompany :one
@ -95,6 +97,37 @@ func (q *Queries) GetCompanyByID(ctx context.Context, id int64) (Company, error)
return i, err
}
const SearchCompanyByName = `-- name: SearchCompanyByName :many
SELECT id, name, admin_id, wallet_id
FROM companies
WHERE name ILIKE '%' || $1 || '%'
`
func (q *Queries) SearchCompanyByName(ctx context.Context, dollar_1 pgtype.Text) ([]Company, error) {
rows, err := q.db.Query(ctx, SearchCompanyByName, dollar_1)
if err != nil {
return nil, err
}
defer rows.Close()
var items []Company
for rows.Next() {
var i Company
if err := rows.Scan(
&i.ID,
&i.Name,
&i.AdminID,
&i.WalletID,
); err != nil {
return nil, err
}
items = append(items, i)
}
if err := rows.Err(); err != nil {
return nil, err
}
return items, nil
}
const UpdateCompany = `-- name: UpdateCompany :one
UPDATE companies
SET name = $1,

View File

@ -310,23 +310,24 @@ type TicketWithOutcome struct {
}
type Transaction struct {
ID int64 `json:"id"`
Amount int64 `json:"amount"`
BranchID int64 `json:"branch_id"`
CashierID int64 `json:"cashier_id"`
BetID int64 `json:"bet_id"`
Type int64 `json:"type"`
PaymentOption int64 `json:"payment_option"`
FullName string `json:"full_name"`
PhoneNumber string `json:"phone_number"`
BankCode string `json:"bank_code"`
BeneficiaryName string `json:"beneficiary_name"`
AccountName string `json:"account_name"`
AccountNumber string `json:"account_number"`
ReferenceNumber string `json:"reference_number"`
Verified bool `json:"verified"`
CreatedAt pgtype.Timestamp `json:"created_at"`
UpdatedAt pgtype.Timestamp `json:"updated_at"`
ID int64 `json:"id"`
Amount int64 `json:"amount"`
BranchID int64 `json:"branch_id"`
CashierID int64 `json:"cashier_id"`
BetID int64 `json:"bet_id"`
NumberOfOutcomes int64 `json:"number_of_outcomes"`
Type int64 `json:"type"`
PaymentOption int64 `json:"payment_option"`
FullName string `json:"full_name"`
PhoneNumber string `json:"phone_number"`
BankCode string `json:"bank_code"`
BeneficiaryName string `json:"beneficiary_name"`
AccountName string `json:"account_name"`
AccountNumber string `json:"account_number"`
ReferenceNumber string `json:"reference_number"`
Verified bool `json:"verified"`
CreatedAt pgtype.Timestamp `json:"created_at"`
UpdatedAt pgtype.Timestamp `json:"updated_at"`
}
type User struct {
@ -341,6 +342,7 @@ type User struct {
PhoneVerified bool `json:"phone_verified"`
CreatedAt pgtype.Timestamptz `json:"created_at"`
UpdatedAt pgtype.Timestamptz `json:"updated_at"`
CompanyID pgtype.Int8 `json:"company_id"`
SuspendedAt pgtype.Timestamptz `json:"suspended_at"`
Suspended bool `json:"suspended"`
ReferralCode pgtype.Text `json:"referral_code"`

View File

@ -10,7 +10,7 @@ import (
)
const CreateTransaction = `-- name: CreateTransaction :one
INSERT INTO transactions (amount, branch_id, cashier_id, bet_id, type, payment_option, full_name, phone_number, bank_code, beneficiary_name, account_name, account_number, reference_number) VALUES ($1, $2, $3, $4, $5, $6, $7, $8, $9, $10, $11, $12, $13) RETURNING id, amount, branch_id, cashier_id, bet_id, type, payment_option, full_name, phone_number, bank_code, beneficiary_name, account_name, account_number, reference_number, verified, created_at, updated_at
INSERT INTO transactions (amount, branch_id, cashier_id, bet_id, type, payment_option, full_name, phone_number, bank_code, beneficiary_name, account_name, account_number, reference_number) VALUES ($1, $2, $3, $4, $5, $6, $7, $8, $9, $10, $11, $12, $13) RETURNING id, amount, branch_id, cashier_id, bet_id, number_of_outcomes, type, payment_option, full_name, phone_number, bank_code, beneficiary_name, account_name, account_number, reference_number, verified, created_at, updated_at
`
type CreateTransactionParams struct {
@ -52,6 +52,7 @@ func (q *Queries) CreateTransaction(ctx context.Context, arg CreateTransactionPa
&i.BranchID,
&i.CashierID,
&i.BetID,
&i.NumberOfOutcomes,
&i.Type,
&i.PaymentOption,
&i.FullName,
@ -69,7 +70,7 @@ func (q *Queries) CreateTransaction(ctx context.Context, arg CreateTransactionPa
}
const GetAllTransactions = `-- name: GetAllTransactions :many
SELECT id, amount, branch_id, cashier_id, bet_id, type, payment_option, full_name, phone_number, bank_code, beneficiary_name, account_name, account_number, reference_number, verified, created_at, updated_at FROM transactions
SELECT id, amount, branch_id, cashier_id, bet_id, number_of_outcomes, type, payment_option, full_name, phone_number, bank_code, beneficiary_name, account_name, account_number, reference_number, verified, created_at, updated_at FROM transactions
`
func (q *Queries) GetAllTransactions(ctx context.Context) ([]Transaction, error) {
@ -87,6 +88,7 @@ func (q *Queries) GetAllTransactions(ctx context.Context) ([]Transaction, error)
&i.BranchID,
&i.CashierID,
&i.BetID,
&i.NumberOfOutcomes,
&i.Type,
&i.PaymentOption,
&i.FullName,
@ -111,7 +113,7 @@ func (q *Queries) GetAllTransactions(ctx context.Context) ([]Transaction, error)
}
const GetTransactionByBranch = `-- name: GetTransactionByBranch :many
SELECT id, amount, branch_id, cashier_id, bet_id, type, payment_option, full_name, phone_number, bank_code, beneficiary_name, account_name, account_number, reference_number, verified, created_at, updated_at FROM transactions WHERE branch_id = $1
SELECT id, amount, branch_id, cashier_id, bet_id, number_of_outcomes, type, payment_option, full_name, phone_number, bank_code, beneficiary_name, account_name, account_number, reference_number, verified, created_at, updated_at FROM transactions WHERE branch_id = $1
`
func (q *Queries) GetTransactionByBranch(ctx context.Context, branchID int64) ([]Transaction, error) {
@ -129,6 +131,7 @@ func (q *Queries) GetTransactionByBranch(ctx context.Context, branchID int64) ([
&i.BranchID,
&i.CashierID,
&i.BetID,
&i.NumberOfOutcomes,
&i.Type,
&i.PaymentOption,
&i.FullName,
@ -153,7 +156,7 @@ func (q *Queries) GetTransactionByBranch(ctx context.Context, branchID int64) ([
}
const GetTransactionByID = `-- name: GetTransactionByID :one
SELECT id, amount, branch_id, cashier_id, bet_id, type, payment_option, full_name, phone_number, bank_code, beneficiary_name, account_name, account_number, reference_number, verified, created_at, updated_at FROM transactions WHERE id = $1
SELECT id, amount, branch_id, cashier_id, bet_id, number_of_outcomes, type, payment_option, full_name, phone_number, bank_code, beneficiary_name, account_name, account_number, reference_number, verified, created_at, updated_at FROM transactions WHERE id = $1
`
func (q *Queries) GetTransactionByID(ctx context.Context, id int64) (Transaction, error) {
@ -165,6 +168,7 @@ func (q *Queries) GetTransactionByID(ctx context.Context, id int64) (Transaction
&i.BranchID,
&i.CashierID,
&i.BetID,
&i.NumberOfOutcomes,
&i.Type,
&i.PaymentOption,
&i.FullName,

View File

@ -54,9 +54,24 @@ INSERT INTO users (
email_verified,
phone_verified,
created_at,
updated_at
updated_at,
suspended,
company_id
)
VALUES (
$1,
$2,
$3,
$4,
$5,
$6,
$7,
$8,
$9,
$10,
$11,
$12
)
VALUES ($1, $2, $3, $4, $5, $6, $7, $8, $9, $10)
RETURNING id,
first_name,
last_name,
@ -66,7 +81,9 @@ RETURNING id,
email_verified,
phone_verified,
created_at,
updated_at
updated_at,
suspended,
company_id
`
type CreateUserParams struct {
@ -80,6 +97,8 @@ type CreateUserParams struct {
PhoneVerified bool `json:"phone_verified"`
CreatedAt pgtype.Timestamptz `json:"created_at"`
UpdatedAt pgtype.Timestamptz `json:"updated_at"`
Suspended bool `json:"suspended"`
CompanyID pgtype.Int8 `json:"company_id"`
}
type CreateUserRow struct {
@ -93,6 +112,8 @@ type CreateUserRow struct {
PhoneVerified bool `json:"phone_verified"`
CreatedAt pgtype.Timestamptz `json:"created_at"`
UpdatedAt pgtype.Timestamptz `json:"updated_at"`
Suspended bool `json:"suspended"`
CompanyID pgtype.Int8 `json:"company_id"`
}
func (q *Queries) CreateUser(ctx context.Context, arg CreateUserParams) (CreateUserRow, error) {
@ -107,6 +128,8 @@ func (q *Queries) CreateUser(ctx context.Context, arg CreateUserParams) (CreateU
arg.PhoneVerified,
arg.CreatedAt,
arg.UpdatedAt,
arg.Suspended,
arg.CompanyID,
)
var i CreateUserRow
err := row.Scan(
@ -120,6 +143,8 @@ func (q *Queries) CreateUser(ctx context.Context, arg CreateUserParams) (CreateU
&i.PhoneVerified,
&i.CreatedAt,
&i.UpdatedAt,
&i.Suspended,
&i.CompanyID,
)
return i, err
}
@ -144,10 +169,29 @@ SELECT id,
email_verified,
phone_verified,
created_at,
updated_at
updated_at,
suspended,
suspended_at,
company_id
FROM users
wHERE (
role = $1
OR $1 IS NULL
)
AND (
company_id = $2
OR $2 IS NULL
)
LIMIT $3 OFFSET $4
`
type GetAllUsersParams struct {
Role string `json:"role"`
CompanyID pgtype.Int8 `json:"company_id"`
Limit int32 `json:"limit"`
Offset int32 `json:"offset"`
}
type GetAllUsersRow struct {
ID int64 `json:"id"`
FirstName string `json:"first_name"`
@ -159,10 +203,18 @@ type GetAllUsersRow struct {
PhoneVerified bool `json:"phone_verified"`
CreatedAt pgtype.Timestamptz `json:"created_at"`
UpdatedAt pgtype.Timestamptz `json:"updated_at"`
Suspended bool `json:"suspended"`
SuspendedAt pgtype.Timestamptz `json:"suspended_at"`
CompanyID pgtype.Int8 `json:"company_id"`
}
func (q *Queries) GetAllUsers(ctx context.Context) ([]GetAllUsersRow, error) {
rows, err := q.db.Query(ctx, GetAllUsers)
func (q *Queries) GetAllUsers(ctx context.Context, arg GetAllUsersParams) ([]GetAllUsersRow, error) {
rows, err := q.db.Query(ctx, GetAllUsers,
arg.Role,
arg.CompanyID,
arg.Limit,
arg.Offset,
)
if err != nil {
return nil, err
}
@ -181,6 +233,9 @@ func (q *Queries) GetAllUsers(ctx context.Context) ([]GetAllUsersRow, error) {
&i.PhoneVerified,
&i.CreatedAt,
&i.UpdatedAt,
&i.Suspended,
&i.SuspendedAt,
&i.CompanyID,
); err != nil {
return nil, err
}
@ -192,6 +247,31 @@ func (q *Queries) GetAllUsers(ctx context.Context) ([]GetAllUsersRow, error) {
return items, nil
}
const GetTotalUsers = `-- name: GetTotalUsers :one
SELECT COUNT(*)
FROM users
wHERE (
role = $1
OR $1 IS NULL
)
AND (
company_id = $2
OR $2 IS NULL
)
`
type GetTotalUsersParams struct {
Role string `json:"role"`
CompanyID pgtype.Int8 `json:"company_id"`
}
func (q *Queries) GetTotalUsers(ctx context.Context, arg GetTotalUsersParams) (int64, error) {
row := q.db.QueryRow(ctx, GetTotalUsers, arg.Role, arg.CompanyID)
var count int64
err := row.Scan(&count)
return count, err
}
const GetUserByEmail = `-- name: GetUserByEmail :one
SELECT id,
first_name,
@ -202,7 +282,10 @@ SELECT id,
email_verified,
phone_verified,
created_at,
updated_at
updated_at,
suspended,
suspended_at,
company_id
FROM users
WHERE email = $1
`
@ -218,6 +301,9 @@ type GetUserByEmailRow struct {
PhoneVerified bool `json:"phone_verified"`
CreatedAt pgtype.Timestamptz `json:"created_at"`
UpdatedAt pgtype.Timestamptz `json:"updated_at"`
Suspended bool `json:"suspended"`
SuspendedAt pgtype.Timestamptz `json:"suspended_at"`
CompanyID pgtype.Int8 `json:"company_id"`
}
func (q *Queries) GetUserByEmail(ctx context.Context, email pgtype.Text) (GetUserByEmailRow, error) {
@ -234,12 +320,15 @@ func (q *Queries) GetUserByEmail(ctx context.Context, email pgtype.Text) (GetUse
&i.PhoneVerified,
&i.CreatedAt,
&i.UpdatedAt,
&i.Suspended,
&i.SuspendedAt,
&i.CompanyID,
)
return i, err
}
const GetUserByID = `-- name: GetUserByID :one
SELECT id, first_name, last_name, email, phone_number, role, password, email_verified, phone_verified, created_at, updated_at, suspended_at, suspended, referral_code, referred_by
SELECT id, first_name, last_name, email, phone_number, role, password, email_verified, phone_verified, created_at, updated_at, company_id, suspended_at, suspended, referral_code, referred_by
FROM users
WHERE id = $1
`
@ -259,6 +348,7 @@ func (q *Queries) GetUserByID(ctx context.Context, id int64) (User, error) {
&i.PhoneVerified,
&i.CreatedAt,
&i.UpdatedAt,
&i.CompanyID,
&i.SuspendedAt,
&i.Suspended,
&i.ReferralCode,
@ -277,7 +367,10 @@ SELECT id,
email_verified,
phone_verified,
created_at,
updated_at
updated_at,
suspended,
suspended_at,
company_id
FROM users
WHERE phone_number = $1
`
@ -293,6 +386,9 @@ type GetUserByPhoneRow struct {
PhoneVerified bool `json:"phone_verified"`
CreatedAt pgtype.Timestamptz `json:"created_at"`
UpdatedAt pgtype.Timestamptz `json:"updated_at"`
Suspended bool `json:"suspended"`
SuspendedAt pgtype.Timestamptz `json:"suspended_at"`
CompanyID pgtype.Int8 `json:"company_id"`
}
func (q *Queries) GetUserByPhone(ctx context.Context, phoneNumber pgtype.Text) (GetUserByPhoneRow, error) {
@ -309,6 +405,9 @@ func (q *Queries) GetUserByPhone(ctx context.Context, phoneNumber pgtype.Text) (
&i.PhoneVerified,
&i.CreatedAt,
&i.UpdatedAt,
&i.Suspended,
&i.SuspendedAt,
&i.CompanyID,
)
return i, err
}
@ -323,7 +422,10 @@ SELECT id,
email_verified,
phone_verified,
created_at,
updated_at
updated_at,
suspended,
suspended_at,
company_id
FROM users
WHERE first_name ILIKE '%' || $1 || '%'
OR last_name ILIKE '%' || $1 || '%'
@ -341,6 +443,9 @@ type SearchUserByNameOrPhoneRow struct {
PhoneVerified bool `json:"phone_verified"`
CreatedAt pgtype.Timestamptz `json:"created_at"`
UpdatedAt pgtype.Timestamptz `json:"updated_at"`
Suspended bool `json:"suspended"`
SuspendedAt pgtype.Timestamptz `json:"suspended_at"`
CompanyID pgtype.Int8 `json:"company_id"`
}
func (q *Queries) SearchUserByNameOrPhone(ctx context.Context, dollar_1 pgtype.Text) ([]SearchUserByNameOrPhoneRow, error) {
@ -363,6 +468,9 @@ func (q *Queries) SearchUserByNameOrPhone(ctx context.Context, dollar_1 pgtype.T
&i.PhoneVerified,
&i.CreatedAt,
&i.UpdatedAt,
&i.Suspended,
&i.SuspendedAt,
&i.CompanyID,
); err != nil {
return nil, err
}
@ -374,6 +482,25 @@ func (q *Queries) SearchUserByNameOrPhone(ctx context.Context, dollar_1 pgtype.T
return items, nil
}
const SuspendUser = `-- name: SuspendUser :exec
UPDATE users
SET suspended = $1,
suspended_at = $2,
updated_at = CURRENT_TIMESTAMP
WHERE id = $3
`
type SuspendUserParams struct {
Suspended bool `json:"suspended"`
SuspendedAt pgtype.Timestamptz `json:"suspended_at"`
ID int64 `json:"id"`
}
func (q *Queries) SuspendUser(ctx context.Context, arg SuspendUserParams) error {
_, err := q.db.Exec(ctx, SuspendUser, arg.Suspended, arg.SuspendedAt, arg.ID)
return err
}
const UpdatePassword = `-- name: UpdatePassword :exec
UPDATE users
SET password = $1,

5
go.mod
View File

@ -23,7 +23,7 @@ require (
github.com/andybalholm/brotli v1.1.1 // indirect
github.com/bytedance/sonic/loader v0.2.4 // indirect
github.com/cloudwego/base64x v0.1.5 // indirect
github.com/fasthttp/websocket v1.5.3 // indirect
github.com/fasthttp/websocket v1.5.8 // indirect
github.com/gabriel-vasile/mimetype v1.4.8 // indirect
github.com/go-openapi/jsonpointer v0.21.1 // indirect
github.com/go-openapi/jsonreference v0.21.0 // indirect
@ -31,6 +31,7 @@ require (
github.com/go-openapi/swag v0.23.1 // indirect
github.com/go-playground/locales v0.14.1 // indirect
github.com/go-playground/universal-translator v0.18.1 // indirect
github.com/gofiber/contrib/websocket v1.3.4
github.com/jackc/pgpassfile v1.0.0 // indirect
github.com/jackc/pgservicefile v0.0.0-20240606120523-5a60cdf6a761 // indirect
github.com/jackc/puddle/v2 v2.2.2 // indirect
@ -44,7 +45,7 @@ require (
github.com/mattn/go-runewidth v0.0.16 // indirect
github.com/rivo/uniseg v0.4.7 // indirect
github.com/rogpeppe/go-internal v1.14.1 // indirect
github.com/savsgio/gotils v0.0.0-20230208104028-c358bd845dee // indirect
github.com/savsgio/gotils v0.0.0-20240303185622-093b76447511 // indirect
github.com/swaggo/files v0.0.0-20220610200504-28940afbdbfe // indirect
github.com/twitchyliquid64/golang-asm v0.15.1 // indirect
github.com/valyala/bytebufferpool v1.0.0 // indirect

6
go.sum
View File

@ -24,6 +24,8 @@ github.com/davecgh/go-spew v1.1.1 h1:vj9j/u1bqnvCEfJOwUhtlOARqs3+rkHYY13jYWTU97c
github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
github.com/fasthttp/websocket v1.5.3 h1:TPpQuLwJYfd4LJPXvHDYPMFWbLjsT91n3GpWtCQtdek=
github.com/fasthttp/websocket v1.5.3/go.mod h1:46gg/UBmTU1kUaTcwQXpUxtRwG2PvIZYeA8oL6vF3Fs=
github.com/fasthttp/websocket v1.5.8 h1:k5DpirKkftIF/w1R8ZzjSgARJrs54Je9YJK37DL/Ah8=
github.com/fasthttp/websocket v1.5.8/go.mod h1:d08g8WaT6nnyvg9uMm8K9zMYyDjfKyj3170AtPRuVU0=
github.com/gabriel-vasile/mimetype v1.4.8 h1:FfZ3gj38NjllZIeJAmMhr+qKL8Wu+nOoI3GqacKw1NM=
github.com/gabriel-vasile/mimetype v1.4.8/go.mod h1:ByKUIKGjh1ODkGM1asKUbQZOLGrPjydw3hYPU2YU9t8=
github.com/ghodss/yaml v1.0.0/go.mod h1:4dBDuWmgqj2HViK6kFavaiC9ZROes6MMH2rRYeMEF04=
@ -49,6 +51,8 @@ github.com/go-playground/universal-translator v0.18.1 h1:Bcnm0ZwsGyWbCzImXv+pAJn
github.com/go-playground/universal-translator v0.18.1/go.mod h1:xekY+UJKNuX9WP91TpwSH2VMlDf28Uj24BCp08ZFTUY=
github.com/go-playground/validator/v10 v10.26.0 h1:SP05Nqhjcvz81uJaRfEV0YBSSSGMc/iMaVtFbr3Sw2k=
github.com/go-playground/validator/v10 v10.26.0/go.mod h1:I5QpIEbmr8On7W0TktmJAumgzX4CA1XNl4ZmDuVHKKo=
github.com/gofiber/contrib/websocket v1.3.4 h1:tWeBdbJ8q0WFQXariLN4dBIbGH9KBU75s0s7YXplOSg=
github.com/gofiber/contrib/websocket v1.3.4/go.mod h1:kTFBPC6YENCnKfKx0BoOFjgXxdz7E85/STdkmZPEmPs=
github.com/gofiber/fiber/v2 v2.32.0/go.mod h1:CMy5ZLiXkn6qwthrl03YMyW1NLfj0rhxz2LKl4t7ZTY=
github.com/gofiber/fiber/v2 v2.52.6 h1:Rfp+ILPiYSvvVuIPvxrBns+HJp8qGLDnLJawAu27XVI=
github.com/gofiber/fiber/v2 v2.52.6/go.mod h1:YEcBbO/FB+5M1IZNBP9FO3J9281zgPAreiI1oqg8nDw=
@ -116,6 +120,8 @@ github.com/rogpeppe/go-internal v1.14.1/go.mod h1:MaRKkUm5W0goXpeCfT7UZI6fk/L7L7
github.com/russross/blackfriday/v2 v2.0.1/go.mod h1:+Rmxgy9KzJVeS9/2gXHxylqXiyQDYRxCVz55jmeOWTM=
github.com/savsgio/gotils v0.0.0-20230208104028-c358bd845dee h1:8Iv5m6xEo1NR1AvpV+7XmhI4r39LGNzwUL4YpMuL5vk=
github.com/savsgio/gotils v0.0.0-20230208104028-c358bd845dee/go.mod h1:qwtSXrKuJh/zsFQ12yEE89xfCrGKK63Rr7ctU/uCo4g=
github.com/savsgio/gotils v0.0.0-20240303185622-093b76447511 h1:KanIMPX0QdEdB4R3CiimCAbxFrhB3j7h0/OvpYGVQa8=
github.com/savsgio/gotils v0.0.0-20240303185622-093b76447511/go.mod h1:sM7Mt7uEoCeFSCBM+qBrqvEo+/9vdmj19wzp3yzUhmg=
github.com/shurcooL/sanitized_anchor_name v1.0.0/go.mod h1:1NzhyTcUVG4SuEtjjoZeVRXNmyL/1OwPU0+IJeTBvfc=
github.com/smartystreets/assertions v0.0.0-20180927180507-b2de0cb4f26d/go.mod h1:OnSkiWE9lh6wB0YB77sQom3nweQdgAjqCqsofrRNTgc=
github.com/smartystreets/goconvey v1.6.4/go.mod h1:syvi0/a8iFYH4r/RixwvyeAJjdLS9QV7WQ/tjFTllLA=

View File

@ -19,15 +19,16 @@ const (
// Transaction only represents when the user cashes out a bet in the shop
// It probably would be better to call it a CashOut or ShopWithdrawal
type Transaction struct {
ID int64
Amount Currency
BranchID int64
CashierID int64
BetID int64
Type TransactionType
PaymentOption PaymentOption
FullName string
PhoneNumber string
ID int64
Amount Currency
BranchID int64
CashierID int64
BetID int64
NumberOfOutcomes int64
Type TransactionType
PaymentOption PaymentOption
FullName string
PhoneNumber string
// Payment Details for bank
BankCode string
BeneficiaryName string
@ -38,14 +39,15 @@ type Transaction struct {
}
type CreateTransaction struct {
Amount Currency
BranchID int64
CashierID int64
BetID int64
Type TransactionType
PaymentOption PaymentOption
FullName string
PhoneNumber string
Amount Currency
BranchID int64
CashierID int64
BetID int64
NumberOfOutcomes int64
Type TransactionType
PaymentOption PaymentOption
FullName string
PhoneNumber string
// Payment Details for bank
BankCode string
BeneficiaryName string

View File

@ -27,15 +27,15 @@ type User struct {
SuspendedAt time.Time
Suspended bool
//
BranchID int64
CompanyID ValidInt64
}
type RegisterUserReq struct {
FirstName string
LastName string
Email string
PhoneNumber string
Password string
Role string
FirstName string
LastName string
Email string
PhoneNumber string
Password string
Role string
Otp string
ReferralCode string `json:"referral_code"`
OtpMedium OtpMedium
@ -47,6 +47,8 @@ type CreateUserReq struct {
PhoneNumber string
Password string
Role string
Suspended bool
CompanyID ValidInt64
}
type ResetPasswordReq struct {
Email string

View File

@ -29,13 +29,23 @@ func (s *Store) GetUserByEmailPhone(ctx context.Context, email, phone string) (d
return domain.User{}, err
}
return domain.User{
ID: user.ID,
FirstName: user.FirstName,
LastName: user.LastName,
Email: user.Email.String,
PhoneNumber: user.PhoneNumber.String,
Password: user.Password,
Role: domain.Role(user.Role),
ID: user.ID,
FirstName: user.FirstName,
LastName: user.LastName,
Email: user.Email.String,
PhoneNumber: user.PhoneNumber.String,
Password: user.Password,
Role: domain.Role(user.Role),
EmailVerified: user.EmailVerified,
PhoneVerified: user.PhoneVerified,
CreatedAt: user.CreatedAt.Time,
UpdatedAt: user.UpdatedAt.Time,
SuspendedAt: user.SuspendedAt.Time,
Suspended: user.Suspended,
CompanyID: domain.ValidInt64{
Value: user.CompanyID.Int64,
Valid: user.CompanyID.Valid,
},
}, nil
}
@ -72,6 +82,25 @@ func (s *Store) GetRefreshToken(ctx context.Context, token string) (domain.Refre
Revoked: rf.Revoked,
}, nil
}
func (s *Store) GetRefreshTokenByUserID(ctx context.Context, id int64) (domain.RefreshToken, error) {
rf, err := s.queries.GetRefreshTokenByUserID(ctx, id)
if err != nil {
if errors.Is(err, sql.ErrNoRows) {
return domain.RefreshToken{}, authentication.ErrRefreshTokenNotFound
}
return domain.RefreshToken{}, err
}
return domain.RefreshToken{
Token: rf.Token,
UserID: rf.UserID,
CreatedAt: rf.CreatedAt.Time,
ExpiresAt: rf.ExpiresAt.Time,
Revoked: rf.Revoked,
}, nil
}
func (s *Store) RevokeRefreshToken(ctx context.Context, token string) error {
return s.queries.RevokeRefreshToken(ctx, token)
}

View File

@ -5,6 +5,7 @@ import (
dbgen "github.com/SamuelTariku/FortuneBet-Backend/gen/db"
"github.com/SamuelTariku/FortuneBet-Backend/internal/domain"
"github.com/jackc/pgx/v5/pgtype"
)
func convertCreateCompany(company domain.CreateCompany) dbgen.CreateCompanyParams {
@ -46,6 +47,23 @@ func (s *Store) GetAllCompanies(ctx context.Context) ([]domain.Company, error) {
return companies, nil
}
func (s *Store) SearchCompanyByName(ctx context.Context, name string) ([]domain.Company, error) {
dbCompanies, err := s.queries.SearchCompanyByName(ctx, pgtype.Text{
String: name,
Valid: true,
})
if err != nil {
return nil, err
}
var companies []domain.Company = make([]domain.Company, 0, len(dbCompanies))
for _, dbCompany := range dbCompanies {
companies = append(companies, convertDBCompany(dbCompany))
}
return companies, nil
}
func (s *Store) GetCompanyByID(ctx context.Context, id int64) (domain.Company, error) {
dbCompany, err := s.queries.GetCompanyByID(ctx, id)

View File

@ -9,19 +9,20 @@ import (
func convertDBTransaction(transaction dbgen.Transaction) domain.Transaction {
return domain.Transaction{
Amount: domain.Currency(transaction.Amount),
BranchID: transaction.BranchID,
CashierID: transaction.CashierID,
BetID: transaction.BetID,
Type: domain.TransactionType(transaction.Type),
PaymentOption: domain.PaymentOption(transaction.PaymentOption),
FullName: transaction.FullName,
PhoneNumber: transaction.PhoneNumber,
BankCode: transaction.BankCode,
BeneficiaryName: transaction.BeneficiaryName,
AccountName: transaction.AccountName,
AccountNumber: transaction.AccountNumber,
ReferenceNumber: transaction.ReferenceNumber,
Amount: domain.Currency(transaction.Amount),
BranchID: transaction.BranchID,
CashierID: transaction.CashierID,
BetID: transaction.BetID,
NumberOfOutcomes: transaction.NumberOfOutcomes,
Type: domain.TransactionType(transaction.Type),
PaymentOption: domain.PaymentOption(transaction.PaymentOption),
FullName: transaction.FullName,
PhoneNumber: transaction.PhoneNumber,
BankCode: transaction.BankCode,
BeneficiaryName: transaction.BeneficiaryName,
AccountName: transaction.AccountName,
AccountNumber: transaction.AccountNumber,
ReferenceNumber: transaction.ReferenceNumber,
}
}

View File

@ -12,7 +12,7 @@ import (
"github.com/jackc/pgx/v5/pgtype"
)
func (s *Store) CreateUser(ctx context.Context, user domain.User, usedOtpId int64) (domain.User, error) {
func (s *Store) CreateUser(ctx context.Context, user domain.User, usedOtpId int64, is_company bool) (domain.User, error) {
err := s.queries.MarkOtpAsUsed(ctx, dbgen.MarkOtpAsUsedParams{
ID: usedOtpId,
UsedAt: pgtype.Timestamptz{
@ -83,23 +83,44 @@ func (s *Store) GetUserByID(ctx context.Context, id int64) (domain.User, error)
Suspended: user.Suspended,
}, nil
}
func (s *Store) GetAllUsers(ctx context.Context, filter user.Filter) ([]domain.User, error) {
users, err := s.queries.GetAllUsers(ctx)
func (s *Store) GetAllUsers(ctx context.Context, filter user.Filter) ([]domain.User, int64, error) {
users, err := s.queries.GetAllUsers(ctx, dbgen.GetAllUsersParams{
Role: filter.Role,
CompanyID: pgtype.Int8{
Int64: filter.CompanyID.Value,
Valid: filter.CompanyID.Valid,
},
Limit: int32(filter.PageSize),
Offset: int32(filter.Page),
})
if err != nil {
return nil, err
return nil, 0, err
}
userList := make([]domain.User, len(users))
for i, user := range users {
userList[i] = domain.User{
ID: user.ID,
FirstName: user.FirstName,
LastName: user.LastName,
Email: user.Email.String,
PhoneNumber: user.PhoneNumber.String,
Role: domain.Role(user.Role),
ID: user.ID,
FirstName: user.FirstName,
LastName: user.LastName,
Email: user.Email.String,
EmailVerified: user.EmailVerified,
PhoneNumber: user.PhoneNumber.String,
Role: domain.Role(user.Role),
PhoneVerified: user.PhoneVerified,
CreatedAt: user.CreatedAt.Time,
UpdatedAt: user.UpdatedAt.Time,
SuspendedAt: user.SuspendedAt.Time,
Suspended: user.Suspended,
}
}
return userList, nil
totalCount, err := s.queries.GetTotalUsers(ctx, dbgen.GetTotalUsersParams{
Role: filter.Role,
CompanyID: pgtype.Int8{
Int64: filter.CompanyID.Value,
Valid: filter.CompanyID.Valid,
},
})
return userList, totalCount, nil
}
func (s *Store) GetAllCashiers(ctx context.Context) ([]domain.User, error) {
@ -110,12 +131,18 @@ func (s *Store) GetAllCashiers(ctx context.Context) ([]domain.User, error) {
userList := make([]domain.User, len(users))
for i, user := range users {
userList[i] = domain.User{
ID: user.ID,
FirstName: user.FirstName,
LastName: user.LastName,
Email: user.Email.String,
PhoneNumber: user.PhoneNumber.String,
Role: domain.Role(user.Role),
ID: user.ID,
FirstName: user.FirstName,
LastName: user.LastName,
Email: user.Email.String,
PhoneNumber: user.PhoneNumber.String,
Role: domain.Role(user.Role),
EmailVerified: user.EmailVerified,
PhoneVerified: user.PhoneVerified,
CreatedAt: user.CreatedAt.Time,
UpdatedAt: user.UpdatedAt.Time,
SuspendedAt: user.SuspendedAt.Time,
Suspended: user.Suspended,
}
}
return userList, nil
@ -129,12 +156,18 @@ func (s *Store) GetCashiersByBranch(ctx context.Context, branchID int64) ([]doma
userList := make([]domain.User, len(users))
for i, user := range users {
userList[i] = domain.User{
ID: user.ID,
FirstName: user.FirstName,
LastName: user.LastName,
Email: user.Email.String,
PhoneNumber: user.PhoneNumber.String,
Role: domain.Role(user.Role),
ID: user.ID,
FirstName: user.FirstName,
LastName: user.LastName,
Email: user.Email.String,
PhoneNumber: user.PhoneNumber.String,
Role: domain.Role(user.Role),
EmailVerified: user.EmailVerified,
PhoneVerified: user.PhoneVerified,
CreatedAt: user.CreatedAt.Time,
UpdatedAt: user.UpdatedAt.Time,
SuspendedAt: user.SuspendedAt.Time,
Suspended: user.Suspended,
}
}
return userList, nil
@ -152,12 +185,18 @@ func (s *Store) SearchUserByNameOrPhone(ctx context.Context, searchString string
userList := make([]domain.User, 0, len(users))
for _, user := range users {
userList = append(userList, domain.User{
ID: user.ID,
FirstName: user.FirstName,
LastName: user.LastName,
Email: user.Email.String,
PhoneNumber: user.PhoneNumber.String,
Role: domain.Role(user.Role),
ID: user.ID,
FirstName: user.FirstName,
LastName: user.LastName,
Email: user.Email.String,
PhoneNumber: user.PhoneNumber.String,
Role: domain.Role(user.Role),
EmailVerified: user.EmailVerified,
PhoneVerified: user.PhoneVerified,
CreatedAt: user.CreatedAt.Time,
UpdatedAt: user.UpdatedAt.Time,
Suspended: user.Suspended,
SuspendedAt: user.SuspendedAt.Time,
})
}
return userList, nil
@ -216,12 +255,18 @@ func (s *Store) GetUserByEmail(ctx context.Context, email string) (domain.User,
return domain.User{}, err
}
return domain.User{
ID: user.ID,
FirstName: user.FirstName,
LastName: user.LastName,
Email: user.Email.String,
PhoneNumber: user.PhoneNumber.String,
Role: domain.Role(user.Role),
ID: user.ID,
FirstName: user.FirstName,
LastName: user.LastName,
Email: user.Email.String,
PhoneNumber: user.PhoneNumber.String,
Role: domain.Role(user.Role),
EmailVerified: user.EmailVerified,
PhoneVerified: user.PhoneVerified,
CreatedAt: user.CreatedAt.Time,
UpdatedAt: user.UpdatedAt.Time,
Suspended: user.Suspended,
SuspendedAt: user.SuspendedAt.Time,
}, nil
}
func (s *Store) GetUserByPhone(ctx context.Context, phoneNum string) (domain.User, error) {
@ -235,13 +280,20 @@ func (s *Store) GetUserByPhone(ctx context.Context, phoneNum string) (domain.Use
}
return domain.User{}, err
}
return domain.User{
ID: user.ID,
FirstName: user.FirstName,
LastName: user.LastName,
Email: user.Email.String,
PhoneNumber: user.PhoneNumber.String,
Role: domain.Role(user.Role),
ID: user.ID,
FirstName: user.FirstName,
LastName: user.LastName,
Email: user.Email.String,
PhoneNumber: user.PhoneNumber.String,
Role: domain.Role(user.Role),
EmailVerified: user.EmailVerified,
PhoneVerified: user.PhoneVerified,
CreatedAt: user.CreatedAt.Time,
UpdatedAt: user.UpdatedAt.Time,
Suspended: user.Suspended,
SuspendedAt: user.SuspendedAt.Time,
}, nil
}
@ -272,7 +324,7 @@ func (s *Store) UpdatePassword(ctx context.Context, identifier string, password
}
return nil
}
func (s *Store) CreateUserWithoutOtp(ctx context.Context, user domain.User) (domain.User, error) {
func (s *Store) CreateUserWithoutOtp(ctx context.Context, user domain.User, is_company bool) (domain.User, error) {
userRes, err := s.queries.CreateUser(ctx, dbgen.CreateUserParams{
FirstName: user.FirstName,
LastName: user.LastName,
@ -296,16 +348,26 @@ func (s *Store) CreateUserWithoutOtp(ctx context.Context, user domain.User) (dom
Time: time.Now(),
Valid: true,
},
Suspended: user.Suspended,
CompanyID: pgtype.Int8{
Int64: user.CompanyID.Value,
Valid: user.CompanyID.Valid,
},
})
if err != nil {
return domain.User{}, err
}
return domain.User{
ID: userRes.ID,
FirstName: userRes.FirstName,
LastName: userRes.LastName,
Email: userRes.Email.String,
PhoneNumber: userRes.PhoneNumber.String,
Role: domain.Role(userRes.Role),
ID: userRes.ID,
FirstName: userRes.FirstName,
LastName: userRes.LastName,
Email: userRes.Email.String,
PhoneNumber: userRes.PhoneNumber.String,
Role: domain.Role(userRes.Role),
EmailVerified: userRes.EmailVerified,
PhoneVerified: userRes.PhoneVerified,
CreatedAt: userRes.CreatedAt.Time,
UpdatedAt: userRes.UpdatedAt.Time,
Suspended: userRes.Suspended,
}, nil
}

View File

@ -16,13 +16,14 @@ var (
ErrUserNotFound = errors.New("user not found")
ErrExpiredToken = errors.New("token expired")
ErrRefreshTokenNotFound = errors.New("refresh token not found") // i.e login again
ErrUserSuspended = errors.New("user has been suspended")
)
type LoginSuccess struct {
UserId int64
Role domain.Role
RfToken string
BranchId int64
UserId int64
Role domain.Role
RfToken string
CompanyID domain.ValidInt64
}
func (s *Service) Login(ctx context.Context, email, phone string, password string) (LoginSuccess, error) {
@ -34,6 +35,9 @@ func (s *Service) Login(ctx context.Context, email, phone string, password strin
if err != nil {
return LoginSuccess{}, err
}
if user.Suspended {
return LoginSuccess{}, ErrUserSuspended
}
refreshToken, err := generateRefreshToken()
if err != nil {
@ -49,24 +53,24 @@ func (s *Service) Login(ctx context.Context, email, phone string, password strin
return LoginSuccess{}, err
}
return LoginSuccess{
UserId: user.ID,
Role: user.Role,
RfToken: refreshToken,
BranchId: user.BranchID,
UserId: user.ID,
Role: user.Role,
RfToken: refreshToken,
CompanyID: user.CompanyID,
}, nil
}
func (s *Service) RefreshToken(ctx context.Context, refToken string) error {
func (s *Service) RefreshToken(ctx context.Context, refToken string) (domain.RefreshToken, error) {
token, err := s.tokenStore.GetRefreshToken(ctx, refToken)
if err != nil {
return err
return domain.RefreshToken{}, err
}
if token.Revoked {
return ErrRefreshTokenNotFound
return domain.RefreshToken{}, ErrRefreshTokenNotFound
}
if token.ExpiresAt.Before(time.Now()) {
return ErrExpiredToken
return domain.RefreshToken{}, ErrExpiredToken
}
// newRefToken, err := generateRefreshToken()
@ -80,8 +84,19 @@ func (s *Service) RefreshToken(ctx context.Context, refToken string) error {
// CreatedAt: time.Now(),
// ExpiresAt: time.Now().Add(time.Duration(s.RefreshExpiry) * time.Second),
// })
return nil
return token, nil
}
func (s *Service) GetLastLogin(ctx context.Context, user_id int64) (*time.Time, error) {
refreshToken, err := s.tokenStore.GetRefreshTokenByUserID(ctx, user_id)
if err != nil {
return nil, err
}
return &refreshToken.CreatedAt, nil
}
func (s *Service) Logout(ctx context.Context, refToken string) error {
token, err := s.tokenStore.GetRefreshToken(ctx, refToken)
if err != nil {

View File

@ -12,5 +12,6 @@ type UserStore interface {
type TokenStore interface {
CreateRefreshToken(ctx context.Context, rt domain.RefreshToken) error
GetRefreshToken(ctx context.Context, token string) (domain.RefreshToken, error)
GetRefreshTokenByUserID(ctx context.Context, id int64) (domain.RefreshToken, error)
RevokeRefreshToken(ctx context.Context, token string) error
}

View File

@ -2,6 +2,8 @@ package bet
import (
"context"
"crypto/rand"
"math/big"
"github.com/SamuelTariku/FortuneBet-Backend/internal/domain"
)
@ -16,6 +18,21 @@ func NewService(betStore BetStore) *Service {
}
}
func (s *Service) GenerateCashoutID() (string, error) {
const chars = "abcdefghijklmnopqrstuvwxyz0123456789"
const length int = 13
charLen := big.NewInt(int64(len(chars)))
result := make([]byte, length)
for i := 0; i < length; i++ {
index, err := rand.Int(rand.Reader, charLen)
if err != nil {
return "", err
}
result[i] = chars[index.Int64()]
}
return string(result), nil
}
func (s *Service) CreateBet(ctx context.Context, bet domain.CreateBet) (domain.Bet, error) {
return s.betStore.CreateBet(ctx, bet)

View File

@ -10,6 +10,7 @@ type CompanyStore interface {
CreateCompany(ctx context.Context, company domain.CreateCompany) (domain.Company, error)
GetAllCompanies(ctx context.Context) ([]domain.Company, error)
GetCompanyByID(ctx context.Context, id int64) (domain.Company, error)
SearchCompanyByName(ctx context.Context, name string) ([]domain.Company, error)
UpdateCompany(ctx context.Context, id int64, company domain.UpdateCompany) (domain.Company, error)
DeleteCompany(ctx context.Context, id int64) error
}

View File

@ -27,6 +27,10 @@ func (s *Service) GetCompanyByID(ctx context.Context, id int64) (domain.Company,
return s.companyStore.GetCompanyByID(ctx, id)
}
func (s *Service) SearchCompanyByName(ctx context.Context, name string) ([]domain.Company, error) {
return s.companyStore.SearchCompanyByName(ctx, name)
}
func (s *Service) UpdateCompany(ctx context.Context, id int64, company domain.UpdateCompany) (domain.Company, error) {
return s.companyStore.UpdateCompany(ctx, id, company)
}

View File

@ -6,7 +6,7 @@ import (
"github.com/SamuelTariku/FortuneBet-Backend/internal/domain"
)
func (s *Service) CreateUser(ctx context.Context, User domain.CreateUserReq) (domain.User, error) {
func (s *Service) CreateUser(ctx context.Context, User domain.CreateUserReq, is_company bool) (domain.User, error) {
// Create User
// creator, err := s.userStore.GetUserByID(ctx, createrUserId)
// if err != nil {
@ -33,7 +33,9 @@ func (s *Service) CreateUser(ctx context.Context, User domain.CreateUserReq) (do
Role: domain.Role(User.Role),
EmailVerified: true,
PhoneVerified: true,
})
Suspended: User.Suspended,
CompanyID: User.CompanyID,
}, is_company)
}
func (s *Service) DeleteUser(ctx context.Context, id int64) error {
@ -42,10 +44,10 @@ func (s *Service) DeleteUser(ctx context.Context, id int64) error {
}
type Filter struct {
Role string
BranchId ValidBranchId
Page int
PageSize int
Role string
CompanyID domain.ValidInt64
Page int
PageSize int
}
type ValidRole struct {
Value domain.Role
@ -56,7 +58,7 @@ type ValidBranchId struct {
Valid bool
}
func (s *Service) GetAllUsers(ctx context.Context, filter Filter) ([]domain.User, error) {
func (s *Service) GetAllUsers(ctx context.Context, filter Filter) ([]domain.User, int64, error) {
// Get all Users
return s.userStore.GetAllUsers(ctx, filter)
}

View File

@ -7,10 +7,10 @@ import (
)
type UserStore interface {
CreateUser(ctx context.Context, user domain.User, usedOtpId int64) (domain.User, error)
CreateUserWithoutOtp(ctx context.Context, user domain.User) (domain.User, error)
CreateUser(ctx context.Context, user domain.User, usedOtpId int64, is_company bool) (domain.User, error)
CreateUserWithoutOtp(ctx context.Context, user domain.User, is_company bool) (domain.User, error)
GetUserByID(ctx context.Context, id int64) (domain.User, error)
GetAllUsers(ctx context.Context, filter Filter) ([]domain.User, error)
GetAllUsers(ctx context.Context, filter Filter) ([]domain.User, int64, error)
GetAllCashiers(ctx context.Context) ([]domain.User, error)
GetCashiersByBranch(ctx context.Context, branchID int64) ([]domain.User, error)
UpdateUser(ctx context.Context, user domain.UpdateUserReq) error

View File

@ -70,7 +70,7 @@ func (s *Service) RegisterUser(ctx context.Context, registerReq domain.RegisterU
PhoneVerified: registerReq.OtpMedium == domain.OtpMediumSms,
}
// create the user and mark otp as used
user, err := s.userStore.CreateUser(ctx, userR, otp.ID)
user, err := s.userStore.CreateUser(ctx, userR, otp.ID, false)
if err != nil {
return domain.User{}, err
}

View File

@ -6,11 +6,11 @@ import (
"github.com/SamuelTariku/FortuneBet-Backend/internal/services/authentication"
"github.com/SamuelTariku/FortuneBet-Backend/internal/services/bet"
referralservice "github.com/SamuelTariku/FortuneBet-Backend/internal/services/referal"
"github.com/SamuelTariku/FortuneBet-Backend/internal/services/branch"
"github.com/SamuelTariku/FortuneBet-Backend/internal/services/company"
"github.com/SamuelTariku/FortuneBet-Backend/internal/services/event"
"github.com/SamuelTariku/FortuneBet-Backend/internal/services/odds"
referralservice "github.com/SamuelTariku/FortuneBet-Backend/internal/services/referal"
"github.com/SamuelTariku/FortuneBet-Backend/internal/services/ticket"
"github.com/SamuelTariku/FortuneBet-Backend/internal/services/transaction"
"github.com/SamuelTariku/FortuneBet-Backend/internal/services/user"

View File

@ -0,0 +1,156 @@
package handlers
import (
"time"
"github.com/SamuelTariku/FortuneBet-Backend/internal/domain"
"github.com/SamuelTariku/FortuneBet-Backend/internal/services/authentication"
"github.com/SamuelTariku/FortuneBet-Backend/internal/services/user"
"github.com/SamuelTariku/FortuneBet-Backend/internal/web_server/response"
"github.com/gofiber/fiber/v2"
)
type CreateAdminReq struct {
FirstName string `json:"first_name" example:"John"`
LastName string `json:"last_name" example:"Doe"`
Email string `json:"email" example:"john.doe@example.com"`
PhoneNumber string `json:"phone_number" example:"1234567890"`
Password string `json:"password" example:"password123"`
CompanyID *int64 `json:"company_id,omitempty" example:"1"`
}
// CreateAdmin godoc
// @Summary Create Admin
// @Description Create Admin
// @Tags admin
// @Accept json
// @Produce json
// @Param manger body CreateAdminReq true "Create admin"
// @Success 200 {object} response.APIResponse
// @Failure 400 {object} response.APIResponse
// @Failure 401 {object} response.APIResponse
// @Failure 500 {object} response.APIResponse
// @Router /admin [post]
func (h *Handler) CreateAdmin(c *fiber.Ctx) error {
var companyID domain.ValidInt64
var req CreateAdminReq
if err := c.BodyParser(&req); err != nil {
h.logger.Error("RegisterUser failed", "error", err)
return response.WriteJSON(c, fiber.StatusBadRequest, "Invalid request", err, nil)
}
valErrs, ok := h.validator.Validate(c, req)
if !ok {
return response.WriteJSON(c, fiber.StatusBadRequest, "Invalid request", valErrs, nil)
}
// Admins can be created without company ids and can be assigned later
if req.CompanyID == nil {
companyID = domain.ValidInt64{
Value: 0,
Valid: false,
}
} else {
companyID = domain.ValidInt64{
Value: *req.CompanyID,
Valid: true,
}
}
user := domain.CreateUserReq{
FirstName: req.FirstName,
LastName: req.LastName,
Email: req.Email,
PhoneNumber: req.PhoneNumber,
Password: req.Password,
Role: string(domain.RoleAdmin),
CompanyID: companyID,
}
_, err := h.userSvc.CreateUser(c.Context(), user, true)
if err != nil {
h.logger.Error("CreateAdmin failed", "error", err)
return response.WriteJSON(c, fiber.StatusInternalServerError, "Failed to create admin", nil, nil)
}
return response.WriteJSON(c, fiber.StatusOK, "Admin created successfully", nil, nil)
}
type AdminRes struct {
ID int64 `json:"id"`
FirstName string `json:"first_name"`
LastName string `json:"last_name"`
Email string `json:"email"`
PhoneNumber string `json:"phone_number"`
Role domain.Role `json:"role"`
EmailVerified bool `json:"email_verified"`
PhoneVerified bool `json:"phone_verified"`
CreatedAt time.Time `json:"created_at"`
UpdatedAt time.Time `json:"updated_at"`
LastLogin time.Time `json:"last_login"`
SuspendedAt time.Time `json:"suspended_at"`
Suspended bool `json:"suspended"`
}
// GetAllAdmins godoc
// @Summary Get all Admins
// @Description Get all Admins
// @Tags admin
// @Accept json
// @Produce json
// @Param page query int false "Page number"
// @Param page_size query int false "Page size"
// @Success 200 {object} AdminRes
// @Failure 400 {object} response.APIResponse
// @Failure 401 {object} response.APIResponse
// @Failure 500 {object} response.APIResponse
// @Router /admin [get]
func (h *Handler) GetAllAdmins(c *fiber.Ctx) error {
filter := user.Filter{
Role: string(domain.RoleAdmin),
CompanyID: domain.ValidInt64{
Value: int64(c.QueryInt("company_id")),
Valid: true,
},
Page: c.QueryInt("page", 1) - 1,
PageSize: c.QueryInt("page_size", 10),
}
valErrs, ok := h.validator.Validate(c, filter)
if !ok {
return response.WriteJSON(c, fiber.StatusBadRequest, "Invalid request", valErrs, nil)
}
admins, total, err := h.userSvc.GetAllUsers(c.Context(), filter)
if err != nil {
h.logger.Error("GetAllAdmins failed", "error", err)
return response.WriteJSON(c, fiber.StatusInternalServerError, "Failed to get Admins", nil, nil)
}
var result []AdminRes = make([]AdminRes, len(admins))
for index, admin := range admins {
lastLogin, err := h.authSvc.GetLastLogin(c.Context(), admin.ID)
if err != nil {
if err == authentication.ErrRefreshTokenNotFound {
lastLogin = &admin.CreatedAt
} else {
h.logger.Error("Failed to get user last login", "userID", admin.ID, "error", err)
return fiber.NewError(fiber.StatusInternalServerError, "Failed to retrieve user last login")
}
}
result[index] = AdminRes{
ID: admin.ID,
FirstName: admin.FirstName,
LastName: admin.LastName,
Email: admin.Email,
PhoneNumber: admin.PhoneNumber,
Role: admin.Role,
EmailVerified: admin.EmailVerified,
PhoneVerified: admin.PhoneVerified,
CreatedAt: admin.CreatedAt,
UpdatedAt: admin.UpdatedAt,
SuspendedAt: admin.SuspendedAt,
Suspended: admin.Suspended,
LastLogin: *lastLogin,
}
}
return response.WritePaginatedJSON(c, fiber.StatusOK, "Admins retrieved successfully", result, nil, filter.Page, int(total))
}

View File

@ -3,13 +3,22 @@ package handlers
import (
"errors"
"github.com/SamuelTariku/FortuneBet-Backend/internal/domain"
"github.com/SamuelTariku/FortuneBet-Backend/internal/services/authentication"
jwtutil "github.com/SamuelTariku/FortuneBet-Backend/internal/web_server/jwt"
"github.com/SamuelTariku/FortuneBet-Backend/internal/web_server/response"
"github.com/gofiber/fiber/v2"
)
type loginCustomerReq struct {
Email string `json:"email" validate:"email" example:"john.doe@example.com"`
PhoneNumber string `json:"phone_number" validate:"required_without=Email" example:"1234567890"`
Password string `json:"password" validate:"required" example:"password123"`
}
type loginCustomerRes struct {
AccessToken string `json:"access_token"`
RefreshToken string `json:"refresh_token"`
Role string `json:"role"`
}
// LoginCustomer godoc
// @Summary Login customer
@ -24,16 +33,6 @@ import (
// @Failure 500 {object} response.APIResponse
// @Router /auth/login [post]
func (h *Handler) LoginCustomer(c *fiber.Ctx) error {
type loginCustomerReq struct {
Email string `json:"email" validate:"email" example:"john.doe@example.com"`
PhoneNumber string `json:"phone_number" validate:"required_without=Email" example:"1234567890"`
Password string `json:"password" validate:"required" example:"password123"`
}
type loginCustomerRes struct {
AccessToken string `json:"access_token"`
RefreshToken string `json:"refresh_token"`
Role string `json:"role"`
}
var req loginCustomerReq
if err := c.BodyParser(&req); err != nil {
@ -51,13 +50,15 @@ func (h *Handler) LoginCustomer(c *fiber.Ctx) error {
switch {
case errors.Is(err, authentication.ErrInvalidPassword), errors.Is(err, authentication.ErrUserNotFound):
return fiber.NewError(fiber.StatusUnauthorized, "Invalid credentials")
case errors.Is(err, authentication.ErrUserSuspended):
return fiber.NewError(fiber.StatusUnauthorized, "User login has been locked")
default:
h.logger.Error("Login failed", "error", err)
return fiber.NewError(fiber.StatusInternalServerError, "Internal server error")
}
}
accessToken, err := jwtutil.CreateJwt(successRes.UserId, successRes.Role, successRes.BranchId, h.jwtConfig.JwtAccessKey, h.jwtConfig.JwtAccessExpiry)
accessToken, err := jwtutil.CreateJwt(successRes.UserId, successRes.Role, successRes.CompanyID, h.jwtConfig.JwtAccessKey, h.jwtConfig.JwtAccessExpiry)
if err != nil {
h.logger.Error("Failed to create access token", "userID", successRes.UserId, "error", err)
return fiber.NewError(fiber.StatusInternalServerError, "Failed to generate access token")
@ -71,6 +72,10 @@ func (h *Handler) LoginCustomer(c *fiber.Ctx) error {
return response.WriteJSON(c, fiber.StatusOK, "Login successful", res, nil)
}
type refreshToken struct {
AccessToken string `json:"access_token" validate:"required" example:"<jwt-token>"`
RefreshToken string `json:"refresh_token" validate:"required" example:"<refresh-token>"`
}
// RefreshToken godoc
// @Summary Refresh token
@ -85,16 +90,13 @@ func (h *Handler) LoginCustomer(c *fiber.Ctx) error {
// @Failure 500 {object} response.APIResponse
// @Router /auth/refresh [post]
func (h *Handler) RefreshToken(c *fiber.Ctx) error {
type refreshTokenReq struct {
AccessToken string `json:"access_token" validate:"required" example:"<jwt-token>"`
RefreshToken string `json:"refresh_token" validate:"required" example:"<refresh-token>"`
}
type loginCustomerRes struct {
AccessToken string `json:"access_token"`
RefreshToken string `json:"refresh_token"`
}
var req refreshTokenReq
var req refreshToken
if err := c.BodyParser(&req); err != nil {
h.logger.Error("Failed to parse RefreshToken request", "error", err)
return fiber.NewError(fiber.StatusBadRequest, "Invalid request body")
@ -104,10 +106,7 @@ func (h *Handler) RefreshToken(c *fiber.Ctx) error {
return response.WriteJSON(c, fiber.StatusBadRequest, "Invalid request", valErrs, nil)
}
userId := c.Locals("user_id").(int64)
role := c.Locals("role").(string)
branchId := c.Locals("branch_id").(int64)
err := h.authSvc.RefreshToken(c.Context(), req.RefreshToken)
refreshToken, err := h.authSvc.RefreshToken(c.Context(), req.RefreshToken)
if err != nil {
h.logger.Info("Refresh token attempt failed", "refreshToken", req.RefreshToken, "error", err)
switch {
@ -121,8 +120,10 @@ func (h *Handler) RefreshToken(c *fiber.Ctx) error {
}
}
user, err := h.userSvc.GetUserByID(c.Context(), refreshToken.UserID)
// Assuming the refreshed token includes userID and role info; adjust if needed
accessToken, err := jwtutil.CreateJwt(userId, domain.Role(role), branchId, h.jwtConfig.JwtAccessKey, h.jwtConfig.JwtAccessExpiry)
accessToken, err := jwtutil.CreateJwt(user.ID, user.Role, user.CompanyID, h.jwtConfig.JwtAccessKey, h.jwtConfig.JwtAccessExpiry)
if err != nil {
h.logger.Error("Failed to create new access token", "error", err)
return fiber.NewError(fiber.StatusInternalServerError, "Failed to generate access token")
@ -135,6 +136,10 @@ func (h *Handler) RefreshToken(c *fiber.Ctx) error {
return response.WriteJSON(c, fiber.StatusOK, "Refresh successful", res, nil)
}
type logoutReq struct {
RefreshToken string `json:"refresh_token" validate:"required" example:"<refresh-token>"`
}
// LogOutCustomer godoc
// @Summary Logout customer
// @Description Logout customer
@ -148,9 +153,6 @@ func (h *Handler) RefreshToken(c *fiber.Ctx) error {
// @Failure 500 {object} response.APIResponse
// @Router /auth/logout [post]
func (h *Handler) LogOutCustomer(c *fiber.Ctx) error {
type logoutReq struct {
RefreshToken string `json:"refresh_token" validate:"required" example:"<refresh-token>"`
}
var req logoutReq
if err := c.BodyParser(&req); err != nil {

View File

@ -3,12 +3,10 @@ package handlers
import (
"encoding/json"
"strconv"
"time"
"github.com/SamuelTariku/FortuneBet-Backend/internal/domain"
"github.com/SamuelTariku/FortuneBet-Backend/internal/web_server/response"
"github.com/gofiber/fiber/v2"
"github.com/google/uuid"
)
type CreateBetOutcomeReq struct {
@ -24,7 +22,6 @@ type CreateBetReq struct {
Status domain.OutcomeStatus `json:"status" example:"1"`
FullName string `json:"full_name" example:"John"`
PhoneNumber string `json:"phone_number" example:"1234567890"`
IsShopBet bool `json:"is_shop_bet" example:"false"`
}
type CreateBetRes struct {
@ -117,7 +114,15 @@ func (h *Handler) CreateBet(c *fiber.Ctx) error {
// Validating user by role
// Differentiating between offline and online bets
user, err := h.userSvc.GetUserByID(c.Context(), userID)
cashoutUUID := uuid.New()
if err != nil {
h.logger.Error("CreateBetReq failed, user id invalid")
return response.WriteJSON(c, fiber.StatusBadRequest, "Invalid request", err, nil)
}
cashoutID, err := h.betSvc.GenerateCashoutID()
if err != nil {
h.logger.Error("CreateBetReq failed, unable to create cashout id")
return response.WriteJSON(c, fiber.StatusInternalServerError, "Invalid request", err, nil)
}
var bet domain.Bet
if user.Role == domain.RoleCashier {
@ -153,8 +158,27 @@ func (h *Handler) CreateBet(c *fiber.Ctx) error {
Value: userID,
Valid: false,
},
IsShopBet: req.IsShopBet,
CashoutID: cashoutUUID.String(),
IsShopBet: true,
CashoutID: cashoutID,
})
} else if user.Role == domain.RoleSuperAdmin {
// This is just for testing
bet, err = h.betSvc.CreateBet(c.Context(), domain.CreateBet{
Amount: domain.ToCurrency(req.Amount),
TotalOdds: req.TotalOdds,
Status: req.Status,
FullName: req.FullName,
PhoneNumber: req.PhoneNumber,
BranchID: domain.ValidInt64{
Value: 1,
Valid: true,
},
UserID: domain.ValidInt64{
Value: userID,
Valid: true,
},
IsShopBet: true,
CashoutID: cashoutID,
})
} else {
// TODO if user is customer, get id from the token then get the wallet id from there and reduce the amount
@ -173,8 +197,8 @@ func (h *Handler) CreateBet(c *fiber.Ctx) error {
Value: userID,
Valid: true,
},
IsShopBet: req.IsShopBet,
CashoutID: cashoutUUID.String(),
IsShopBet: false,
CashoutID: cashoutID,
})
}
@ -200,10 +224,10 @@ func (h *Handler) CreateBet(c *fiber.Ctx) error {
}
// Checking to make sure the event hasn't already started
currentTime := time.Now()
if event.StartTime.Before(currentTime) {
return response.WriteJSON(c, fiber.StatusBadRequest, "The event has already expired", nil, nil)
}
// currentTime := time.Now()
// if event.StartTime.Before(currentTime) {
// return response.WriteJSON(c, fiber.StatusBadRequest, "The event has already expired", nil, nil)
// }
odds, err := h.prematchSvc.GetRawOddsByMarketID(c.Context(), marketIDStr, eventIDStr)
@ -224,7 +248,7 @@ func (h *Handler) CreateBet(c *fiber.Ctx) error {
rawBytes, err := json.Marshal(raw)
err = json.Unmarshal(rawBytes, &rawOdd)
if err != nil {
h.logger.Error("Failed to unmarshal raw odd:", err)
h.logger.Error("Failed to unmarshal raw odd", "error", err)
continue
}
if rawOdd.ID == oddIDStr {

View File

@ -1,10 +1,12 @@
package handlers
import (
"fmt"
"strconv"
"time"
"github.com/SamuelTariku/FortuneBet-Backend/internal/domain"
"github.com/SamuelTariku/FortuneBet-Backend/internal/services/authentication"
"github.com/SamuelTariku/FortuneBet-Backend/internal/web_server/response"
"github.com/gofiber/fiber/v2"
)
@ -16,6 +18,7 @@ type CreateCashierReq struct {
PhoneNumber string `json:"phone_number" example:"1234567890"`
Password string `json:"password" example:"password123"`
BranchID int64 `json:"branch_id" example:"1"`
Suspended bool `json:"suspended" example:"false"`
}
// CreateCashier godoc
@ -31,6 +34,10 @@ type CreateCashierReq struct {
// @Failure 500 {object} response.APIResponse
// @Router /cashiers [post]
func (h *Handler) CreateCashier(c *fiber.Ctx) error {
// Get user_id from middleware
companyID := c.Locals("company_id").(domain.ValidInt64)
var req CreateCashierReq
if err := c.BodyParser(&req); err != nil {
h.logger.Error("RegisterUser failed", "error", err)
@ -47,8 +54,11 @@ func (h *Handler) CreateCashier(c *fiber.Ctx) error {
PhoneNumber: req.PhoneNumber,
Password: req.Password,
Role: string(domain.RoleCashier),
Suspended: req.Suspended,
CompanyID: companyID,
}
newUser, err := h.userSvc.CreateUser(c.Context(), userRequest)
fmt.Print(req.Suspended)
newUser, err := h.userSvc.CreateUser(c.Context(), userRequest, true)
if err != nil {
h.logger.Error("CreateCashier failed", "error", err)
return response.WriteJSON(c, fiber.StatusInternalServerError, "Failed to create cashier", nil, nil)
@ -76,6 +86,7 @@ type GetCashierRes struct {
UpdatedAt time.Time `json:"updated_at"`
SuspendedAt time.Time `json:"suspended_at"`
Suspended bool `json:"suspended"`
LastLogin time.Time `json:"last_login"`
}
// GetAllCashiers godoc
@ -113,9 +124,19 @@ func (h *Handler) GetAllCashiers(c *fiber.Ctx) error {
return response.WriteJSON(c, fiber.StatusInternalServerError, "Failed to get cashiers", nil, nil)
}
var result []GetCashierRes
var result []GetCashierRes = make([]GetCashierRes, 0, len(cashiers))
for _, cashier := range cashiers {
lastLogin, err := h.authSvc.GetLastLogin(c.Context(), cashier.ID)
if err != nil {
if err == authentication.ErrRefreshTokenNotFound {
lastLogin = &cashier.CreatedAt
} else {
h.logger.Error("Failed to get user last login", "userID", cashier.ID, "error", err)
return fiber.NewError(fiber.StatusInternalServerError, "Failed to retrieve user last login")
}
}
result = append(result, GetCashierRes{
ID: cashier.ID,
FirstName: cashier.FirstName,
@ -129,6 +150,7 @@ func (h *Handler) GetAllCashiers(c *fiber.Ctx) error {
UpdatedAt: cashier.UpdatedAt,
SuspendedAt: cashier.SuspendedAt,
Suspended: cashier.Suspended,
LastLogin: *lastLogin,
})
}
@ -148,6 +170,7 @@ type updateUserReq struct {
// @Tags cashier
// @Accept json
// @Produce json
// @Param id path int true "Cashier ID"
// @Param cashier body updateUserReq true "Update cashier"
// @Success 200 {object} response.APIResponse
// @Failure 400 {object} response.APIResponse
@ -155,6 +178,12 @@ type updateUserReq struct {
// @Failure 500 {object} response.APIResponse
// @Router /cashiers/{id} [put]
func (h *Handler) UpdateCashier(c *fiber.Ctx) error {
cashierIdStr := c.Params("id")
cashierId, err := strconv.ParseInt(cashierIdStr, 10, 64)
if err != nil {
h.logger.Error("UpdateCashier failed", "error", err)
return response.WriteJSON(c, fiber.StatusBadRequest, "Invalid cashier ID", nil, nil)
}
var req updateUserReq
if err := c.BodyParser(&req); err != nil {
h.logger.Error("UpdateCashier failed", "error", err)
@ -166,12 +195,7 @@ func (h *Handler) UpdateCashier(c *fiber.Ctx) error {
if !ok {
return response.WriteJSON(c, fiber.StatusBadRequest, "Invalid request", valErrs, nil)
}
cashierIdStr := c.Params("id")
cashierId, err := strconv.ParseInt(cashierIdStr, 10, 64)
if err != nil {
h.logger.Error("UpdateCashier failed", "error", err)
return response.WriteJSON(c, fiber.StatusBadRequest, "Invalid cashier ID", nil, nil)
}
err = h.userSvc.UpdateUser(c.Context(), domain.UpdateUserReq{
UserId: cashierId,
FirstName: domain.ValidString{

View File

@ -143,6 +143,37 @@ func (h *Handler) GetCompanyByID(c *fiber.Ctx) error {
}
// GetAllCompanies godoc
// @Summary Gets all companies
// @Description Gets all companies
// @Tags company
// @Accept json
// @Produce json
// @Success 200 {array} CompanyRes
// @Failure 400 {object} response.APIResponse
// @Failure 500 {object} response.APIResponse
// @Router /search/company [get]
func (h *Handler) SearchCompany(c *fiber.Ctx) error {
searchQuery := c.Query("q")
if searchQuery == "" {
return response.WriteJSON(c, fiber.StatusBadRequest, "Search query is required", nil, nil)
}
companies, err := h.companySvc.SearchCompanyByName(c.Context(), searchQuery)
if err != nil {
h.logger.Error("Failed to get companies", "error", err)
return response.WriteJSON(c, fiber.StatusInternalServerError, "Failed to get companies", err, nil)
}
var result []CompanyRes = make([]CompanyRes, 0, len(companies))
for _, company := range companies {
result = append(result, convertCompany(company))
}
return response.WriteJSON(c, fiber.StatusOK, "All Companies retrieved", result, nil)
}
// UpdateCompany godoc
// @Summary Updates a company
// @Description Updates a company

View File

@ -2,8 +2,10 @@ package handlers
import (
"strconv"
"time"
"github.com/SamuelTariku/FortuneBet-Backend/internal/domain"
"github.com/SamuelTariku/FortuneBet-Backend/internal/services/authentication"
"github.com/SamuelTariku/FortuneBet-Backend/internal/services/user"
"github.com/SamuelTariku/FortuneBet-Backend/internal/web_server/response"
"github.com/gofiber/fiber/v2"
@ -15,11 +17,12 @@ type CreateManagerReq struct {
Email string `json:"email" example:"john.doe@example.com"`
PhoneNumber string `json:"phone_number" example:"1234567890"`
Password string `json:"password" example:"password123"`
CompanyID *int64 `json:"company_id,omitempty" example:"1"`
}
// CreateManagers godoc
// @Summary Create Managers
// @Description Create Managers
// CreateManager godoc
// @Summary Create Manager
// @Description Create Manager
// @Tags manager
// @Accept json
// @Produce json
@ -30,6 +33,9 @@ type CreateManagerReq struct {
// @Failure 500 {object} response.APIResponse
// @Router /managers [post]
func (h *Handler) CreateManager(c *fiber.Ctx) error {
// Get user_id from middleware
var req CreateManagerReq
if err := c.BodyParser(&req); err != nil {
h.logger.Error("RegisterUser failed", "error", err)
@ -39,6 +45,22 @@ func (h *Handler) CreateManager(c *fiber.Ctx) error {
if !ok {
return response.WriteJSON(c, fiber.StatusBadRequest, "Invalid request", valErrs, nil)
}
var companyID domain.ValidInt64
role := c.Locals("role").(domain.Role)
if role == domain.RoleSuperAdmin {
if req.CompanyID == nil {
h.logger.Error("RegisterUser failed error: company id is required")
return response.WriteJSON(c, fiber.StatusBadRequest, "Invalid request", "Company ID is required", nil)
}
companyID = domain.ValidInt64{
Value: *req.CompanyID,
Valid: true,
}
} else {
companyID = c.Locals("company_id").(domain.ValidInt64)
}
user := domain.CreateUserReq{
FirstName: req.FirstName,
LastName: req.LastName,
@ -46,16 +68,33 @@ func (h *Handler) CreateManager(c *fiber.Ctx) error {
PhoneNumber: req.PhoneNumber,
Password: req.Password,
Role: string(domain.RoleBranchManager),
CompanyID: companyID,
}
_, err := h.userSvc.CreateUser(c.Context(), user)
_, err := h.userSvc.CreateUser(c.Context(), user, true)
if err != nil {
h.logger.Error("CreateManagers failed", "error", err)
return response.WriteJSON(c, fiber.StatusInternalServerError, "Failed to create Managers", nil, nil)
h.logger.Error("CreateManager failed", "error", err)
return response.WriteJSON(c, fiber.StatusInternalServerError, "Failed to create manager", nil, nil)
}
return response.WriteJSON(c, fiber.StatusOK, "Managers created successfully", nil, nil)
return response.WriteJSON(c, fiber.StatusOK, "Manager created successfully", nil, nil)
}
type ManagersRes struct {
ID int64 `json:"id"`
FirstName string `json:"first_name"`
LastName string `json:"last_name"`
Email string `json:"email"`
PhoneNumber string `json:"phone_number"`
Role domain.Role `json:"role"`
EmailVerified bool `json:"email_verified"`
PhoneVerified bool `json:"phone_verified"`
CreatedAt time.Time `json:"created_at"`
UpdatedAt time.Time `json:"updated_at"`
LastLogin time.Time `json:"last_login"`
SuspendedAt time.Time `json:"suspended_at"`
Suspended bool `json:"suspended"`
}
// GetAllManagers godoc
// @Summary Get all Managers
// @Description Get all Managers
@ -64,7 +103,7 @@ func (h *Handler) CreateManager(c *fiber.Ctx) error {
// @Produce json
// @Param page query int false "Page number"
// @Param page_size query int false "Page size"
// @Success 200 {object} response.APIResponse
// @Success 200 {object} ManagersRes
// @Failure 400 {object} response.APIResponse
// @Failure 401 {object} response.APIResponse
// @Failure 500 {object} response.APIResponse
@ -72,24 +111,52 @@ func (h *Handler) CreateManager(c *fiber.Ctx) error {
func (h *Handler) GetAllManagers(c *fiber.Ctx) error {
filter := user.Filter{
Role: string(domain.RoleBranchManager),
BranchId: user.ValidBranchId{
Value: int64(c.QueryInt("branch_id")),
CompanyID: domain.ValidInt64{
Value: int64(c.QueryInt("company_id")),
Valid: true,
},
Page: c.QueryInt("page", 1),
Page: c.QueryInt("page", 1) - 1,
PageSize: c.QueryInt("page_size", 10),
}
valErrs, ok := h.validator.Validate(c, filter)
if !ok {
return response.WriteJSON(c, fiber.StatusBadRequest, "Invalid request", valErrs, nil)
}
Managers, err := h.userSvc.GetAllUsers(c.Context(), filter)
managers, total, err := h.userSvc.GetAllUsers(c.Context(), filter)
if err != nil {
h.logger.Error("GetAllManagers failed", "error", err)
return response.WriteJSON(c, fiber.StatusInternalServerError, "Failed to get Managers", nil, nil)
}
return response.WriteJSON(c, fiber.StatusOK, "Managers retrieved successfully", Managers, nil)
var result []ManagersRes = make([]ManagersRes, len(managers))
for index, manager := range managers {
lastLogin, err := h.authSvc.GetLastLogin(c.Context(), manager.ID)
if err != nil {
if err == authentication.ErrRefreshTokenNotFound {
lastLogin = &manager.CreatedAt
} else {
h.logger.Error("Failed to get user last login", "userID", manager.ID, "error", err)
return fiber.NewError(fiber.StatusInternalServerError, "Failed to retrieve user last login")
}
}
result[index] = ManagersRes{
ID: manager.ID,
FirstName: manager.FirstName,
LastName: manager.LastName,
Email: manager.Email,
PhoneNumber: manager.PhoneNumber,
Role: manager.Role,
EmailVerified: manager.EmailVerified,
PhoneVerified: manager.PhoneVerified,
CreatedAt: manager.CreatedAt,
UpdatedAt: manager.UpdatedAt,
SuspendedAt: manager.SuspendedAt,
Suspended: manager.Suspended,
LastLogin: *lastLogin,
}
}
return response.WritePaginatedJSON(c, fiber.StatusOK, "Managers retrieved successfully", result, nil, filter.Page, int(total))
}

View File

@ -3,7 +3,6 @@ package handlers
import (
"encoding/json"
"strconv"
"time"
"github.com/SamuelTariku/FortuneBet-Backend/internal/domain"
"github.com/SamuelTariku/FortuneBet-Backend/internal/web_server/response"
@ -77,10 +76,10 @@ func (h *Handler) CreateTicket(c *fiber.Ctx) error {
}
// Checking to make sure the event hasn't already started
currentTime := time.Now()
if event.StartTime.Before(currentTime) {
return response.WriteJSON(c, fiber.StatusBadRequest, "The event has already expired", nil, nil)
}
// currentTime := time.Now()
// if event.StartTime.Before(currentTime) {
// return response.WriteJSON(c, fiber.StatusBadRequest, "The event has already expired", nil, nil)
// }
odds, err := h.prematchSvc.GetRawOddsByMarketID(c.Context(), marketIDStr, eventIDStr)
@ -184,8 +183,8 @@ func (h *Handler) GetTicketByID(c *fiber.Ctx) error {
ticket, err := h.ticketSvc.GetTicketByID(c.Context(), id)
if err != nil {
h.logger.Error("Failed to get ticket by ID", "ticketID", id, "error", err)
return fiber.NewError(fiber.StatusInternalServerError, "Failed to retrieve ticket")
// h.logger.Error("Failed to get ticket by ID", "ticketID", id, "error", err)
return fiber.NewError(fiber.StatusNotFound, "Failed to retrieve ticket")
}
res := TicketRes{

View File

@ -9,21 +9,22 @@ import (
)
type TransactionRes struct {
ID int64 `json:"id" example:"1"`
Amount float32 `json:"amount" example:"100.0"`
BranchID int64 `json:"branch_id" example:"1"`
CashierID int64 `json:"cashier_id" example:"1"`
BetID int64 `json:"bet_id" example:"1"`
Type int64 `json:"type" example:"1"`
PaymentOption domain.PaymentOption `json:"payment_option" example:"1"`
FullName string `json:"full_name" example:"John Smith"`
PhoneNumber string `json:"phone_number" example:"0911111111"`
BankCode string `json:"bank_code"`
BeneficiaryName string `json:"beneficiary_name"`
AccountName string `json:"account_name"`
AccountNumber string `json:"account_number"`
ReferenceNumber string `json:"reference_number"`
Verified bool `json:"verified" example:"true"`
ID int64 `json:"id" example:"1"`
Amount float32 `json:"amount" example:"100.0"`
BranchID int64 `json:"branch_id" example:"1"`
CashierID int64 `json:"cashier_id" example:"1"`
BetID int64 `json:"bet_id" example:"1"`
NumberOfOutcomes int64 `json:"number_of_outcomes" example:"1"`
Type int64 `json:"type" example:"1"`
PaymentOption domain.PaymentOption `json:"payment_option" example:"1"`
FullName string `json:"full_name" example:"John Smith"`
PhoneNumber string `json:"phone_number" example:"0911111111"`
BankCode string `json:"bank_code"`
BeneficiaryName string `json:"beneficiary_name"`
AccountName string `json:"account_name"`
AccountNumber string `json:"account_number"`
ReferenceNumber string `json:"reference_number"`
Verified bool `json:"verified" example:"true"`
}
type CreateTransactionReq struct {
@ -115,20 +116,22 @@ func (h *Handler) CreateTransaction(c *fiber.Ctx) error {
return response.WriteJSON(c, fiber.StatusBadRequest, "Invalid request", valErrs, nil)
}
// TODO: Validate the bet id and add the number of outcomes
transaction, err := h.transactionSvc.CreateTransaction(c.Context(), domain.CreateTransaction{
BranchID: branchID,
CashierID: userID,
Amount: domain.ToCurrency(req.Amount),
BetID: req.BetID,
Type: domain.TransactionType(req.Type),
PaymentOption: domain.PaymentOption(req.PaymentOption),
FullName: req.FullName,
PhoneNumber: req.PhoneNumber,
BankCode: req.BankCode,
BeneficiaryName: req.BeneficiaryName,
AccountName: req.AccountName,
AccountNumber: req.AccountNumber,
ReferenceNumber: req.ReferenceNumber,
BranchID: branchID,
CashierID: userID,
Amount: domain.ToCurrency(req.Amount),
BetID: req.BetID,
NumberOfOutcomes: 1,
Type: domain.TransactionType(req.Type),
PaymentOption: domain.PaymentOption(req.PaymentOption),
FullName: req.FullName,
PhoneNumber: req.PhoneNumber,
BankCode: req.BankCode,
BeneficiaryName: req.BeneficiaryName,
AccountName: req.AccountName,
AccountNumber: req.AccountNumber,
ReferenceNumber: req.ReferenceNumber,
})
if err != nil {
@ -232,6 +235,10 @@ func (h *Handler) GetTransactionByID(c *fiber.Ctx) error {
return response.WriteJSON(c, fiber.StatusOK, "Transaction retrieved successfully", res, nil)
}
type UpdateTransactionVerifiedReq struct {
Verified bool `json:"verified" validate:"required" example:"true"`
}
// UpdateTransactionVerified godoc
// @Summary Updates the verified field of a transaction
// @Description Updates the verified status of a transaction
@ -245,9 +252,6 @@ func (h *Handler) GetTransactionByID(c *fiber.Ctx) error {
// @Failure 500 {object} response.APIResponse
// @Router /transaction/{id} [patch]
func (h *Handler) UpdateTransactionVerified(c *fiber.Ctx) error {
type UpdateTransactionVerifiedReq struct {
Verified bool `json:"verified" validate:"required" example:"true"`
}
transactionID := c.Params("id")
id, err := strconv.ParseInt(transactionID, 10, 64)

View File

@ -2,14 +2,25 @@ package handlers
import (
"errors"
"strconv"
"time"
"github.com/SamuelTariku/FortuneBet-Backend/internal/domain"
"github.com/SamuelTariku/FortuneBet-Backend/internal/services/authentication"
"github.com/SamuelTariku/FortuneBet-Backend/internal/web_server/response"
"github.com/gofiber/fiber/v2"
)
type CheckPhoneEmailExistReq struct {
Email string `json:"email" validate:"email" example:"john.doe@example.com"`
PhoneNumber string `json:"phone_number" validate:"required" example:"1234567890"`
}
type CheckPhoneEmailExistRes struct {
EmailExist bool `json:"email_exist"`
PhoneNumberExist bool `json:"phone_number_exist"`
}
// CheckPhoneEmailExist godoc
// @Summary Check if phone number or email exist
// @Description Check if phone number or email exist
@ -22,14 +33,6 @@ import (
// @Failure 500 {object} response.APIResponse
// @Router /user/checkPhoneEmailExist [post]
func (h *Handler) CheckPhoneEmailExist(c *fiber.Ctx) error {
type CheckPhoneEmailExistReq struct {
Email string `json:"email" validate:"email" example:"john.doe@example.com"`
PhoneNumber string `json:"phone_number" validate:"required" example:"1234567890"`
}
type CheckPhoneEmailExistRes struct {
EmailExist bool `json:"email_exist"`
PhoneNumberExist bool `json:"phone_number_exist"`
}
var req CheckPhoneEmailExistReq
if err := c.BodyParser(&req); err != nil {
@ -54,6 +57,11 @@ func (h *Handler) CheckPhoneEmailExist(c *fiber.Ctx) error {
return response.WriteJSON(c, fiber.StatusOK, "Check successful", res, nil)
}
type RegisterCodeReq struct {
Email string `json:"email" validate:"email" example:"john.doe@example.com"`
PhoneNumber string `json:"phone_number" validate:"required_without=Email" example:"1234567890"`
}
// SendRegisterCode godoc
// @Summary Send register code
// @Description Send register code
@ -66,10 +74,6 @@ func (h *Handler) CheckPhoneEmailExist(c *fiber.Ctx) error {
// @Failure 500 {object} response.APIResponse
// @Router /user/sendRegisterCode [post]
func (h *Handler) SendRegisterCode(c *fiber.Ctx) error {
type RegisterCodeReq struct {
Email string `json:"email" validate:"email" example:"john.doe@example.com"`
PhoneNumber string `json:"phone_number" validate:"required_without=Email" example:"1234567890"`
}
var req RegisterCodeReq
if err := c.BodyParser(&req); err != nil {
@ -101,6 +105,16 @@ func (h *Handler) SendRegisterCode(c *fiber.Ctx) error {
return response.WriteJSON(c, fiber.StatusOK, "Code sent successfully", nil, nil)
}
type RegisterUserReq struct {
FirstName string `json:"first_name" example:"John"`
LastName string `json:"last_name" example:"Doe"`
Email string `json:"email" example:"john.doe@example.com"`
PhoneNumber string `json:"phone_number" example:"1234567890"`
Password string `json:"password" example:"password123"`
Otp string `json:"otp" example:"123456"`
ReferalCode string `json:"referal_code" example:"ABC123"`
}
// RegisterUser godoc
// @Summary Register user
// @Description Register user
@ -113,15 +127,6 @@ func (h *Handler) SendRegisterCode(c *fiber.Ctx) error {
// @Failure 500 {object} response.APIResponse
// @Router /user/register [post]
func (h *Handler) RegisterUser(c *fiber.Ctx) error {
type RegisterUserReq struct {
FirstName string `json:"first_name" example:"John"`
LastName string `json:"last_name" example:"Doe"`
Email string `json:"email" example:"john.doe@example.com"`
PhoneNumber string `json:"phone_number" example:"1234567890"`
Password string `json:"password" example:"password123"`
Otp string `json:"otp" example:"123456"`
ReferalCode string `json:"referal_code" example:"ABC123"`
}
var req RegisterUserReq
if err := c.BodyParser(&req); err != nil {
@ -192,6 +197,11 @@ func (h *Handler) RegisterUser(c *fiber.Ctx) error {
return response.WriteJSON(c, fiber.StatusOK, "Registration successful", nil, nil)
}
type ResetCodeReq struct {
Email string `json:"email" validate:"email" example:"john.doe@example.com"`
PhoneNumber string `json:"phone_number" validate:"required_without=Email" example:"1234567890"`
}
// SendResetCode godoc
// @Summary Send reset code
// @Description Send reset code
@ -204,10 +214,6 @@ func (h *Handler) RegisterUser(c *fiber.Ctx) error {
// @Failure 500 {object} response.APIResponse
// @Router /user/sendResetCode [post]
func (h *Handler) SendResetCode(c *fiber.Ctx) error {
type ResetCodeReq struct {
Email string `json:"email" validate:"email" example:"john.doe@example.com"`
PhoneNumber string `json:"phone_number" validate:"required_without=Email" example:"1234567890"`
}
var req ResetCodeReq
if err := c.BodyParser(&req); err != nil {
@ -239,6 +245,13 @@ func (h *Handler) SendResetCode(c *fiber.Ctx) error {
return response.WriteJSON(c, fiber.StatusOK, "Code sent successfully", nil, nil)
}
type ResetPasswordReq struct {
Email string `json:"email" validate:"email" example:"john.doe@example.com"`
PhoneNumber string `json:"phone_number" validate:"required_without=Email" example:"1234567890"`
Password string `json:"password" validate:"required,min=8" example:"newpassword123"`
Otp string `json:"otp" validate:"required" example:"123456"`
}
// ResetPassword godoc
// @Summary Reset password
// @Description Reset password
@ -251,12 +264,6 @@ func (h *Handler) SendResetCode(c *fiber.Ctx) error {
// @Failure 500 {object} response.APIResponse
// @Router /user/resetPassword [post]
func (h *Handler) ResetPassword(c *fiber.Ctx) error {
type ResetPasswordReq struct {
Email string `json:"email" validate:"email" example:"john.doe@example.com"`
PhoneNumber string `json:"phone_number" validate:"required_without=Email" example:"1234567890"`
Password string `json:"password" validate:"required,min=8" example:"newpassword123"`
Otp string `json:"otp" validate:"required" example:"123456"`
}
var req ResetPasswordReq
if err := c.BodyParser(&req); err != nil {
@ -301,6 +308,7 @@ type UserProfileRes struct {
PhoneVerified bool `json:"phone_verified"`
CreatedAt time.Time `json:"created_at"`
UpdatedAt time.Time `json:"updated_at"`
LastLogin time.Time `json:"last_login"`
SuspendedAt time.Time `json:"suspended_at"`
Suspended bool `json:"suspended"`
}
@ -330,6 +338,15 @@ func (h *Handler) UserProfile(c *fiber.Ctx) error {
return fiber.NewError(fiber.StatusInternalServerError, "Failed to retrieve user profile")
}
lastLogin, err := h.authSvc.GetLastLogin(c.Context(), user.ID)
if err != nil {
if err != authentication.ErrRefreshTokenNotFound {
h.logger.Error("Failed to get user last login", "userID", userID, "error", err)
return fiber.NewError(fiber.StatusInternalServerError, "Failed to retrieve user last login")
}
lastLogin = &user.CreatedAt
}
res := UserProfileRes{
ID: user.ID,
FirstName: user.FirstName,
@ -343,6 +360,7 @@ func (h *Handler) UserProfile(c *fiber.Ctx) error {
UpdatedAt: user.UpdatedAt,
SuspendedAt: user.SuspendedAt,
Suspended: user.Suspended,
LastLogin: *lastLogin,
}
return response.WriteJSON(c, fiber.StatusOK, "User profile retrieved successfully", res, nil)
}
@ -374,6 +392,7 @@ type SearchUserByNameOrPhoneReq struct {
// @Failure 500 {object} response.APIResponse
// @Router /user/search [post]
func (h *Handler) SearchUserByNameOrPhone(c *fiber.Ctx) error {
// TODO: Add filtering by role based on which user is calling this
var req SearchUserByNameOrPhoneReq
if err := c.BodyParser(&req); err != nil {
h.logger.Error("SearchUserByNameOrPhone failed", "error", err)
@ -395,6 +414,15 @@ func (h *Handler) SearchUserByNameOrPhone(c *fiber.Ctx) error {
}
var res []UserProfileRes = make([]UserProfileRes, 0, len(users))
for _, user := range users {
lastLogin, err := h.authSvc.GetLastLogin(c.Context(), user.ID)
if err != nil {
if err != authentication.ErrRefreshTokenNotFound {
h.logger.Error("Failed to get user last login", "userID", user.ID, "error", err)
return fiber.NewError(fiber.StatusInternalServerError, "Failed to retrieve user last login")
}
lastLogin = &user.CreatedAt
}
res = append(res, UserProfileRes{
ID: user.ID,
FirstName: user.FirstName,
@ -408,8 +436,80 @@ func (h *Handler) SearchUserByNameOrPhone(c *fiber.Ctx) error {
UpdatedAt: user.UpdatedAt,
SuspendedAt: user.SuspendedAt,
Suspended: user.Suspended,
LastLogin: *lastLogin,
})
}
return response.WriteJSON(c, fiber.StatusOK, "Search Successful", res, nil)
}
// GetUserByID godoc
// @Summary Get user by id
// @Description Get a single user by id
// @Tags user
// @Accept json
// @Produce json
// @Param id path int true "User ID"
// @Success 200 {object} response.APIResponse
// @Failure 400 {object} response.APIResponse
// @Failure 401 {object} response.APIResponse
// @Failure 500 {object} response.APIResponse
// @Router /user/single/{id} [get]
func (h *Handler) GetUserByID(c *fiber.Ctx) error {
// branchId := int64(12) //c.Locals("branch_id").(int64)
// filter := user.Filter{
// Role: string(domain.RoleUser),
// BranchId: user.ValidBranchId{
// Value: branchId,
// Valid: true,
// },
// Page: c.QueryInt("page", 1),
// PageSize: c.QueryInt("page_size", 10),
// }
// valErrs, ok := validator.Validate(c, filter)
// if !ok {
// return response.WriteJSON(c, fiber.StatusBadRequest, "Invalid request", valErrs, nil)
// }
userIDstr := c.Params("id")
userID, err := strconv.ParseInt(userIDstr, 10, 64)
if err != nil {
h.logger.Error("UpdateCashier failed", "error", err)
return response.WriteJSON(c, fiber.StatusBadRequest, "Invalid cashier ID", nil, nil)
}
user, err := h.userSvc.GetUserByID(c.Context(), userID)
if err != nil {
h.logger.Error("GetAllCashiers failed", "error", err)
return response.WriteJSON(c, fiber.StatusInternalServerError, "Failed to get cashiers", nil, nil)
}
lastLogin, err := h.authSvc.GetLastLogin(c.Context(), user.ID)
if err != nil {
if err != authentication.ErrRefreshTokenNotFound {
h.logger.Error("Failed to get user last login", "userID", user.ID, "error", err)
return fiber.NewError(fiber.StatusInternalServerError, "Failed to retrieve user last login")
}
lastLogin = &user.CreatedAt
}
res := UserProfileRes{
ID: user.ID,
FirstName: user.FirstName,
LastName: user.LastName,
Email: user.Email,
PhoneNumber: user.PhoneNumber,
Role: user.Role,
EmailVerified: user.EmailVerified,
PhoneVerified: user.PhoneVerified,
CreatedAt: user.CreatedAt,
UpdatedAt: user.UpdatedAt,
SuspendedAt: user.SuspendedAt,
Suspended: user.Suspended,
LastLogin: *lastLogin,
}
return response.WriteJSON(c, fiber.StatusOK, "Cashiers retrieved successfully", res, nil)
}

View File

@ -6,6 +6,16 @@ import (
"github.com/gofiber/fiber/v2"
)
type launchVirtualGameReq struct {
GameID string `json:"game_id" validate:"required" example:"crash_001"`
Currency string `json:"currency" validate:"required,len=3" example:"USD"`
Mode string `json:"mode" validate:"required,oneof=REAL DEMO" example:"REAL"`
}
type launchVirtualGameRes struct {
LaunchURL string `json:"launch_url"`
}
// LaunchVirtualGame godoc
// @Summary Launch a PopOK virtual game
// @Description Generates a URL to launch a PopOK game
@ -20,15 +30,6 @@ import (
// @Failure 500 {object} response.APIResponse
// @Router /virtual-game/launch [post]
func (h *Handler) LaunchVirtualGame(c *fiber.Ctx) error {
type launchVirtualGameReq struct {
GameID string `json:"game_id" validate:"required" example:"crash_001"`
Currency string `json:"currency" validate:"required,len=3" example:"USD"`
Mode string `json:"mode" validate:"required,oneof=REAL DEMO" example:"REAL"`
}
type launchVirtualGameRes struct {
LaunchURL string `json:"launch_url"`
}
userID, ok := c.Locals("user_id").(int64)
if !ok || userID == 0 {

View File

@ -175,6 +175,10 @@ func (h *Handler) GetAllBranchWallets(c *fiber.Ctx) error {
}
type UpdateWalletActiveReq struct {
IsActive bool `json:"is_active" validate:"required" example:"true"`
}
// UpdateWalletActive godoc
// @Summary Activate and Deactivate Wallet
// @Description Can activate and deactivate wallet
@ -188,9 +192,6 @@ func (h *Handler) GetAllBranchWallets(c *fiber.Ctx) error {
// @Failure 500 {object} response.APIResponse
// @Router /wallet/{id} [patch]
func (h *Handler) UpdateWalletActive(c *fiber.Ctx) error {
type UpdateWalletActiveReq struct {
IsActive bool `json:"is_active" validate:"required" example:"true"`
}
walletID := c.Params("id")
id, err := strconv.ParseInt(walletID, 10, 64)

View File

@ -17,9 +17,9 @@ var (
type UserClaim struct {
jwt.RegisteredClaims
UserId int64
Role domain.Role
BranchId int64
UserId int64
Role domain.Role
CompanyID domain.ValidInt64
}
type PopOKClaim struct {
@ -37,7 +37,7 @@ type JwtConfig struct {
JwtAccessExpiry int
}
func CreateJwt(userId int64, Role domain.Role, BranchId int64, key string, expiry int) (string, error) {
func CreateJwt(userId int64, Role domain.Role, CompanyID domain.ValidInt64, key string, expiry int) (string, error) {
token := jwt.NewWithClaims(jwt.SigningMethodHS256, UserClaim{
RegisteredClaims: jwt.RegisteredClaims{
Issuer: "github.com/lafetz/snippitstash",
@ -46,9 +46,9 @@ func CreateJwt(userId int64, Role domain.Role, BranchId int64, key string, expir
NotBefore: jwt.NewNumericDate(time.Now()),
ExpiresAt: jwt.NewNumericDate(time.Now().Add(time.Duration(expiry) * time.Second)),
},
UserId: userId,
Role: Role,
BranchId: BranchId,
UserId: userId,
Role: Role,
CompanyID: CompanyID,
})
jwtToken, err := token.SignedString([]byte(key))
return jwtToken, err

View File

@ -44,7 +44,7 @@ func (a *App) authMiddleware(c *fiber.Ctx) error {
}
c.Locals("user_id", claim.UserId)
c.Locals("role", claim.Role)
c.Locals("branch_id", claim.BranchId)
c.Locals("company_id", claim.CompanyID)
c.Locals("refresh_token", refreshToken)
return c.Next()

View File

@ -7,6 +7,7 @@ import (
_ "github.com/SamuelTariku/FortuneBet-Backend/docs"
"github.com/SamuelTariku/FortuneBet-Backend/internal/domain"
"github.com/SamuelTariku/FortuneBet-Backend/internal/web_server/handlers"
"github.com/gofiber/fiber/v2"
fiberSwagger "github.com/swaggo/fiber-swagger"
)
@ -33,7 +34,7 @@ func (a *App) initAppRoutes() {
// Auth Routes
a.fiber.Post("/auth/login", h.LoginCustomer)
a.fiber.Post("/auth/refresh", a.authMiddleware, h.RefreshToken)
a.fiber.Post("/auth/refresh", h.RefreshToken)
a.fiber.Post("/auth/logout", a.authMiddleware, h.LogOutCustomer)
a.fiber.Get("/auth/test", a.authMiddleware, func(c *fiber.Ctx) error {
userID, ok := c.Locals("user_id").(int64)
@ -68,6 +69,7 @@ func (a *App) initAppRoutes() {
a.fiber.Post("/user/sendRegisterCode", h.SendRegisterCode)
a.fiber.Post("/user/checkPhoneEmailExist", h.CheckPhoneEmailExist)
a.fiber.Get("/user/profile", a.authMiddleware, h.UserProfile)
a.fiber.Get("/user/single/:id", a.authMiddleware, h.GetUserByID)
a.fiber.Get("/user/wallet", a.authMiddleware, h.GetCustomerWallet)
a.fiber.Post("/user/search", a.authMiddleware, h.SearchUserByNameOrPhone)
@ -81,15 +83,15 @@ func (a *App) initAppRoutes() {
a.fiber.Get("/cashiers", a.authMiddleware, h.GetAllCashiers)
a.fiber.Post("/cashiers", a.authMiddleware, h.CreateCashier)
a.fiber.Put("/cashiers/:id", a.authMiddleware, h.UpdateCashier)
//
a.fiber.Get("/admin", a.authMiddleware, h.GetAllAdmins)
a.fiber.Post("/admin", a.authMiddleware, h.CreateAdmin)
a.fiber.Get("/managers", a.authMiddleware, h.GetAllManagers)
a.fiber.Post("/managers", a.authMiddleware, h.CreateManager)
a.fiber.Put("/managers/:id", a.authMiddleware, h.UpdateManagers)
a.fiber.Get("/manager/:id/branch", a.authMiddleware, h.GetBranchByManagerID)
a.fiber.Get("/company/:id/branch", a.authMiddleware, h.GetBranchByCompanyID)
a.fiber.Get("/prematch/odds/:event_id", h.GetPrematchOdds)
a.fiber.Get("/prematch/odds", h.GetALLPrematchOdds)
a.fiber.Get("/prematch/odds/upcoming/:upcoming_id/market/:market_id", h.GetRawOddsByMarketID)
@ -125,6 +127,8 @@ func (a *App) initAppRoutes() {
a.fiber.Get("/company/:id", a.authMiddleware, a.SuperAdminOnly, h.GetCompanyByID)
a.fiber.Put("/company/:id", a.authMiddleware, a.SuperAdminOnly, h.UpdateCompany)
a.fiber.Delete("/company/:id", a.authMiddleware, a.SuperAdminOnly, h.DeleteCompany)
a.fiber.Get("/company/:id/branch", a.authMiddleware, h.GetBranchByCompanyID)
a.fiber.Get("/search/company", a.authMiddleware, h.SearchCompany)
// Ticket Routes
a.fiber.Post("/ticket", h.CreateTicket)
@ -132,12 +136,12 @@ func (a *App) initAppRoutes() {
a.fiber.Get("/ticket/:id", h.GetTicketByID)
// Bet Routes
a.fiber.Post("/bet", h.CreateBet)
a.fiber.Get("/bet", h.GetAllBet)
a.fiber.Get("/bet/:id", h.GetBetByID)
a.fiber.Post("/bet", a.authMiddleware, h.CreateBet)
a.fiber.Get("/bet", a.authMiddleware, h.GetAllBet)
a.fiber.Get("/bet/:id", a.authMiddleware, h.GetBetByID)
a.fiber.Get("/bet/cashout/:id", a.authMiddleware, h.GetBetByCashoutID)
a.fiber.Patch("/bet/:id", h.UpdateCashOut)
a.fiber.Delete("/bet/:id", h.DeleteBet)
a.fiber.Patch("/bet/:id", a.authMiddleware, h.UpdateCashOut)
a.fiber.Delete("/bet/:id", a.authMiddleware, h.DeleteBet)
// Wallet
a.fiber.Get("/wallet", h.GetAllWallets)
@ -152,10 +156,10 @@ func (a *App) initAppRoutes() {
a.fiber.Post("/transfer/refill/:id", a.authMiddleware, h.RefillWallet)
// Transactions /transactions
a.fiber.Post("/transaction", h.CreateTransaction)
a.fiber.Get("/transaction", h.GetAllTransactions)
a.fiber.Get("/transaction/:id", h.GetTransactionByID)
a.fiber.Patch("/transaction/:id", h.UpdateTransactionVerified)
a.fiber.Post("/transaction", a.authMiddleware, h.CreateTransaction)
a.fiber.Get("/transaction", a.authMiddleware, h.GetAllTransactions)
a.fiber.Get("/transaction/:id", a.authMiddleware, h.GetTransactionByID)
a.fiber.Patch("/transaction/:id", a.authMiddleware, h.UpdateTransactionVerified)
// Notification Routes
a.fiber.Get("/notifications/ws/connect/:recipientID", h.ConnectSocket)