Forked from
whats-this / api
23 commits behind the upstream repository.
createuser.go 2.97 KiB
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{
Success: true,
StatusCode: nil,
UserID: userIDStr,
UserToken: tokenStr,
})
}