customer/internal/interface/controller/http/account/controllers.go

292 lines
8.0 KiB
Go

package account
import (
"bufio"
"context"
"encoding/json"
"fmt"
"github.com/gofiber/fiber/v2"
"go.uber.org/zap"
"math"
qutils "penahub.gitlab.yandexcloud.net/backend/quiz/common.git/utils"
"penahub.gitlab.yandexcloud.net/pena-services/customer/internal/errors"
"penahub.gitlab.yandexcloud.net/pena-services/customer/internal/interface/client"
"penahub.gitlab.yandexcloud.net/pena-services/customer/internal/interface/controller/http"
"penahub.gitlab.yandexcloud.net/pena-services/customer/internal/interface/repository"
"penahub.gitlab.yandexcloud.net/pena-services/customer/internal/models"
"strconv"
)
type Deps struct {
MiddleWare *http.MiddleWare
AccountRepo *repository.AccountRepository
Logger *zap.Logger
Encrypt *qutils.Encrypt
AuthClient *client.AuthClient
}
type AccountController struct {
middleWare *http.MiddleWare
accountRepo *repository.AccountRepository
logger *zap.Logger
encrypt *qutils.Encrypt
authClient *client.AuthClient
}
func NewAccountController(deps Deps) *AccountController {
return &AccountController{
middleWare: deps.MiddleWare,
accountRepo: deps.AccountRepo,
logger: deps.Logger,
encrypt: deps.Encrypt,
authClient: deps.AuthClient,
}
}
func (receiver *AccountController) Delete(ctx *fiber.Ctx) error {
userID, ok := receiver.middleWare.ExtractUserID(ctx)
if !ok || userID == "" {
return receiver.middleWare.NoAuth(ctx)
}
account, err := receiver.accountRepo.Remove(ctx.Context(), userID)
if err != nil {
return receiver.middleWare.ErrorOld(ctx, err)
}
return ctx.Status(fiber.StatusOK).JSON(account)
}
func (receiver *AccountController) Update(ctx *fiber.Ctx) error {
userID, ok := receiver.middleWare.ExtractUserID(ctx)
if !ok || userID == "" {
return receiver.middleWare.NoAuth(ctx)
}
var request models.Name
if err := ctx.BodyParser(&request); err != nil {
return receiver.middleWare.Error(ctx, fiber.StatusBadRequest, "failed to bind json", err)
}
account, err := receiver.accountRepo.UpdateName(ctx.Context(), userID, &request)
if err != nil {
return receiver.middleWare.ErrorOld(ctx, err)
}
return ctx.Status(fiber.StatusOK).JSON(account)
}
func (receiver *AccountController) SetVerificationStatus(ctx *fiber.Ctx) error {
userID := ctx.Params("userId")
if userID == "" {
return receiver.middleWare.Error(ctx, fiber.StatusBadRequest, "invalid format for parameter userId")
}
var request models.SetAccountStatus
if err := ctx.BodyParser(&request); err != nil {
return receiver.middleWare.Error(ctx, fiber.StatusBadRequest, "failed to bind json", err)
}
account, err := receiver.accountRepo.SetStatus(ctx.Context(), userID, request.Status)
if err != nil {
receiver.logger.Error("failed to set status on <SetVerificationStatus> of <AccountService>", zap.Error(err))
return receiver.middleWare.ErrorOld(ctx, err)
}
return ctx.Status(fiber.StatusOK).JSON(account)
}
func (receiver *AccountController) Get(ctx *fiber.Ctx) error {
userID, ok := receiver.middleWare.ExtractUserID(ctx)
if !ok || userID == "" {
return receiver.middleWare.NoAuth(ctx)
}
hlogger := receiver.middleWare.ExtractLogger(ctx)
account, err := receiver.accountRepo.FindByUserID(ctx.Context(), userID)
if err != nil {
return receiver.middleWare.ErrorOld(ctx, err)
}
hlogger.Emit(models.InfoGetAccount{
CtxUserID: userID,
CtxAccountID: account.ID,
})
return ctx.Status(fiber.StatusOK).JSON(account)
}
func (receiver *AccountController) Create(ctx *fiber.Ctx) error {
userID, ok := receiver.middleWare.ExtractUserID(ctx)
if !ok || userID == "" {
return receiver.middleWare.NoAuth(ctx)
}
hlogger := receiver.middleWare.ExtractLogger(ctx)
var er error
quizFrom := ctx.Cookies("quizFrom")
quizUser := ctx.Cookies("quizUser")
if quizUser != "" {
quizUser, er = receiver.encrypt.DecryptStr([]byte(quizUser))
if er != nil {
return receiver.middleWare.ErrorOld(ctx, er)
}
}
account, err := receiver.accountRepo.FindByUserID(ctx.Context(), userID)
if err != nil && err.Type() != errors.ErrNotFound {
return receiver.middleWare.ErrorOld(ctx, err)
}
if account != nil {
return receiver.middleWare.Error(ctx, fiber.StatusBadRequest, "account exists")
}
user, err := receiver.authClient.GetUser(ctx.Context(), userID)
if err != nil {
return receiver.middleWare.ErrorOld(ctx, err)
}
account, err = receiver.accountRepo.Insert(ctx.Context(), &models.Account{
UserID: user.ID, Wallet: models.Wallet{Currency: models.DefaultCurrency}, From: quizFrom, Partner: quizUser})
if err != nil {
return receiver.middleWare.ErrorOld(ctx, err)
}
quiz := ""
if quizFrom != "" {
quiz = "quiz"
}
hlogger.Emit(models.InfoCreateAccount{
CtxUserID: userID,
CtxAccountID: account.ID,
KeyFromSource: quiz,
KeyFromID: quizFrom,
KeyFromPartner: quizUser,
CtxLogin: user.Login,
CtxEmail: user.Email,
CtxPhone: user.PhoneNumber,
KeyCurrency: account.Wallet.Currency,
})
return ctx.Status(fiber.StatusOK).JSON(account)
}
func (receiver *AccountController) DeleteCurrent(ctx *fiber.Ctx) error {
userID := ctx.Params("userId")
if userID == "" {
return receiver.middleWare.Error(ctx, fiber.StatusBadRequest, "invalid format for parameter userId")
}
account, err := receiver.accountRepo.Remove(ctx.Context(), userID)
if err != nil {
return receiver.middleWare.ErrorOld(ctx, err)
}
return ctx.Status(fiber.StatusOK).JSON(account)
}
func (receiver *AccountController) GetCurrent(ctx *fiber.Ctx) error {
userID := ctx.Params("userId")
if userID == "" {
return receiver.middleWare.Error(ctx, fiber.StatusBadRequest, "invalid format for parameter userId")
}
account, err := receiver.accountRepo.FindByUserID(ctx.Context(), userID)
if err != nil {
return receiver.middleWare.ErrorOld(ctx, err)
}
return ctx.Status(fiber.StatusOK).JSON(account)
}
func (receiver *AccountController) Pagination(ctx *fiber.Ctx) error {
pageStr := ctx.Query("page", "1")
limitStr := ctx.Query("limit", "100")
page, err := strconv.ParseInt(pageStr, 10, 64)
if err != nil || page < 1 {
page = 1
}
limit, err := strconv.ParseInt(limitStr, 10, 64)
if err != nil || limit < 1 {
limit = models.DefaultLimit
} else {
limit = int64(math.Min(float64(limit), float64(models.DefaultLimit)))
}
count, err := receiver.accountRepo.CountAll(ctx.Context())
if err != nil {
return receiver.middleWare.ErrorOld(ctx, err)
}
if count == 0 {
response := models.PaginationResponse[models.Account]{TotalPages: 0, Records: []models.Account{}}
return ctx.Status(fiber.StatusOK).JSON(response)
}
totalPages := int64(math.Ceil(float64(count) / float64(limit)))
accounts, err := receiver.accountRepo.FindMany(ctx.Context(), page, limit)
if err != nil {
return receiver.middleWare.ErrorOld(ctx, err)
}
response := models.PaginationResponse[models.Account]{
TotalPages: totalPages,
Records: accounts,
}
return ctx.Status(fiber.StatusOK).JSON(response)
}
func (receiver *AccountController) AccountPipe(ctx *fiber.Ctx) error {
userID, ok := receiver.middleWare.ExtractUserID(ctx)
if !ok || userID == "" {
return receiver.middleWare.NoAuth(ctx)
}
ctx.Set(fiber.HeaderContentType, "text/event-stream")
ctx.Set("Cache-Control", "no-cache")
ctx.Set("Connection", "keep-alive")
ctx.Set("Transfer-Encoding", "chunked")
accountCh := make(chan models.Account)
cancelCtx, cancel := context.WithCancel(ctx.Context())
go func(ctx context.Context) {
defer close(accountCh)
if err := receiver.accountRepo.AccountPipe(ctx, userID, accountCh); err != nil {
receiver.logger.Error("error in account pipe repo method", zap.Error(err))
}
}(cancelCtx)
ctx.Status(fiber.StatusOK).Context().SetBodyStreamWriter(func(w *bufio.Writer) {
for {
select {
case account, ok := <-accountCh:
if !ok {
return
}
accountJSON, err := json.Marshal(account)
if err != nil {
receiver.logger.Error("error marshal account JSON", zap.Error(err))
continue
}
fmt.Fprintf(w, "data: %s\n\n", accountJSON)
if err := w.Flush(); err != nil {
receiver.logger.Error("error flushing", zap.Error(err))
cancel()
return
}
}
}
})
return nil
}