fix registration and password reset
This commit is contained in:
parent
d1a33b18dc
commit
ca7aa9d67c
|
|
@ -7,8 +7,11 @@ import (
|
|||
|
||||
"github.com/SamuelTariku/FortuneBet-Backend/internal/config"
|
||||
customlogger "github.com/SamuelTariku/FortuneBet-Backend/internal/logger"
|
||||
mockemail "github.com/SamuelTariku/FortuneBet-Backend/internal/mocks/mock_email"
|
||||
mocksms "github.com/SamuelTariku/FortuneBet-Backend/internal/mocks/mock_sms"
|
||||
"github.com/SamuelTariku/FortuneBet-Backend/internal/repository"
|
||||
"github.com/SamuelTariku/FortuneBet-Backend/internal/services/authentication"
|
||||
"github.com/SamuelTariku/FortuneBet-Backend/internal/services/user"
|
||||
httpserver "github.com/SamuelTariku/FortuneBet-Backend/internal/web_server"
|
||||
jwtutil "github.com/SamuelTariku/FortuneBet-Backend/internal/web_server/jwt"
|
||||
customvalidator "github.com/SamuelTariku/FortuneBet-Backend/internal/web_server/validator"
|
||||
|
|
@ -44,10 +47,14 @@ func main() {
|
|||
store := repository.NewStore(db)
|
||||
v := customvalidator.NewCustomValidator(validator.New())
|
||||
authSvc := authentication.NewService(store, store, cfg.RefreshExpiry)
|
||||
mockSms := mocksms.NewMockSMS()
|
||||
mockemail := mockemail.NewMockEmail()
|
||||
userSvc := user.NewService(store, store, mockSms, mockemail)
|
||||
app := httpserver.NewApp(cfg.Port, v, authSvc, logger, jwtutil.JwtConfig{
|
||||
JwtAccessKey: cfg.JwtKey,
|
||||
JwtAccessExpiry: cfg.AccessExpiry,
|
||||
})
|
||||
}, userSvc,
|
||||
)
|
||||
logger.Info("Starting server", "port", cfg.Port)
|
||||
if err := app.Run(); err != nil {
|
||||
logger.Error("Failed to start server", "error", err)
|
||||
|
|
|
|||
|
|
@ -4,18 +4,16 @@ CREATE TABLE users (
|
|||
last_name VARCHAR(255) NOT NULL,
|
||||
email VARCHAR(255) UNIQUE ,
|
||||
phone_number VARCHAR(20) UNIQUE,
|
||||
password BYTEA NOT NULL,
|
||||
role VARCHAR(50) NOT NULL,
|
||||
password BYTEA NOT NULL,
|
||||
email_verified BOOLEAN NOT NULL DEFAULT FALSE,
|
||||
phone_verified BOOLEAN NOT NULL DEFAULT FALSE,
|
||||
created_at TIMESTAMPTZ NOT NULL DEFAULT CURRENT_TIMESTAMP,
|
||||
updated_at TIMESTAMPTZ NOT NULL DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP,
|
||||
updated_at TIMESTAMPTZ ,
|
||||
--
|
||||
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 AND phone_number IS NULL) OR
|
||||
(email IS NULL AND phone_number IS NOT NULL)
|
||||
)
|
||||
CHECK (email IS NOT NULL OR phone_number IS NOT NULL)
|
||||
);
|
||||
CREATE TABLE refresh_tokens (
|
||||
id BIGSERIAL PRIMARY KEY,
|
||||
|
|
@ -26,19 +24,38 @@ CREATE TABLE refresh_tokens (
|
|||
revoked BOOLEAN DEFAULT FALSE NOT NULL,
|
||||
CONSTRAINT unique_token UNIQUE (token)
|
||||
);
|
||||
-----
|
||||
CREATE TABLE otps (
|
||||
id BIGSERIAL PRIMARY KEY,
|
||||
sent_to VARCHAR(255) NOT NULL,
|
||||
medium VARCHAR(50) NOT NULL,
|
||||
otp_for VARCHAR(50) NOT NULL,
|
||||
otp VARCHAR(10) NOT NULL,
|
||||
used BOOLEAN NOT NULL DEFAULT FALSE,
|
||||
used_at TIMESTAMPTZ,
|
||||
created_at TIMESTAMPTZ NOT NULL DEFAULT CURRENT_TIMESTAMP,
|
||||
expires_at TIMESTAMPTZ NOT NULL
|
||||
);
|
||||
----------------------------------------------seed data-------------------------------------------------------------
|
||||
-------------------------------------- DO NOT USE IN PRODUCTION-------------------------------------------------
|
||||
|
||||
CREATE EXTENSION IF NOT EXISTS pgcrypto;
|
||||
|
||||
INSERT INTO users (first_name, last_name, email, phone_number, password, role, created_at, updated_at)
|
||||
VALUES (
|
||||
INSERT INTO users (
|
||||
first_name, last_name, email, phone_number, password, role,
|
||||
email_verified, phone_verified, created_at, updated_at,
|
||||
suspended_at, suspended
|
||||
) VALUES (
|
||||
'John',
|
||||
'Doe',
|
||||
'john.doe@example.com',
|
||||
'1234567890',
|
||||
NULL,
|
||||
crypt('password123', gen_salt('bf'))::bytea,
|
||||
'user',
|
||||
'customer',
|
||||
TRUE,
|
||||
FALSE,
|
||||
CURRENT_TIMESTAMP,
|
||||
CURRENT_TIMESTAMP
|
||||
CURRENT_TIMESTAMP,
|
||||
NULL,
|
||||
FALSE
|
||||
);
|
||||
14
db/query/otp.sql
Normal file
14
db/query/otp.sql
Normal file
|
|
@ -0,0 +1,14 @@
|
|||
-- name: CreateOtp :exec
|
||||
INSERT INTO otps (sent_to, medium, otp_for, otp, used, created_at, expires_at)
|
||||
VALUES ($1, $2, $3, $4, FALSE, CURRENT_TIMESTAMP, $5);
|
||||
|
||||
-- name: GetOtp :one
|
||||
SELECT id, sent_to, medium, otp_for, otp, used, used_at, created_at, expires_at
|
||||
FROM otps
|
||||
WHERE sent_to = $1 AND otp_for = $2 AND medium = $3
|
||||
ORDER BY created_at DESC LIMIT 1;
|
||||
|
||||
-- name: MarkOtpAsUsed :exec
|
||||
UPDATE otps
|
||||
SET used = TRUE, used_at = CURRENT_TIMESTAMP
|
||||
WHERE id = $1;
|
||||
|
|
@ -1,16 +1,42 @@
|
|||
-- name: CreateUser :one
|
||||
INSERT INTO users (first_name, last_name, email, phone_number, password, role, verified)
|
||||
VALUES ($1, $2, $3, $4, $5, $6, $7)
|
||||
RETURNING *;
|
||||
|
||||
INSERT INTO users (first_name, last_name, email, phone_number, role, password, email_verified, phone_verified, created_at, updated_at)
|
||||
VALUES ($1, $2, $3, $4, $5, $6, $7, $8, CURRENT_TIMESTAMP, CURRENT_TIMESTAMP)
|
||||
RETURNING id, first_name, last_name, email, phone_number, role, email_verified, phone_verified, created_at, updated_at;
|
||||
|
||||
-- name: GetUserByID :one
|
||||
SELECT * FROM users WHERE id = $1;
|
||||
SELECT *
|
||||
FROM users
|
||||
WHERE id = $1;
|
||||
|
||||
-- name: GetAllUsers :many
|
||||
SELECT * FROM users;
|
||||
SELECT id, first_name, last_name, email, phone_number, role, email_verified, phone_verified, created_at, updated_at
|
||||
FROM users;
|
||||
|
||||
-- name: UpdateUser :exec
|
||||
UPDATE users SET first_name = $2, last_name = $3, email = $4, phone_number = $5, password = $6, role = $7, verified = $8, updated_at = CURRENT_TIMESTAMP WHERE id = $1;
|
||||
UPDATE users
|
||||
SET first_name = $1, last_name = $2, email = $3, phone_number = $4, role = $5, updated_at = CURRENT_TIMESTAMP
|
||||
WHERE id = $6;
|
||||
|
||||
-- name: DeleteUser :exec
|
||||
DELETE FROM users WHERE id = $1;
|
||||
DELETE FROM users
|
||||
WHERE id = $1;
|
||||
|
||||
-- name: CheckPhoneEmailExist :one
|
||||
SELECT
|
||||
EXISTS (SELECT 1 FROM users WHERE users.phone_number = $1 AND users.phone_number IS NOT NULL) AS phone_exists,
|
||||
EXISTS (SELECT 1 FROM users WHERE users.email = $2 AND users.email IS NOT NULL) AS email_exists;
|
||||
-- name: GetUserByEmail :one
|
||||
SELECT id, first_name, last_name, email, phone_number, role, email_verified, phone_verified, created_at, updated_at
|
||||
FROM users
|
||||
WHERE email = $1;
|
||||
|
||||
-- name: GetUserByPhone :one
|
||||
SELECT id, first_name, last_name, email, phone_number, role, email_verified, phone_verified, created_at, updated_at
|
||||
FROM users
|
||||
WHERE phone_number = $1;
|
||||
|
||||
-- name: UpdatePassword :exec
|
||||
UPDATE users
|
||||
SET password = $1, updated_at = CURRENT_TIMESTAMP
|
||||
WHERE (email = $2 OR phone_number = $3);
|
||||
447
docs/docs.go
447
docs/docs.go
|
|
@ -44,7 +44,7 @@ const docTemplate = `{
|
|||
"in": "body",
|
||||
"required": true,
|
||||
"schema": {
|
||||
"$ref": "#/definitions/httpserver.loginCustomerReq"
|
||||
"$ref": "#/definitions/handlers.loginCustomerReq"
|
||||
}
|
||||
}
|
||||
],
|
||||
|
|
@ -52,7 +52,7 @@ const docTemplate = `{
|
|||
"200": {
|
||||
"description": "OK",
|
||||
"schema": {
|
||||
"$ref": "#/definitions/httpserver.loginCustomerRes"
|
||||
"$ref": "#/definitions/handlers.loginCustomerRes"
|
||||
}
|
||||
},
|
||||
"400": {
|
||||
|
|
@ -96,7 +96,7 @@ const docTemplate = `{
|
|||
"in": "body",
|
||||
"required": true,
|
||||
"schema": {
|
||||
"$ref": "#/definitions/httpserver.logoutReq"
|
||||
"$ref": "#/definitions/handlers.logoutReq"
|
||||
}
|
||||
}
|
||||
],
|
||||
|
|
@ -148,7 +148,7 @@ const docTemplate = `{
|
|||
"in": "body",
|
||||
"required": true,
|
||||
"schema": {
|
||||
"$ref": "#/definitions/httpserver.refreshToken"
|
||||
"$ref": "#/definitions/handlers.refreshToken"
|
||||
}
|
||||
}
|
||||
],
|
||||
|
|
@ -156,7 +156,7 @@ const docTemplate = `{
|
|||
"200": {
|
||||
"description": "OK",
|
||||
"schema": {
|
||||
"$ref": "#/definitions/httpserver.loginCustomerRes"
|
||||
"$ref": "#/definitions/handlers.loginCustomerRes"
|
||||
}
|
||||
},
|
||||
"400": {
|
||||
|
|
@ -179,10 +179,439 @@ const docTemplate = `{
|
|||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
"/user/checkPhoneEmailExist": {
|
||||
"post": {
|
||||
"description": "Check if phone number or email exist",
|
||||
"consumes": [
|
||||
"application/json"
|
||||
],
|
||||
"produces": [
|
||||
"application/json"
|
||||
],
|
||||
"tags": [
|
||||
"user"
|
||||
],
|
||||
"summary": "Check if phone number or email exist",
|
||||
"parameters": [
|
||||
{
|
||||
"description": "Check phone number or email exist",
|
||||
"name": "checkPhoneEmailExist",
|
||||
"in": "body",
|
||||
"required": true,
|
||||
"schema": {
|
||||
"$ref": "#/definitions/handlers.CheckPhoneEmailExistReq"
|
||||
}
|
||||
}
|
||||
],
|
||||
"responses": {
|
||||
"200": {
|
||||
"description": "OK",
|
||||
"schema": {
|
||||
"$ref": "#/definitions/handlers.CheckPhoneEmailExistRes"
|
||||
}
|
||||
},
|
||||
"400": {
|
||||
"description": "Bad Request",
|
||||
"schema": {
|
||||
"$ref": "#/definitions/response.APIResponse"
|
||||
}
|
||||
},
|
||||
"500": {
|
||||
"description": "Internal Server Error",
|
||||
"schema": {
|
||||
"$ref": "#/definitions/response.APIResponse"
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
"/user/profile": {
|
||||
"get": {
|
||||
"security": [
|
||||
{
|
||||
"Bearer": []
|
||||
}
|
||||
],
|
||||
"description": "Get user profile",
|
||||
"consumes": [
|
||||
"application/json"
|
||||
],
|
||||
"produces": [
|
||||
"application/json"
|
||||
],
|
||||
"tags": [
|
||||
"user"
|
||||
],
|
||||
"summary": "Get user profile",
|
||||
"responses": {
|
||||
"200": {
|
||||
"description": "OK",
|
||||
"schema": {
|
||||
"$ref": "#/definitions/handlers.UserProfileRes"
|
||||
}
|
||||
},
|
||||
"400": {
|
||||
"description": "Bad Request",
|
||||
"schema": {
|
||||
"$ref": "#/definitions/response.APIResponse"
|
||||
}
|
||||
},
|
||||
"500": {
|
||||
"description": "Internal Server Error",
|
||||
"schema": {
|
||||
"$ref": "#/definitions/response.APIResponse"
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
"/user/register": {
|
||||
"post": {
|
||||
"description": "Register user",
|
||||
"consumes": [
|
||||
"application/json"
|
||||
],
|
||||
"produces": [
|
||||
"application/json"
|
||||
],
|
||||
"tags": [
|
||||
"user"
|
||||
],
|
||||
"summary": "Register user",
|
||||
"parameters": [
|
||||
{
|
||||
"description": "Register user",
|
||||
"name": "registerUser",
|
||||
"in": "body",
|
||||
"required": true,
|
||||
"schema": {
|
||||
"$ref": "#/definitions/handlers.RegisterUserReq"
|
||||
}
|
||||
}
|
||||
],
|
||||
"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"
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
"/user/resetPassword": {
|
||||
"post": {
|
||||
"description": "Reset password",
|
||||
"consumes": [
|
||||
"application/json"
|
||||
],
|
||||
"produces": [
|
||||
"application/json"
|
||||
],
|
||||
"tags": [
|
||||
"user"
|
||||
],
|
||||
"summary": "Reset password",
|
||||
"parameters": [
|
||||
{
|
||||
"description": "Reset password",
|
||||
"name": "resetPassword",
|
||||
"in": "body",
|
||||
"required": true,
|
||||
"schema": {
|
||||
"$ref": "#/definitions/handlers.ResetPasswordReq"
|
||||
}
|
||||
}
|
||||
],
|
||||
"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"
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
"/user/sendRegisterCode": {
|
||||
"post": {
|
||||
"description": "Send register code",
|
||||
"consumes": [
|
||||
"application/json"
|
||||
],
|
||||
"produces": [
|
||||
"application/json"
|
||||
],
|
||||
"tags": [
|
||||
"user"
|
||||
],
|
||||
"summary": "Send register code",
|
||||
"parameters": [
|
||||
{
|
||||
"description": "Send register code",
|
||||
"name": "registerCode",
|
||||
"in": "body",
|
||||
"required": true,
|
||||
"schema": {
|
||||
"$ref": "#/definitions/handlers.RegisterCodeReq"
|
||||
}
|
||||
}
|
||||
],
|
||||
"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"
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
"/user/sendResetCode": {
|
||||
"post": {
|
||||
"description": "Send reset code",
|
||||
"consumes": [
|
||||
"application/json"
|
||||
],
|
||||
"produces": [
|
||||
"application/json"
|
||||
],
|
||||
"tags": [
|
||||
"user"
|
||||
],
|
||||
"summary": "Send reset code",
|
||||
"parameters": [
|
||||
{
|
||||
"description": "Send reset code",
|
||||
"name": "resetCode",
|
||||
"in": "body",
|
||||
"required": true,
|
||||
"schema": {
|
||||
"$ref": "#/definitions/handlers.ResetCodeReq"
|
||||
}
|
||||
}
|
||||
],
|
||||
"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"
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
"definitions": {
|
||||
"httpserver.loginCustomerReq": {
|
||||
"domain.Role": {
|
||||
"type": "string",
|
||||
"enum": [
|
||||
"admin",
|
||||
"customer",
|
||||
"super_admin",
|
||||
"branch_manager",
|
||||
"cashier"
|
||||
],
|
||||
"x-enum-varnames": [
|
||||
"RoleAdmin",
|
||||
"RoleCustomer",
|
||||
"RoleSuperAdmin",
|
||||
"RoleBranchManager",
|
||||
"RoleCashier"
|
||||
]
|
||||
},
|
||||
"handlers.CheckPhoneEmailExistReq": {
|
||||
"type": "object",
|
||||
"properties": {
|
||||
"email": {
|
||||
"type": "string",
|
||||
"example": "john.doe@example.com"
|
||||
},
|
||||
"phone_number": {
|
||||
"type": "string",
|
||||
"example": "1234567890"
|
||||
}
|
||||
}
|
||||
},
|
||||
"handlers.CheckPhoneEmailExistRes": {
|
||||
"type": "object",
|
||||
"properties": {
|
||||
"email_exist": {
|
||||
"type": "boolean"
|
||||
},
|
||||
"phone_number_exist": {
|
||||
"type": "boolean"
|
||||
}
|
||||
}
|
||||
},
|
||||
"handlers.RegisterCodeReq": {
|
||||
"type": "object",
|
||||
"properties": {
|
||||
"email": {
|
||||
"type": "string",
|
||||
"example": "john.doe@example.com"
|
||||
},
|
||||
"phone_number": {
|
||||
"type": "string",
|
||||
"example": "1234567890"
|
||||
}
|
||||
}
|
||||
},
|
||||
"handlers.RegisterUserReq": {
|
||||
"type": "object",
|
||||
"properties": {
|
||||
"email": {
|
||||
"type": "string",
|
||||
"example": "john.doe@example.com"
|
||||
},
|
||||
"first_name": {
|
||||
"type": "string",
|
||||
"example": "John"
|
||||
},
|
||||
"last_name": {
|
||||
"type": "string",
|
||||
"example": "Doe"
|
||||
},
|
||||
"otp": {
|
||||
"description": "Role string",
|
||||
"type": "string",
|
||||
"example": "123456"
|
||||
},
|
||||
"password": {
|
||||
"type": "string",
|
||||
"example": "password123"
|
||||
},
|
||||
"phone_number": {
|
||||
"type": "string",
|
||||
"example": "1234567890"
|
||||
},
|
||||
"referal_code": {
|
||||
"type": "string",
|
||||
"example": "ABC123"
|
||||
}
|
||||
}
|
||||
},
|
||||
"handlers.ResetCodeReq": {
|
||||
"type": "object",
|
||||
"properties": {
|
||||
"email": {
|
||||
"type": "string",
|
||||
"example": "john.doe@example.com"
|
||||
},
|
||||
"phone_number": {
|
||||
"type": "string",
|
||||
"example": "1234567890"
|
||||
}
|
||||
}
|
||||
},
|
||||
"handlers.ResetPasswordReq": {
|
||||
"type": "object",
|
||||
"properties": {
|
||||
"email": {
|
||||
"type": "string"
|
||||
},
|
||||
"otp": {
|
||||
"type": "string"
|
||||
},
|
||||
"password": {
|
||||
"type": "string"
|
||||
},
|
||||
"phoneNumber": {
|
||||
"type": "string"
|
||||
}
|
||||
}
|
||||
},
|
||||
"handlers.UserProfileRes": {
|
||||
"type": "object",
|
||||
"properties": {
|
||||
"created_at": {
|
||||
"type": "string"
|
||||
},
|
||||
"email": {
|
||||
"type": "string"
|
||||
},
|
||||
"email_verified": {
|
||||
"type": "boolean"
|
||||
},
|
||||
"first_name": {
|
||||
"type": "string"
|
||||
},
|
||||
"id": {
|
||||
"type": "integer"
|
||||
},
|
||||
"last_name": {
|
||||
"type": "string"
|
||||
},
|
||||
"phone_number": {
|
||||
"type": "string"
|
||||
},
|
||||
"phone_verified": {
|
||||
"type": "boolean"
|
||||
},
|
||||
"role": {
|
||||
"$ref": "#/definitions/domain.Role"
|
||||
},
|
||||
"suspended": {
|
||||
"type": "boolean"
|
||||
},
|
||||
"suspended_at": {
|
||||
"type": "string"
|
||||
},
|
||||
"updated_at": {
|
||||
"type": "string"
|
||||
}
|
||||
}
|
||||
},
|
||||
"handlers.loginCustomerReq": {
|
||||
"type": "object",
|
||||
"properties": {
|
||||
"email": {
|
||||
|
|
@ -199,7 +628,7 @@ const docTemplate = `{
|
|||
}
|
||||
}
|
||||
},
|
||||
"httpserver.loginCustomerRes": {
|
||||
"handlers.loginCustomerRes": {
|
||||
"type": "object",
|
||||
"properties": {
|
||||
"access_token": {
|
||||
|
|
@ -210,7 +639,7 @@ const docTemplate = `{
|
|||
}
|
||||
}
|
||||
},
|
||||
"httpserver.logoutReq": {
|
||||
"handlers.logoutReq": {
|
||||
"type": "object",
|
||||
"properties": {
|
||||
"refresh_token": {
|
||||
|
|
@ -218,7 +647,7 @@ const docTemplate = `{
|
|||
}
|
||||
}
|
||||
},
|
||||
"httpserver.refreshToken": {
|
||||
"handlers.refreshToken": {
|
||||
"type": "object",
|
||||
"properties": {
|
||||
"access_token": {
|
||||
|
|
|
|||
|
|
@ -36,7 +36,7 @@
|
|||
"in": "body",
|
||||
"required": true,
|
||||
"schema": {
|
||||
"$ref": "#/definitions/httpserver.loginCustomerReq"
|
||||
"$ref": "#/definitions/handlers.loginCustomerReq"
|
||||
}
|
||||
}
|
||||
],
|
||||
|
|
@ -44,7 +44,7 @@
|
|||
"200": {
|
||||
"description": "OK",
|
||||
"schema": {
|
||||
"$ref": "#/definitions/httpserver.loginCustomerRes"
|
||||
"$ref": "#/definitions/handlers.loginCustomerRes"
|
||||
}
|
||||
},
|
||||
"400": {
|
||||
|
|
@ -88,7 +88,7 @@
|
|||
"in": "body",
|
||||
"required": true,
|
||||
"schema": {
|
||||
"$ref": "#/definitions/httpserver.logoutReq"
|
||||
"$ref": "#/definitions/handlers.logoutReq"
|
||||
}
|
||||
}
|
||||
],
|
||||
|
|
@ -140,7 +140,7 @@
|
|||
"in": "body",
|
||||
"required": true,
|
||||
"schema": {
|
||||
"$ref": "#/definitions/httpserver.refreshToken"
|
||||
"$ref": "#/definitions/handlers.refreshToken"
|
||||
}
|
||||
}
|
||||
],
|
||||
|
|
@ -148,7 +148,7 @@
|
|||
"200": {
|
||||
"description": "OK",
|
||||
"schema": {
|
||||
"$ref": "#/definitions/httpserver.loginCustomerRes"
|
||||
"$ref": "#/definitions/handlers.loginCustomerRes"
|
||||
}
|
||||
},
|
||||
"400": {
|
||||
|
|
@ -171,10 +171,439 @@
|
|||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
"/user/checkPhoneEmailExist": {
|
||||
"post": {
|
||||
"description": "Check if phone number or email exist",
|
||||
"consumes": [
|
||||
"application/json"
|
||||
],
|
||||
"produces": [
|
||||
"application/json"
|
||||
],
|
||||
"tags": [
|
||||
"user"
|
||||
],
|
||||
"summary": "Check if phone number or email exist",
|
||||
"parameters": [
|
||||
{
|
||||
"description": "Check phone number or email exist",
|
||||
"name": "checkPhoneEmailExist",
|
||||
"in": "body",
|
||||
"required": true,
|
||||
"schema": {
|
||||
"$ref": "#/definitions/handlers.CheckPhoneEmailExistReq"
|
||||
}
|
||||
}
|
||||
],
|
||||
"responses": {
|
||||
"200": {
|
||||
"description": "OK",
|
||||
"schema": {
|
||||
"$ref": "#/definitions/handlers.CheckPhoneEmailExistRes"
|
||||
}
|
||||
},
|
||||
"400": {
|
||||
"description": "Bad Request",
|
||||
"schema": {
|
||||
"$ref": "#/definitions/response.APIResponse"
|
||||
}
|
||||
},
|
||||
"500": {
|
||||
"description": "Internal Server Error",
|
||||
"schema": {
|
||||
"$ref": "#/definitions/response.APIResponse"
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
"/user/profile": {
|
||||
"get": {
|
||||
"security": [
|
||||
{
|
||||
"Bearer": []
|
||||
}
|
||||
],
|
||||
"description": "Get user profile",
|
||||
"consumes": [
|
||||
"application/json"
|
||||
],
|
||||
"produces": [
|
||||
"application/json"
|
||||
],
|
||||
"tags": [
|
||||
"user"
|
||||
],
|
||||
"summary": "Get user profile",
|
||||
"responses": {
|
||||
"200": {
|
||||
"description": "OK",
|
||||
"schema": {
|
||||
"$ref": "#/definitions/handlers.UserProfileRes"
|
||||
}
|
||||
},
|
||||
"400": {
|
||||
"description": "Bad Request",
|
||||
"schema": {
|
||||
"$ref": "#/definitions/response.APIResponse"
|
||||
}
|
||||
},
|
||||
"500": {
|
||||
"description": "Internal Server Error",
|
||||
"schema": {
|
||||
"$ref": "#/definitions/response.APIResponse"
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
"/user/register": {
|
||||
"post": {
|
||||
"description": "Register user",
|
||||
"consumes": [
|
||||
"application/json"
|
||||
],
|
||||
"produces": [
|
||||
"application/json"
|
||||
],
|
||||
"tags": [
|
||||
"user"
|
||||
],
|
||||
"summary": "Register user",
|
||||
"parameters": [
|
||||
{
|
||||
"description": "Register user",
|
||||
"name": "registerUser",
|
||||
"in": "body",
|
||||
"required": true,
|
||||
"schema": {
|
||||
"$ref": "#/definitions/handlers.RegisterUserReq"
|
||||
}
|
||||
}
|
||||
],
|
||||
"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"
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
"/user/resetPassword": {
|
||||
"post": {
|
||||
"description": "Reset password",
|
||||
"consumes": [
|
||||
"application/json"
|
||||
],
|
||||
"produces": [
|
||||
"application/json"
|
||||
],
|
||||
"tags": [
|
||||
"user"
|
||||
],
|
||||
"summary": "Reset password",
|
||||
"parameters": [
|
||||
{
|
||||
"description": "Reset password",
|
||||
"name": "resetPassword",
|
||||
"in": "body",
|
||||
"required": true,
|
||||
"schema": {
|
||||
"$ref": "#/definitions/handlers.ResetPasswordReq"
|
||||
}
|
||||
}
|
||||
],
|
||||
"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"
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
"/user/sendRegisterCode": {
|
||||
"post": {
|
||||
"description": "Send register code",
|
||||
"consumes": [
|
||||
"application/json"
|
||||
],
|
||||
"produces": [
|
||||
"application/json"
|
||||
],
|
||||
"tags": [
|
||||
"user"
|
||||
],
|
||||
"summary": "Send register code",
|
||||
"parameters": [
|
||||
{
|
||||
"description": "Send register code",
|
||||
"name": "registerCode",
|
||||
"in": "body",
|
||||
"required": true,
|
||||
"schema": {
|
||||
"$ref": "#/definitions/handlers.RegisterCodeReq"
|
||||
}
|
||||
}
|
||||
],
|
||||
"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"
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
"/user/sendResetCode": {
|
||||
"post": {
|
||||
"description": "Send reset code",
|
||||
"consumes": [
|
||||
"application/json"
|
||||
],
|
||||
"produces": [
|
||||
"application/json"
|
||||
],
|
||||
"tags": [
|
||||
"user"
|
||||
],
|
||||
"summary": "Send reset code",
|
||||
"parameters": [
|
||||
{
|
||||
"description": "Send reset code",
|
||||
"name": "resetCode",
|
||||
"in": "body",
|
||||
"required": true,
|
||||
"schema": {
|
||||
"$ref": "#/definitions/handlers.ResetCodeReq"
|
||||
}
|
||||
}
|
||||
],
|
||||
"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"
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
"definitions": {
|
||||
"httpserver.loginCustomerReq": {
|
||||
"domain.Role": {
|
||||
"type": "string",
|
||||
"enum": [
|
||||
"admin",
|
||||
"customer",
|
||||
"super_admin",
|
||||
"branch_manager",
|
||||
"cashier"
|
||||
],
|
||||
"x-enum-varnames": [
|
||||
"RoleAdmin",
|
||||
"RoleCustomer",
|
||||
"RoleSuperAdmin",
|
||||
"RoleBranchManager",
|
||||
"RoleCashier"
|
||||
]
|
||||
},
|
||||
"handlers.CheckPhoneEmailExistReq": {
|
||||
"type": "object",
|
||||
"properties": {
|
||||
"email": {
|
||||
"type": "string",
|
||||
"example": "john.doe@example.com"
|
||||
},
|
||||
"phone_number": {
|
||||
"type": "string",
|
||||
"example": "1234567890"
|
||||
}
|
||||
}
|
||||
},
|
||||
"handlers.CheckPhoneEmailExistRes": {
|
||||
"type": "object",
|
||||
"properties": {
|
||||
"email_exist": {
|
||||
"type": "boolean"
|
||||
},
|
||||
"phone_number_exist": {
|
||||
"type": "boolean"
|
||||
}
|
||||
}
|
||||
},
|
||||
"handlers.RegisterCodeReq": {
|
||||
"type": "object",
|
||||
"properties": {
|
||||
"email": {
|
||||
"type": "string",
|
||||
"example": "john.doe@example.com"
|
||||
},
|
||||
"phone_number": {
|
||||
"type": "string",
|
||||
"example": "1234567890"
|
||||
}
|
||||
}
|
||||
},
|
||||
"handlers.RegisterUserReq": {
|
||||
"type": "object",
|
||||
"properties": {
|
||||
"email": {
|
||||
"type": "string",
|
||||
"example": "john.doe@example.com"
|
||||
},
|
||||
"first_name": {
|
||||
"type": "string",
|
||||
"example": "John"
|
||||
},
|
||||
"last_name": {
|
||||
"type": "string",
|
||||
"example": "Doe"
|
||||
},
|
||||
"otp": {
|
||||
"description": "Role string",
|
||||
"type": "string",
|
||||
"example": "123456"
|
||||
},
|
||||
"password": {
|
||||
"type": "string",
|
||||
"example": "password123"
|
||||
},
|
||||
"phone_number": {
|
||||
"type": "string",
|
||||
"example": "1234567890"
|
||||
},
|
||||
"referal_code": {
|
||||
"type": "string",
|
||||
"example": "ABC123"
|
||||
}
|
||||
}
|
||||
},
|
||||
"handlers.ResetCodeReq": {
|
||||
"type": "object",
|
||||
"properties": {
|
||||
"email": {
|
||||
"type": "string",
|
||||
"example": "john.doe@example.com"
|
||||
},
|
||||
"phone_number": {
|
||||
"type": "string",
|
||||
"example": "1234567890"
|
||||
}
|
||||
}
|
||||
},
|
||||
"handlers.ResetPasswordReq": {
|
||||
"type": "object",
|
||||
"properties": {
|
||||
"email": {
|
||||
"type": "string"
|
||||
},
|
||||
"otp": {
|
||||
"type": "string"
|
||||
},
|
||||
"password": {
|
||||
"type": "string"
|
||||
},
|
||||
"phoneNumber": {
|
||||
"type": "string"
|
||||
}
|
||||
}
|
||||
},
|
||||
"handlers.UserProfileRes": {
|
||||
"type": "object",
|
||||
"properties": {
|
||||
"created_at": {
|
||||
"type": "string"
|
||||
},
|
||||
"email": {
|
||||
"type": "string"
|
||||
},
|
||||
"email_verified": {
|
||||
"type": "boolean"
|
||||
},
|
||||
"first_name": {
|
||||
"type": "string"
|
||||
},
|
||||
"id": {
|
||||
"type": "integer"
|
||||
},
|
||||
"last_name": {
|
||||
"type": "string"
|
||||
},
|
||||
"phone_number": {
|
||||
"type": "string"
|
||||
},
|
||||
"phone_verified": {
|
||||
"type": "boolean"
|
||||
},
|
||||
"role": {
|
||||
"$ref": "#/definitions/domain.Role"
|
||||
},
|
||||
"suspended": {
|
||||
"type": "boolean"
|
||||
},
|
||||
"suspended_at": {
|
||||
"type": "string"
|
||||
},
|
||||
"updated_at": {
|
||||
"type": "string"
|
||||
}
|
||||
}
|
||||
},
|
||||
"handlers.loginCustomerReq": {
|
||||
"type": "object",
|
||||
"properties": {
|
||||
"email": {
|
||||
|
|
@ -191,7 +620,7 @@
|
|||
}
|
||||
}
|
||||
},
|
||||
"httpserver.loginCustomerRes": {
|
||||
"handlers.loginCustomerRes": {
|
||||
"type": "object",
|
||||
"properties": {
|
||||
"access_token": {
|
||||
|
|
@ -202,7 +631,7 @@
|
|||
}
|
||||
}
|
||||
},
|
||||
"httpserver.logoutReq": {
|
||||
"handlers.logoutReq": {
|
||||
"type": "object",
|
||||
"properties": {
|
||||
"refresh_token": {
|
||||
|
|
@ -210,7 +639,7 @@
|
|||
}
|
||||
}
|
||||
},
|
||||
"httpserver.refreshToken": {
|
||||
"handlers.refreshToken": {
|
||||
"type": "object",
|
||||
"properties": {
|
||||
"access_token": {
|
||||
|
|
|
|||
|
|
@ -1,5 +1,116 @@
|
|||
definitions:
|
||||
httpserver.loginCustomerReq:
|
||||
domain.Role:
|
||||
enum:
|
||||
- admin
|
||||
- customer
|
||||
- super_admin
|
||||
- branch_manager
|
||||
- cashier
|
||||
type: string
|
||||
x-enum-varnames:
|
||||
- RoleAdmin
|
||||
- RoleCustomer
|
||||
- RoleSuperAdmin
|
||||
- RoleBranchManager
|
||||
- RoleCashier
|
||||
handlers.CheckPhoneEmailExistReq:
|
||||
properties:
|
||||
email:
|
||||
example: john.doe@example.com
|
||||
type: string
|
||||
phone_number:
|
||||
example: "1234567890"
|
||||
type: string
|
||||
type: object
|
||||
handlers.CheckPhoneEmailExistRes:
|
||||
properties:
|
||||
email_exist:
|
||||
type: boolean
|
||||
phone_number_exist:
|
||||
type: boolean
|
||||
type: object
|
||||
handlers.RegisterCodeReq:
|
||||
properties:
|
||||
email:
|
||||
example: john.doe@example.com
|
||||
type: string
|
||||
phone_number:
|
||||
example: "1234567890"
|
||||
type: string
|
||||
type: object
|
||||
handlers.RegisterUserReq:
|
||||
properties:
|
||||
email:
|
||||
example: john.doe@example.com
|
||||
type: string
|
||||
first_name:
|
||||
example: John
|
||||
type: string
|
||||
last_name:
|
||||
example: Doe
|
||||
type: string
|
||||
otp:
|
||||
description: Role string
|
||||
example: "123456"
|
||||
type: string
|
||||
password:
|
||||
example: password123
|
||||
type: string
|
||||
phone_number:
|
||||
example: "1234567890"
|
||||
type: string
|
||||
referal_code:
|
||||
example: ABC123
|
||||
type: string
|
||||
type: object
|
||||
handlers.ResetCodeReq:
|
||||
properties:
|
||||
email:
|
||||
example: john.doe@example.com
|
||||
type: string
|
||||
phone_number:
|
||||
example: "1234567890"
|
||||
type: string
|
||||
type: object
|
||||
handlers.ResetPasswordReq:
|
||||
properties:
|
||||
email:
|
||||
type: string
|
||||
otp:
|
||||
type: string
|
||||
password:
|
||||
type: string
|
||||
phoneNumber:
|
||||
type: string
|
||||
type: object
|
||||
handlers.UserProfileRes:
|
||||
properties:
|
||||
created_at:
|
||||
type: string
|
||||
email:
|
||||
type: string
|
||||
email_verified:
|
||||
type: boolean
|
||||
first_name:
|
||||
type: string
|
||||
id:
|
||||
type: integer
|
||||
last_name:
|
||||
type: string
|
||||
phone_number:
|
||||
type: string
|
||||
phone_verified:
|
||||
type: boolean
|
||||
role:
|
||||
$ref: '#/definitions/domain.Role'
|
||||
suspended:
|
||||
type: boolean
|
||||
suspended_at:
|
||||
type: string
|
||||
updated_at:
|
||||
type: string
|
||||
type: object
|
||||
handlers.loginCustomerReq:
|
||||
properties:
|
||||
email:
|
||||
example: john.doe@example.com
|
||||
|
|
@ -11,19 +122,19 @@ definitions:
|
|||
example: "1234567890"
|
||||
type: string
|
||||
type: object
|
||||
httpserver.loginCustomerRes:
|
||||
handlers.loginCustomerRes:
|
||||
properties:
|
||||
access_token:
|
||||
type: string
|
||||
refresh_token:
|
||||
type: string
|
||||
type: object
|
||||
httpserver.logoutReq:
|
||||
handlers.logoutReq:
|
||||
properties:
|
||||
refresh_token:
|
||||
type: string
|
||||
type: object
|
||||
httpserver.refreshToken:
|
||||
handlers.refreshToken:
|
||||
properties:
|
||||
access_token:
|
||||
type: string
|
||||
|
|
@ -73,14 +184,14 @@ paths:
|
|||
name: login
|
||||
required: true
|
||||
schema:
|
||||
$ref: '#/definitions/httpserver.loginCustomerReq'
|
||||
$ref: '#/definitions/handlers.loginCustomerReq'
|
||||
produces:
|
||||
- application/json
|
||||
responses:
|
||||
"200":
|
||||
description: OK
|
||||
schema:
|
||||
$ref: '#/definitions/httpserver.loginCustomerRes'
|
||||
$ref: '#/definitions/handlers.loginCustomerRes'
|
||||
"400":
|
||||
description: Bad Request
|
||||
schema:
|
||||
|
|
@ -107,7 +218,7 @@ paths:
|
|||
name: logout
|
||||
required: true
|
||||
schema:
|
||||
$ref: '#/definitions/httpserver.logoutReq'
|
||||
$ref: '#/definitions/handlers.logoutReq'
|
||||
produces:
|
||||
- application/json
|
||||
responses:
|
||||
|
|
@ -141,14 +252,14 @@ paths:
|
|||
name: refresh
|
||||
required: true
|
||||
schema:
|
||||
$ref: '#/definitions/httpserver.refreshToken'
|
||||
$ref: '#/definitions/handlers.refreshToken'
|
||||
produces:
|
||||
- application/json
|
||||
responses:
|
||||
"200":
|
||||
description: OK
|
||||
schema:
|
||||
$ref: '#/definitions/httpserver.loginCustomerRes'
|
||||
$ref: '#/definitions/handlers.loginCustomerRes'
|
||||
"400":
|
||||
description: Bad Request
|
||||
schema:
|
||||
|
|
@ -164,6 +275,181 @@ paths:
|
|||
summary: Refresh token
|
||||
tags:
|
||||
- auth
|
||||
/user/checkPhoneEmailExist:
|
||||
post:
|
||||
consumes:
|
||||
- application/json
|
||||
description: Check if phone number or email exist
|
||||
parameters:
|
||||
- description: Check phone number or email exist
|
||||
in: body
|
||||
name: checkPhoneEmailExist
|
||||
required: true
|
||||
schema:
|
||||
$ref: '#/definitions/handlers.CheckPhoneEmailExistReq'
|
||||
produces:
|
||||
- application/json
|
||||
responses:
|
||||
"200":
|
||||
description: OK
|
||||
schema:
|
||||
$ref: '#/definitions/handlers.CheckPhoneEmailExistRes'
|
||||
"400":
|
||||
description: Bad Request
|
||||
schema:
|
||||
$ref: '#/definitions/response.APIResponse'
|
||||
"500":
|
||||
description: Internal Server Error
|
||||
schema:
|
||||
$ref: '#/definitions/response.APIResponse'
|
||||
summary: Check if phone number or email exist
|
||||
tags:
|
||||
- user
|
||||
/user/profile:
|
||||
get:
|
||||
consumes:
|
||||
- application/json
|
||||
description: Get user profile
|
||||
produces:
|
||||
- application/json
|
||||
responses:
|
||||
"200":
|
||||
description: OK
|
||||
schema:
|
||||
$ref: '#/definitions/handlers.UserProfileRes'
|
||||
"400":
|
||||
description: Bad Request
|
||||
schema:
|
||||
$ref: '#/definitions/response.APIResponse'
|
||||
"500":
|
||||
description: Internal Server Error
|
||||
schema:
|
||||
$ref: '#/definitions/response.APIResponse'
|
||||
security:
|
||||
- Bearer: []
|
||||
summary: Get user profile
|
||||
tags:
|
||||
- user
|
||||
/user/register:
|
||||
post:
|
||||
consumes:
|
||||
- application/json
|
||||
description: Register user
|
||||
parameters:
|
||||
- description: Register user
|
||||
in: body
|
||||
name: registerUser
|
||||
required: true
|
||||
schema:
|
||||
$ref: '#/definitions/handlers.RegisterUserReq'
|
||||
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: Register user
|
||||
tags:
|
||||
- user
|
||||
/user/resetPassword:
|
||||
post:
|
||||
consumes:
|
||||
- application/json
|
||||
description: Reset password
|
||||
parameters:
|
||||
- description: Reset password
|
||||
in: body
|
||||
name: resetPassword
|
||||
required: true
|
||||
schema:
|
||||
$ref: '#/definitions/handlers.ResetPasswordReq'
|
||||
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: Reset password
|
||||
tags:
|
||||
- user
|
||||
/user/sendRegisterCode:
|
||||
post:
|
||||
consumes:
|
||||
- application/json
|
||||
description: Send register code
|
||||
parameters:
|
||||
- description: Send register code
|
||||
in: body
|
||||
name: registerCode
|
||||
required: true
|
||||
schema:
|
||||
$ref: '#/definitions/handlers.RegisterCodeReq'
|
||||
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: Send register code
|
||||
tags:
|
||||
- user
|
||||
/user/sendResetCode:
|
||||
post:
|
||||
consumes:
|
||||
- application/json
|
||||
description: Send reset code
|
||||
parameters:
|
||||
- description: Send reset code
|
||||
in: body
|
||||
name: resetCode
|
||||
required: true
|
||||
schema:
|
||||
$ref: '#/definitions/handlers.ResetCodeReq'
|
||||
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: Send reset code
|
||||
tags:
|
||||
- user
|
||||
securityDefinitions:
|
||||
Bearer:
|
||||
in: header
|
||||
|
|
|
|||
|
|
@ -55,13 +55,13 @@ func (q *Queries) GetRefreshToken(ctx context.Context, token string) (RefreshTok
|
|||
}
|
||||
|
||||
const GetUserByEmailPhone = `-- name: GetUserByEmailPhone :one
|
||||
SELECT id, first_name, last_name, email, phone_number, password, role, verified, created_at, updated_at FROM users
|
||||
SELECT id, first_name, last_name, email, phone_number, role, password, email_verified, phone_verified, created_at, updated_at, suspended_at, suspended FROM users
|
||||
WHERE email = $1 OR phone_number = $2
|
||||
`
|
||||
|
||||
type GetUserByEmailPhoneParams struct {
|
||||
Email string
|
||||
PhoneNumber string
|
||||
Email pgtype.Text
|
||||
PhoneNumber pgtype.Text
|
||||
}
|
||||
|
||||
func (q *Queries) GetUserByEmailPhone(ctx context.Context, arg GetUserByEmailPhoneParams) (User, error) {
|
||||
|
|
@ -73,11 +73,14 @@ func (q *Queries) GetUserByEmailPhone(ctx context.Context, arg GetUserByEmailPho
|
|||
&i.LastName,
|
||||
&i.Email,
|
||||
&i.PhoneNumber,
|
||||
&i.Password,
|
||||
&i.Role,
|
||||
&i.Verified,
|
||||
&i.Password,
|
||||
&i.EmailVerified,
|
||||
&i.PhoneVerified,
|
||||
&i.CreatedAt,
|
||||
&i.UpdatedAt,
|
||||
&i.SuspendedAt,
|
||||
&i.Suspended,
|
||||
)
|
||||
return i, err
|
||||
}
|
||||
|
|
|
|||
|
|
@ -8,6 +8,18 @@ import (
|
|||
"github.com/jackc/pgx/v5/pgtype"
|
||||
)
|
||||
|
||||
type Otp struct {
|
||||
ID int64
|
||||
SentTo string
|
||||
Medium string
|
||||
OtpFor string
|
||||
Otp string
|
||||
Used bool
|
||||
UsedAt pgtype.Timestamptz
|
||||
CreatedAt pgtype.Timestamptz
|
||||
ExpiresAt pgtype.Timestamptz
|
||||
}
|
||||
|
||||
type RefreshToken struct {
|
||||
ID int64
|
||||
UserID int64
|
||||
|
|
@ -21,11 +33,14 @@ type User struct {
|
|||
ID int64
|
||||
FirstName string
|
||||
LastName string
|
||||
Email string
|
||||
PhoneNumber string
|
||||
Password []byte
|
||||
Email pgtype.Text
|
||||
PhoneNumber pgtype.Text
|
||||
Role string
|
||||
Verified pgtype.Bool
|
||||
Password []byte
|
||||
EmailVerified bool
|
||||
PhoneVerified bool
|
||||
CreatedAt pgtype.Timestamptz
|
||||
UpdatedAt pgtype.Timestamptz
|
||||
SuspendedAt pgtype.Timestamptz
|
||||
Suspended bool
|
||||
}
|
||||
|
|
|
|||
77
gen/db/otp.sql.go
Normal file
77
gen/db/otp.sql.go
Normal file
|
|
@ -0,0 +1,77 @@
|
|||
// Code generated by sqlc. DO NOT EDIT.
|
||||
// versions:
|
||||
// sqlc v1.26.0
|
||||
// source: otp.sql
|
||||
|
||||
package dbgen
|
||||
|
||||
import (
|
||||
"context"
|
||||
|
||||
"github.com/jackc/pgx/v5/pgtype"
|
||||
)
|
||||
|
||||
const CreateOtp = `-- name: CreateOtp :exec
|
||||
INSERT INTO otps (sent_to, medium, otp_for, otp, used, created_at, expires_at)
|
||||
VALUES ($1, $2, $3, $4, FALSE, CURRENT_TIMESTAMP, $5)
|
||||
`
|
||||
|
||||
type CreateOtpParams struct {
|
||||
SentTo string
|
||||
Medium string
|
||||
OtpFor string
|
||||
Otp string
|
||||
ExpiresAt pgtype.Timestamptz
|
||||
}
|
||||
|
||||
func (q *Queries) CreateOtp(ctx context.Context, arg CreateOtpParams) error {
|
||||
_, err := q.db.Exec(ctx, CreateOtp,
|
||||
arg.SentTo,
|
||||
arg.Medium,
|
||||
arg.OtpFor,
|
||||
arg.Otp,
|
||||
arg.ExpiresAt,
|
||||
)
|
||||
return err
|
||||
}
|
||||
|
||||
const GetOtp = `-- name: GetOtp :one
|
||||
SELECT id, sent_to, medium, otp_for, otp, used, used_at, created_at, expires_at
|
||||
FROM otps
|
||||
WHERE sent_to = $1 AND otp_for = $2 AND medium = $3
|
||||
ORDER BY created_at DESC LIMIT 1
|
||||
`
|
||||
|
||||
type GetOtpParams struct {
|
||||
SentTo string
|
||||
OtpFor string
|
||||
Medium string
|
||||
}
|
||||
|
||||
func (q *Queries) GetOtp(ctx context.Context, arg GetOtpParams) (Otp, error) {
|
||||
row := q.db.QueryRow(ctx, GetOtp, arg.SentTo, arg.OtpFor, arg.Medium)
|
||||
var i Otp
|
||||
err := row.Scan(
|
||||
&i.ID,
|
||||
&i.SentTo,
|
||||
&i.Medium,
|
||||
&i.OtpFor,
|
||||
&i.Otp,
|
||||
&i.Used,
|
||||
&i.UsedAt,
|
||||
&i.CreatedAt,
|
||||
&i.ExpiresAt,
|
||||
)
|
||||
return i, err
|
||||
}
|
||||
|
||||
const MarkOtpAsUsed = `-- name: MarkOtpAsUsed :exec
|
||||
UPDATE otps
|
||||
SET used = TRUE, used_at = CURRENT_TIMESTAMP
|
||||
WHERE id = $1
|
||||
`
|
||||
|
||||
func (q *Queries) MarkOtpAsUsed(ctx context.Context, id int64) error {
|
||||
_, err := q.db.Exec(ctx, MarkOtpAsUsed, id)
|
||||
return err
|
||||
}
|
||||
|
|
@ -11,42 +11,81 @@ import (
|
|||
"github.com/jackc/pgx/v5/pgtype"
|
||||
)
|
||||
|
||||
const CheckPhoneEmailExist = `-- name: CheckPhoneEmailExist :one
|
||||
SELECT
|
||||
EXISTS (SELECT 1 FROM users WHERE users.phone_number = $1 AND users.phone_number IS NOT NULL) AS phone_exists,
|
||||
EXISTS (SELECT 1 FROM users WHERE users.email = $2 AND users.email IS NOT NULL) AS email_exists
|
||||
`
|
||||
|
||||
type CheckPhoneEmailExistParams struct {
|
||||
PhoneNumber pgtype.Text
|
||||
Email pgtype.Text
|
||||
}
|
||||
|
||||
type CheckPhoneEmailExistRow struct {
|
||||
PhoneExists bool
|
||||
EmailExists bool
|
||||
}
|
||||
|
||||
func (q *Queries) CheckPhoneEmailExist(ctx context.Context, arg CheckPhoneEmailExistParams) (CheckPhoneEmailExistRow, error) {
|
||||
row := q.db.QueryRow(ctx, CheckPhoneEmailExist, arg.PhoneNumber, arg.Email)
|
||||
var i CheckPhoneEmailExistRow
|
||||
err := row.Scan(&i.PhoneExists, &i.EmailExists)
|
||||
return i, err
|
||||
}
|
||||
|
||||
const CreateUser = `-- name: CreateUser :one
|
||||
INSERT INTO users (first_name, last_name, email, phone_number, password, role, verified)
|
||||
VALUES ($1, $2, $3, $4, $5, $6, $7)
|
||||
RETURNING id, first_name, last_name, email, phone_number, password, role, verified, created_at, updated_at
|
||||
|
||||
INSERT INTO users (first_name, last_name, email, phone_number, role, password, email_verified, phone_verified, created_at, updated_at)
|
||||
VALUES ($1, $2, $3, $4, $5, $6, $7, $8, CURRENT_TIMESTAMP, CURRENT_TIMESTAMP)
|
||||
RETURNING id, first_name, last_name, email, phone_number, role, email_verified, phone_verified, created_at, updated_at
|
||||
`
|
||||
|
||||
type CreateUserParams struct {
|
||||
FirstName string
|
||||
LastName string
|
||||
Email string
|
||||
PhoneNumber string
|
||||
Password []byte
|
||||
Email pgtype.Text
|
||||
PhoneNumber pgtype.Text
|
||||
Role string
|
||||
Verified pgtype.Bool
|
||||
Password []byte
|
||||
EmailVerified bool
|
||||
PhoneVerified bool
|
||||
}
|
||||
|
||||
func (q *Queries) CreateUser(ctx context.Context, arg CreateUserParams) (User, error) {
|
||||
type CreateUserRow struct {
|
||||
ID int64
|
||||
FirstName string
|
||||
LastName string
|
||||
Email pgtype.Text
|
||||
PhoneNumber pgtype.Text
|
||||
Role string
|
||||
EmailVerified bool
|
||||
PhoneVerified bool
|
||||
CreatedAt pgtype.Timestamptz
|
||||
UpdatedAt pgtype.Timestamptz
|
||||
}
|
||||
|
||||
func (q *Queries) CreateUser(ctx context.Context, arg CreateUserParams) (CreateUserRow, error) {
|
||||
row := q.db.QueryRow(ctx, CreateUser,
|
||||
arg.FirstName,
|
||||
arg.LastName,
|
||||
arg.Email,
|
||||
arg.PhoneNumber,
|
||||
arg.Password,
|
||||
arg.Role,
|
||||
arg.Verified,
|
||||
arg.Password,
|
||||
arg.EmailVerified,
|
||||
arg.PhoneVerified,
|
||||
)
|
||||
var i User
|
||||
var i CreateUserRow
|
||||
err := row.Scan(
|
||||
&i.ID,
|
||||
&i.FirstName,
|
||||
&i.LastName,
|
||||
&i.Email,
|
||||
&i.PhoneNumber,
|
||||
&i.Password,
|
||||
&i.Role,
|
||||
&i.Verified,
|
||||
&i.EmailVerified,
|
||||
&i.PhoneVerified,
|
||||
&i.CreatedAt,
|
||||
&i.UpdatedAt,
|
||||
)
|
||||
|
|
@ -54,7 +93,8 @@ func (q *Queries) CreateUser(ctx context.Context, arg CreateUserParams) (User, e
|
|||
}
|
||||
|
||||
const DeleteUser = `-- name: DeleteUser :exec
|
||||
DELETE FROM users WHERE id = $1
|
||||
DELETE FROM users
|
||||
WHERE id = $1
|
||||
`
|
||||
|
||||
func (q *Queries) DeleteUser(ctx context.Context, id int64) error {
|
||||
|
|
@ -63,27 +103,41 @@ func (q *Queries) DeleteUser(ctx context.Context, id int64) error {
|
|||
}
|
||||
|
||||
const GetAllUsers = `-- name: GetAllUsers :many
|
||||
SELECT id, first_name, last_name, email, phone_number, password, role, verified, created_at, updated_at FROM users
|
||||
SELECT id, first_name, last_name, email, phone_number, role, email_verified, phone_verified, created_at, updated_at
|
||||
FROM users
|
||||
`
|
||||
|
||||
func (q *Queries) GetAllUsers(ctx context.Context) ([]User, error) {
|
||||
type GetAllUsersRow struct {
|
||||
ID int64
|
||||
FirstName string
|
||||
LastName string
|
||||
Email pgtype.Text
|
||||
PhoneNumber pgtype.Text
|
||||
Role string
|
||||
EmailVerified bool
|
||||
PhoneVerified bool
|
||||
CreatedAt pgtype.Timestamptz
|
||||
UpdatedAt pgtype.Timestamptz
|
||||
}
|
||||
|
||||
func (q *Queries) GetAllUsers(ctx context.Context) ([]GetAllUsersRow, error) {
|
||||
rows, err := q.db.Query(ctx, GetAllUsers)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
defer rows.Close()
|
||||
var items []User
|
||||
var items []GetAllUsersRow
|
||||
for rows.Next() {
|
||||
var i User
|
||||
var i GetAllUsersRow
|
||||
if err := rows.Scan(
|
||||
&i.ID,
|
||||
&i.FirstName,
|
||||
&i.LastName,
|
||||
&i.Email,
|
||||
&i.PhoneNumber,
|
||||
&i.Password,
|
||||
&i.Role,
|
||||
&i.Verified,
|
||||
&i.EmailVerified,
|
||||
&i.PhoneVerified,
|
||||
&i.CreatedAt,
|
||||
&i.UpdatedAt,
|
||||
); err != nil {
|
||||
|
|
@ -97,8 +151,47 @@ func (q *Queries) GetAllUsers(ctx context.Context) ([]User, error) {
|
|||
return items, nil
|
||||
}
|
||||
|
||||
const GetUserByEmail = `-- name: GetUserByEmail :one
|
||||
SELECT id, first_name, last_name, email, phone_number, role, email_verified, phone_verified, created_at, updated_at
|
||||
FROM users
|
||||
WHERE email = $1
|
||||
`
|
||||
|
||||
type GetUserByEmailRow struct {
|
||||
ID int64
|
||||
FirstName string
|
||||
LastName string
|
||||
Email pgtype.Text
|
||||
PhoneNumber pgtype.Text
|
||||
Role string
|
||||
EmailVerified bool
|
||||
PhoneVerified bool
|
||||
CreatedAt pgtype.Timestamptz
|
||||
UpdatedAt pgtype.Timestamptz
|
||||
}
|
||||
|
||||
func (q *Queries) GetUserByEmail(ctx context.Context, email pgtype.Text) (GetUserByEmailRow, error) {
|
||||
row := q.db.QueryRow(ctx, GetUserByEmail, email)
|
||||
var i GetUserByEmailRow
|
||||
err := row.Scan(
|
||||
&i.ID,
|
||||
&i.FirstName,
|
||||
&i.LastName,
|
||||
&i.Email,
|
||||
&i.PhoneNumber,
|
||||
&i.Role,
|
||||
&i.EmailVerified,
|
||||
&i.PhoneVerified,
|
||||
&i.CreatedAt,
|
||||
&i.UpdatedAt,
|
||||
)
|
||||
return i, err
|
||||
}
|
||||
|
||||
const GetUserByID = `-- name: GetUserByID :one
|
||||
SELECT id, first_name, last_name, email, phone_number, password, role, verified, created_at, updated_at FROM users WHERE id = $1
|
||||
SELECT id, first_name, last_name, email, phone_number, role, password, email_verified, phone_verified, created_at, updated_at, suspended_at, suspended
|
||||
FROM users
|
||||
WHERE id = $1
|
||||
`
|
||||
|
||||
func (q *Queries) GetUserByID(ctx context.Context, id int64) (User, error) {
|
||||
|
|
@ -110,40 +203,95 @@ func (q *Queries) GetUserByID(ctx context.Context, id int64) (User, error) {
|
|||
&i.LastName,
|
||||
&i.Email,
|
||||
&i.PhoneNumber,
|
||||
&i.Password,
|
||||
&i.Role,
|
||||
&i.Verified,
|
||||
&i.Password,
|
||||
&i.EmailVerified,
|
||||
&i.PhoneVerified,
|
||||
&i.CreatedAt,
|
||||
&i.UpdatedAt,
|
||||
&i.SuspendedAt,
|
||||
&i.Suspended,
|
||||
)
|
||||
return i, err
|
||||
}
|
||||
|
||||
const GetUserByPhone = `-- name: GetUserByPhone :one
|
||||
SELECT id, first_name, last_name, email, phone_number, role, email_verified, phone_verified, created_at, updated_at
|
||||
FROM users
|
||||
WHERE phone_number = $1
|
||||
`
|
||||
|
||||
type GetUserByPhoneRow struct {
|
||||
ID int64
|
||||
FirstName string
|
||||
LastName string
|
||||
Email pgtype.Text
|
||||
PhoneNumber pgtype.Text
|
||||
Role string
|
||||
EmailVerified bool
|
||||
PhoneVerified bool
|
||||
CreatedAt pgtype.Timestamptz
|
||||
UpdatedAt pgtype.Timestamptz
|
||||
}
|
||||
|
||||
func (q *Queries) GetUserByPhone(ctx context.Context, phoneNumber pgtype.Text) (GetUserByPhoneRow, error) {
|
||||
row := q.db.QueryRow(ctx, GetUserByPhone, phoneNumber)
|
||||
var i GetUserByPhoneRow
|
||||
err := row.Scan(
|
||||
&i.ID,
|
||||
&i.FirstName,
|
||||
&i.LastName,
|
||||
&i.Email,
|
||||
&i.PhoneNumber,
|
||||
&i.Role,
|
||||
&i.EmailVerified,
|
||||
&i.PhoneVerified,
|
||||
&i.CreatedAt,
|
||||
&i.UpdatedAt,
|
||||
)
|
||||
return i, err
|
||||
}
|
||||
|
||||
const UpdatePassword = `-- name: UpdatePassword :exec
|
||||
UPDATE users
|
||||
SET password = $1, updated_at = CURRENT_TIMESTAMP
|
||||
WHERE (email = $2 OR phone_number = $3)
|
||||
`
|
||||
|
||||
type UpdatePasswordParams struct {
|
||||
Password []byte
|
||||
Email pgtype.Text
|
||||
PhoneNumber pgtype.Text
|
||||
}
|
||||
|
||||
func (q *Queries) UpdatePassword(ctx context.Context, arg UpdatePasswordParams) error {
|
||||
_, err := q.db.Exec(ctx, UpdatePassword, arg.Password, arg.Email, arg.PhoneNumber)
|
||||
return err
|
||||
}
|
||||
|
||||
const UpdateUser = `-- name: UpdateUser :exec
|
||||
UPDATE users SET first_name = $2, last_name = $3, email = $4, phone_number = $5, password = $6, role = $7, verified = $8, updated_at = CURRENT_TIMESTAMP WHERE id = $1
|
||||
UPDATE users
|
||||
SET first_name = $1, last_name = $2, email = $3, phone_number = $4, role = $5, updated_at = CURRENT_TIMESTAMP
|
||||
WHERE id = $6
|
||||
`
|
||||
|
||||
type UpdateUserParams struct {
|
||||
ID int64
|
||||
FirstName string
|
||||
LastName string
|
||||
Email string
|
||||
PhoneNumber string
|
||||
Password []byte
|
||||
Email pgtype.Text
|
||||
PhoneNumber pgtype.Text
|
||||
Role string
|
||||
Verified pgtype.Bool
|
||||
ID int64
|
||||
}
|
||||
|
||||
func (q *Queries) UpdateUser(ctx context.Context, arg UpdateUserParams) error {
|
||||
_, err := q.db.Exec(ctx, UpdateUser,
|
||||
arg.ID,
|
||||
arg.FirstName,
|
||||
arg.LastName,
|
||||
arg.Email,
|
||||
arg.PhoneNumber,
|
||||
arg.Password,
|
||||
arg.Role,
|
||||
arg.Verified,
|
||||
arg.ID,
|
||||
)
|
||||
return err
|
||||
}
|
||||
|
|
|
|||
|
|
@ -1,6 +1,16 @@
|
|||
package domain
|
||||
|
||||
import "time"
|
||||
import (
|
||||
"errors"
|
||||
"time"
|
||||
)
|
||||
|
||||
var (
|
||||
ErrOtpNotFound = errors.New("otp not found")
|
||||
ErrOtpAlreadyUsed = errors.New("otp already used")
|
||||
ErrInvalidOtp = errors.New("invalid otp")
|
||||
ErrOtpExpired = errors.New("otp expired")
|
||||
)
|
||||
|
||||
type OtpFor string
|
||||
|
||||
|
|
|
|||
|
|
@ -1,6 +1,13 @@
|
|||
package domain
|
||||
|
||||
import "time"
|
||||
import (
|
||||
"errors"
|
||||
"time"
|
||||
)
|
||||
|
||||
var (
|
||||
ErrUserNotFound = errors.New("user not found")
|
||||
)
|
||||
|
||||
type User struct {
|
||||
ID int64
|
||||
|
|
|
|||
18
internal/mocks/mock_email/email.go
Normal file
18
internal/mocks/mock_email/email.go
Normal file
|
|
@ -0,0 +1,18 @@
|
|||
package mockemail
|
||||
|
||||
import (
|
||||
"context"
|
||||
"fmt"
|
||||
)
|
||||
|
||||
type MockEmail struct {
|
||||
}
|
||||
|
||||
func NewMockEmail() *MockEmail {
|
||||
return &MockEmail{}
|
||||
}
|
||||
|
||||
func (m *MockEmail) SendEmailOTP(ctx context.Context, email string, otp string) error {
|
||||
fmt.Println("MockEmail: Sending OTP to", email, "with OTP:", otp)
|
||||
return nil
|
||||
}
|
||||
19
internal/mocks/mock_sms/sms.go
Normal file
19
internal/mocks/mock_sms/sms.go
Normal file
|
|
@ -0,0 +1,19 @@
|
|||
package mocksms
|
||||
|
||||
import (
|
||||
"context"
|
||||
"fmt"
|
||||
)
|
||||
|
||||
type MockSMS struct {
|
||||
}
|
||||
|
||||
func NewMockSMS() *MockSMS {
|
||||
return &MockSMS{}
|
||||
}
|
||||
func (m *MockSMS) SendSMSOTP(ctx context.Context, phoneNumber, otp string) error {
|
||||
fmt.Println("MockSMS: Sending OTP to", phoneNumber, "with OTP:", otp)
|
||||
return nil
|
||||
}
|
||||
|
||||
// func (m *MockSms){}
|
||||
|
|
@ -13,8 +13,14 @@ import (
|
|||
|
||||
func (s *Store) GetUserByEmailPhone(ctx context.Context, email, phone string) (domain.User, error) {
|
||||
user, err := s.queries.GetUserByEmailPhone(ctx, dbgen.GetUserByEmailPhoneParams{
|
||||
Email: email,
|
||||
PhoneNumber: phone,
|
||||
Email: pgtype.Text{
|
||||
String: email,
|
||||
Valid: true,
|
||||
},
|
||||
PhoneNumber: pgtype.Text{
|
||||
String: phone,
|
||||
Valid: true,
|
||||
},
|
||||
})
|
||||
if err != nil {
|
||||
if errors.Is(err, sql.ErrNoRows) {
|
||||
|
|
@ -26,8 +32,8 @@ func (s *Store) GetUserByEmailPhone(ctx context.Context, email, phone string) (d
|
|||
ID: user.ID,
|
||||
FirstName: user.FirstName,
|
||||
LastName: user.LastName,
|
||||
Email: user.Email,
|
||||
PhoneNumber: user.PhoneNumber,
|
||||
Email: user.Email.String,
|
||||
PhoneNumber: user.PhoneNumber.String,
|
||||
Password: user.Password,
|
||||
Role: domain.Role(user.Role),
|
||||
}, nil
|
||||
|
|
|
|||
50
internal/repository/otp.go
Normal file
50
internal/repository/otp.go
Normal file
|
|
@ -0,0 +1,50 @@
|
|||
package repository
|
||||
|
||||
import (
|
||||
"context"
|
||||
"database/sql"
|
||||
|
||||
dbgen "github.com/SamuelTariku/FortuneBet-Backend/gen/db"
|
||||
"github.com/SamuelTariku/FortuneBet-Backend/internal/domain"
|
||||
"github.com/jackc/pgx/v5/pgtype"
|
||||
)
|
||||
|
||||
func (s *Store) CreateOtp(ctx context.Context, otp domain.Otp) error {
|
||||
return s.queries.CreateOtp(ctx, dbgen.CreateOtpParams{
|
||||
SentTo: otp.SentTo,
|
||||
Medium: string(otp.Medium),
|
||||
OtpFor: string(otp.For),
|
||||
Otp: otp.Otp,
|
||||
ExpiresAt: pgtype.Timestamptz{
|
||||
Time: otp.ExpiresAt,
|
||||
Valid: true,
|
||||
},
|
||||
})
|
||||
}
|
||||
func (s *Store) GetOtp(ctx context.Context, sentTo string, sentfor domain.OtpFor, medium domain.OtpMedium) (domain.Otp, error) {
|
||||
row, err := s.queries.GetOtp(ctx, dbgen.GetOtpParams{
|
||||
SentTo: sentTo,
|
||||
Medium: string(medium),
|
||||
OtpFor: string(sentfor),
|
||||
})
|
||||
if err != nil {
|
||||
if err == sql.ErrNoRows {
|
||||
return domain.Otp{}, domain.ErrOtpNotFound
|
||||
}
|
||||
return domain.Otp{}, err
|
||||
}
|
||||
return domain.Otp{
|
||||
ID: row.ID,
|
||||
SentTo: row.SentTo,
|
||||
Medium: domain.OtpMedium(row.Medium),
|
||||
For: domain.OtpFor(row.OtpFor),
|
||||
Otp: row.Otp,
|
||||
Used: row.Used,
|
||||
UsedAt: row.UsedAt.Time,
|
||||
CreatedAt: row.CreatedAt.Time,
|
||||
ExpiresAt: row.ExpiresAt.Time,
|
||||
}, nil
|
||||
}
|
||||
func (s *Store) MarkOtpAsUsed(ctx context.Context, otp domain.Otp) error {
|
||||
return s.queries.MarkOtpAsUsed(ctx, otp.ID)
|
||||
}
|
||||
|
|
@ -2,47 +2,70 @@ package repository
|
|||
|
||||
import (
|
||||
"context"
|
||||
"database/sql"
|
||||
"errors"
|
||||
"fmt"
|
||||
|
||||
dbgen "github.com/SamuelTariku/FortuneBet-Backend/gen/db"
|
||||
"github.com/SamuelTariku/FortuneBet-Backend/internal/domain"
|
||||
"github.com/jackc/pgx/v5/pgtype"
|
||||
)
|
||||
|
||||
func (s *Store) CreateUser(ctx context.Context, firstName, lastName, email, phoneNumber, password, role string, verified bool) (domain.User, error) {
|
||||
user, err := s.queries.CreateUser(ctx, dbgen.CreateUserParams{
|
||||
FirstName: firstName,
|
||||
LastName: lastName,
|
||||
Email: email,
|
||||
PhoneNumber: phoneNumber,
|
||||
// Password: password,
|
||||
Role: role,
|
||||
func (s *Store) CreateUser(ctx context.Context, user domain.User, usedOtpId int64) (domain.User, error) {
|
||||
err := s.queries.MarkOtpAsUsed(ctx, usedOtpId)
|
||||
if err != nil {
|
||||
return domain.User{}, err
|
||||
}
|
||||
userRes, err := s.queries.CreateUser(ctx, dbgen.CreateUserParams{
|
||||
FirstName: user.FirstName,
|
||||
LastName: user.LastName,
|
||||
Email: pgtype.Text{
|
||||
String: user.Email,
|
||||
Valid: user.Email != "",
|
||||
},
|
||||
PhoneNumber: pgtype.Text{
|
||||
String: user.PhoneNumber,
|
||||
Valid: user.PhoneNumber != "",
|
||||
},
|
||||
Password: user.Password,
|
||||
Role: string(user.Role),
|
||||
EmailVerified: user.EmailVerified,
|
||||
PhoneVerified: user.PhoneVerified,
|
||||
})
|
||||
if err != nil {
|
||||
return domain.User{}, err
|
||||
}
|
||||
return domain.User{
|
||||
ID: user.ID,
|
||||
FirstName: user.FirstName,
|
||||
LastName: user.LastName,
|
||||
Email: user.Email,
|
||||
PhoneNumber: user.PhoneNumber,
|
||||
Password: user.Password,
|
||||
// Role: user.Role,
|
||||
ID: userRes.ID,
|
||||
FirstName: userRes.FirstName,
|
||||
LastName: userRes.LastName,
|
||||
Email: userRes.Email.String,
|
||||
PhoneNumber: userRes.PhoneNumber.String,
|
||||
Role: domain.Role(userRes.Role),
|
||||
}, nil
|
||||
|
||||
}
|
||||
func (s *Store) GetUserByID(ctx context.Context, id int64) (domain.User, error) {
|
||||
user, err := s.queries.GetUserByID(ctx, id)
|
||||
if err != nil {
|
||||
if errors.Is(err, sql.ErrNoRows) {
|
||||
return domain.User{}, domain.ErrUserNotFound
|
||||
}
|
||||
return domain.User{}, err
|
||||
}
|
||||
return domain.User{
|
||||
ID: user.ID,
|
||||
FirstName: user.FirstName,
|
||||
LastName: user.LastName,
|
||||
Email: user.Email,
|
||||
PhoneNumber: user.PhoneNumber,
|
||||
Email: user.Email.String,
|
||||
PhoneNumber: user.PhoneNumber.String,
|
||||
Role: domain.Role(user.Role),
|
||||
EmailVerified: user.EmailVerified,
|
||||
Password: user.Password,
|
||||
// Role: user.Role,
|
||||
PhoneVerified: user.PhoneVerified,
|
||||
CreatedAt: user.CreatedAt.Time,
|
||||
UpdatedAt: user.UpdatedAt.Time,
|
||||
SuspendedAt: user.SuspendedAt.Time,
|
||||
Suspended: user.Suspended,
|
||||
}, nil
|
||||
}
|
||||
func (s *Store) GetAllUsers(ctx context.Context) ([]domain.User, error) {
|
||||
|
|
@ -50,32 +73,118 @@ func (s *Store) GetAllUsers(ctx context.Context) ([]domain.User, error) {
|
|||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
var result []domain.User
|
||||
for _, user := range users {
|
||||
result = append(result, domain.User{
|
||||
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,
|
||||
PhoneNumber: user.PhoneNumber,
|
||||
Password: user.Password,
|
||||
// Role: user.Role,
|
||||
})
|
||||
Email: user.Email.String,
|
||||
PhoneNumber: user.PhoneNumber.String,
|
||||
Role: domain.Role(user.Role),
|
||||
}
|
||||
return result, nil
|
||||
}
|
||||
return userList, nil
|
||||
}
|
||||
func (s *Store) UpdateUser(ctx context.Context, id int64, firstName, lastName, email, phoneNumber, password, role string, verified bool) error {
|
||||
func (s *Store) UpdateUser(ctx context.Context, user domain.UpdateUserReq) error {
|
||||
err := s.queries.UpdateUser(ctx, dbgen.UpdateUserParams{
|
||||
ID: id,
|
||||
FirstName: firstName,
|
||||
LastName: lastName,
|
||||
Email: email,
|
||||
PhoneNumber: phoneNumber,
|
||||
// Password: password,
|
||||
Role: role,
|
||||
// ID: user.ID,
|
||||
// FirstName: user.FirstName,
|
||||
// LastName: user.LastName,
|
||||
// Email: user.Email,
|
||||
// PhoneNumber: user.PhoneNumber,
|
||||
})
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
return nil
|
||||
}
|
||||
func (s *Store) DeleteUser(ctx context.Context, id int64) error {
|
||||
return s.queries.DeleteUser(ctx, id)
|
||||
err := s.queries.DeleteUser(ctx, id)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
return nil
|
||||
}
|
||||
func (s *Store) CheckPhoneEmailExist(ctx context.Context, phoneNum, email string) (bool, bool, error) {
|
||||
fmt.Printf("phoneNum: %s, email: %s\n", phoneNum, email)
|
||||
row, err := s.queries.CheckPhoneEmailExist(ctx, dbgen.CheckPhoneEmailExistParams{
|
||||
PhoneNumber: pgtype.Text{
|
||||
String: phoneNum,
|
||||
Valid: phoneNum != "",
|
||||
},
|
||||
Email: pgtype.Text{
|
||||
String: email,
|
||||
|
||||
Valid: email != "",
|
||||
},
|
||||
})
|
||||
fmt.Printf("row: %+v\n", row)
|
||||
if err != nil {
|
||||
return false, false, err
|
||||
}
|
||||
return row.EmailExists, row.PhoneExists, nil
|
||||
}
|
||||
|
||||
func (s *Store) GetUserByEmail(ctx context.Context, email string) (domain.User, error) {
|
||||
user, err := s.queries.GetUserByEmail(ctx, pgtype.Text{
|
||||
String: email,
|
||||
Valid: true,
|
||||
})
|
||||
if err != nil {
|
||||
if errors.Is(err, sql.ErrNoRows) {
|
||||
return domain.User{}, domain.ErrUserNotFound
|
||||
}
|
||||
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),
|
||||
}, nil
|
||||
}
|
||||
func (s *Store) GetUserByPhone(ctx context.Context, phoneNum string) (domain.User, error) {
|
||||
user, err := s.queries.GetUserByPhone(ctx, pgtype.Text{
|
||||
String: phoneNum,
|
||||
Valid: true,
|
||||
})
|
||||
if err != nil {
|
||||
if errors.Is(err, sql.ErrNoRows) {
|
||||
return domain.User{}, domain.ErrUserNotFound
|
||||
}
|
||||
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),
|
||||
}, nil
|
||||
}
|
||||
|
||||
func (s *Store) UpdatePassword(ctx context.Context, identifier string, password []byte, usedOtpId int64) error {
|
||||
err := s.queries.MarkOtpAsUsed(ctx, usedOtpId)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
err = s.queries.UpdatePassword(ctx, dbgen.UpdatePasswordParams{
|
||||
Password: password,
|
||||
Email: pgtype.Text{
|
||||
String: identifier,
|
||||
Valid: true,
|
||||
},
|
||||
PhoneNumber: pgtype.Text{
|
||||
String: identifier,
|
||||
Valid: true,
|
||||
},
|
||||
})
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
|
|
|||
44
internal/services/user/common.go
Normal file
44
internal/services/user/common.go
Normal file
|
|
@ -0,0 +1,44 @@
|
|||
package user
|
||||
|
||||
import (
|
||||
"context"
|
||||
"time"
|
||||
|
||||
"github.com/SamuelTariku/FortuneBet-Backend/internal/domain"
|
||||
"golang.org/x/crypto/bcrypt"
|
||||
)
|
||||
|
||||
func (s *Service) SendOtp(ctx context.Context, sentTo string, otpFor domain.OtpFor, medium domain.OtpMedium) error {
|
||||
otpCode := "123456" // Generate OTP code
|
||||
|
||||
otp := domain.Otp{
|
||||
SentTo: sentTo,
|
||||
Medium: medium,
|
||||
For: otpFor,
|
||||
Otp: otpCode,
|
||||
Used: false,
|
||||
CreatedAt: time.Now(),
|
||||
ExpiresAt: time.Now().Add(OtpExpiry),
|
||||
}
|
||||
|
||||
err := s.otpStore.CreateOtp(ctx, otp)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
switch medium {
|
||||
case domain.OtpMediumSms:
|
||||
return s.smsGateway.SendSMSOTP(ctx, sentTo, otpCode)
|
||||
case domain.OtpMediumEmail:
|
||||
return s.emailGateway.SendEmailOTP(ctx, sentTo, otpCode)
|
||||
}
|
||||
return nil
|
||||
}
|
||||
func hashPassword(plaintextPassword string) ([]byte, error) {
|
||||
hash, err := bcrypt.GenerateFromPassword([]byte(plaintextPassword), 12)
|
||||
if err != nil {
|
||||
return []byte{}, err
|
||||
}
|
||||
|
||||
return hash, nil
|
||||
}
|
||||
|
|
@ -27,5 +27,4 @@ type EmailGateway interface {
|
|||
type OtpStore interface {
|
||||
CreateOtp(ctx context.Context, otp domain.Otp) error
|
||||
GetOtp(ctx context.Context, sentTo string, sentfor domain.OtpFor, medium domain.OtpMedium) (domain.Otp, error)
|
||||
MarkUsed(ctx context.Context, id int64) error
|
||||
}
|
||||
|
|
|
|||
79
internal/services/user/register.go
Normal file
79
internal/services/user/register.go
Normal file
|
|
@ -0,0 +1,79 @@
|
|||
package user
|
||||
|
||||
import (
|
||||
"context"
|
||||
"fmt"
|
||||
"time"
|
||||
|
||||
"github.com/SamuelTariku/FortuneBet-Backend/internal/domain"
|
||||
)
|
||||
|
||||
func (s *Service) CheckPhoneEmailExist(ctx context.Context, phoneNum, email string) (bool, bool, error) { // email,phone,error
|
||||
return s.userStore.CheckPhoneEmailExist(ctx, phoneNum, email)
|
||||
}
|
||||
func (s *Service) SendRegisterCode(ctx context.Context, medium domain.OtpMedium, sentTo string) error {
|
||||
var err error
|
||||
// check if user exists
|
||||
switch medium {
|
||||
case domain.OtpMediumEmail:
|
||||
_, err = s.userStore.GetUserByEmail(ctx, sentTo)
|
||||
case domain.OtpMediumSms:
|
||||
_, err = s.userStore.GetUserByPhone(ctx, sentTo)
|
||||
}
|
||||
|
||||
if err != nil && err != domain.ErrUserNotFound {
|
||||
|
||||
return err
|
||||
}
|
||||
|
||||
// send otp based on the medium
|
||||
return s.SendOtp(ctx, sentTo, domain.OtpRegister, medium)
|
||||
}
|
||||
func (s *Service) RegisterUser(ctx context.Context, registerReq domain.RegisterUserReq) (domain.User, error) { // normal
|
||||
// get otp
|
||||
fmt.Printf("registerReq: %+v\n", registerReq)
|
||||
var sentTo string
|
||||
if registerReq.OtpMedium == domain.OtpMediumEmail {
|
||||
sentTo = registerReq.Email
|
||||
} else {
|
||||
sentTo = registerReq.PhoneNumber
|
||||
}
|
||||
//
|
||||
otp, err := s.otpStore.GetOtp(
|
||||
ctx, sentTo,
|
||||
domain.OtpRegister, registerReq.OtpMedium)
|
||||
if err != nil {
|
||||
return domain.User{}, err
|
||||
}
|
||||
// verify otp
|
||||
if otp.Used {
|
||||
return domain.User{}, domain.ErrOtpAlreadyUsed
|
||||
}
|
||||
if time.Now().After(otp.ExpiresAt) {
|
||||
return domain.User{}, domain.ErrOtpExpired
|
||||
}
|
||||
if otp.Otp != registerReq.Otp {
|
||||
return domain.User{}, domain.ErrInvalidOtp
|
||||
}
|
||||
|
||||
hashedPassword, err := hashPassword(registerReq.Password)
|
||||
if err != nil {
|
||||
return domain.User{}, err
|
||||
}
|
||||
userR := domain.User{
|
||||
FirstName: registerReq.FirstName,
|
||||
LastName: registerReq.LastName,
|
||||
Email: registerReq.Email,
|
||||
PhoneNumber: registerReq.PhoneNumber,
|
||||
Password: hashedPassword,
|
||||
Role: "user",
|
||||
EmailVerified: registerReq.OtpMedium == domain.OtpMediumEmail,
|
||||
PhoneVerified: registerReq.OtpMedium == domain.OtpMediumSms,
|
||||
}
|
||||
// create the user and mark otp as used
|
||||
user, err := s.userStore.CreateUser(ctx, userR, otp.ID)
|
||||
if err != nil {
|
||||
return domain.User{}, err
|
||||
}
|
||||
return user, nil
|
||||
}
|
||||
63
internal/services/user/reset.go
Normal file
63
internal/services/user/reset.go
Normal file
|
|
@ -0,0 +1,63 @@
|
|||
package user
|
||||
|
||||
import (
|
||||
"context"
|
||||
"time"
|
||||
|
||||
"github.com/SamuelTariku/FortuneBet-Backend/internal/domain"
|
||||
)
|
||||
|
||||
func (s *Service) SendResetCode(ctx context.Context, medium domain.OtpMedium, sentTo string) error {
|
||||
|
||||
var err error
|
||||
// check if user exists
|
||||
switch medium {
|
||||
case domain.OtpMediumEmail:
|
||||
_, err = s.userStore.GetUserByEmail(ctx, sentTo)
|
||||
case domain.OtpMediumSms:
|
||||
_, err = s.userStore.GetUserByPhone(ctx, sentTo)
|
||||
}
|
||||
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
return s.SendOtp(ctx, sentTo, domain.OtpReset, medium)
|
||||
|
||||
}
|
||||
|
||||
func (s *Service) ResetPassword(ctx context.Context, resetReq domain.ResetPasswordReq) error {
|
||||
var sentTo string
|
||||
if resetReq.OtpMedium == domain.OtpMediumEmail {
|
||||
sentTo = resetReq.Email
|
||||
} else {
|
||||
sentTo = resetReq.PhoneNumber
|
||||
}
|
||||
otp, err := s.otpStore.GetOtp(
|
||||
ctx, sentTo,
|
||||
domain.OtpReset, resetReq.OtpMedium)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
//
|
||||
if otp.Used {
|
||||
return domain.ErrOtpAlreadyUsed
|
||||
}
|
||||
if time.Now().After(otp.ExpiresAt) {
|
||||
return domain.ErrOtpExpired
|
||||
}
|
||||
if otp.Otp != resetReq.Otp {
|
||||
return domain.ErrInvalidOtp
|
||||
}
|
||||
// hash password
|
||||
hashedPassword, err := hashPassword(resetReq.Password)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
// reset pass and mark otp as used
|
||||
err = s.userStore.UpdatePassword(ctx, sentTo, hashedPassword, otp.ID)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
|
@ -1,24 +1,13 @@
|
|||
package user
|
||||
|
||||
import (
|
||||
"context"
|
||||
"errors"
|
||||
"time"
|
||||
|
||||
"github.com/SamuelTariku/FortuneBet-Backend/internal/domain"
|
||||
"golang.org/x/crypto/bcrypt"
|
||||
)
|
||||
|
||||
const (
|
||||
OtpExpiry = 5 * time.Minute
|
||||
)
|
||||
|
||||
var (
|
||||
ErrOtpAlreadyUsed = errors.New("otp already used")
|
||||
ErrInvalidOtp = errors.New("invalid otp")
|
||||
ErrOtpExpired = errors.New("otp expired")
|
||||
)
|
||||
|
||||
type Service struct {
|
||||
userStore UserStore
|
||||
otpStore OtpStore
|
||||
|
|
@ -27,179 +16,14 @@ type Service struct {
|
|||
}
|
||||
|
||||
func NewService(
|
||||
userStore UserStore, RefreshExpiry int,
|
||||
userStore UserStore,
|
||||
otpStore OtpStore, smsGateway SmsGateway,
|
||||
emailGateway EmailGateway,
|
||||
) *Service {
|
||||
return &Service{
|
||||
userStore: userStore,
|
||||
otpStore: otpStore,
|
||||
smsGateway: smsGateway,
|
||||
emailGateway: emailGateway,
|
||||
}
|
||||
}
|
||||
|
||||
func (s *Service) CheckPhoneEmailExist(ctx context.Context, phoneNum, email string) (bool, bool, error) { // email,phone,error
|
||||
return s.userStore.CheckPhoneEmailExist(ctx, phoneNum, email)
|
||||
}
|
||||
func (s *Service) SendRegisterCode(ctx context.Context, medium domain.OtpMedium, sentTo string) error {
|
||||
var err error
|
||||
// check if user exists
|
||||
switch medium {
|
||||
case domain.OtpMediumEmail:
|
||||
_, err = s.userStore.GetUserByEmail(ctx, sentTo)
|
||||
case domain.OtpMediumSms:
|
||||
_, err = s.userStore.GetUserByPhone(ctx, sentTo)
|
||||
}
|
||||
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
// send otp based on the medium
|
||||
return s.SendOtp(ctx, sentTo, domain.OtpReset, medium)
|
||||
}
|
||||
func (s *Service) RegisterUser(ctx context.Context, registerReq domain.RegisterUserReq) (domain.User, error) { // normal
|
||||
// get otp
|
||||
var sentTo string
|
||||
if registerReq.OtpMedium == domain.OtpMediumEmail {
|
||||
sentTo = registerReq.Email
|
||||
} else {
|
||||
sentTo = registerReq.PhoneNumber
|
||||
}
|
||||
//
|
||||
otp, err := s.otpStore.GetOtp(
|
||||
ctx, sentTo,
|
||||
domain.OtpRegister, registerReq.OtpMedium)
|
||||
if err != nil {
|
||||
return domain.User{}, err
|
||||
}
|
||||
// verify otp
|
||||
if otp.Used {
|
||||
return domain.User{}, ErrOtpAlreadyUsed
|
||||
}
|
||||
if time.Now().After(otp.ExpiresAt) {
|
||||
return domain.User{}, ErrOtpExpired
|
||||
}
|
||||
if otp.Otp != registerReq.Otp {
|
||||
return domain.User{}, ErrInvalidOtp
|
||||
}
|
||||
|
||||
hashedPassword, err := hashPassword(registerReq.Password)
|
||||
if err != nil {
|
||||
return domain.User{}, err
|
||||
}
|
||||
userR := domain.User{
|
||||
FirstName: registerReq.FirstName,
|
||||
LastName: registerReq.LastName,
|
||||
Email: registerReq.Email,
|
||||
PhoneNumber: registerReq.PhoneNumber,
|
||||
Password: hashedPassword,
|
||||
Role: "user",
|
||||
EmailVerified: registerReq.OtpMedium == domain.OtpMediumEmail,
|
||||
PhoneVerified: registerReq.OtpMedium == domain.OtpMediumSms,
|
||||
}
|
||||
// create the user and mark otp as used
|
||||
user, err := s.userStore.CreateUser(ctx, userR, otp.ID)
|
||||
if err != nil {
|
||||
return domain.User{}, err
|
||||
}
|
||||
return user, nil
|
||||
}
|
||||
|
||||
func (s *Service) SendResetCode(ctx context.Context, medium domain.OtpMedium, sentTo string) error {
|
||||
|
||||
var err error
|
||||
// check if user exists
|
||||
switch medium {
|
||||
case domain.OtpMediumEmail:
|
||||
_, err = s.userStore.GetUserByEmail(ctx, sentTo)
|
||||
case domain.OtpMediumSms:
|
||||
_, err = s.userStore.GetUserByPhone(ctx, sentTo)
|
||||
}
|
||||
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
return s.SendOtp(ctx, sentTo, domain.OtpReset, medium)
|
||||
|
||||
}
|
||||
|
||||
func (s *Service) ResetPassword(ctx context.Context, resetReq domain.ResetPasswordReq) error {
|
||||
var sentTo string
|
||||
if resetReq.OtpMedium == domain.OtpMediumEmail {
|
||||
sentTo = resetReq.Email
|
||||
} else {
|
||||
sentTo = resetReq.PhoneNumber
|
||||
}
|
||||
otp, err := s.otpStore.GetOtp(
|
||||
ctx, sentTo,
|
||||
domain.OtpRegister, resetReq.OtpMedium)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
//
|
||||
if otp.Used {
|
||||
return ErrOtpAlreadyUsed
|
||||
}
|
||||
if time.Now().After(otp.ExpiresAt) {
|
||||
return ErrOtpExpired
|
||||
}
|
||||
if otp.Otp != resetReq.Otp {
|
||||
return ErrInvalidOtp
|
||||
}
|
||||
// hash password
|
||||
hashedPassword, err := hashPassword(resetReq.Password)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
// reset pass and mark otp as used
|
||||
err = s.userStore.UpdatePassword(ctx, sentTo, hashedPassword, otp.ID)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
return nil
|
||||
}
|
||||
func (s *Service) SendOtp(ctx context.Context, sentTo string, otpFor domain.OtpFor, medium domain.OtpMedium) error {
|
||||
otpCode := "123456" // Generate OTP code
|
||||
|
||||
otp := domain.Otp{
|
||||
SentTo: sentTo,
|
||||
Medium: medium,
|
||||
For: otpFor,
|
||||
Otp: otpCode,
|
||||
Used: false,
|
||||
CreatedAt: time.Now(),
|
||||
ExpiresAt: time.Now().Add(OtpExpiry),
|
||||
}
|
||||
|
||||
err := s.otpStore.CreateOtp(ctx, otp)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
switch medium {
|
||||
case domain.OtpMediumSms:
|
||||
return s.smsGateway.SendSMSOTP(ctx, sentTo, otpCode)
|
||||
case domain.OtpMediumEmail:
|
||||
return s.emailGateway.SendEmailOTP(ctx, sentTo, otpCode)
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func (s *Service) UpdateUser(ctx context.Context, user domain.UpdateUserReq) error {
|
||||
// update user
|
||||
return s.userStore.UpdateUser(ctx, user)
|
||||
|
||||
}
|
||||
func (s *Service) GetUserByID(ctx context.Context, id int64) (domain.User, error) {
|
||||
return s.userStore.GetUserByID(ctx, id)
|
||||
}
|
||||
|
||||
func hashPassword(plaintextPassword string) ([]byte, error) {
|
||||
hash, err := bcrypt.GenerateFromPassword([]byte(plaintextPassword), 12)
|
||||
if err != nil {
|
||||
return []byte{}, err
|
||||
}
|
||||
|
||||
return hash, nil
|
||||
}
|
||||
|
|
|
|||
16
internal/services/user/user.go
Normal file
16
internal/services/user/user.go
Normal file
|
|
@ -0,0 +1,16 @@
|
|||
package user
|
||||
|
||||
import (
|
||||
"context"
|
||||
|
||||
"github.com/SamuelTariku/FortuneBet-Backend/internal/domain"
|
||||
)
|
||||
|
||||
func (s *Service) UpdateUser(ctx context.Context, user domain.UpdateUserReq) error {
|
||||
// update user
|
||||
return s.userStore.UpdateUser(ctx, user)
|
||||
|
||||
}
|
||||
func (s *Service) GetUserByID(ctx context.Context, id int64) (domain.User, error) {
|
||||
return s.userStore.GetUserByID(ctx, id)
|
||||
}
|
||||
|
|
@ -5,6 +5,7 @@ import (
|
|||
"log/slog"
|
||||
|
||||
"github.com/SamuelTariku/FortuneBet-Backend/internal/services/authentication"
|
||||
"github.com/SamuelTariku/FortuneBet-Backend/internal/services/user"
|
||||
jwtutil "github.com/SamuelTariku/FortuneBet-Backend/internal/web_server/jwt"
|
||||
customvalidator "github.com/SamuelTariku/FortuneBet-Backend/internal/web_server/validator"
|
||||
"github.com/bytedance/sonic"
|
||||
|
|
@ -16,6 +17,7 @@ type App struct {
|
|||
logger *slog.Logger
|
||||
port int
|
||||
authSvc *authentication.Service
|
||||
userSvc *user.Service
|
||||
validator *customvalidator.CustomValidator
|
||||
JwtConfig jwtutil.JwtConfig
|
||||
}
|
||||
|
|
@ -25,6 +27,7 @@ func NewApp(
|
|||
authSvc *authentication.Service,
|
||||
logger *slog.Logger,
|
||||
JwtConfig jwtutil.JwtConfig,
|
||||
userSvc *user.Service,
|
||||
) *App {
|
||||
app := fiber.New(fiber.Config{
|
||||
CaseSensitive: true,
|
||||
|
|
@ -39,6 +42,7 @@ func NewApp(
|
|||
validator: validator,
|
||||
logger: logger,
|
||||
JwtConfig: JwtConfig,
|
||||
userSvc: userSvc,
|
||||
}
|
||||
|
||||
s.initAppRoutes()
|
||||
|
|
|
|||
|
|
@ -3,7 +3,6 @@ package handlers
|
|||
import (
|
||||
"errors"
|
||||
"log/slog"
|
||||
"strconv"
|
||||
|
||||
"github.com/SamuelTariku/FortuneBet-Backend/internal/services/authentication"
|
||||
jwtutil "github.com/SamuelTariku/FortuneBet-Backend/internal/web_server/jwt"
|
||||
|
|
@ -66,7 +65,7 @@ func LoginCustomer(
|
|||
return nil
|
||||
|
||||
}
|
||||
accessToken, err := jwtutil.CreateJwt(strconv.Itoa(int(successRes.UserId)), successRes.Role, JwtConfig.JwtAccessKey, JwtConfig.JwtAccessExpiry)
|
||||
accessToken, err := jwtutil.CreateJwt(successRes.UserId, successRes.Role, JwtConfig.JwtAccessKey, JwtConfig.JwtAccessExpiry)
|
||||
res := loginCustomerRes{
|
||||
AccessToken: accessToken,
|
||||
RefreshToken: successRes.RfToken,
|
||||
|
|
@ -119,7 +118,7 @@ func RefreshToken(logger *slog.Logger, authSvc *authentication.Service,
|
|||
response.WriteJSON(c, fiber.StatusInternalServerError, "Internal server error", nil, nil)
|
||||
return nil
|
||||
}
|
||||
accessToken, err := jwtutil.CreateJwt("", "", JwtConfig.JwtAccessKey, JwtConfig.JwtAccessExpiry)
|
||||
accessToken, err := jwtutil.CreateJwt(0, "", JwtConfig.JwtAccessKey, JwtConfig.JwtAccessExpiry)
|
||||
if err != nil {
|
||||
logger.Error("Create jwt failed", "error", err)
|
||||
response.WriteJSON(c, fiber.StatusInternalServerError, "Internal server error", nil, nil)
|
||||
|
|
|
|||
365
internal/web_server/handlers/user.go
Normal file
365
internal/web_server/handlers/user.go
Normal file
|
|
@ -0,0 +1,365 @@
|
|||
package handlers
|
||||
|
||||
import (
|
||||
"errors"
|
||||
"log/slog"
|
||||
"time"
|
||||
|
||||
"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 CheckPhoneEmailExistReq struct {
|
||||
Email string `json:"email" example:"john.doe@example.com"`
|
||||
PhoneNumber string `json:"phone_number" 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
|
||||
// @Tags user
|
||||
// @Accept json
|
||||
// @Produce json
|
||||
// @Param checkPhoneEmailExist body CheckPhoneEmailExistReq true "Check phone number or email exist"
|
||||
// @Success 200 {object} CheckPhoneEmailExistRes
|
||||
// @Failure 400 {object} response.APIResponse
|
||||
// @Failure 500 {object} response.APIResponse
|
||||
// @Router /user/checkPhoneEmailExist [post]
|
||||
func CheckPhoneEmailExist(logger *slog.Logger, userSvc *user.Service,
|
||||
validator *customvalidator.CustomValidator) fiber.Handler {
|
||||
return func(c *fiber.Ctx) error {
|
||||
var req CheckPhoneEmailExistReq
|
||||
if err := c.BodyParser(&req); err != nil {
|
||||
logger.Error("CheckPhoneEmailExist 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
|
||||
}
|
||||
emailExist, phoneExist, err := userSvc.CheckPhoneEmailExist(c.Context(), req.PhoneNumber, req.Email)
|
||||
if err != nil {
|
||||
logger.Error("CheckPhoneEmailExist failed", "error", err)
|
||||
return c.Status(fiber.StatusInternalServerError).JSON(fiber.Map{
|
||||
"error": "Internal server error",
|
||||
})
|
||||
}
|
||||
res := CheckPhoneEmailExistRes{
|
||||
EmailExist: emailExist,
|
||||
PhoneNumberExist: phoneExist,
|
||||
}
|
||||
return response.WriteJSON(c, fiber.StatusOK, "Check Success", res, nil)
|
||||
}
|
||||
}
|
||||
|
||||
type RegisterCodeReq struct {
|
||||
Email string `json:"email" example:"john.doe@example.com"`
|
||||
PhoneNumber string `json:"phone_number" example:"1234567890"`
|
||||
}
|
||||
|
||||
// SendRegisterCode godoc
|
||||
// @Summary Send register code
|
||||
// @Description Send register code
|
||||
// @Tags user
|
||||
// @Accept json
|
||||
// @Produce json
|
||||
// @Param registerCode body RegisterCodeReq true "Send register code"
|
||||
// @Success 200 {object} response.APIResponse
|
||||
// @Failure 400 {object} response.APIResponse
|
||||
// @Failure 500 {object} response.APIResponse
|
||||
// @Router /user/sendRegisterCode [post]
|
||||
func SendRegisterCode(logger *slog.Logger, userSvc *user.Service,
|
||||
validator *customvalidator.CustomValidator) fiber.Handler {
|
||||
return func(c *fiber.Ctx) error {
|
||||
var req RegisterCodeReq
|
||||
if err := c.BodyParser(&req); err != nil {
|
||||
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
|
||||
}
|
||||
var sentTo string
|
||||
var medium domain.OtpMedium
|
||||
if req.Email != "" {
|
||||
sentTo = req.Email
|
||||
medium = domain.OtpMediumEmail
|
||||
}
|
||||
if req.PhoneNumber != "" {
|
||||
sentTo = req.PhoneNumber
|
||||
medium = domain.OtpMediumSms
|
||||
}
|
||||
if err := userSvc.SendRegisterCode(c.Context(), medium, sentTo); err != nil {
|
||||
logger.Error("SendRegisterCode failed", "error", err)
|
||||
return c.Status(fiber.StatusInternalServerError).JSON(fiber.Map{
|
||||
"error": "Internal server 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"`
|
||||
//Role string
|
||||
Otp string `json:"otp" example:"123456"`
|
||||
ReferalCode string `json:"referal_code" example:"ABC123"`
|
||||
//
|
||||
|
||||
}
|
||||
|
||||
// RegisterUser godoc
|
||||
// @Summary Register user
|
||||
// @Description Register user
|
||||
// @Tags user
|
||||
// @Accept json
|
||||
// @Produce json
|
||||
// @Param registerUser body RegisterUserReq true "Register user"
|
||||
// @Success 200 {object} response.APIResponse
|
||||
// @Failure 400 {object} response.APIResponse
|
||||
// @Failure 500 {object} response.APIResponse
|
||||
// @Router /user/register [post]
|
||||
func RegisterUser(logger *slog.Logger, userSvc *user.Service,
|
||||
validator *customvalidator.CustomValidator) fiber.Handler {
|
||||
return func(c *fiber.Ctx) error {
|
||||
var req RegisterUserReq
|
||||
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.RegisterUserReq{
|
||||
FirstName: req.FirstName,
|
||||
LastName: req.LastName,
|
||||
Email: req.Email,
|
||||
PhoneNumber: req.PhoneNumber,
|
||||
Password: req.Password,
|
||||
Otp: req.Otp,
|
||||
ReferalCode: req.ReferalCode,
|
||||
OtpMedium: domain.OtpMediumEmail,
|
||||
}
|
||||
medium, err := getMedium(req.Email, req.PhoneNumber)
|
||||
if err != nil {
|
||||
logger.Error("RegisterUser failed", "error", err)
|
||||
return c.Status(fiber.StatusBadRequest).JSON(fiber.Map{
|
||||
"error": err.Error(),
|
||||
})
|
||||
}
|
||||
user.OtpMedium = medium
|
||||
if _, err := userSvc.RegisterUser(c.Context(), user); err != nil {
|
||||
if errors.Is(err, domain.ErrOtpAlreadyUsed) {
|
||||
return response.WriteJSON(c, fiber.StatusBadRequest, "Otp already used", nil, nil)
|
||||
}
|
||||
if errors.Is(err, domain.ErrOtpExpired) {
|
||||
return response.WriteJSON(c, fiber.StatusBadRequest, "Otp expired", nil, nil)
|
||||
}
|
||||
if errors.Is(err, domain.ErrInvalidOtp) {
|
||||
return response.WriteJSON(c, fiber.StatusBadRequest, "Invalid otp", nil, nil)
|
||||
}
|
||||
if errors.Is(err, domain.ErrOtpNotFound) {
|
||||
return response.WriteJSON(c, fiber.StatusBadRequest, "User already exist", nil, nil)
|
||||
}
|
||||
logger.Error("RegisterUser failed", "error", err)
|
||||
return c.Status(fiber.StatusInternalServerError).JSON(fiber.Map{
|
||||
"error": "Internal server error",
|
||||
})
|
||||
}
|
||||
return response.WriteJSON(c, fiber.StatusOK, "Registration successful", nil, nil)
|
||||
}
|
||||
}
|
||||
|
||||
type ResetCodeReq struct {
|
||||
Email string `json:"email" example:"john.doe@example.com"`
|
||||
PhoneNumber string `json:"phone_number" example:"1234567890"`
|
||||
}
|
||||
|
||||
// SendResetCode godoc
|
||||
// @Summary Send reset code
|
||||
// @Description Send reset code
|
||||
// @Tags user
|
||||
// @Accept json
|
||||
// @Produce json
|
||||
// @Param resetCode body ResetCodeReq true "Send reset code"
|
||||
// @Success 200 {object} response.APIResponse
|
||||
// @Failure 400 {object} response.APIResponse
|
||||
// @Failure 500 {object} response.APIResponse
|
||||
// @Router /user/sendResetCode [post]
|
||||
func SendResetCode(logger *slog.Logger, userSvc *user.Service,
|
||||
validator *customvalidator.CustomValidator) fiber.Handler {
|
||||
return func(c *fiber.Ctx) error {
|
||||
var req ResetCodeReq
|
||||
if err := c.BodyParser(&req); err != nil {
|
||||
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
|
||||
}
|
||||
var sentTo string
|
||||
var medium domain.OtpMedium
|
||||
if req.Email != "" {
|
||||
sentTo = req.Email
|
||||
medium = domain.OtpMediumEmail
|
||||
}
|
||||
if req.PhoneNumber != "" {
|
||||
sentTo = req.PhoneNumber
|
||||
medium = domain.OtpMediumSms
|
||||
}
|
||||
if err := userSvc.SendResetCode(c.Context(), medium, sentTo); err != nil {
|
||||
logger.Error("SendResetCode failed", "error", err)
|
||||
return c.Status(fiber.StatusInternalServerError).JSON(fiber.Map{
|
||||
"error": "Internal server error",
|
||||
})
|
||||
}
|
||||
return response.WriteJSON(c, fiber.StatusOK, "Code sent successfully", nil, nil)
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
type ResetPasswordReq struct {
|
||||
Email string
|
||||
PhoneNumber string
|
||||
Password string
|
||||
Otp string
|
||||
}
|
||||
|
||||
// ResetPassword godoc
|
||||
// @Summary Reset password
|
||||
// @Description Reset password
|
||||
// @Tags user
|
||||
// @Accept json
|
||||
// @Produce json
|
||||
// @Param resetPassword body ResetPasswordReq true "Reset password"
|
||||
// @Success 200 {object} response.APIResponse
|
||||
// @Failure 400 {object} response.APIResponse
|
||||
// @Failure 500 {object} response.APIResponse
|
||||
// @Router /user/resetPassword [post]
|
||||
func ResetPassword(logger *slog.Logger, userSvc *user.Service,
|
||||
validator *customvalidator.CustomValidator) fiber.Handler {
|
||||
return func(c *fiber.Ctx) error {
|
||||
var req ResetPasswordReq
|
||||
if err := c.BodyParser(&req); err != nil {
|
||||
logger.Error("ResetPassword 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.ResetPasswordReq{
|
||||
Email: req.Email,
|
||||
PhoneNumber: req.PhoneNumber,
|
||||
Password: req.Password,
|
||||
Otp: req.Otp,
|
||||
}
|
||||
medium, err := getMedium(req.Email, req.PhoneNumber)
|
||||
if err != nil {
|
||||
logger.Error("ResetPassword failed", "error", err)
|
||||
return c.Status(fiber.StatusBadRequest).JSON(fiber.Map{
|
||||
"error": err.Error(),
|
||||
})
|
||||
}
|
||||
user.OtpMedium = medium
|
||||
if err := userSvc.ResetPassword(c.Context(), user); err != nil {
|
||||
logger.Error("ResetPassword failed", "error", err)
|
||||
return c.Status(fiber.StatusInternalServerError).JSON(fiber.Map{
|
||||
"error": "Internal server error",
|
||||
})
|
||||
}
|
||||
return response.WriteJSON(c, fiber.StatusOK, "Password reset successful", nil, nil)
|
||||
}
|
||||
}
|
||||
|
||||
type UserProfileRes 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"`
|
||||
SuspendedAt time.Time `json:"suspended_at"`
|
||||
Suspended bool `json:"suspended"`
|
||||
}
|
||||
|
||||
// UserProfile godoc
|
||||
// @Summary Get user profile
|
||||
// @Description Get user profile
|
||||
// @Tags user
|
||||
// @Accept json
|
||||
// @Produce json
|
||||
// @Success 200 {object} UserProfileRes
|
||||
// @Failure 400 {object} response.APIResponse
|
||||
// @Failure 500 {object} response.APIResponse
|
||||
// @Security Bearer
|
||||
// @Router /user/profile [get]
|
||||
func UserProfile(logger *slog.Logger, userSvc *user.Service) fiber.Handler {
|
||||
return func(c *fiber.Ctx) error {
|
||||
userId := c.Locals("user_id").(int64)
|
||||
user, err := userSvc.GetUserByID(c.Context(), userId)
|
||||
if err != nil {
|
||||
logger.Error("GetUserProfile failed", "error", err)
|
||||
return c.Status(fiber.StatusInternalServerError).JSON(fiber.Map{
|
||||
"error": "Internal server error",
|
||||
})
|
||||
}
|
||||
|
||||
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,
|
||||
}
|
||||
return response.WriteJSON(c, fiber.StatusOK, "User profile retrieved successfully", res, nil)
|
||||
}
|
||||
}
|
||||
func getMedium(email, phoneNumber string) (domain.OtpMedium, error) {
|
||||
if email != "" {
|
||||
return domain.OtpMediumEmail, nil
|
||||
}
|
||||
if phoneNumber != "" {
|
||||
return domain.OtpMediumSms, nil
|
||||
}
|
||||
return "", errors.New("both email and phone number are empty")
|
||||
}
|
||||
|
|
@ -20,7 +20,7 @@ var (
|
|||
|
||||
type UserClaim struct {
|
||||
jwt.RegisteredClaims
|
||||
UserId string
|
||||
UserId int64
|
||||
Role domain.Role
|
||||
}
|
||||
type JwtConfig struct {
|
||||
|
|
@ -28,7 +28,7 @@ type JwtConfig struct {
|
|||
JwtAccessExpiry int
|
||||
}
|
||||
|
||||
func CreateJwt(userId string, Role domain.Role, key string, expiry int) (string, error) {
|
||||
func CreateJwt(userId int64, Role domain.Role, key string, expiry int) (string, error) {
|
||||
token := jwt.NewWithClaims(jwt.SigningMethodHS256, UserClaim{RegisteredClaims: jwt.RegisteredClaims{Issuer: "github.com/lafetz/snippitstash",
|
||||
IssuedAt: jwt.NewNumericDate(time.Now()),
|
||||
Audience: jwt.ClaimStrings{"fortune.com"},
|
||||
|
|
|
|||
|
|
@ -34,7 +34,7 @@ func (a *App) authMiddleware(c *fiber.Ctx) error {
|
|||
|
||||
// refreshToken = c.Cookies("refresh_token", "")
|
||||
|
||||
return fiber.NewError(fiber.StatusUnauthorized, "Refresh token missing")
|
||||
// return fiber.NewError(fiber.StatusUnauthorized, "Refresh token missing")
|
||||
}
|
||||
c.Locals("user_id", claim.UserId)
|
||||
c.Locals("role", claim.Role)
|
||||
|
|
|
|||
|
|
@ -20,5 +20,19 @@ func (a *App) initAppRoutes() {
|
|||
a.logger.Info("Refresh Token: " + refreshToken.(string))
|
||||
return c.SendString("Test endpoint")
|
||||
})
|
||||
a.fiber.Post("/user/resetPassword", handlers.ResetPassword(a.logger, a.userSvc, a.validator))
|
||||
a.fiber.Post("/user/sendResetCode", handlers.SendResetCode(a.logger, a.userSvc, a.validator))
|
||||
a.fiber.Post("/user/register", handlers.RegisterUser(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.Get("/user/profile", a.authMiddleware, handlers.UserProfile(a.logger, a.userSvc))
|
||||
// Swagger
|
||||
a.fiber.Get("/swagger/*", fiberSwagger.WrapHandler)
|
||||
}
|
||||
|
||||
///user/profile get
|
||||
// @Router /user/resetPassword [post]
|
||||
// @Router /user/sendResetCode [post]
|
||||
// @Router /user/register [post]
|
||||
// @Router /user/sendRegisterCode [post]
|
||||
// @Router /user/checkPhoneEmailExist [post]
|
||||
|
|
|
|||
Loading…
Reference in New Issue
Block a user