package account_client import ( "bufio" "context" "encoding/json" "fmt" "github.com/gofiber/fiber/v2" "go.uber.org/zap" "penahub.gitlab.yandexcloud.net/backend/penahub_common/log_mw" qutils "penahub.gitlab.yandexcloud.net/backend/quiz/common.git/utils" "gitea.pena/PenaSide/customer/internal/errors" "gitea.pena/PenaSide/customer/internal/interface/client" "gitea.pena/PenaSide/customer/internal/interface/controller/http" "gitea.pena/PenaSide/customer/internal/interface/repository" "gitea.pena/PenaSide/customer/internal/models" "time" ) 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 of ", 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 := log_mw.ExtractLogger(ctx) account, err := receiver.accountRepo.FindByUserID(ctx.Context(), userID) if err != nil { if err.Type() == errors.ErrNotFound { return receiver.middleWare.Error(ctx, fiber.StatusNotFound, "no account", err) } 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 := log_mw.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) 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 map[string]interface{}) 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) { pingTicker := time.NewTicker(5 * time.Second) defer pingTicker.Stop() for { select { case accountData, ok := <-accountCh: if !ok { return } accountJSON, err := json.Marshal(accountData) 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() receiver.logger.Info("Close connection Account Pipe sse") return } case <-pingTicker.C: fmt.Fprintf(w, "data: %s\n\n", `{"event": "ping"}`) if err := w.Flush(); err != nil { receiver.logger.Error("error sending ping Account Pipe sse, close connection", zap.Error(err)) cancel() return } } } }) return nil }