appidea-restapi/routers/api/v1/user/user.go

228 lines
5.1 KiB
Go

package user
import (
"database/sql"
"fmt"
"regexp"
"strings"
"time"
"unicode"
"git.umbach.dev/app-idea/rest-api/modules/database"
"git.umbach.dev/app-idea/rest-api/modules/debug"
"github.com/gofiber/fiber/v2"
"github.com/google/uuid"
"github.com/zhengxiaowai/shortuuid"
"golang.org/x/crypto/bcrypt"
)
//err = bcrypt.CompareHashAndPassword(hashedPassword, []byte("hello wolrd"))
//fmt.Println(err)
func NewUser(c *fiber.Ctx) error {
// swagger:operation POST /users user usersNewUser
// ---
// 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-254)
// type: string
// required: true
// - name: password
// in: query
// description: password of the user (length 6-250)
// type: string
// required: true
// - name: hashtag
// in: query
// description: hashtag of the client (length 2-6, UPPERCASE)
// 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':
// description: user created
// '400':
// description: format is not correct
// '422':
// description: username, email or/and hashtag already assigned
type LoginInput struct {
Username string `json:"username"`
Email string `json:"email"`
Password string `json:"password"`
Hashtag string `json:"hashtag"`
}
var input LoginInput
if err := c.BodyParser(&input); err != nil {
return c.SendStatus(fiber.StatusBadRequest)
}
if !isValid(input.Username, 3, 30) || !isEmailValid(input.Email) || !isValid(input.Password, 6, 250) {
return c.SendStatus(fiber.StatusForbidden)
}
db, err := database.GetDatabase()
if db == nil || err != nil {
return c.SendStatus(fiber.StatusInternalServerError)
}
defer db.Close()
if !isEmailAvailable(db, input.Email) {
return c.SendStatus(fiber.StatusUnprocessableEntity)
}
if input.Hashtag == "" {
input.Hashtag = RandomHashtag(db, 6)
} else if !isHashtagValid(db, input.Hashtag) {
return c.SendStatus(fiber.StatusUnprocessableEntity)
}
password := []byte(input.Password)
hashedPassword, err := bcrypt.GenerateFromPassword(password, bcrypt.DefaultCost)
if err != nil {
debug.Msg("bcrypt password error", err)
return c.SendStatus(fiber.StatusInternalServerError)
}
user_id := strings.Replace(uuid.New().String(), "-", "", -1)
created := time.Now().Format("2006-02-01 15:04:05")
stmt, err := db.Prepare("INSERT INTO users (user_id, user_hashtag, username, email, password, created) VALUES (?, ?, ?, ?, ?, ?);")
stmt.Exec(user_id, input.Hashtag, input.Username, input.Email, hashedPassword, created)
stmt.Close()
debug.Msg("user created", user_id, input.Hashtag, input.Username, input.Email)
return c.SendStatus(fiber.StatusCreated)
}
func RandomHashtag(db *sql.DB, n int) string {
c := make(chan bool)
var s string
for {
su := shortuuid.NewShortUUID()
su.SetAlphabet("ABCDEFGHJKLMNPQRSTUVWXYZ")
s = su.Random(n)
go func() {
err := db.QueryRow("SELECT user_hashtag FROM users WHERE user_hashtag = ?", s).Scan(&s)
if err == sql.ErrNoRows {
c <- true
} else {
c <- false
}
}()
if msg := <-c; msg {
break
}
}
return s
}
func isHashtagValid(db *sql.DB, h string) bool {
if !isUpper(h) || len(h) < 2 || len(h) > 6 {
return false
}
err := db.QueryRow("SELECT user_hashtag FROM users WHERE user_hashtag = ?", h).Scan(&h)
fmt.Println("isHashtagValid", err == sql.ErrNoRows)
if err == sql.ErrNoRows {
return true
}
return false
}
func isUpper(s string) bool {
for _, r := range s {
if !unicode.IsUpper(r) && unicode.IsLetter(r) {
return false
}
}
return true
}
func isValid(s string, min int, max int) bool {
if len(s) < min || len(s) > max {
return false
}
return true
}
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])?)*$")
func isEmailValid(e string) bool {
if len(e) < 3 || len(e) > 254 {
return false
}
return emailRegex.MatchString(e)
}
func isEmailAvailable(db *sql.DB, e string) bool {
err := db.QueryRow("SELECT email FROM users WHERE email = ?", e).Scan(&e)
if err == sql.ErrNoRows {
return true
}
return false
}
func GetUser(c *fiber.Ctx) error {
return c.SendString("user")
}
func GetUsers(c *fiber.Ctx) error {
db, err := database.GetDatabase()
// c.Params("id")
if db == nil || err != nil {
return c.SendString("db error")
}
list := []string{}
var (
name string
)
rows, err := db.Query("SELECT username FROM users;")
fmt.Println("err", err)
defer rows.Close()
fmt.Println("reading data:")
for rows.Next() {
err := rows.Scan(&name)
fmt.Printf("Data row = (%s, %s)\n", name, err)
list = append(list, name)
}
err = rows.Err()
fmt.Println("Done")
return c.JSON(list)
}