add user management functionality with OTP verification and consent handling, DI introduced
This commit is contained in:
51
internal/api/auth/domain/auth_domain.go
Normal file
51
internal/api/auth/domain/auth_domain.go
Normal file
@ -0,0 +1,51 @@
|
||||
package domain
|
||||
|
||||
import "context"
|
||||
|
||||
type AuthRepository interface {
|
||||
SaveOtpRequest(ctx context.Context, uuid string, code string) error
|
||||
GetOtpRequest(ctx context.Context, uuid string) (*string, error)
|
||||
}
|
||||
|
||||
type ErrOtpNotFound struct {
|
||||
Uuid string
|
||||
}
|
||||
|
||||
func (e ErrOtpNotFound) Error() string {
|
||||
return "OTP request not found for UUID: " + e.Uuid
|
||||
}
|
||||
|
||||
type ErrUserNotFound struct {
|
||||
PhoneNumber string
|
||||
}
|
||||
|
||||
func (e ErrUserNotFound) Error() string {
|
||||
return "User not found with phone number: " + e.PhoneNumber
|
||||
}
|
||||
|
||||
type ErrOtpInvalid struct {
|
||||
Code string
|
||||
Uuid string
|
||||
}
|
||||
|
||||
func (e ErrOtpInvalid) Error() string {
|
||||
return "Invalid OTP code: " + e.Code + " for UUID: " + e.Uuid
|
||||
}
|
||||
|
||||
type ErrInvalidHydraAccept struct {
|
||||
Message string
|
||||
Uuid string
|
||||
}
|
||||
|
||||
func (e ErrInvalidHydraAccept) Error() string {
|
||||
return "Invalid Hydra accept request: " + e.Message + " for UUID: " + e.Uuid
|
||||
}
|
||||
|
||||
type ErrInvalidPhoneNumber struct {
|
||||
PhoneNumber string
|
||||
Err error
|
||||
}
|
||||
|
||||
func (e ErrInvalidPhoneNumber) Error() string {
|
||||
return "Invalid phone number: " + e.PhoneNumber + ", error: " + e.Err.Error()
|
||||
}
|
||||
20
internal/api/auth/fx.go
Normal file
20
internal/api/auth/fx.go
Normal file
@ -0,0 +1,20 @@
|
||||
package auth
|
||||
|
||||
import (
|
||||
"git.logidex.ru/fakz9/logidex-id/internal/api/auth/handler"
|
||||
"git.logidex.ru/fakz9/logidex-id/internal/api/auth/repo"
|
||||
"git.logidex.ru/fakz9/logidex-id/internal/api/auth/service"
|
||||
"github.com/gofiber/fiber/v2"
|
||||
"go.uber.org/fx"
|
||||
)
|
||||
|
||||
var Module = fx.Options(
|
||||
fx.Provide(
|
||||
repo.NewAuthRepo,
|
||||
service.NewAuthService,
|
||||
handler.NewAuthHandler,
|
||||
),
|
||||
fx.Invoke(func(handler *handler.AuthHandler, router fiber.Router) {
|
||||
handler.RegisterRoutes(router)
|
||||
}),
|
||||
)
|
||||
@ -14,6 +14,9 @@ import (
|
||||
type AcceptConsentRequest struct {
|
||||
// ConsentChallenge The consent challenge to accept
|
||||
ConsentChallenge string `json:"consent_challenge"`
|
||||
|
||||
// PhoneNumber Phone number associated with the consent
|
||||
PhoneNumber string `json:"phone_number"`
|
||||
}
|
||||
|
||||
// AcceptConsentResponse defines model for AcceptConsentResponse.
|
||||
@ -56,6 +59,9 @@ type VerifyOTPRequest struct {
|
||||
|
||||
// VerifyOTPResponse defines model for VerifyOTPResponse.
|
||||
type VerifyOTPResponse struct {
|
||||
// Message Confirmation message
|
||||
Message string `json:"message"`
|
||||
|
||||
// Ok Status of the verification
|
||||
Ok bool `json:"ok"`
|
||||
|
||||
|
||||
@ -2,113 +2,68 @@ package handler
|
||||
|
||||
import (
|
||||
"context"
|
||||
"fmt"
|
||||
"git.logidex.ru/fakz9/logidex-id/internal/hydra_client"
|
||||
"git.logidex.ru/fakz9/logidex-id/internal/redis"
|
||||
|
||||
"git.logidex.ru/fakz9/logidex-id/internal/api/auth/service"
|
||||
"github.com/gofiber/fiber/v2"
|
||||
hydraApi "github.com/ory/hydra-client-go"
|
||||
)
|
||||
|
||||
type AuthHandler struct{}
|
||||
|
||||
func (a AuthHandler) PostAuthConsentAccept(ctx context.Context, request PostAuthConsentAcceptRequestObject) (PostAuthConsentAcceptResponseObject, error) {
|
||||
hydraClient := hydra_client.GetClient()
|
||||
|
||||
hydraRequest := hydraApi.AcceptConsentRequest{}
|
||||
hydraRequest.SetGrantScope([]string{"openid"})
|
||||
hydraRequest.SetRemember(true)
|
||||
hydraRequest.SetRememberFor(3600) // 1 hour
|
||||
|
||||
hydraResponse, r, err := hydraClient.AdminApi.
|
||||
AcceptConsentRequest(ctx).
|
||||
ConsentChallenge(request.Body.ConsentChallenge).
|
||||
AcceptConsentRequest(hydraRequest).
|
||||
Execute()
|
||||
if err != nil {
|
||||
return PostAuthConsentAccept400JSONResponse{
|
||||
RedirectUrl: "",
|
||||
Ok: false,
|
||||
Message: "Failed to accept consent request",
|
||||
}, nil
|
||||
}
|
||||
fmt.Println(r)
|
||||
return PostAuthConsentAccept200JSONResponse{
|
||||
RedirectUrl: hydraResponse.RedirectTo,
|
||||
Ok: true,
|
||||
Message: "Успешно",
|
||||
}, nil
|
||||
type AuthHandler struct {
|
||||
service service.AuthService
|
||||
}
|
||||
|
||||
func (a AuthHandler) PostAuthOtpRequest(ctx context.Context, request PostAuthOtpRequestRequestObject) (PostAuthOtpRequestResponseObject, error) {
|
||||
redisClient := redis.GetClient()
|
||||
|
||||
// TODO implement OTP request logic
|
||||
|
||||
err := redisClient.Do(ctx, redisClient.B().Set().Key("otp:"+request.Body.PhoneNumber).Value("123456").Build()).Error()
|
||||
func (h AuthHandler) PostAuthOtpRequest(ctx context.Context, request PostAuthOtpRequestRequestObject) (PostAuthOtpRequestResponseObject, error) {
|
||||
err := h.service.OtpRequest(ctx, request.Body.PhoneNumber)
|
||||
if err != nil {
|
||||
return PostAuthOtpRequest400JSONResponse{
|
||||
Message: "Failed to set OTP in Redis",
|
||||
Message: err.Error(),
|
||||
Ok: false,
|
||||
}, nil
|
||||
}
|
||||
|
||||
return PostAuthOtpRequest200JSONResponse{
|
||||
Message: "Код успешно отправлен",
|
||||
Message: "OTP request successful",
|
||||
Ok: true,
|
||||
}, nil
|
||||
}
|
||||
|
||||
func (a AuthHandler) PostAuthOtpVerify(ctx context.Context, request PostAuthOtpVerifyRequestObject) (PostAuthOtpVerifyResponseObject, error) {
|
||||
redisClient := redis.GetClient()
|
||||
hydraClient := hydra_client.GetClient()
|
||||
|
||||
sentOtp, err := redisClient.Do(ctx, redisClient.B().Get().Key("otp:"+request.Body.PhoneNumber).Build()).ToString()
|
||||
func (h AuthHandler) PostAuthOtpVerify(ctx context.Context, request PostAuthOtpVerifyRequestObject) (PostAuthOtpVerifyResponseObject, error) {
|
||||
redirectUrl, err := h.service.OtpVerify(ctx, request.Body.PhoneNumber, request.Body.Otp, request.Body.LoginChallenge)
|
||||
if err != nil {
|
||||
return PostAuthOtpVerify400JSONResponse{
|
||||
RedirectUrl: "",
|
||||
Message: err.Error(),
|
||||
Ok: false,
|
||||
}, nil
|
||||
}
|
||||
if sentOtp != request.Body.Otp {
|
||||
return PostAuthOtpVerify400JSONResponse{
|
||||
RedirectUrl: "",
|
||||
Ok: false,
|
||||
}, nil
|
||||
}
|
||||
hydraRequest := hydraApi.AcceptLoginRequest{}
|
||||
// TODO read user from database by phone number
|
||||
|
||||
hydraRequest.SetSubject("some-user-id") // Replace with actual user ID
|
||||
hydraRequest.SetRemember(true)
|
||||
hydraRequest.SetRememberFor(3600) // 1 hour
|
||||
|
||||
hydraResponse, r, err := hydraClient.AdminApi.
|
||||
AcceptLoginRequest(ctx).
|
||||
LoginChallenge(request.Body.LoginChallenge).
|
||||
AcceptLoginRequest(hydraRequest).
|
||||
Execute()
|
||||
fmt.Println(r)
|
||||
if err != nil {
|
||||
return PostAuthOtpVerify400JSONResponse{
|
||||
RedirectUrl: "",
|
||||
Ok: false,
|
||||
}, nil
|
||||
}
|
||||
return PostAuthOtpVerify200JSONResponse{
|
||||
RedirectUrl: hydraResponse.RedirectTo,
|
||||
Message: "OTP verification successful",
|
||||
Ok: true,
|
||||
RedirectUrl: redirectUrl,
|
||||
}, nil
|
||||
}
|
||||
|
||||
func (h AuthHandler) PostAuthConsentAccept(ctx context.Context, request PostAuthConsentAcceptRequestObject) (PostAuthConsentAcceptResponseObject, error) {
|
||||
redirectUrl, err := h.service.AcceptConsent(ctx, request.Body.PhoneNumber, request.Body.ConsentChallenge)
|
||||
if err != nil {
|
||||
return PostAuthConsentAccept400JSONResponse{
|
||||
Message: err.Error(),
|
||||
Ok: false,
|
||||
RedirectUrl: "",
|
||||
}, nil
|
||||
}
|
||||
return PostAuthConsentAccept200JSONResponse{
|
||||
Message: "Consent accepted successfully",
|
||||
Ok: true,
|
||||
RedirectUrl: redirectUrl,
|
||||
}, nil
|
||||
}
|
||||
|
||||
var _ StrictServerInterface = (*AuthHandler)(nil)
|
||||
|
||||
func NewAuthHandler() *AuthHandler {
|
||||
return &AuthHandler{}
|
||||
func NewAuthHandler(service service.AuthService) *AuthHandler {
|
||||
return &AuthHandler{service: service}
|
||||
}
|
||||
|
||||
func RegisterApp(router fiber.Router) {
|
||||
//authGroup := router.Group("/auth")
|
||||
server := NewStrictHandler(NewAuthHandler(), nil)
|
||||
func (h AuthHandler) RegisterRoutes(router fiber.Router) {
|
||||
server := NewStrictHandler(h, nil)
|
||||
RegisterHandlers(router, server)
|
||||
|
||||
}
|
||||
|
||||
45
internal/api/auth/repo/auth_repo.go
Normal file
45
internal/api/auth/repo/auth_repo.go
Normal file
@ -0,0 +1,45 @@
|
||||
package repo
|
||||
|
||||
import (
|
||||
"context"
|
||||
"time"
|
||||
|
||||
"git.logidex.ru/fakz9/logidex-id/internal/api/auth/domain"
|
||||
"github.com/redis/rueidis"
|
||||
)
|
||||
|
||||
type authRepo struct {
|
||||
redisClient rueidis.Client
|
||||
}
|
||||
|
||||
func (a authRepo) GetOtpRequest(ctx context.Context, uuid string) (*string, error) {
|
||||
redisClient := a.redisClient
|
||||
|
||||
resp, err := redisClient.Do(ctx, redisClient.B().Get().Key("otp:"+uuid).Build()).ToString()
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
if resp == "" {
|
||||
// create error
|
||||
return nil, &domain.ErrOtpNotFound{Uuid: uuid}
|
||||
}
|
||||
|
||||
return &resp, nil
|
||||
}
|
||||
|
||||
func (a authRepo) SaveOtpRequest(ctx context.Context, uuid string, code string) error {
|
||||
redisClient := a.redisClient
|
||||
|
||||
err := redisClient.Do(ctx, redisClient.B().Set().Key("otp:"+uuid).Value(code).Ex(120*time.Second).Build()).Error()
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
return nil
|
||||
|
||||
}
|
||||
|
||||
func NewAuthRepo(redisClient rueidis.Client) domain.AuthRepository {
|
||||
return &authRepo{redisClient: redisClient}
|
||||
}
|
||||
167
internal/api/auth/service/auth_service.go
Normal file
167
internal/api/auth/service/auth_service.go
Normal file
@ -0,0 +1,167 @@
|
||||
package service
|
||||
|
||||
import (
|
||||
"context"
|
||||
"strconv"
|
||||
|
||||
"git.logidex.ru/fakz9/logidex-id/internal/api/auth/domain"
|
||||
userDomain "git.logidex.ru/fakz9/logidex-id/internal/api/user/domain"
|
||||
"git.logidex.ru/fakz9/logidex-id/internal/phoneutil"
|
||||
hydraApi "github.com/ory/hydra-client-go"
|
||||
)
|
||||
|
||||
type AuthService interface {
|
||||
OtpRequest(ctx context.Context, phoneNumber string) error
|
||||
OtpVerify(ctx context.Context, phoneNumber, code string, loggingChallenge string) (string, error)
|
||||
AcceptConsent(ctx context.Context, phoneNumber string, challenge string) (string, error)
|
||||
}
|
||||
|
||||
type authService struct {
|
||||
repo domain.AuthRepository
|
||||
userRepo userDomain.UserRepository
|
||||
hydraClient *hydraApi.APIClient
|
||||
}
|
||||
|
||||
func (a authService) AcceptConsent(ctx context.Context, phoneNumber string, challenge string) (string, error) {
|
||||
phoneNumber, err := phoneutil.ParseAndFormatPhoneNumber(phoneNumber)
|
||||
if err != nil {
|
||||
return "", domain.ErrInvalidPhoneNumber{
|
||||
PhoneNumber: phoneNumber,
|
||||
Err: err,
|
||||
}
|
||||
}
|
||||
user, err := a.userRepo.GetUserByPhoneNumber(ctx, phoneNumber)
|
||||
if err != nil {
|
||||
return "", err
|
||||
}
|
||||
if user == nil {
|
||||
return "", domain.ErrUserNotFound{PhoneNumber: phoneNumber}
|
||||
}
|
||||
request := hydraApi.AcceptConsentRequest{}
|
||||
request.SetGrantScope([]string{"openid"})
|
||||
request.SetRemember(true)
|
||||
request.SetRememberFor(3600)
|
||||
|
||||
rsp, rawRsp, err := a.hydraClient.AdminApi.
|
||||
AcceptConsentRequest(ctx).
|
||||
ConsentChallenge(challenge).
|
||||
AcceptConsentRequest(request).
|
||||
Execute()
|
||||
if err != nil {
|
||||
return "", err
|
||||
}
|
||||
if rawRsp.StatusCode != 200 {
|
||||
return "", domain.ErrInvalidHydraAccept{
|
||||
Message: "Hydra response is nil: " + strconv.Itoa(rawRsp.StatusCode),
|
||||
Uuid: "",
|
||||
}
|
||||
}
|
||||
redirectTo, ok := rsp.GetRedirectToOk()
|
||||
if !ok || redirectTo == nil {
|
||||
return "", domain.ErrInvalidHydraAccept{
|
||||
Message: "Hydra redirectTo is nil",
|
||||
Uuid: "",
|
||||
}
|
||||
}
|
||||
// TODO: Verify user in the database
|
||||
_, err = a.userRepo.VerifyUser(ctx, user.Uuid.String())
|
||||
if err != nil {
|
||||
return "", err
|
||||
}
|
||||
return *redirectTo, nil
|
||||
}
|
||||
|
||||
func (a authService) OtpRequest(ctx context.Context, phoneNumber string) error {
|
||||
phoneNumber, err := phoneutil.ParseAndFormatPhoneNumber(phoneNumber)
|
||||
if err != nil {
|
||||
return domain.ErrInvalidPhoneNumber{
|
||||
PhoneNumber: phoneNumber,
|
||||
Err: err,
|
||||
}
|
||||
}
|
||||
|
||||
user, err := a.userRepo.GetUserByPhoneNumber(ctx, phoneNumber)
|
||||
//if err != nil {
|
||||
// return err
|
||||
//}
|
||||
|
||||
if user == nil {
|
||||
// Create a new user if it does not exist
|
||||
user, err = a.userRepo.CreateUser(ctx, phoneNumber)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
code := "123456"
|
||||
err = a.repo.SaveOtpRequest(ctx, user.Uuid.String(), code)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
// TODO implement sending OTP code via SMS
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
func (a authService) OtpVerify(ctx context.Context, phoneNumber string, code string, loggingChallenge string) (string, error) {
|
||||
phoneNumber, err := phoneutil.ParseAndFormatPhoneNumber(phoneNumber)
|
||||
if err != nil {
|
||||
return "", domain.ErrInvalidPhoneNumber{
|
||||
PhoneNumber: phoneNumber,
|
||||
Err: err,
|
||||
}
|
||||
}
|
||||
|
||||
user, err := a.userRepo.GetUserByPhoneNumber(ctx, phoneNumber)
|
||||
if err != nil {
|
||||
return "", err
|
||||
}
|
||||
if user == nil {
|
||||
return "", domain.ErrUserNotFound{PhoneNumber: phoneNumber}
|
||||
}
|
||||
otp, err := a.repo.GetOtpRequest(ctx, user.Uuid.String())
|
||||
if err != nil {
|
||||
return "", err
|
||||
}
|
||||
if otp == nil {
|
||||
return "", domain.ErrOtpNotFound{Uuid: user.Uuid.String()}
|
||||
}
|
||||
if *otp != code {
|
||||
return "", domain.ErrOtpInvalid{Uuid: user.Uuid.String(), Code: code}
|
||||
}
|
||||
request := hydraApi.AcceptLoginRequest{}
|
||||
request.SetSubject(user.Uuid.String())
|
||||
request.SetRemember(true)
|
||||
request.SetRememberFor(3600) // 1 hour
|
||||
|
||||
rsp, rawRsp, err := a.hydraClient.AdminApi.
|
||||
AcceptLoginRequest(ctx).
|
||||
LoginChallenge(loggingChallenge).
|
||||
AcceptLoginRequest(request).
|
||||
Execute()
|
||||
if err != nil {
|
||||
return "", err
|
||||
}
|
||||
if rawRsp.StatusCode != 200 {
|
||||
return "", domain.ErrInvalidHydraAccept{
|
||||
Message: "Hydra response is nil: " + strconv.Itoa(rawRsp.StatusCode),
|
||||
Uuid: user.Uuid.String(),
|
||||
}
|
||||
}
|
||||
|
||||
redirectTo, ok := rsp.GetRedirectToOk()
|
||||
if !ok || redirectTo == nil {
|
||||
return "", domain.ErrInvalidHydraAccept{
|
||||
Message: "Hydra redirectTo is nil",
|
||||
Uuid: user.Uuid.String(),
|
||||
}
|
||||
}
|
||||
return *redirectTo, nil
|
||||
}
|
||||
|
||||
func NewAuthService(repo domain.AuthRepository, userRepo userDomain.UserRepository, hydraClient *hydraApi.APIClient) AuthService {
|
||||
return &authService{
|
||||
repo: repo,
|
||||
userRepo: userRepo,
|
||||
hydraClient: hydraClient,
|
||||
}
|
||||
}
|
||||
27
internal/api/user/domain/user_domain.go
Normal file
27
internal/api/user/domain/user_domain.go
Normal file
@ -0,0 +1,27 @@
|
||||
package domain
|
||||
|
||||
import (
|
||||
"context"
|
||||
|
||||
db "git.logidex.ru/fakz9/logidex-id/internal/db/generated"
|
||||
)
|
||||
|
||||
type User struct {
|
||||
Uuid string `json:"uuid"`
|
||||
PhoneNumber string `json:"phone_number"`
|
||||
}
|
||||
|
||||
type UserRepository interface {
|
||||
GetUserByPhoneNumber(ctx context.Context, phoneNumber string) (*db.User, error)
|
||||
GetUserByUuid(ctx context.Context, uuid string) (*db.User, error)
|
||||
CreateUser(ctx context.Context, phoneNumber string) (*db.User, error)
|
||||
VerifyUser(ctx context.Context, uuid string) (*db.User, error)
|
||||
}
|
||||
|
||||
type ErrUserNotFound struct {
|
||||
PhoneNumber string
|
||||
}
|
||||
|
||||
func (e ErrUserNotFound) Error() string {
|
||||
return "User not found with phone number: " + e.PhoneNumber
|
||||
}
|
||||
20
internal/api/user/fx.go
Normal file
20
internal/api/user/fx.go
Normal file
@ -0,0 +1,20 @@
|
||||
package user
|
||||
|
||||
import (
|
||||
"git.logidex.ru/fakz9/logidex-id/internal/api/user/handler"
|
||||
"git.logidex.ru/fakz9/logidex-id/internal/api/user/repo"
|
||||
"git.logidex.ru/fakz9/logidex-id/internal/api/user/service"
|
||||
"github.com/gofiber/fiber/v2"
|
||||
"go.uber.org/fx"
|
||||
)
|
||||
|
||||
var Module = fx.Options(
|
||||
fx.Provide(
|
||||
repo.NewUserRepo,
|
||||
service.NewUserService,
|
||||
handler.NewUserHandler,
|
||||
),
|
||||
fx.Invoke(func(handler *handler.UserHandler, router fiber.Router) {
|
||||
handler.RegisterRoutes(router)
|
||||
}),
|
||||
)
|
||||
@ -9,52 +9,30 @@ import (
|
||||
|
||||
"github.com/gofiber/fiber/v2"
|
||||
"github.com/oapi-codegen/runtime"
|
||||
openapi_types "github.com/oapi-codegen/runtime/types"
|
||||
)
|
||||
|
||||
// User defines model for User.
|
||||
type User struct {
|
||||
Email openapi_types.Email `json:"email"`
|
||||
Id string `json:"id"`
|
||||
Username string `json:"username"`
|
||||
PhoneNumber string `json:"phone_number"`
|
||||
Uuid string `json:"uuid"`
|
||||
}
|
||||
|
||||
// UserCreate defines model for UserCreate.
|
||||
type UserCreate struct {
|
||||
Email openapi_types.Email `json:"email"`
|
||||
Password string `json:"password"`
|
||||
Username string `json:"username"`
|
||||
PhoneNumber string `json:"phone_number"`
|
||||
}
|
||||
|
||||
// UserUpdate defines model for UserUpdate.
|
||||
type UserUpdate struct {
|
||||
Email openapi_types.Email `json:"email"`
|
||||
Username string `json:"username"`
|
||||
}
|
||||
|
||||
// PostUsersJSONRequestBody defines body for PostUsers for application/json ContentType.
|
||||
type PostUsersJSONRequestBody = UserCreate
|
||||
|
||||
// PutUsersUserIdJSONRequestBody defines body for PutUsersUserId for application/json ContentType.
|
||||
type PutUsersUserIdJSONRequestBody = UserUpdate
|
||||
// CreateUserJSONRequestBody defines body for CreateUser for application/json ContentType.
|
||||
type CreateUserJSONRequestBody = UserCreate
|
||||
|
||||
// ServerInterface represents all server handlers.
|
||||
type ServerInterface interface {
|
||||
// Get all users
|
||||
// (GET /users)
|
||||
GetUsers(c *fiber.Ctx) error
|
||||
// Create a new user
|
||||
// Create a new user with phone number
|
||||
// (POST /users)
|
||||
PostUsers(c *fiber.Ctx) error
|
||||
// Delete a user by ID
|
||||
// (DELETE /users/{userId})
|
||||
DeleteUsersUserId(c *fiber.Ctx, userId string) error
|
||||
CreateUser(c *fiber.Ctx) error
|
||||
// Get a user by ID
|
||||
// (GET /users/{userId})
|
||||
GetUsersUserId(c *fiber.Ctx, userId string) error
|
||||
// Update a user by ID
|
||||
// (PUT /users/{userId})
|
||||
PutUsersUserId(c *fiber.Ctx, userId string) error
|
||||
GetUserById(c *fiber.Ctx, userId string) error
|
||||
}
|
||||
|
||||
// ServerInterfaceWrapper converts contexts to parameters.
|
||||
@ -64,20 +42,14 @@ type ServerInterfaceWrapper struct {
|
||||
|
||||
type MiddlewareFunc fiber.Handler
|
||||
|
||||
// GetUsers operation middleware
|
||||
func (siw *ServerInterfaceWrapper) GetUsers(c *fiber.Ctx) error {
|
||||
// CreateUser operation middleware
|
||||
func (siw *ServerInterfaceWrapper) CreateUser(c *fiber.Ctx) error {
|
||||
|
||||
return siw.Handler.GetUsers(c)
|
||||
return siw.Handler.CreateUser(c)
|
||||
}
|
||||
|
||||
// PostUsers operation middleware
|
||||
func (siw *ServerInterfaceWrapper) PostUsers(c *fiber.Ctx) error {
|
||||
|
||||
return siw.Handler.PostUsers(c)
|
||||
}
|
||||
|
||||
// DeleteUsersUserId operation middleware
|
||||
func (siw *ServerInterfaceWrapper) DeleteUsersUserId(c *fiber.Ctx) error {
|
||||
// GetUserById operation middleware
|
||||
func (siw *ServerInterfaceWrapper) GetUserById(c *fiber.Ctx) error {
|
||||
|
||||
var err error
|
||||
|
||||
@ -89,39 +61,7 @@ func (siw *ServerInterfaceWrapper) DeleteUsersUserId(c *fiber.Ctx) error {
|
||||
return fiber.NewError(fiber.StatusBadRequest, fmt.Errorf("Invalid format for parameter userId: %w", err).Error())
|
||||
}
|
||||
|
||||
return siw.Handler.DeleteUsersUserId(c, userId)
|
||||
}
|
||||
|
||||
// GetUsersUserId operation middleware
|
||||
func (siw *ServerInterfaceWrapper) GetUsersUserId(c *fiber.Ctx) error {
|
||||
|
||||
var err error
|
||||
|
||||
// ------------- Path parameter "userId" -------------
|
||||
var userId string
|
||||
|
||||
err = runtime.BindStyledParameterWithOptions("simple", "userId", c.Params("userId"), &userId, runtime.BindStyledParameterOptions{Explode: false, Required: true})
|
||||
if err != nil {
|
||||
return fiber.NewError(fiber.StatusBadRequest, fmt.Errorf("Invalid format for parameter userId: %w", err).Error())
|
||||
}
|
||||
|
||||
return siw.Handler.GetUsersUserId(c, userId)
|
||||
}
|
||||
|
||||
// PutUsersUserId operation middleware
|
||||
func (siw *ServerInterfaceWrapper) PutUsersUserId(c *fiber.Ctx) error {
|
||||
|
||||
var err error
|
||||
|
||||
// ------------- Path parameter "userId" -------------
|
||||
var userId string
|
||||
|
||||
err = runtime.BindStyledParameterWithOptions("simple", "userId", c.Params("userId"), &userId, runtime.BindStyledParameterOptions{Explode: false, Required: true})
|
||||
if err != nil {
|
||||
return fiber.NewError(fiber.StatusBadRequest, fmt.Errorf("Invalid format for parameter userId: %w", err).Error())
|
||||
}
|
||||
|
||||
return siw.Handler.PutUsersUserId(c, userId)
|
||||
return siw.Handler.GetUserById(c, userId)
|
||||
}
|
||||
|
||||
// FiberServerOptions provides options for the Fiber server.
|
||||
@ -145,143 +85,67 @@ func RegisterHandlersWithOptions(router fiber.Router, si ServerInterface, option
|
||||
router.Use(fiber.Handler(m))
|
||||
}
|
||||
|
||||
router.Get(options.BaseURL+"/users", wrapper.GetUsers)
|
||||
router.Post(options.BaseURL+"/users", wrapper.CreateUser)
|
||||
|
||||
router.Post(options.BaseURL+"/users", wrapper.PostUsers)
|
||||
|
||||
router.Delete(options.BaseURL+"/users/:userId", wrapper.DeleteUsersUserId)
|
||||
|
||||
router.Get(options.BaseURL+"/users/:userId", wrapper.GetUsersUserId)
|
||||
|
||||
router.Put(options.BaseURL+"/users/:userId", wrapper.PutUsersUserId)
|
||||
router.Get(options.BaseURL+"/users/:userId", wrapper.GetUserById)
|
||||
|
||||
}
|
||||
|
||||
type GetUsersRequestObject struct {
|
||||
type CreateUserRequestObject struct {
|
||||
Body *CreateUserJSONRequestBody
|
||||
}
|
||||
|
||||
type GetUsersResponseObject interface {
|
||||
VisitGetUsersResponse(ctx *fiber.Ctx) error
|
||||
type CreateUserResponseObject interface {
|
||||
VisitCreateUserResponse(ctx *fiber.Ctx) error
|
||||
}
|
||||
|
||||
type GetUsers200JSONResponse []User
|
||||
type CreateUser200JSONResponse User
|
||||
|
||||
func (response GetUsers200JSONResponse) VisitGetUsersResponse(ctx *fiber.Ctx) error {
|
||||
func (response CreateUser200JSONResponse) VisitCreateUserResponse(ctx *fiber.Ctx) error {
|
||||
ctx.Response().Header.Set("Content-Type", "application/json")
|
||||
ctx.Status(200)
|
||||
|
||||
return ctx.JSON(&response)
|
||||
}
|
||||
|
||||
type PostUsersRequestObject struct {
|
||||
Body *PostUsersJSONRequestBody
|
||||
}
|
||||
|
||||
type PostUsersResponseObject interface {
|
||||
VisitPostUsersResponse(ctx *fiber.Ctx) error
|
||||
}
|
||||
|
||||
type PostUsers201JSONResponse User
|
||||
|
||||
func (response PostUsers201JSONResponse) VisitPostUsersResponse(ctx *fiber.Ctx) error {
|
||||
ctx.Response().Header.Set("Content-Type", "application/json")
|
||||
ctx.Status(201)
|
||||
|
||||
return ctx.JSON(&response)
|
||||
}
|
||||
|
||||
type DeleteUsersUserIdRequestObject struct {
|
||||
type GetUserByIdRequestObject struct {
|
||||
UserId string `json:"userId"`
|
||||
}
|
||||
|
||||
type DeleteUsersUserIdResponseObject interface {
|
||||
VisitDeleteUsersUserIdResponse(ctx *fiber.Ctx) error
|
||||
type GetUserByIdResponseObject interface {
|
||||
VisitGetUserByIdResponse(ctx *fiber.Ctx) error
|
||||
}
|
||||
|
||||
type DeleteUsersUserId204Response struct {
|
||||
type GetUserById200JSONResponse struct {
|
||||
User User `json:"user"`
|
||||
}
|
||||
|
||||
func (response DeleteUsersUserId204Response) VisitDeleteUsersUserIdResponse(ctx *fiber.Ctx) error {
|
||||
ctx.Status(204)
|
||||
return nil
|
||||
}
|
||||
|
||||
type DeleteUsersUserId404Response struct {
|
||||
}
|
||||
|
||||
func (response DeleteUsersUserId404Response) VisitDeleteUsersUserIdResponse(ctx *fiber.Ctx) error {
|
||||
ctx.Status(404)
|
||||
return nil
|
||||
}
|
||||
|
||||
type GetUsersUserIdRequestObject struct {
|
||||
UserId string `json:"userId"`
|
||||
}
|
||||
|
||||
type GetUsersUserIdResponseObject interface {
|
||||
VisitGetUsersUserIdResponse(ctx *fiber.Ctx) error
|
||||
}
|
||||
|
||||
type GetUsersUserId200JSONResponse User
|
||||
|
||||
func (response GetUsersUserId200JSONResponse) VisitGetUsersUserIdResponse(ctx *fiber.Ctx) error {
|
||||
func (response GetUserById200JSONResponse) VisitGetUserByIdResponse(ctx *fiber.Ctx) error {
|
||||
ctx.Response().Header.Set("Content-Type", "application/json")
|
||||
ctx.Status(200)
|
||||
|
||||
return ctx.JSON(&response)
|
||||
}
|
||||
|
||||
type GetUsersUserId404Response struct {
|
||||
type GetUserById404JSONResponse struct {
|
||||
Message string `json:"message"`
|
||||
}
|
||||
|
||||
func (response GetUsersUserId404Response) VisitGetUsersUserIdResponse(ctx *fiber.Ctx) error {
|
||||
ctx.Status(404)
|
||||
return nil
|
||||
}
|
||||
|
||||
type PutUsersUserIdRequestObject struct {
|
||||
UserId string `json:"userId"`
|
||||
Body *PutUsersUserIdJSONRequestBody
|
||||
}
|
||||
|
||||
type PutUsersUserIdResponseObject interface {
|
||||
VisitPutUsersUserIdResponse(ctx *fiber.Ctx) error
|
||||
}
|
||||
|
||||
type PutUsersUserId200JSONResponse User
|
||||
|
||||
func (response PutUsersUserId200JSONResponse) VisitPutUsersUserIdResponse(ctx *fiber.Ctx) error {
|
||||
func (response GetUserById404JSONResponse) VisitGetUserByIdResponse(ctx *fiber.Ctx) error {
|
||||
ctx.Response().Header.Set("Content-Type", "application/json")
|
||||
ctx.Status(200)
|
||||
ctx.Status(404)
|
||||
|
||||
return ctx.JSON(&response)
|
||||
}
|
||||
|
||||
type PutUsersUserId404Response struct {
|
||||
}
|
||||
|
||||
func (response PutUsersUserId404Response) VisitPutUsersUserIdResponse(ctx *fiber.Ctx) error {
|
||||
ctx.Status(404)
|
||||
return nil
|
||||
}
|
||||
|
||||
// StrictServerInterface represents all server handlers.
|
||||
type StrictServerInterface interface {
|
||||
// Get all users
|
||||
// (GET /users)
|
||||
GetUsers(ctx context.Context, request GetUsersRequestObject) (GetUsersResponseObject, error)
|
||||
// Create a new user
|
||||
// Create a new user with phone number
|
||||
// (POST /users)
|
||||
PostUsers(ctx context.Context, request PostUsersRequestObject) (PostUsersResponseObject, error)
|
||||
// Delete a user by ID
|
||||
// (DELETE /users/{userId})
|
||||
DeleteUsersUserId(ctx context.Context, request DeleteUsersUserIdRequestObject) (DeleteUsersUserIdResponseObject, error)
|
||||
CreateUser(ctx context.Context, request CreateUserRequestObject) (CreateUserResponseObject, error)
|
||||
// Get a user by ID
|
||||
// (GET /users/{userId})
|
||||
GetUsersUserId(ctx context.Context, request GetUsersUserIdRequestObject) (GetUsersUserIdResponseObject, error)
|
||||
// Update a user by ID
|
||||
// (PUT /users/{userId})
|
||||
PutUsersUserId(ctx context.Context, request PutUsersUserIdRequestObject) (PutUsersUserIdResponseObject, error)
|
||||
GetUserById(ctx context.Context, request GetUserByIdRequestObject) (GetUserByIdResponseObject, error)
|
||||
}
|
||||
|
||||
type StrictHandlerFunc func(ctx *fiber.Ctx, args interface{}) (interface{}, error)
|
||||
@ -297,54 +161,29 @@ type strictHandler struct {
|
||||
middlewares []StrictMiddlewareFunc
|
||||
}
|
||||
|
||||
// GetUsers operation middleware
|
||||
func (sh *strictHandler) GetUsers(ctx *fiber.Ctx) error {
|
||||
var request GetUsersRequestObject
|
||||
// CreateUser operation middleware
|
||||
func (sh *strictHandler) CreateUser(ctx *fiber.Ctx) error {
|
||||
var request CreateUserRequestObject
|
||||
|
||||
handler := func(ctx *fiber.Ctx, request interface{}) (interface{}, error) {
|
||||
return sh.ssi.GetUsers(ctx.UserContext(), request.(GetUsersRequestObject))
|
||||
}
|
||||
for _, middleware := range sh.middlewares {
|
||||
handler = middleware(handler, "GetUsers")
|
||||
}
|
||||
|
||||
response, err := handler(ctx, request)
|
||||
|
||||
if err != nil {
|
||||
return fiber.NewError(fiber.StatusBadRequest, err.Error())
|
||||
} else if validResponse, ok := response.(GetUsersResponseObject); ok {
|
||||
if err := validResponse.VisitGetUsersResponse(ctx); err != nil {
|
||||
return fiber.NewError(fiber.StatusBadRequest, err.Error())
|
||||
}
|
||||
} else if response != nil {
|
||||
return fmt.Errorf("unexpected response type: %T", response)
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
// PostUsers operation middleware
|
||||
func (sh *strictHandler) PostUsers(ctx *fiber.Ctx) error {
|
||||
var request PostUsersRequestObject
|
||||
|
||||
var body PostUsersJSONRequestBody
|
||||
var body CreateUserJSONRequestBody
|
||||
if err := ctx.BodyParser(&body); err != nil {
|
||||
return fiber.NewError(fiber.StatusBadRequest, err.Error())
|
||||
}
|
||||
request.Body = &body
|
||||
|
||||
handler := func(ctx *fiber.Ctx, request interface{}) (interface{}, error) {
|
||||
return sh.ssi.PostUsers(ctx.UserContext(), request.(PostUsersRequestObject))
|
||||
return sh.ssi.CreateUser(ctx.UserContext(), request.(CreateUserRequestObject))
|
||||
}
|
||||
for _, middleware := range sh.middlewares {
|
||||
handler = middleware(handler, "PostUsers")
|
||||
handler = middleware(handler, "CreateUser")
|
||||
}
|
||||
|
||||
response, err := handler(ctx, request)
|
||||
|
||||
if err != nil {
|
||||
return fiber.NewError(fiber.StatusBadRequest, err.Error())
|
||||
} else if validResponse, ok := response.(PostUsersResponseObject); ok {
|
||||
if err := validResponse.VisitPostUsersResponse(ctx); err != nil {
|
||||
} else if validResponse, ok := response.(CreateUserResponseObject); ok {
|
||||
if err := validResponse.VisitCreateUserResponse(ctx); err != nil {
|
||||
return fiber.NewError(fiber.StatusBadRequest, err.Error())
|
||||
}
|
||||
} else if response != nil {
|
||||
@ -353,85 +192,25 @@ func (sh *strictHandler) PostUsers(ctx *fiber.Ctx) error {
|
||||
return nil
|
||||
}
|
||||
|
||||
// DeleteUsersUserId operation middleware
|
||||
func (sh *strictHandler) DeleteUsersUserId(ctx *fiber.Ctx, userId string) error {
|
||||
var request DeleteUsersUserIdRequestObject
|
||||
// GetUserById operation middleware
|
||||
func (sh *strictHandler) GetUserById(ctx *fiber.Ctx, userId string) error {
|
||||
var request GetUserByIdRequestObject
|
||||
|
||||
request.UserId = userId
|
||||
|
||||
handler := func(ctx *fiber.Ctx, request interface{}) (interface{}, error) {
|
||||
return sh.ssi.DeleteUsersUserId(ctx.UserContext(), request.(DeleteUsersUserIdRequestObject))
|
||||
return sh.ssi.GetUserById(ctx.UserContext(), request.(GetUserByIdRequestObject))
|
||||
}
|
||||
for _, middleware := range sh.middlewares {
|
||||
handler = middleware(handler, "DeleteUsersUserId")
|
||||
handler = middleware(handler, "GetUserById")
|
||||
}
|
||||
|
||||
response, err := handler(ctx, request)
|
||||
|
||||
if err != nil {
|
||||
return fiber.NewError(fiber.StatusBadRequest, err.Error())
|
||||
} else if validResponse, ok := response.(DeleteUsersUserIdResponseObject); ok {
|
||||
if err := validResponse.VisitDeleteUsersUserIdResponse(ctx); err != nil {
|
||||
return fiber.NewError(fiber.StatusBadRequest, err.Error())
|
||||
}
|
||||
} else if response != nil {
|
||||
return fmt.Errorf("unexpected response type: %T", response)
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
// GetUsersUserId operation middleware
|
||||
func (sh *strictHandler) GetUsersUserId(ctx *fiber.Ctx, userId string) error {
|
||||
var request GetUsersUserIdRequestObject
|
||||
|
||||
request.UserId = userId
|
||||
|
||||
handler := func(ctx *fiber.Ctx, request interface{}) (interface{}, error) {
|
||||
return sh.ssi.GetUsersUserId(ctx.UserContext(), request.(GetUsersUserIdRequestObject))
|
||||
}
|
||||
for _, middleware := range sh.middlewares {
|
||||
handler = middleware(handler, "GetUsersUserId")
|
||||
}
|
||||
|
||||
response, err := handler(ctx, request)
|
||||
|
||||
if err != nil {
|
||||
return fiber.NewError(fiber.StatusBadRequest, err.Error())
|
||||
} else if validResponse, ok := response.(GetUsersUserIdResponseObject); ok {
|
||||
if err := validResponse.VisitGetUsersUserIdResponse(ctx); err != nil {
|
||||
return fiber.NewError(fiber.StatusBadRequest, err.Error())
|
||||
}
|
||||
} else if response != nil {
|
||||
return fmt.Errorf("unexpected response type: %T", response)
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
// PutUsersUserId operation middleware
|
||||
func (sh *strictHandler) PutUsersUserId(ctx *fiber.Ctx, userId string) error {
|
||||
var request PutUsersUserIdRequestObject
|
||||
|
||||
request.UserId = userId
|
||||
|
||||
var body PutUsersUserIdJSONRequestBody
|
||||
if err := ctx.BodyParser(&body); err != nil {
|
||||
return fiber.NewError(fiber.StatusBadRequest, err.Error())
|
||||
}
|
||||
request.Body = &body
|
||||
|
||||
handler := func(ctx *fiber.Ctx, request interface{}) (interface{}, error) {
|
||||
return sh.ssi.PutUsersUserId(ctx.UserContext(), request.(PutUsersUserIdRequestObject))
|
||||
}
|
||||
for _, middleware := range sh.middlewares {
|
||||
handler = middleware(handler, "PutUsersUserId")
|
||||
}
|
||||
|
||||
response, err := handler(ctx, request)
|
||||
|
||||
if err != nil {
|
||||
return fiber.NewError(fiber.StatusBadRequest, err.Error())
|
||||
} else if validResponse, ok := response.(PutUsersUserIdResponseObject); ok {
|
||||
if err := validResponse.VisitPutUsersUserIdResponse(ctx); err != nil {
|
||||
} else if validResponse, ok := response.(GetUserByIdResponseObject); ok {
|
||||
if err := validResponse.VisitGetUserByIdResponse(ctx); err != nil {
|
||||
return fiber.NewError(fiber.StatusBadRequest, err.Error())
|
||||
}
|
||||
} else if response != nil {
|
||||
|
||||
@ -1,3 +1,3 @@
|
||||
package handler
|
||||
|
||||
//go:generate go tool oapi-codegen -config ../../../../api/user/cfg.yaml ../../../../api/user/user.yaml
|
||||
//go:generate go tool oapi-codegen -config ../../../../api/user/cfg.yaml ../../../../api/user/api.yaml
|
||||
|
||||
@ -2,46 +2,52 @@ package handler
|
||||
|
||||
import (
|
||||
"context"
|
||||
|
||||
"git.logidex.ru/fakz9/logidex-id/internal/api/user/service"
|
||||
"github.com/gofiber/fiber/v2"
|
||||
"github.com/jinzhu/copier"
|
||||
)
|
||||
|
||||
type UserHandler struct {
|
||||
service service.UserService
|
||||
}
|
||||
|
||||
func (h UserHandler) CreateUser(ctx context.Context, request CreateUserRequestObject) (CreateUserResponseObject, error) {
|
||||
//TODO implement me
|
||||
panic("implement me")
|
||||
}
|
||||
|
||||
func (h UserHandler) GetUserById(ctx context.Context, request GetUserByIdRequestObject) (GetUserByIdResponseObject, error) {
|
||||
user, err := h.service.GetUserByUuid(ctx, request.UserId)
|
||||
if err != nil {
|
||||
return GetUserById404JSONResponse{
|
||||
Message: err.Error(),
|
||||
}, nil
|
||||
}
|
||||
var responseUser User
|
||||
err = copier.Copy(responseUser, user)
|
||||
if err != nil {
|
||||
return GetUserById404JSONResponse{Message: err.Error()}, nil
|
||||
|
||||
}
|
||||
return GetUserById200JSONResponse{User: responseUser}, nil
|
||||
}
|
||||
|
||||
var _ StrictServerInterface = (*UserHandler)(nil)
|
||||
|
||||
func (u UserHandler) GetUsers(ctx context.Context, request GetUsersRequestObject) (GetUsersResponseObject, error) {
|
||||
var response = make([]User, 0)
|
||||
|
||||
return GetUsers200JSONResponse(response), nil
|
||||
func NewUserHandler(
|
||||
service service.UserService,
|
||||
) *UserHandler {
|
||||
return &UserHandler{service: service}
|
||||
}
|
||||
|
||||
func (u UserHandler) PostUsers(ctx context.Context, request PostUsersRequestObject) (PostUsersResponseObject, error) {
|
||||
//TODO implement me
|
||||
panic("implement me")
|
||||
}
|
||||
|
||||
func (u UserHandler) DeleteUsersUserId(ctx context.Context, request DeleteUsersUserIdRequestObject) (DeleteUsersUserIdResponseObject, error) {
|
||||
//TODO implement me
|
||||
panic("implement me")
|
||||
}
|
||||
|
||||
func (u UserHandler) GetUsersUserId(ctx context.Context, request GetUsersUserIdRequestObject) (GetUsersUserIdResponseObject, error) {
|
||||
//TODO implement me
|
||||
panic("implement me")
|
||||
}
|
||||
|
||||
func (u UserHandler) PutUsersUserId(ctx context.Context, request PutUsersUserIdRequestObject) (PutUsersUserIdResponseObject, error) {
|
||||
//TODO implement me
|
||||
panic("implement me")
|
||||
}
|
||||
|
||||
func NewUserHandler() *UserHandler {
|
||||
return &UserHandler{}
|
||||
}
|
||||
|
||||
func RegisterApp(router fiber.Router) {
|
||||
server := NewStrictHandler(NewUserHandler(), nil)
|
||||
func (h UserHandler) RegisterRoutes(router fiber.Router) {
|
||||
server := NewStrictHandler(h, nil)
|
||||
RegisterHandlers(router, server)
|
||||
|
||||
}
|
||||
|
||||
//func RegisterUserHandler(router fiber.Router) {
|
||||
// server := NewStrictHandler(NewUserHandler(), nil)
|
||||
// RegisterHandlers(router, server)
|
||||
//
|
||||
//}
|
||||
|
||||
70
internal/api/user/repo/user_repo.go
Normal file
70
internal/api/user/repo/user_repo.go
Normal file
@ -0,0 +1,70 @@
|
||||
package repo
|
||||
|
||||
import (
|
||||
"context"
|
||||
|
||||
"git.logidex.ru/fakz9/logidex-id/internal/api/user/domain"
|
||||
db "git.logidex.ru/fakz9/logidex-id/internal/db/generated"
|
||||
"github.com/google/uuid"
|
||||
)
|
||||
|
||||
type userRepo struct {
|
||||
db db.DBTX
|
||||
}
|
||||
|
||||
func (u userRepo) VerifyUser(ctx context.Context, requestUuid string) (*db.User, error) {
|
||||
//TODO implement me
|
||||
|
||||
queries := db.New(u.db)
|
||||
uuidParsed, err := uuid.Parse(requestUuid)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
dbUser, err := queries.UpdateUserVerified(ctx, uuidParsed)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return &dbUser, nil
|
||||
}
|
||||
|
||||
func (u userRepo) GetUserByUuid(ctx context.Context, requestUuid string) (*db.User, error) {
|
||||
queries := db.New(u.db)
|
||||
uuidParsed, err := uuid.Parse(requestUuid)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
dbUser, err := queries.GetUserByUUID(ctx, uuidParsed)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return &dbUser, nil
|
||||
}
|
||||
|
||||
func userFromDbToDomain(dbUser db.User) *domain.User {
|
||||
return &domain.User{
|
||||
PhoneNumber: dbUser.PhoneNumber,
|
||||
Uuid: dbUser.Uuid.String(),
|
||||
}
|
||||
}
|
||||
|
||||
func (u userRepo) CreateUser(ctx context.Context, phoneNumber string) (*db.User, error) {
|
||||
queries := db.New(u.db)
|
||||
user, err := queries.CreateUser(ctx, phoneNumber)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return &user, nil
|
||||
}
|
||||
|
||||
func (u userRepo) GetUserByPhoneNumber(ctx context.Context, phoneNumber string) (*db.User, error) {
|
||||
queries := db.New(u.db)
|
||||
dbUser, err := queries.GetByPhoneNumber(ctx, phoneNumber)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return &dbUser, nil
|
||||
}
|
||||
|
||||
func NewUserRepo(db db.DBTX) domain.UserRepository {
|
||||
return &userRepo{db: db}
|
||||
}
|
||||
@ -1,4 +0,0 @@
|
||||
package repository
|
||||
|
||||
type UserRepo interface {
|
||||
}
|
||||
@ -1,13 +1,47 @@
|
||||
package service
|
||||
|
||||
import (
|
||||
"git.logidex.ru/fakz9/logidex-id/internal/api/user/repository"
|
||||
"context"
|
||||
|
||||
"git.logidex.ru/fakz9/logidex-id/internal/api/user/domain"
|
||||
)
|
||||
|
||||
type UserService struct {
|
||||
repo *repository.UserRepo
|
||||
type UserService interface {
|
||||
GetUserByPhoneNumber(ctx context.Context, phoneNumber string) (*domain.User, error)
|
||||
GetUserByUuid(ctx context.Context, phoneNumber string) (*domain.User, error)
|
||||
}
|
||||
type userService struct {
|
||||
repo domain.UserRepository
|
||||
}
|
||||
|
||||
func NewUserService(repo *repository.UserRepo) *UserService {
|
||||
return &UserService{repo: repo}
|
||||
func (u userService) GetUserByUuid(ctx context.Context, uuid string) (*domain.User, error) {
|
||||
dbUser, err := u.repo.GetUserByUuid(ctx, uuid)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
if dbUser == nil {
|
||||
return nil, domain.ErrUserNotFound{PhoneNumber: uuid}
|
||||
}
|
||||
return &domain.User{
|
||||
Uuid: dbUser.Uuid.String(),
|
||||
PhoneNumber: dbUser.PhoneNumber,
|
||||
}, nil
|
||||
}
|
||||
|
||||
func (u userService) GetUserByPhoneNumber(ctx context.Context, phoneNumber string) (*domain.User, error) {
|
||||
dbUser, err := u.repo.GetUserByPhoneNumber(ctx, phoneNumber)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
if dbUser == nil {
|
||||
return nil, domain.ErrUserNotFound{PhoneNumber: phoneNumber}
|
||||
}
|
||||
return &domain.User{
|
||||
Uuid: dbUser.Uuid.String(),
|
||||
PhoneNumber: dbUser.PhoneNumber,
|
||||
}, nil
|
||||
}
|
||||
|
||||
func NewUserService(repo domain.UserRepository) UserService {
|
||||
return &userService{repo: repo}
|
||||
}
|
||||
|
||||
@ -1,9 +1,10 @@
|
||||
package config
|
||||
|
||||
import (
|
||||
"log"
|
||||
|
||||
"github.com/joho/godotenv"
|
||||
"github.com/spf13/viper"
|
||||
"log"
|
||||
)
|
||||
|
||||
type Config struct {
|
||||
@ -20,11 +21,16 @@ type Config struct {
|
||||
Host string
|
||||
Password string
|
||||
}
|
||||
DB struct {
|
||||
Host string
|
||||
Port int
|
||||
User string
|
||||
Password string
|
||||
Dbname string
|
||||
}
|
||||
}
|
||||
|
||||
var Cfg *Config
|
||||
|
||||
func Init() {
|
||||
func NewConfig() Config {
|
||||
err := godotenv.Load()
|
||||
if err != nil {
|
||||
log.Println("Error loading .env file")
|
||||
@ -48,5 +54,5 @@ func Init() {
|
||||
if err != nil {
|
||||
log.Fatalf("Unable to decode config into struct, %v", err)
|
||||
}
|
||||
Cfg = &config
|
||||
return config
|
||||
}
|
||||
|
||||
24
internal/db/database.go
Normal file
24
internal/db/database.go
Normal file
@ -0,0 +1,24 @@
|
||||
package db
|
||||
|
||||
import (
|
||||
"context"
|
||||
"strconv"
|
||||
|
||||
"git.logidex.ru/fakz9/logidex-id/internal/config"
|
||||
"github.com/jackc/pgx/v5/pgxpool"
|
||||
)
|
||||
|
||||
func NewDatabasePool(cfg config.Config) *pgxpool.Pool {
|
||||
ctx := context.Background()
|
||||
connUrl := "postgresql://" + cfg.DB.User + ":" + cfg.DB.Password + "@" + cfg.DB.Host + ":" + strconv.Itoa(cfg.DB.Port) + "/" + cfg.DB.Dbname
|
||||
|
||||
pool, err := pgxpool.New(ctx, connUrl)
|
||||
if err != nil {
|
||||
panic("Failed to connect to database: " + err.Error())
|
||||
}
|
||||
err = pool.Ping(ctx)
|
||||
if err != nil {
|
||||
panic("Failed to ping database: " + err.Error())
|
||||
}
|
||||
return pool
|
||||
}
|
||||
15
internal/db/fx.go
Normal file
15
internal/db/fx.go
Normal file
@ -0,0 +1,15 @@
|
||||
package db
|
||||
|
||||
import (
|
||||
sqlcdb "git.logidex.ru/fakz9/logidex-id/internal/db/generated"
|
||||
"go.uber.org/fx"
|
||||
)
|
||||
|
||||
var Module = fx.Options(
|
||||
fx.Provide(
|
||||
fx.Annotate(
|
||||
NewDatabasePool,
|
||||
fx.As(new(sqlcdb.DBTX)),
|
||||
),
|
||||
),
|
||||
)
|
||||
9
internal/db/migrations/001_init.sql
Normal file
9
internal/db/migrations/001_init.sql
Normal file
@ -0,0 +1,9 @@
|
||||
CREATE TABLE IF NOT EXISTS users
|
||||
(
|
||||
uuid UUID PRIMARY KEY DEFAULT gen_random_uuid() NOT NULL,
|
||||
phone_number VARCHAR(20) NOT NULL CHECK (phone_number ~ '^\+[0-9]{10,15}$'),
|
||||
created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP NOT NULL,
|
||||
verified BOOLEAN DEFAULT FALSE NOT NULL,
|
||||
verified_at TIMESTAMP NULL
|
||||
|
||||
);
|
||||
23
internal/db/queries/users.sql
Normal file
23
internal/db/queries/users.sql
Normal file
@ -0,0 +1,23 @@
|
||||
-- name: GetByPhoneNumber :one
|
||||
SELECT *
|
||||
FROM users
|
||||
WHERE phone_number = $1
|
||||
LIMIT 1;
|
||||
|
||||
-- name: GetUserByUUID :one
|
||||
SELECT *
|
||||
FROM users
|
||||
WHERE uuid = $1
|
||||
LIMIT 1;
|
||||
|
||||
-- name: CreateUser :one
|
||||
INSERT INTO users (phone_number)
|
||||
VALUES ($1)
|
||||
RETURNING *;
|
||||
|
||||
-- name: UpdateUserVerified :one
|
||||
UPDATE users
|
||||
SET verified = TRUE,
|
||||
verified_at = CURRENT_TIMESTAMP
|
||||
WHERE uuid = $1
|
||||
RETURNING *;
|
||||
@ -3,26 +3,15 @@ package hydra_client
|
||||
import (
|
||||
"git.logidex.ru/fakz9/logidex-id/internal/config"
|
||||
hydraApi "github.com/ory/hydra-client-go"
|
||||
"sync"
|
||||
)
|
||||
|
||||
var (
|
||||
client *hydraApi.APIClient
|
||||
initClient sync.Once
|
||||
)
|
||||
|
||||
func InitClient() {
|
||||
func NewHydraClient(appConfig config.Config) *hydraApi.APIClient {
|
||||
cfg := hydraApi.NewConfiguration()
|
||||
cfg.AddDefaultHeader("X-Secret", config.Cfg.Hydra.Password)
|
||||
cfg.AddDefaultHeader("X-Secret", appConfig.Hydra.Password)
|
||||
cfg.Servers = []hydraApi.ServerConfiguration{
|
||||
{
|
||||
URL: config.Cfg.Hydra.Host,
|
||||
URL: appConfig.Hydra.Host,
|
||||
},
|
||||
}
|
||||
client = hydraApi.NewAPIClient(cfg)
|
||||
}
|
||||
|
||||
func GetClient() *hydraApi.APIClient {
|
||||
initClient.Do(InitClient)
|
||||
return client
|
||||
return hydraApi.NewAPIClient(cfg)
|
||||
}
|
||||
|
||||
19
internal/phoneutil/phoneutil.go
Normal file
19
internal/phoneutil/phoneutil.go
Normal file
@ -0,0 +1,19 @@
|
||||
package phoneutil
|
||||
|
||||
import (
|
||||
"errors"
|
||||
|
||||
"github.com/nyaruka/phonenumbers"
|
||||
)
|
||||
|
||||
func ParseAndFormatPhoneNumber(phoneNumber string) (string, error) {
|
||||
parsedNumber, err := phonenumbers.Parse(phoneNumber, "RU")
|
||||
if err != nil {
|
||||
return "", err
|
||||
}
|
||||
result := phonenumbers.Format(parsedNumber, phonenumbers.E164)
|
||||
if result == "" {
|
||||
return "", errors.New("failed to format phone number")
|
||||
}
|
||||
return result, nil
|
||||
}
|
||||
@ -1,26 +1,21 @@
|
||||
package redis
|
||||
|
||||
import (
|
||||
"strconv"
|
||||
|
||||
"git.logidex.ru/fakz9/logidex-id/internal/config"
|
||||
"github.com/redis/rueidis"
|
||||
"strconv"
|
||||
)
|
||||
|
||||
var client rueidis.Client
|
||||
|
||||
func Init() error {
|
||||
func NewRedisClient(cfg config.Config) rueidis.Client {
|
||||
var err error
|
||||
client, err = rueidis.NewClient(rueidis.ClientOption{
|
||||
client, err := rueidis.NewClient(rueidis.ClientOption{
|
||||
// Set the address of your Redis server
|
||||
InitAddress: []string{config.Cfg.Redis.Host + ":" + strconv.Itoa(config.Cfg.Redis.Port)},
|
||||
Password: config.Cfg.Redis.Password,
|
||||
InitAddress: []string{cfg.Redis.Host + ":" + strconv.Itoa(cfg.Redis.Port)},
|
||||
Password: cfg.Redis.Password,
|
||||
})
|
||||
if err != nil {
|
||||
return err
|
||||
return nil
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func GetClient() rueidis.Client {
|
||||
return client
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user