package user import ( "encoding/base64" "encoding/json" "strings" "time" "git.umbach.dev/app-idea/rest-api/modules/database" "git.umbach.dev/app-idea/rest-api/modules/rabbitmq" "git.umbach.dev/app-idea/rest-api/modules/structs" "github.com/gofiber/fiber/v2" "github.com/google/uuid" log "github.com/sirupsen/logrus" "golang.org/x/crypto/bcrypt" ) type RegisterInput struct { Username string `json:"username"` Email string `json:"email"` Password string `json:"password"` Hashtag string `json:"hashtag"` LanguageId int `json:"languageId"` } func NewUser(c *fiber.Ctx) error { // swagger:operation POST /users User user // --- // summary: Create new user // produces: // - application/json // parameters: // - name: username // in: query // description: username of the user (length 3-30) // type: string // required: true // - name: email // in: query // description: email of the user (length 3-255) // type: string // required: true // - name: password // in: query // description: password (base64) of the user (length 6-250) // type: string // required: true // - name: hashtag // in: query // description: hashtag of the client (length 2-6, UPPERCASE (Letters, Numbers)) // type: string // - name: avatar_url // in: query // description: avatar url of the client // type: string // - name: location // in: query // description: location of the client (length 1-20) (for example Frankfurt) // type: string // responses: // '201': // "$ref": "#/definitions/User" // '400': // description: format is not correct // '422': // description: username, email or/and hashtag already assigned var input RegisterInput if err := c.BodyParser(&input); err != nil { log.Debugln("bodyParser failed:", err) return c.SendStatus(fiber.StatusBadRequest) } decodedPassword, err := base64.StdEncoding.DecodeString(input.Password) if err != nil { log.Debugln("base64 decoding failed:", err) return c.SendStatus(fiber.StatusBadRequest) } input.Password = string(decodedPassword) if !isUsernameValid(input.Username) || !isEmailValid(input.Email) || !isPasswordValid(input.Password) { return c.SendStatus(fiber.StatusForbidden) } user := structs.User{Email: input.Email} db := database.DB if !isEmailAvailable(db, user.Email) { return c.SendStatus(fiber.StatusUnprocessableEntity) } if input.Hashtag == "" { input.Hashtag, err = generateRandomHashtag(db, 6) if err != nil { return c.SendStatus(fiber.StatusInternalServerError) } } else if !isHashtagValid(db, 1, input.Hashtag) { return c.SendStatus(fiber.StatusUnprocessableEntity) } hashedPassword, err := bcrypt.GenerateFromPassword([]byte(input.Password), bcrypt.DefaultCost) if err != nil { log.Warnln("Failed to bcrypt password", err) return c.SendStatus(fiber.StatusInternalServerError) } now := time.Now() user.Id = strings.Replace(uuid.New().String(), "-", "", -1) user.Hashtag = input.Hashtag user.Name = input.Username user.Password = string(hashedPassword) user.LanguageId = input.LanguageId user.State = 1 user.LastLogin = now user.CreatedAt = now res := db.Create(&user) if res.Error != nil { log.Warnln("Failed to insert user to db:", res.Error) return c.SendStatus(fiber.StatusInternalServerError) } userActionId, err := createUserAction(user.Id) if err != nil { return c.SendStatus(fiber.StatusInternalServerError) } rabbitmq.PublishMail(user.Email, 0, user.LanguageId, json.RawMessage(`{"name": "`+user.Name+`", "email": "`+user.Email+`", "url": "http://localhost:3000/api/v1/user/action/0/`+userActionId+`"}`)) sessionId, err := createUserSession(db, user.Id, c.IP(), string(c.Context().UserAgent())) if err != nil { return c.SendStatus(fiber.StatusInternalServerError) } expires := getUserSessionExpiresTime() cfg := cfg.Settings.Cookies c.Cookie(&fiber.Cookie{Name: cfg.SessionId, Value: sessionId, Secure: true, HTTPOnly: true, Expires: expires}) c.Cookie(&fiber.Cookie{Name: cfg.Username, Value: input.Username, Secure: true, Expires: expires}) c.Cookie(&fiber.Cookie{Name: cfg.UserHashtag, Value: input.Hashtag, Secure: true, Expires: expires}) log.Debugln("user created", user) return c.SendStatus(fiber.StatusCreated) } func ActivateUser(c *fiber.Ctx) error { // swagger:operation POST /user/action/{actionType}/{actionId} User activation // --- // summary: Activate user // parameters: // - name: id // in: query // description: action id // type: string // required: true // responses: // '200': // description: User was activated // '401': // description: action Id is incorrect or expired db := database.DB deleteExpiredUserActions(db) userAction := structs.UserAction{Id: c.Params("actionId")} log.Infoln("activate user", c.Params("actionType"), c.Params("actionId")) db.Select("user_id").Where("id = ?", c.Params("actionId")).Find(&userAction) log.Infoln("usA", userAction) if userAction.UserId == "" { log.Info("StatusUnauthorized") return c.SendStatus(fiber.StatusUnauthorized) } db.Delete(userAction) db.Model(structs.User{Id: userAction.UserId}).Update("state", 0) return c.SendStatus(fiber.StatusOK) }