Skip to content
Snippets Groups Projects
createuser.go 2.97 KiB
Newer Older
  • Learn to ignore specific revisions
  • package routes
    
    import (
    	"encoding/json"
    	"net/http"
    	"regexp"
    	"strings"
    
    	"owo.codes/whats-this/api/lib/apierrors"
    	"owo.codes/whats-this/api/lib/db"
    	"owo.codes/whats-this/api/lib/middleware"
    
    	"github.com/go-chi/render"
    	"github.com/gofrs/uuid"
    	"github.com/rs/zerolog/log"
    )
    
    // Username regex.
    var usernameRegex = regexp.MustCompile(`(?i)^[a-z0-9][a-z0-9_-]+[a-z0-9]$`)
    
    // Email regex. Taken from http://www.golangprograms.com/regular-expression-to-validate-email-address.html
    var emailRegex = regexp.MustCompile("^[a-zA-Z0-9.!#$%&'*+/=?^_`{|}~-]+@[a-zA-Z0-9](?:[a-zA-Z0-9-]{0,61}[a-zA-Z0-9])?(?:\\.[a-zA-Z0-9](?:[a-zA-Z0-9-]{0,61}[a-zA-Z0-9])?)*$")
    
    // createUserBody is the request body for a create user request.
    type createUserBody struct {
    	Email    string `json:"email"`
    	Username string `json:"username"`
    }
    
    // createUserResponse is the response body for a successful create user request.
    type createUserResponse struct {
    	Success    bool   `json:"success"`
    	StatusCode *int   `json:"errorcode"`
    	UserID     string `json:"id"`
    	UserToken  string `json:"token"`
    }
    
    // CreateUser handles user creation requests.
    func CreateUser(w http.ResponseWriter, r *http.Request) {
    	// Only authorized admin users can use this route
    	user := middleware.GetAuthorizedUser(r)
    	if user.ID == "" || user.IsBlocked || !user.IsAdmin {
    		panic(apierrors.Unauthorized)
    	}
    
    	// Parse request body
    	decoder := json.NewDecoder(r.Body)
    	var body createUserBody
    	err := decoder.Decode(&body)
    	if err != nil {
    		panic(apierrors.InvalidJSONPayload)
    	}
    
    	// Validate request body
    	if !emailRegex.MatchString(body.Email) && !usernameRegex.MatchString(body.Username) {
    		panic(apierrors.InvalidEmailOrUsername)
    	}
    
    	// Check if user exists
    	body.Email = strings.TrimSpace(body.Email)
    	body.Username = strings.TrimSpace(body.Username)
    	exists, err := db.CheckUserExistsByUsernameOrEmail(body.Username, body.Email)
    	if err != nil {
    		log.Error().Err(err).Msg("failed to check if user exists")
    		panic(apierrors.InternalServerError)
    	}
    	if exists {
    		panic(apierrors.UserExists)
    	}
    
    	// Insert user
    	userID, err := uuid.NewV4()
    	if err != nil {
    		log.Error().Err(err).Msg("failed to generate new user UUID")
    		panic(apierrors.InternalServerError)
    	}
    	userIDStr := userID.String()
    	err = db.InsertUser(userIDStr, body.Username, body.Email)
    	if err != nil {
    		log.Error().Err(err).Msg("failed to insert user into database")
    		panic(apierrors.InternalServerError)
    	}
    
    	// Insert token
    	token, err := uuid.NewV4()
    	if err != nil {
    		log.Error().Err(err).Msg("failed to generate new token UUID")
    		panic(apierrors.InternalServerError)
    	}
    	tokenStr := token.String()
    	err = db.InsertToken(userIDStr, tokenStr)
    	if err != nil {
    		log.Error().Err(err).Msg("failed to insert token into database")
    		panic(apierrors.InternalServerError)
    	}
    
    	// Return response
    	w.Header().Set("Content-Type", "application/json")
    	w.WriteHeader(http.StatusOK)
    	render.JSON(w, r, createUserResponse{
    
    Dean's avatar
    Dean committed
    		Success:    true,
    
    Dean's avatar
    Dean committed
    		UserID:     userIDStr,
    		UserToken:  tokenStr,