Merge branch 'main' into wallet

This commit is contained in:
Samuel Tariku 2025-04-10 16:34:11 +03:00
commit a97cb32bba
14 changed files with 1512 additions and 35 deletions

View File

@ -716,6 +716,163 @@ const docTemplate = `{
} }
} }
}, },
"/cashiers": {
"get": {
"description": "Get all cashiers",
"consumes": [
"application/json"
],
"produces": [
"application/json"
],
"tags": [
"cashier"
],
"summary": "Get all cashiers",
"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 cashier",
"consumes": [
"application/json"
],
"produces": [
"application/json"
],
"tags": [
"cashier"
],
"summary": "Create cashier",
"parameters": [
{
"description": "Create cashier",
"name": "cashier",
"in": "body",
"required": true,
"schema": {
"$ref": "#/definitions/handlers.CreateCashierReq"
}
}
],
"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"
}
}
}
}
},
"/cashiers/{id}": {
"put": {
"description": "Update cashier",
"consumes": [
"application/json"
],
"produces": [
"application/json"
],
"tags": [
"cashier"
],
"summary": "Update cashier",
"parameters": [
{
"description": "Update cashier",
"name": "cashier",
"in": "body",
"required": true,
"schema": {
"$ref": "#/definitions/handlers.updateUserReq"
}
}
],
"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"
}
}
}
}
},
"/company/{id}/branch": { "/company/{id}/branch": {
"get": { "get": {
"description": "Gets branches by company id", "description": "Gets branches by company id",
@ -810,6 +967,163 @@ const docTemplate = `{
} }
} }
}, },
"/managers": {
"get": {
"description": "Get all Managers",
"consumes": [
"application/json"
],
"produces": [
"application/json"
],
"tags": [
"manager"
],
"summary": "Get all Managers",
"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 Managers",
"consumes": [
"application/json"
],
"produces": [
"application/json"
],
"tags": [
"manager"
],
"summary": "Create Managers",
"parameters": [
{
"description": "Create manager",
"name": "manger",
"in": "body",
"required": true,
"schema": {
"$ref": "#/definitions/handlers.CreateManagerReq"
}
}
],
"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"
}
}
}
}
},
"/managers/{id}": {
"put": {
"description": "Update Managers",
"consumes": [
"application/json"
],
"produces": [
"application/json"
],
"tags": [
"Managers"
],
"summary": "Update Managers",
"parameters": [
{
"description": "Update Managers",
"name": "Managers",
"in": "body",
"required": true,
"schema": {
"$ref": "#/definitions/handlers.updateUserReq"
}
}
],
"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"
}
}
}
}
},
"/operation": { "/operation": {
"post": { "post": {
"description": "Creates a operation", "description": "Creates a operation",
@ -2074,6 +2388,60 @@ const docTemplate = `{
} }
} }
}, },
"handlers.CreateCashierReq": {
"type": "object",
"properties": {
"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.CreateManagerReq": {
"type": "object",
"properties": {
"branch_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.CreateSupportedOperationReq": { "handlers.CreateSupportedOperationReq": {
"type": "object", "type": "object",
"properties": { "properties": {
@ -2594,6 +2962,9 @@ const docTemplate = `{
}, },
"refresh_token": { "refresh_token": {
"type": "string" "type": "string"
},
"role": {
"type": "string"
} }
} }
}, },
@ -2616,6 +2987,23 @@ const docTemplate = `{
} }
} }
}, },
"handlers.updateUserReq": {
"type": "object",
"properties": {
"first_name": {
"type": "string",
"example": "John"
},
"last_name": {
"type": "string",
"example": "Doe"
},
"suspended": {
"type": "boolean",
"example": false
}
}
},
"response.APIResponse": { "response.APIResponse": {
"type": "object", "type": "object",
"properties": { "properties": {

View File

@ -708,6 +708,163 @@
} }
} }
}, },
"/cashiers": {
"get": {
"description": "Get all cashiers",
"consumes": [
"application/json"
],
"produces": [
"application/json"
],
"tags": [
"cashier"
],
"summary": "Get all cashiers",
"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 cashier",
"consumes": [
"application/json"
],
"produces": [
"application/json"
],
"tags": [
"cashier"
],
"summary": "Create cashier",
"parameters": [
{
"description": "Create cashier",
"name": "cashier",
"in": "body",
"required": true,
"schema": {
"$ref": "#/definitions/handlers.CreateCashierReq"
}
}
],
"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"
}
}
}
}
},
"/cashiers/{id}": {
"put": {
"description": "Update cashier",
"consumes": [
"application/json"
],
"produces": [
"application/json"
],
"tags": [
"cashier"
],
"summary": "Update cashier",
"parameters": [
{
"description": "Update cashier",
"name": "cashier",
"in": "body",
"required": true,
"schema": {
"$ref": "#/definitions/handlers.updateUserReq"
}
}
],
"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"
}
}
}
}
},
"/company/{id}/branch": { "/company/{id}/branch": {
"get": { "get": {
"description": "Gets branches by company id", "description": "Gets branches by company id",
@ -802,6 +959,163 @@
} }
} }
}, },
"/managers": {
"get": {
"description": "Get all Managers",
"consumes": [
"application/json"
],
"produces": [
"application/json"
],
"tags": [
"manager"
],
"summary": "Get all Managers",
"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 Managers",
"consumes": [
"application/json"
],
"produces": [
"application/json"
],
"tags": [
"manager"
],
"summary": "Create Managers",
"parameters": [
{
"description": "Create manager",
"name": "manger",
"in": "body",
"required": true,
"schema": {
"$ref": "#/definitions/handlers.CreateManagerReq"
}
}
],
"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"
}
}
}
}
},
"/managers/{id}": {
"put": {
"description": "Update Managers",
"consumes": [
"application/json"
],
"produces": [
"application/json"
],
"tags": [
"Managers"
],
"summary": "Update Managers",
"parameters": [
{
"description": "Update Managers",
"name": "Managers",
"in": "body",
"required": true,
"schema": {
"$ref": "#/definitions/handlers.updateUserReq"
}
}
],
"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"
}
}
}
}
},
"/operation": { "/operation": {
"post": { "post": {
"description": "Creates a operation", "description": "Creates a operation",
@ -2066,6 +2380,60 @@
} }
} }
}, },
"handlers.CreateCashierReq": {
"type": "object",
"properties": {
"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.CreateManagerReq": {
"type": "object",
"properties": {
"branch_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.CreateSupportedOperationReq": { "handlers.CreateSupportedOperationReq": {
"type": "object", "type": "object",
"properties": { "properties": {
@ -2586,6 +2954,9 @@
}, },
"refresh_token": { "refresh_token": {
"type": "string" "type": "string"
},
"role": {
"type": "string"
} }
} }
}, },
@ -2608,6 +2979,23 @@
} }
} }
}, },
"handlers.updateUserReq": {
"type": "object",
"properties": {
"first_name": {
"type": "string",
"example": "John"
},
"last_name": {
"type": "string",
"example": "Doe"
},
"suspended": {
"type": "boolean",
"example": false
}
}
},
"response.APIResponse": { "response.APIResponse": {
"type": "object", "type": "object",
"properties": { "properties": {

View File

@ -204,6 +204,45 @@ definitions:
type: integer type: integer
type: array type: array
type: object type: object
handlers.CreateCashierReq:
properties:
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.CreateManagerReq:
properties:
branch_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.CreateSupportedOperationReq: handlers.CreateSupportedOperationReq:
properties: properties:
description: description:
@ -565,6 +604,8 @@ definitions:
type: string type: string
refresh_token: refresh_token:
type: string type: string
role:
type: string
type: object type: object
handlers.logoutReq: handlers.logoutReq:
properties: properties:
@ -578,6 +619,18 @@ definitions:
refresh_token: refresh_token:
type: string type: string
type: object type: object
handlers.updateUserReq:
properties:
first_name:
example: John
type: string
last_name:
example: Doe
type: string
suspended:
example: false
type: boolean
type: object
response.APIResponse: response.APIResponse:
properties: properties:
data: {} data: {}
@ -1067,6 +1120,109 @@ paths:
summary: Delete the branch operation summary: Delete the branch operation
tags: tags:
- branch - branch
/cashiers:
get:
consumes:
- application/json
description: Get all cashiers
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 cashiers
tags:
- cashier
post:
consumes:
- application/json
description: Create cashier
parameters:
- description: Create cashier
in: body
name: cashier
required: true
schema:
$ref: '#/definitions/handlers.CreateCashierReq'
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 cashier
tags:
- cashier
/cashiers/{id}:
put:
consumes:
- application/json
description: Update cashier
parameters:
- description: Update cashier
in: body
name: cashier
required: true
schema:
$ref: '#/definitions/handlers.updateUserReq'
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: Update cashier
tags:
- cashier
/company/{id}/branch: /company/{id}/branch:
get: get:
consumes: consumes:
@ -1129,6 +1285,109 @@ paths:
summary: Gets branches by manager id summary: Gets branches by manager id
tags: tags:
- branch - branch
/managers:
get:
consumes:
- application/json
description: Get all Managers
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 Managers
tags:
- manager
post:
consumes:
- application/json
description: Create Managers
parameters:
- description: Create manager
in: body
name: manger
required: true
schema:
$ref: '#/definitions/handlers.CreateManagerReq'
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 Managers
tags:
- manager
/managers/{id}:
put:
consumes:
- application/json
description: Update Managers
parameters:
- description: Update Managers
in: body
name: Managers
required: true
schema:
$ref: '#/definitions/handlers.updateUserReq'
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: Update Managers
tags:
- Managers
/operation: /operation:
post: post:
consumes: consumes:

View File

@ -26,6 +26,8 @@ type User struct {
// //
SuspendedAt time.Time SuspendedAt time.Time
Suspended bool Suspended bool
//
BranchID int64
} }
type RegisterUserReq struct { type RegisterUserReq struct {
FirstName string FirstName string
@ -39,6 +41,15 @@ type RegisterUserReq struct {
// //
OtpMedium OtpMedium OtpMedium OtpMedium
} }
type CreateUserReq struct {
BranchID int64
FirstName string
LastName string
Email string
PhoneNumber string
Password string
Role string
}
type ResetPasswordReq struct { type ResetPasswordReq struct {
Email string Email string
PhoneNumber string PhoneNumber string
@ -47,6 +58,7 @@ type ResetPasswordReq struct {
OtpMedium OtpMedium OtpMedium OtpMedium
} }
type UpdateUserReq struct { type UpdateUserReq struct {
UserId int64
FirstName ValidString FirstName ValidString
LastName ValidString LastName ValidString
Suspended ValidBool Suspended ValidBool

View File

@ -8,6 +8,7 @@ import (
dbgen "github.com/SamuelTariku/FortuneBet-Backend/gen/db" dbgen "github.com/SamuelTariku/FortuneBet-Backend/gen/db"
"github.com/SamuelTariku/FortuneBet-Backend/internal/domain" "github.com/SamuelTariku/FortuneBet-Backend/internal/domain"
"github.com/SamuelTariku/FortuneBet-Backend/internal/services/user"
"github.com/jackc/pgx/v5/pgtype" "github.com/jackc/pgx/v5/pgtype"
) )
@ -82,7 +83,7 @@ func (s *Store) GetUserByID(ctx context.Context, id int64) (domain.User, error)
Suspended: user.Suspended, Suspended: user.Suspended,
}, nil }, nil
} }
func (s *Store) GetAllUsers(ctx context.Context) ([]domain.User, error) { func (s *Store) GetAllUsers(ctx context.Context, filter user.Filter) ([]domain.User, error) {
users, err := s.queries.GetAllUsers(ctx) users, err := s.queries.GetAllUsers(ctx)
if err != nil { if err != nil {
return nil, err return nil, err
@ -233,3 +234,6 @@ func (s *Store) UpdatePassword(ctx context.Context, identifier string, password
} }
return nil return nil
} }
func (s *Store) CreateUserWithoutOtp(ctx context.Context, user domain.CreateUserReq) (domain.User, error) {
return domain.User{}, nil
}

View File

@ -22,6 +22,7 @@ type LoginSuccess struct {
UserId int64 UserId int64
Role domain.Role Role domain.Role
RfToken string RfToken string
BranchId int64
} }
func (s *Service) Login(ctx context.Context, email, phone string, password string) (LoginSuccess, error) { func (s *Service) Login(ctx context.Context, email, phone string, password string) (LoginSuccess, error) {
@ -51,37 +52,35 @@ func (s *Service) Login(ctx context.Context, email, phone string, password strin
UserId: user.ID, UserId: user.ID,
Role: user.Role, Role: user.Role,
RfToken: refreshToken, RfToken: refreshToken,
BranchId: user.BranchID,
}, nil }, nil
} }
func (s *Service) RefreshToken(ctx context.Context, refToken string) (string, error) { func (s *Service) RefreshToken(ctx context.Context, refToken string) error {
token, err := s.tokenStore.GetRefreshToken(ctx, refToken) token, err := s.tokenStore.GetRefreshToken(ctx, refToken)
if err != nil { if err != nil {
return "", err return err
} }
if token.Revoked { if token.Revoked {
return "", ErrRefreshTokenNotFound return ErrRefreshTokenNotFound
} }
if token.ExpiresAt.Before(time.Now()) { if token.ExpiresAt.Before(time.Now()) {
return "", ErrExpiredToken return ErrExpiredToken
} }
newRefToken, err := generateRefreshToken() // newRefToken, err := generateRefreshToken()
if err != nil { // if err != nil {
return "", err // return "", err
} // }
err = s.tokenStore.CreateRefreshToken(ctx, domain.RefreshToken{ // err = s.tokenStore.CreateRefreshToken(ctx, domain.RefreshToken{
Token: newRefToken, // Token: newRefToken,
UserID: token.UserID, // UserID: token.UserID,
CreatedAt: time.Now(), // CreatedAt: time.Now(),
ExpiresAt: time.Now().Add(time.Duration(s.RefreshExpiry) * time.Second), // ExpiresAt: time.Now().Add(time.Duration(s.RefreshExpiry) * time.Second),
}) // })
if err != nil { return nil
return "", err
}
return newRefToken, nil
} }
func (s *Service) Logout(ctx context.Context, refToken string) error { func (s *Service) Logout(ctx context.Context, refToken string) error {
token, err := s.tokenStore.GetRefreshToken(ctx, refToken) token, err := s.tokenStore.GetRefreshToken(ctx, refToken)

View File

@ -0,0 +1,53 @@
package user
import (
"context"
"github.com/SamuelTariku/FortuneBet-Backend/internal/domain"
)
func (s *Service) CreateUser(ctx context.Context, User domain.CreateUserReq) (domain.User, error) {
// Create User
// creator, err := s.userStore.GetUserByID(ctx, createrUserId)
// if err != nil {
// return domain.User{}, err
// }
// if creator.Role != domain.RoleAdmin {
// User.BranchID = creator.BranchID
// User.Role = string(domain.RoleCashier)
// } else {
// User.BranchID = branchId
// User.Role = string(domain.RoleBranchManager)
// }
return s.userStore.CreateUserWithoutOtp(ctx, User)
}
func (s *Service) DeleteUser(ctx context.Context, id int64) error {
// Delete User
return s.userStore.DeleteUser(ctx, id)
}
type Filter struct {
Role string
BranchId ValidBranchId
Page int
PageSize int
}
type ValidRole struct {
Value domain.Role
Valid bool
}
type ValidBranchId struct {
Value int64
Valid bool
}
func (s *Service) GetAllUsers(ctx context.Context, filter Filter) ([]domain.User, error) {
// Get all Users
return s.userStore.GetAllUsers(ctx, filter)
}
func (s *Service) GetUserById(ctx context.Context, id int64) (domain.User, error) {
return s.userStore.GetUserByID(ctx, id)
}

View File

@ -8,8 +8,9 @@ import (
type UserStore interface { type UserStore interface {
CreateUser(ctx context.Context, user domain.User, usedOtpId int64) (domain.User, error) CreateUser(ctx context.Context, user domain.User, usedOtpId int64) (domain.User, error)
CreateUserWithoutOtp(ctx context.Context, user domain.CreateUserReq) (domain.User, error)
GetUserByID(ctx context.Context, id int64) (domain.User, error) GetUserByID(ctx context.Context, id int64) (domain.User, error)
GetAllUsers(ctx context.Context) ([]domain.User, error) GetAllUsers(ctx context.Context, filter Filter) ([]domain.User, error)
UpdateUser(ctx context.Context, user domain.UpdateUserReq) error UpdateUser(ctx context.Context, user domain.UpdateUserReq) error
DeleteUser(ctx context.Context, id int64) error DeleteUser(ctx context.Context, id int64) error
CheckPhoneEmailExist(ctx context.Context, phoneNum, email string) (bool, bool, error) CheckPhoneEmailExist(ctx context.Context, phoneNum, email string) (bool, bool, error)

View File

@ -4,6 +4,7 @@ import (
"errors" "errors"
"log/slog" "log/slog"
"github.com/SamuelTariku/FortuneBet-Backend/internal/domain"
"github.com/SamuelTariku/FortuneBet-Backend/internal/services/authentication" "github.com/SamuelTariku/FortuneBet-Backend/internal/services/authentication"
jwtutil "github.com/SamuelTariku/FortuneBet-Backend/internal/web_server/jwt" jwtutil "github.com/SamuelTariku/FortuneBet-Backend/internal/web_server/jwt"
"github.com/SamuelTariku/FortuneBet-Backend/internal/web_server/response" "github.com/SamuelTariku/FortuneBet-Backend/internal/web_server/response"
@ -20,6 +21,7 @@ type loginCustomerReq struct {
type loginCustomerRes struct { type loginCustomerRes struct {
AccessToken string `json:"access_token"` AccessToken string `json:"access_token"`
RefreshToken string `json:"refresh_token"` RefreshToken string `json:"refresh_token"`
Role string `json:"role"`
} }
// LoginCustomer godoc // LoginCustomer godoc
@ -65,10 +67,11 @@ func LoginCustomer(
return nil return nil
} }
accessToken, err := jwtutil.CreateJwt(successRes.UserId, successRes.Role, JwtConfig.JwtAccessKey, JwtConfig.JwtAccessExpiry) accessToken, err := jwtutil.CreateJwt(successRes.UserId, successRes.Role, successRes.BranchId, JwtConfig.JwtAccessKey, JwtConfig.JwtAccessExpiry)
res := loginCustomerRes{ res := loginCustomerRes{
AccessToken: accessToken, AccessToken: accessToken,
RefreshToken: successRes.RfToken, RefreshToken: successRes.RfToken,
Role: string(successRes.Role),
} }
return response.WriteJSON(c, fiber.StatusOK, "Login successful", res, nil) return response.WriteJSON(c, fiber.StatusOK, "Login successful", res, nil)
} }
@ -103,7 +106,10 @@ func RefreshToken(logger *slog.Logger, authSvc *authentication.Service,
response.WriteJSON(c, fiber.StatusBadRequest, "Invalid request", valErrs, nil) response.WriteJSON(c, fiber.StatusBadRequest, "Invalid request", valErrs, nil)
return nil return nil
} }
rf, err := authSvc.RefreshToken(c.Context(), req.RefreshToken) userId := c.Locals("user_id").(int64)
role := c.Locals("role").(string)
branchId := c.Locals("branch_id").(int64)
err := authSvc.RefreshToken(c.Context(), req.RefreshToken)
if err != nil { if err != nil {
logger.Info("Refresh token failed", "error", err) logger.Info("Refresh token failed", "error", err)
if errors.Is(err, authentication.ErrExpiredToken) { if errors.Is(err, authentication.ErrExpiredToken) {
@ -118,7 +124,7 @@ func RefreshToken(logger *slog.Logger, authSvc *authentication.Service,
response.WriteJSON(c, fiber.StatusInternalServerError, "Internal server error", nil, nil) response.WriteJSON(c, fiber.StatusInternalServerError, "Internal server error", nil, nil)
return nil return nil
} }
accessToken, err := jwtutil.CreateJwt(0, "", JwtConfig.JwtAccessKey, JwtConfig.JwtAccessExpiry) accessToken, err := jwtutil.CreateJwt(userId, domain.Role(role), branchId, JwtConfig.JwtAccessKey, JwtConfig.JwtAccessExpiry)
if err != nil { if err != nil {
logger.Error("Create jwt failed", "error", err) logger.Error("Create jwt failed", "error", err)
response.WriteJSON(c, fiber.StatusInternalServerError, "Internal server error", nil, nil) response.WriteJSON(c, fiber.StatusInternalServerError, "Internal server error", nil, nil)
@ -127,7 +133,7 @@ func RefreshToken(logger *slog.Logger, authSvc *authentication.Service,
res := loginCustomerRes{ res := loginCustomerRes{
AccessToken: accessToken, AccessToken: accessToken,
RefreshToken: rf, RefreshToken: req.RefreshToken,
} }
return response.WriteJSON(c, fiber.StatusOK, "refresh successful", res, nil) return response.WriteJSON(c, fiber.StatusOK, "refresh successful", res, nil)
} }

View File

@ -0,0 +1,178 @@
package handlers
import (
"log/slog"
"strconv"
"github.com/SamuelTariku/FortuneBet-Backend/internal/domain"
"github.com/SamuelTariku/FortuneBet-Backend/internal/services/user"
"github.com/SamuelTariku/FortuneBet-Backend/internal/web_server/response"
customvalidator "github.com/SamuelTariku/FortuneBet-Backend/internal/web_server/validator"
"github.com/gofiber/fiber/v2"
)
type CreateCashierReq 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"`
}
// CreateCashier godoc
// @Summary Create cashier
// @Description Create cashier
// @Tags cashier
// @Accept json
// @Produce json
// @Param cashier body CreateCashierReq true "Create cashier"
// @Success 200 {object} response.APIResponse
// @Failure 400 {object} response.APIResponse
// @Failure 401 {object} response.APIResponse
// @Failure 500 {object} response.APIResponse
// @Router /cashiers [post]
func CreateCashier(logger *slog.Logger, userSvc *user.Service, validator *customvalidator.CustomValidator) fiber.Handler {
return func(c *fiber.Ctx) error {
creatorBranch := c.Locals("branch_id").(int64)
var req CreateCashierReq
if err := c.BodyParser(&req); err != nil {
logger.Error("RegisterUser failed", "error", err)
return c.Status(fiber.StatusBadRequest).JSON(fiber.Map{
"error": "Invalid request",
})
}
valErrs, ok := validator.Validate(c, req)
if !ok {
response.WriteJSON(c, fiber.StatusBadRequest, "Invalid request", valErrs, nil)
return nil
}
user := domain.CreateUserReq{
FirstName: req.FirstName,
LastName: req.LastName,
Email: req.Email,
PhoneNumber: req.PhoneNumber,
Password: req.Password,
Role: string(domain.RoleCashier),
BranchID: creatorBranch,
}
_, err := userSvc.CreateUser(c.Context(), user)
if err != nil {
logger.Error("CreateCashier failed", "error", err)
response.WriteJSON(c, fiber.StatusInternalServerError, "Failed to create cashier", nil, nil)
return nil
}
response.WriteJSON(c, fiber.StatusOK, "Cashier created successfully", nil, nil)
return nil
}
}
// GetAllCashiers godoc
// @Summary Get all cashiers
// @Description Get all cashiers
// @Tags cashier
// @Accept json
// @Produce json
// @Param page query int false "Page number"
// @Param page_size query int false "Page size"
// @Success 200 {object} response.APIResponse
// @Failure 400 {object} response.APIResponse
// @Failure 401 {object} response.APIResponse
// @Failure 500 {object} response.APIResponse
// @Router /cashiers [get]
func GetAllCashiers(logger *slog.Logger, userSvc *user.Service, validator *customvalidator.CustomValidator) fiber.Handler {
return func(c *fiber.Ctx) error {
branchId := int64(12) //c.Locals("branch_id").(int64)
filter := user.Filter{
Role: string(domain.RoleCashier),
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 {
response.WriteJSON(c, fiber.StatusBadRequest, "Invalid request", valErrs, nil)
return nil
}
cashiers, err := userSvc.GetAllUsers(c.Context(), filter)
if err != nil {
logger.Error("GetAllCashiers failed", "error", err)
response.WriteJSON(c, fiber.StatusInternalServerError, "Failed to get cashiers", nil, nil)
return nil
}
response.WriteJSON(c, fiber.StatusOK, "Cashiers retrieved successfully", cashiers, nil)
return nil
}
}
type updateUserReq struct {
FirstName string `json:"first_name" example:"John"`
LastName string `json:"last_name" example:"Doe"`
Suspended bool `json:"suspended" example:"false"`
}
// UpdateCashier godoc
// @Summary Update cashier
// @Description Update cashier
// @Tags cashier
// @Accept json
// @Produce json
// @Param cashier body updateUserReq true "Update cashier"
// @Success 200 {object} response.APIResponse
// @Failure 400 {object} response.APIResponse
// @Failure 401 {object} response.APIResponse
// @Failure 500 {object} response.APIResponse
// @Router /cashiers/{id} [put]
func UpdateCashier(logger *slog.Logger, userSvc *user.Service, validator *customvalidator.CustomValidator) fiber.Handler {
return func(c *fiber.Ctx) error {
var req updateUserReq
if err := c.BodyParser(&req); err != nil {
logger.Error("UpdateCashier failed", "error", err)
response.WriteJSON(c, fiber.StatusBadRequest, "Invalid request", nil, nil)
return nil
}
valErrs, ok := validator.Validate(c, req)
if !ok {
response.WriteJSON(c, fiber.StatusBadRequest, "Invalid request", valErrs, nil)
return nil
}
cashierIdStr := c.Params("id")
cashierId, err := strconv.ParseInt(cashierIdStr, 10, 64)
if err != nil {
logger.Error("UpdateCashier failed", "error", err)
response.WriteJSON(c, fiber.StatusBadRequest, "Invalid cashier ID", nil, nil)
return nil
}
err = userSvc.UpdateUser(c.Context(), domain.UpdateUserReq{
UserId: cashierId,
FirstName: domain.ValidString{
Value: req.FirstName,
Valid: req.FirstName != "",
},
LastName: domain.ValidString{
Value: req.LastName,
Valid: req.LastName != "",
},
Suspended: domain.ValidBool{
Value: req.Suspended,
Valid: true,
},
},
)
if err != nil {
logger.Error("UpdateCashier failed", "error", err)
response.WriteJSON(c, fiber.StatusInternalServerError, "Failed to update cashier", nil, nil)
return nil
}
response.WriteJSON(c, fiber.StatusOK, "Cashier updated successfully", nil, nil)
return nil
}
}

View File

@ -0,0 +1,170 @@
package handlers
import (
"log/slog"
"strconv"
"github.com/SamuelTariku/FortuneBet-Backend/internal/domain"
"github.com/SamuelTariku/FortuneBet-Backend/internal/services/user"
"github.com/SamuelTariku/FortuneBet-Backend/internal/web_server/response"
customvalidator "github.com/SamuelTariku/FortuneBet-Backend/internal/web_server/validator"
"github.com/gofiber/fiber/v2"
)
type CreateManagerReq 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"`
BranchId int64 `json:"branch_id" example:"1"`
}
// CreateManagers godoc
// @Summary Create Managers
// @Description Create Managers
// @Tags manager
// @Accept json
// @Produce json
// @Param manger body CreateManagerReq true "Create manager"
// @Success 200 {object} response.APIResponse
// @Failure 400 {object} response.APIResponse
// @Failure 401 {object} response.APIResponse
// @Failure 500 {object} response.APIResponse
// @Router /managers [post]
func CreateManager(logger *slog.Logger, userSvc *user.Service, validator *customvalidator.CustomValidator) fiber.Handler {
return func(c *fiber.Ctx) error {
var req CreateManagerReq
if err := c.BodyParser(&req); err != nil {
logger.Error("RegisterUser failed", "error", err)
return c.Status(fiber.StatusBadRequest).JSON(fiber.Map{
"error": "Invalid request",
})
}
valErrs, ok := validator.Validate(c, req)
if !ok {
response.WriteJSON(c, fiber.StatusBadRequest, "Invalid request", valErrs, nil)
return nil
}
user := domain.CreateUserReq{
FirstName: req.FirstName,
LastName: req.LastName,
Email: req.Email,
PhoneNumber: req.PhoneNumber,
Password: req.Password,
Role: string(domain.RoleBranchManager),
BranchID: req.BranchId,
}
_, err := userSvc.CreateUser(c.Context(), user)
if err != nil {
logger.Error("CreateManagers failed", "error", err)
response.WriteJSON(c, fiber.StatusInternalServerError, "Failed to create Managers", nil, nil)
return nil
}
response.WriteJSON(c, fiber.StatusOK, "Managers created successfully", nil, nil)
return nil
}
}
// GetAllManagers godoc
// @Summary Get all Managers
// @Description Get all Managers
// @Tags manager
// @Accept json
// @Produce json
// @Param page query int false "Page number"
// @Param page_size query int false "Page size"
// @Success 200 {object} response.APIResponse
// @Failure 400 {object} response.APIResponse
// @Failure 401 {object} response.APIResponse
// @Failure 500 {object} response.APIResponse
// @Router /managers [get]
func GetAllManagers(logger *slog.Logger, userSvc *user.Service, validator *customvalidator.CustomValidator) fiber.Handler {
return func(c *fiber.Ctx) error {
filter := user.Filter{
Role: string(domain.RoleBranchManager),
BranchId: user.ValidBranchId{
Value: int64(c.QueryInt("branch_id")),
Valid: true,
},
Page: c.QueryInt("page", 1),
PageSize: c.QueryInt("page_size", 10),
}
valErrs, ok := validator.Validate(c, filter)
if !ok {
response.WriteJSON(c, fiber.StatusBadRequest, "Invalid request", valErrs, nil)
return nil
}
Managers, err := userSvc.GetAllUsers(c.Context(), filter)
if err != nil {
logger.Error("GetAllManagers failed", "error", err)
response.WriteJSON(c, fiber.StatusInternalServerError, "Failed to get Managers", nil, nil)
return nil
}
response.WriteJSON(c, fiber.StatusOK, "Managers retrieved successfully", Managers, nil)
return nil
}
}
// UpdateManagers godoc
// @Summary Update Managers
// @Description Update Managers
// @Tags Managers
// @Accept json
// @Produce json
// @Param Managers body updateUserReq true "Update Managers"
// @Success 200 {object} response.APIResponse
// @Failure 400 {object} response.APIResponse
// @Failure 401 {object} response.APIResponse
// @Failure 500 {object} response.APIResponse
// @Router /managers/{id} [put]
func UPdateManagers(logger *slog.Logger, userSvc *user.Service, validator *customvalidator.CustomValidator) fiber.Handler {
return func(c *fiber.Ctx) error {
var req updateUserReq
if err := c.BodyParser(&req); err != nil {
logger.Error("UpdateManagers failed", "error", err)
response.WriteJSON(c, fiber.StatusBadRequest, "Invalid request", nil, nil)
return nil
}
valErrs, ok := validator.Validate(c, req)
if !ok {
response.WriteJSON(c, fiber.StatusBadRequest, "Invalid request", valErrs, nil)
return nil
}
ManagersIdStr := c.Params("id")
ManagersId, err := strconv.ParseInt(ManagersIdStr, 10, 64)
if err != nil {
logger.Error("UpdateManagers failed", "error", err)
response.WriteJSON(c, fiber.StatusBadRequest, "Invalid Managers ID", nil, nil)
return nil
}
err = userSvc.UpdateUser(c.Context(), domain.UpdateUserReq{
UserId: ManagersId,
FirstName: domain.ValidString{
Value: req.FirstName,
Valid: req.FirstName != "",
},
LastName: domain.ValidString{
Value: req.LastName,
Valid: req.LastName != "",
},
Suspended: domain.ValidBool{
Value: req.Suspended,
Valid: true,
},
},
)
if err != nil {
logger.Error("UpdateManagers failed", "error", err)
response.WriteJSON(c, fiber.StatusInternalServerError, "Failed to update Managers", nil, nil)
return nil
}
response.WriteJSON(c, fiber.StatusOK, "Managers updated successfully", nil, nil)
return nil
}
}

