package account import ( "database/sql" "encoding/json" "errors" "fmt" "github.com/go-redis/redis/v8" "github.com/gofiber/fiber/v2" "penahub.gitlab.yandexcloud.net/backend/penahub_common/log_mw" "penahub.gitlab.yandexcloud.net/backend/quiz/common.git/dal" "penahub.gitlab.yandexcloud.net/backend/quiz/common.git/middleware" "penahub.gitlab.yandexcloud.net/backend/quiz/common.git/model" "penahub.gitlab.yandexcloud.net/backend/quiz/common.git/pj_errors" "penahub.gitlab.yandexcloud.net/backend/quiz/core/internal/brokers" "penahub.gitlab.yandexcloud.net/backend/quiz/core/internal/clients/auth" "penahub.gitlab.yandexcloud.net/backend/quiz/core/internal/models" "strconv" "time" ) type Deps struct { Dal *dal.DAL AuthClient *auth.AuthClient Producer *brokers.Producer ServiceName string RedisClient *redis.Client } type Account struct { dal *dal.DAL authClient *auth.AuthClient producer *brokers.Producer serviceName string redisClient *redis.Client } func NewAccountController(deps Deps) *Account { return &Account{ dal: deps.Dal, authClient: deps.AuthClient, producer: deps.Producer, serviceName: deps.ServiceName, redisClient: deps.RedisClient, } } type CreateAccountReq struct { UserID string `json:"userId"` } type CreateAccountResp struct { CreatedAccount model.Account `json:"created_account"` } type DeleteAccountResp struct { DeletedAccountID string `json:"account_Id"` } type GetPrivilegeByUserIDReq struct { UserID string `json:"userId"` } type DeleteAccountByUserIDReq struct { UserID string `json:"userId"` } type DeleteAccountByUserIDResp struct { DeletedAccountUserID string `json:"userId"` } type GetAccountsReq struct { Limit uint64 `json:"limit"` Page uint64 `json:"page"` } type GetAccountsResp struct { Count uint64 `json:"count"` Items []model.Account `json:"items"` } // getCurrentAccount обработчик для получения текущего аккаунта func (r *Account) GetCurrentAccount(ctx *fiber.Ctx) error { accountID, ok := middleware.GetAccountId(ctx) if !ok { return ctx.Status(fiber.StatusUnauthorized).SendString("account id is required") } account, err := r.dal.AccountRepo.GetAccountByID(ctx.Context(), accountID) if err != nil && err != sql.ErrNoRows { return ctx.Status(fiber.StatusInternalServerError).SendString(err.Error()) } //TODO: fix this later if account.ID == "" { return ctx.Status(fiber.StatusNotFound).SendString("no account") } return ctx.Status(fiber.StatusOK).JSON(account) } // createAccount обработчик для создания нового аккаунта func (r *Account) CreateAccount(ctx *fiber.Ctx) error { accountID, ok := middleware.GetAccountId(ctx) if !ok { return ctx.Status(fiber.StatusUnauthorized).SendString("account id is required") } hlogger := log_mw.ExtractLogger(ctx) existingAccount, err := r.dal.AccountRepo.GetAccountByID(ctx.Context(), accountID) if err != nil && err != sql.ErrNoRows { return ctx.Status(fiber.StatusInternalServerError).SendString(err.Error()) } if existingAccount.ID != "" { return ctx.Status(fiber.StatusConflict).SendString("user with this ID already exists") } email, err := r.authClient.GetUserEmail(accountID) if err != nil { return err } newAccount := model.Account{ UserID: accountID, CreatedAt: time.Now(), Deleted: false, Privileges: map[string]model.ShortPrivilege{ "quizUnlimTime": { PrivilegeID: "quizUnlimTime", PrivilegeName: "Безлимит Опросов", Amount: 14, CreatedAt: time.Now(), }, }, } createdAcc, err := r.dal.AccountRepo.CreateAccount(ctx.Context(), &newAccount) if err != nil { return ctx.Status(fiber.StatusInternalServerError).SendString(err.Error()) } _, err = r.dal.AccountRepo.PostLeadTarget(ctx.Context(), model.LeadTarget{ AccountID: accountID, Target: email, Type: model.LeadTargetEmail, QuizID: 0, }) if err != nil { return ctx.Status(fiber.StatusInternalServerError).SendString(err.Error()) } hlogger.Emit(models.InfoAccountCreated{ CtxUserID: accountID, CtxAccountID: createdAcc.ID, }) err = r.producer.ToMailNotify(ctx.Context(), brokers.Message{ AccountID: accountID, Email: email, ServiceKey: r.serviceName, SendAt: time.Now(), }) if err != nil { return ctx.Status(fiber.StatusInternalServerError).SendString(err.Error()) } return ctx.JSON(CreateAccountResp{ CreatedAccount: newAccount, }) } // deleteAccount обработчик для удаления текущего аккаунта func (r *Account) DeleteAccount(ctx *fiber.Ctx) error { accountID, ok := middleware.GetAccountId(ctx) if !ok { return ctx.Status(fiber.StatusUnauthorized).SendString("account id is required") } account, err := r.dal.AccountRepo.GetAccountByID(ctx.Context(), accountID) if err != nil { return ctx.Status(fiber.StatusInternalServerError).SendString(err.Error()) } if err := r.dal.AccountRepo.DeleteAccount(ctx.Context(), account.ID); err != nil { return ctx.Status(fiber.StatusInternalServerError).SendString(err.Error()) } return ctx.JSON(DeleteAccountResp{ DeletedAccountID: accountID, }) } // getPrivilegeByUserID обработчик для получения привилегий аккаунта по ID пользователя func (r *Account) GetPrivilegeByUserID(ctx *fiber.Ctx) error { var req GetPrivilegeByUserIDReq if err := ctx.BodyParser(&req); err != nil { return ctx.Status(fiber.StatusBadRequest).SendString("Invalid request data") } privilege, err := r.dal.AccountRepo.GetPrivilegesByAccountID(ctx.Context(), req.UserID) if err != nil { return ctx.Status(fiber.StatusInternalServerError).SendString(err.Error()) } return ctx.Status(fiber.StatusOK).JSON(privilege) } // deleteAccountByUserID обработчик для удаления аккаунта по ID пользователя func (r *Account) DeleteAccountByUserID(ctx *fiber.Ctx) error { var req DeleteAccountByUserIDReq if err := ctx.BodyParser(&req); err != nil { return ctx.Status(fiber.StatusBadRequest).SendString("Invalid request data") } existingAccount, err := r.dal.AccountRepo.GetAccountByID(ctx.Context(), req.UserID) if err != nil { return ctx.Status(fiber.StatusInternalServerError).SendString(err.Error()) } if existingAccount.ID == "" { return ctx.Status(fiber.StatusInternalServerError).SendString("user with this ID not found") } if err := r.dal.AccountRepo.DeleteAccount(ctx.Context(), existingAccount.ID); err != nil { return ctx.Status(fiber.StatusInternalServerError).SendString(err.Error()) } return ctx.JSON(DeleteAccountByUserIDResp{ DeletedAccountUserID: req.UserID, }) } // getAccounts обработчик для получения списка аккаунтов с пагинацией func (r *Account) GetAccounts(ctx *fiber.Ctx) error { var req GetAccountsReq if err := ctx.BodyParser(&req); err != nil { return ctx.Status(fiber.StatusBadRequest).SendString("Invalid request data") } _, ok := middleware.GetAccountId(ctx) if !ok { return ctx.Status(fiber.StatusUnauthorized).SendString("account id is required") } accounts, totalCount, err := r.dal.AccountRepo.GetAccounts(ctx.Context(), req.Limit, req.Page) if err != nil { return ctx.Status(fiber.StatusInternalServerError).SendString(err.Error()) } response := GetAccountsResp{ Count: totalCount, Items: accounts, } return ctx.Status(fiber.StatusOK).JSON(response) } func (r *Account) ManualDone(ctx *fiber.Ctx) error { var req struct { Id string `json:"id"` } if err := ctx.BodyParser(&req); err != nil { return ctx.Status(fiber.StatusBadRequest).SendString("Invalid request data") } if req.Id == "" { return ctx.Status(fiber.StatusBadRequest).SendString("User id is required") } err := r.dal.AccountRepo.ManualDone(ctx.Context(), req.Id) if err != nil { if errors.Is(err, pj_errors.ErrNotFound) { return ctx.Status(fiber.StatusNotFound).SendString("user don't have this privilege") } return ctx.Status(fiber.StatusInternalServerError).SendString("Internal Server Error") } return ctx.SendStatus(fiber.StatusOK) } func (r *Account) PostLeadTarget(ctx *fiber.Ctx) error { var req struct { Type string `json:"type"` QuizID int32 `json:"quizID"` Target string `json:"target"` Name string `json:"name"` } if err := ctx.BodyParser(&req); err != nil { return ctx.Status(fiber.StatusBadRequest).SendString("Invalid request data") } accountID, ok := middleware.GetAccountId(ctx) if !ok { return ctx.Status(fiber.StatusUnauthorized).SendString("account id is required") } //accountID := "64f2cd7a7047f28fdabf6d9e" if _, ok := model.ValidLeadTargetTypes[req.Type]; !ok { return ctx.Status(fiber.StatusBadRequest).SendString("Invalid type") } if req.Type == "" || (req.Target == "" && req.Type != string(model.LeadTargetTg)) { return ctx.Status(fiber.StatusBadRequest).SendString("Type and Target don't be nil") } switch req.Type { case "mail": _, err := r.dal.AccountRepo.PostLeadTarget(ctx.Context(), model.LeadTarget{ AccountID: accountID, Target: req.Target, Type: model.LeadTargetType(req.Type), QuizID: req.QuizID, }) if err != nil { return ctx.Status(fiber.StatusInternalServerError).SendString(err.Error()) } return ctx.SendStatus(fiber.StatusOK) case "telegram": targets, err := r.dal.AccountRepo.GetLeadTarget(ctx.Context(), accountID, req.QuizID) if err != nil && !errors.Is(err, pj_errors.ErrNotFound) { return ctx.Status(fiber.StatusInternalServerError).SendString(err.Error()) } if !errors.Is(err, pj_errors.ErrNotFound) { for _, t := range targets { if t.Type == model.LeadTargetTg { return ctx.Status(fiber.StatusAlreadyReported).SendString("LeadTarget for this quiz already exist") } } } task := model.TgRedisTask{ Name: req.Name, QuizID: req.QuizID, AccountID: accountID, } taskKey := fmt.Sprintf("telegram_task:%d", time.Now().UnixNano()) taskData, err := json.Marshal(task) if err != nil { return ctx.Status(fiber.StatusInternalServerError).SendString(err.Error()) } if err := r.redisClient.Set(ctx.Context(), taskKey, taskData, 0).Err(); err != nil { return ctx.Status(fiber.StatusInternalServerError).SendString(err.Error()) } case "whatsapp": return ctx.Status(fiber.StatusOK).SendString("todo") } return nil } func (r *Account) DeleteLeadTarget(ctx *fiber.Ctx) error { leadIDStr := ctx.Params("id") leadID, err := strconv.ParseInt(leadIDStr, 10, 64) if err != nil { return ctx.Status(fiber.StatusBadRequest).SendString("Invalid lead ID format") } err = r.dal.AccountRepo.DeleteLeadTarget(ctx.Context(), leadID) if err != nil { return ctx.Status(fiber.StatusInternalServerError).SendString(err.Error()) } return ctx.SendStatus(fiber.StatusOK) } func (r *Account) GetLeadTarget(ctx *fiber.Ctx) error { accountID, ok := middleware.GetAccountId(ctx) if !ok { return ctx.Status(fiber.StatusUnauthorized).SendString("account id is required") } quizIDStr := ctx.Params("quizID") quizID, err := strconv.ParseInt(quizIDStr, 10, 64) if err != nil { return ctx.Status(fiber.StatusBadRequest).SendString("Invalid quiz ID format") } result, err := r.dal.AccountRepo.GetLeadTarget(ctx.Context(), accountID, int32(quizID)) if err != nil { switch { case errors.Is(err, pj_errors.ErrNotFound): return ctx.Status(fiber.StatusNotFound).SendString("this lead target not found") default: return ctx.Status(fiber.StatusInternalServerError).SendString(err.Error()) } } return ctx.Status(fiber.StatusOK).JSON(result) } func (r *Account) UpdateLeadTarget(ctx *fiber.Ctx) error { var req struct { ID int64 `json:"id"` Target string `json:"target"` } if err := ctx.BodyParser(&req); err != nil { return ctx.Status(fiber.StatusBadRequest).SendString("Invalid request data") } if req.ID == 0 || req.Target == "" { return ctx.Status(fiber.StatusBadRequest).SendString("ID and Target don't be nil") } result, err := r.dal.AccountRepo.UpdateLeadTarget(ctx.Context(), model.LeadTarget{ ID: req.ID, Target: req.Target, }) if err != nil { switch { case errors.Is(err, pj_errors.ErrNotFound): return ctx.Status(fiber.StatusNotFound).SendString("this lead target not found") default: return ctx.Status(fiber.StatusInternalServerError).SendString(err.Error()) } } return ctx.Status(fiber.StatusOK).JSON(result) }