View File

@ -18,17 +18,24 @@ var (
ErrRefreshTokenNotFound = errors.New("refresh token not found") // i.e login again ErrRefreshTokenNotFound = errors.New("refresh token not found") // i.e login again
) )
type User struct {
UserId int64
Role domain.Role
BranchId int64
RefreshToken string
}
type UserClaim struct { type UserClaim struct {
jwt.RegisteredClaims jwt.RegisteredClaims
UserId int64 UserId int64
Role domain.Role Role domain.Role
BranchId int64
} }
type JwtConfig struct { type JwtConfig struct {
JwtAccessKey string JwtAccessKey string
JwtAccessExpiry int JwtAccessExpiry int
} }
func CreateJwt(userId int64, Role domain.Role, key string, expiry int) (string, error) { func CreateJwt(userId int64, Role domain.Role, BranchId int64, key string, expiry int) (string, error) {
token := jwt.NewWithClaims(jwt.SigningMethodHS256, UserClaim{RegisteredClaims: jwt.RegisteredClaims{Issuer: "github.com/lafetz/snippitstash", token := jwt.NewWithClaims(jwt.SigningMethodHS256, UserClaim{RegisteredClaims: jwt.RegisteredClaims{Issuer: "github.com/lafetz/snippitstash",
IssuedAt: jwt.NewNumericDate(time.Now()), IssuedAt: jwt.NewNumericDate(time.Now()),
Audience: jwt.ClaimStrings{"fortune.com"}, Audience: jwt.ClaimStrings{"fortune.com"},
@ -36,6 +43,7 @@ func CreateJwt(userId int64, Role domain.Role, key string, expiry int) (string,
ExpiresAt: jwt.NewNumericDate(time.Now().Add(time.Duration(expiry) * time.Second))}, ExpiresAt: jwt.NewNumericDate(time.Now().Add(time.Duration(expiry) * time.Second))},
UserId: userId, UserId: userId,
Role: Role, Role: Role,
BranchId: BranchId,
}) })
jwtToken, err := token.SignedString([]byte(key)) // jwtToken, err := token.SignedString([]byte(key)) //
return jwtToken, err return jwtToken, err

View File

@ -44,6 +44,7 @@ func (a *App) authMiddleware(c *fiber.Ctx) error {
} }
c.Locals("user_id", claim.UserId) c.Locals("user_id", claim.UserId)
c.Locals("role", claim.Role) c.Locals("role", claim.Role)
c.Locals("branch_id", claim.BranchId)
c.Locals("refresh_token", refreshToken) c.Locals("refresh_token", refreshToken)
if claim.Role != domain.RoleCustomer { if claim.Role != domain.RoleCustomer {

View File

@ -39,7 +39,17 @@ func (a *App) initAppRoutes() {
a.fiber.Post("/user/sendRegisterCode", handlers.SendRegisterCode(a.logger, a.userSvc, a.validator)) a.fiber.Post("/user/sendRegisterCode", handlers.SendRegisterCode(a.logger, a.userSvc, a.validator))
a.fiber.Post("/user/checkPhoneEmailExist", handlers.CheckPhoneEmailExist(a.logger, a.userSvc, a.validator)) a.fiber.Post("/user/checkPhoneEmailExist", handlers.CheckPhoneEmailExist(a.logger, a.userSvc, a.validator))
a.fiber.Get("/user/profile", a.authMiddleware, handlers.UserProfile(a.logger, a.userSvc)) a.fiber.Get("/user/profile", a.authMiddleware, handlers.UserProfile(a.logger, a.userSvc))
//
//, a.authMiddleware
a.fiber.Get("/cashiers", handlers.GetAllCashiers(a.logger, a.userSvc, a.validator))
a.fiber.Post("/cashiers", handlers.CreateCashier(a.logger, a.userSvc, a.validator))
a.fiber.Put("/cashiers/:id", handlers.UpdateCashier(a.logger, a.userSvc, a.validator))
//
a.fiber.Get("/managers", handlers.GetAllManagers(a.logger, a.userSvc, a.validator))
a.fiber.Post("/managers", handlers.CreateManager(a.logger, a.userSvc, a.validator))
a.fiber.Put("/managers/:id", handlers.UPdateManagers(a.logger, a.userSvc, a.validator))
//
a.fiber.Get("/user/wallet", a.authMiddleware, handlers.GetCustomerWallet(a.logger, a.walletSvc, a.validator)) a.fiber.Get("/user/wallet", a.authMiddleware, handlers.GetCustomerWallet(a.logger, a.walletSvc, a.validator))
a.fiber.Post("/user/search", handlers.SearchUserByNameOrPhone(a.logger, a.userSvc, a.validator)) a.fiber.Post("/user/search", handlers.SearchUserByNameOrPhone(a.logger, a.userSvc, a.validator))