master
Alex 2021-05-09 21:00:34 +02:00
parent a58aa7306c
commit 8ad6b6e623
8 changed files with 175 additions and 57 deletions

View File

@ -9,7 +9,7 @@ POST http://localhost:3000/api/v1/users
Content-Type: application/json Content-Type: application/json
{ {
"username": "alex", "username": "ben4",
"email": "restapi@roese.dev", "email": "ben4@roese.dev",
"password": "teksmkamsdkasdmaskdmaskdm" "password": "teksmkamsdkasdmaskdmaskdm"
} }

10
main.go
View File

@ -22,23 +22,17 @@
package main package main
import ( import (
"git.umbach.dev/app-idea/rest-api/routers/config" "git.umbach.dev/app-idea/rest-api/modules/config"
"git.umbach.dev/app-idea/rest-api/routers/database" "git.umbach.dev/app-idea/rest-api/modules/database"
"git.umbach.dev/app-idea/rest-api/routers/router" "git.umbach.dev/app-idea/rest-api/routers/router"
_ "github.com/go-sql-driver/mysql" _ "github.com/go-sql-driver/mysql"
"github.com/gofiber/fiber/v2" "github.com/gofiber/fiber/v2"
"github.com/gofiber/fiber/v2/middleware/cors"
) )
func main() { func main() {
app := fiber.New() app := fiber.New()
app.Use(cors.New(cors.Config{
AllowOrigins: "*",
AllowHeaders: "Origin, Content-Type, Accept",
}))
router.SetupRoutes(app) router.SetupRoutes(app)
config.LoadConfig() config.LoadConfig()

View File

@ -5,7 +5,8 @@ import (
"errors" "errors"
"fmt" "fmt"
"git.umbach.dev/app-idea/rest-api/routers/config" "git.umbach.dev/app-idea/rest-api/modules/config"
"git.umbach.dev/app-idea/rest-api/modules/debug"
) )
func getConnectionString() string { func getConnectionString() string {
@ -15,10 +16,11 @@ func getConnectionString() string {
} }
func GetDatabase() (*sql.DB, error) { func GetDatabase() (*sql.DB, error) {
db, err := sql.Open("mysql", getConnectionString()) db, _ := sql.Open("mysql", getConnectionString())
if err != nil { if err := db.Ping(); err != nil {
return db, errors.New("db error") debug.Msg("DB Connection error:", err.Error())
return nil, errors.New(err.Error())
} }
return db, nil return db, nil

View File

@ -4,7 +4,7 @@ import (
"fmt" "fmt"
"os" "os"
"git.umbach.dev/app-idea/rest-api/routers/config" "git.umbach.dev/app-idea/rest-api/modules/config"
) )
func Msg(msg ...interface{}) { func Msg(msg ...interface{}) {

1
modules/mailer/mailer.go Normal file
View File

@ -0,0 +1 @@
package mailer

View File

@ -1,19 +1,23 @@
package user package user
import ( import (
"database/sql"
"fmt" "fmt"
"math/rand" "math/rand"
"regexp"
"strings" "strings"
"time" "time"
"git.umbach.dev/app-idea/rest-api/routers/database" "git.umbach.dev/app-idea/rest-api/modules/database"
"git.umbach.dev/app-idea/rest-api/routers/debug" "git.umbach.dev/app-idea/rest-api/modules/debug"
"github.com/gofiber/fiber/v2" "github.com/gofiber/fiber/v2"
"github.com/google/uuid" "github.com/google/uuid"
"golang.org/x/crypto/bcrypt" "golang.org/x/crypto/bcrypt"
) )
// NewUser //err = bcrypt.CompareHashAndPassword(hashedPassword, []byte("hello wolrd"))
//fmt.Println(err)
func NewUser(c *fiber.Ctx) error { func NewUser(c *fiber.Ctx) error {
// swagger:operation POST /users user usersNewUser // swagger:operation POST /users user usersNewUser
// --- // ---
@ -28,25 +32,33 @@ func NewUser(c *fiber.Ctx) error {
// required: true // required: true
// - name: email // - name: email
// in: query // in: query
// description: email of the user (length max 200) // description: email of the user (length 3-254)
// type: string // type: string
// required: true // required: true
// - name: password // - name: password
// in: query // in: query
// description: password of the user (length 6-256) // description: password of the user (length 6-250)
// type: string // type: string
// required: true // required: true
// - name: hashtag
// in: query
// description: hashtag of the client (length 1-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: // responses:
// '200': // '201':
// description: user created // description: user created
// schema: // '400':
// type: object // description: format is not correct
// example: // '422':
// user_hastag: XVLBZG // description: username or/and email already already assigned
// default:
// description: unexpected error
// schema:
// "$ref": "#/definitions/errorModel"
type LoginInput struct { type LoginInput struct {
Username string `json:"username"` Username string `json:"username"`
Email string `json:"email"` Email string `json:"email"`
@ -55,17 +67,24 @@ func NewUser(c *fiber.Ctx) error {
var input LoginInput var input LoginInput
// check if given request is complete
if err := c.BodyParser(&input); err != nil { if err := c.BodyParser(&input); err != nil {
return c.Status(fiber.StatusBadRequest).JSON(fiber.Map{"status": "error", "message": "Error on login request", "data": err}) return c.SendStatus(fiber.StatusBadRequest)
} }
// check in database if username and email is available if !isValid(input.Username, 3, 30) || !isEmailValid(input.Email) || !isValid(input.Password, 6, 250) {
return c.SendStatus(fiber.StatusForbidden)
}
db, err := database.GetDatabase() db, err := database.GetDatabase()
if db == nil || err != nil { if db == nil || err != nil {
return c.SendString(err.Error()) return c.SendStatus(fiber.StatusInternalServerError)
}
defer db.Close()
if !isValueAvailable(db, "email", input.Email) {
return c.SendStatus(fiber.StatusUnprocessableEntity)
} }
password := []byte(input.Password) password := []byte(input.Password)
@ -73,38 +92,139 @@ func NewUser(c *fiber.Ctx) error {
hashedPassword, err := bcrypt.GenerateFromPassword(password, bcrypt.DefaultCost) hashedPassword, err := bcrypt.GenerateFromPassword(password, bcrypt.DefaultCost)
if err != nil { if err != nil {
panic(err) debug.Msg("bcrypt password error", err)
return c.SendStatus(fiber.StatusInternalServerError)
} }
//err = bcrypt.CompareHashAndPassword(hashedPassword, []byte("hello wolrd"))
//fmt.Println(err)
user_id := strings.Replace(uuid.New().String(), "-", "", -1) user_id := strings.Replace(uuid.New().String(), "-", "", -1)
user_hashtag := RandomHashtag(6) user_hashtag := RandomHashtag(db, 6)
created := time.Now().Format("2006-02-01 15:04:05") created := time.Now().Format("2006-02-01 15:04:05")
sqlStatement, err := db.Prepare("INSERT INTO users (user_id, user_hashtag, username, email, password, created) VALUES (?, ?, ?, ?, ?, ?);") stmt, err := db.Prepare("INSERT INTO users (user_id, user_hashtag, username, email, password, created) VALUES (?, ?, ?, ?, ?, ?);")
sqlStatement.Exec(user_id, user_hashtag, input.Username, input.Email, hashedPassword, created) stmt.Exec(user_id, user_hashtag, input.Username, input.Email, hashedPassword, created)
db.Close() stmt.Close()
debug.Msg("user created", user_id, user_hashtag, input.Username, input.Email) debug.Msg("user created", user_id, user_hashtag, input.Username, input.Email)
return c.JSON(fiber.Map{"status": 1, "user_hashtag": user_hashtag}) return c.SendStatus(fiber.StatusCreated)
} }
func RandomHashtag(n int) string { func RandomHashtag(db *sql.DB, n int) string {
var letters = []rune("ABCDEFGHIJKLMNOPQRSTUVWXYZ") var letters = []rune("ABCDEFGHIJKLMNOPQRSTUVWXYZ")
s := make([]rune, n) s := make([]rune, n)
c := make(chan bool)
for i := range s { for {
s[i] = letters[rand.Intn(len(letters))] // TODO: randomness is very bad -> always the same
for i := range s {
s[i] = letters[rand.Intn(len(letters))]
}
fmt.Println(string(s))
go func() {
fmt.Println("conncur", string(s))
err := db.QueryRow("SELECT user_hashtag FROM users WHERE user_hashtag = ?", string(s)).Scan(&s)
fmt.Println("ErrNoRows?", err == sql.ErrNoRows)
if err == sql.ErrNoRows {
c <- true
} else {
c <- false
}
}()
msg := <-c
fmt.Println("msg", msg)
if msg {
break
}
} }
//fmt.Println(s) fmt.Println("r string", string(s))
return string(s) return string(s)
/*
err := db.QueryRow("SELECT user_hashtag FROM users WHERE user_hashtag = ?", s).Scan(&s)
if err == sql.ErrNoRows {
return true
}
return false
return string(s) */
}
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 isValueAvailable(db *sql.DB, t string, v string) bool {
q := fmt.Sprintf("SELECT %s FROM users WHERE %s = ?", t, t)
err := db.QueryRow(q, v).Scan(&v)
fmt.Println(err == sql.ErrNoRows)
if err == sql.ErrNoRows {
return true
}
return false
/*
if email == "" {
return true
}
return false */
/*
ctx := context.Background()
tsql := fmt.Sprintf("SELECT email FROM users WHERE email = '%s'", e)
rows, err := db.QueryContext(ctx, tsql)
if err != nil {
panic(err)
}
fmt.Println("rows", rows)
defer rows.Close()
var count int
for rows.Next() {
var email string
err := rows.Scan(&email)
if err != nil {
panic(err)
}
fmt.Printf("Email: %s \n", email)
count++
}
fmt.Println(count)
*/
} }
func GetUser(c *fiber.Ctx) error { func GetUser(c *fiber.Ctx) error {
@ -127,6 +247,7 @@ func GetUsers(c *fiber.Ctx) error {
) )
rows, err := db.Query("SELECT username FROM users;") rows, err := db.Query("SELECT username FROM users;")
fmt.Println("err", err)
defer rows.Close() defer rows.Close()
fmt.Println("reading data:") fmt.Println("reading data:")
for rows.Next() { for rows.Next() {

View File

@ -26,24 +26,24 @@ paths:
name: email name: email
required: true required: true
type: string type: string
- description: password of the user (length 6-256) - description: password of the user (length 6-250)
in: query in: query
name: password name: password
required: true required: true
type: string type: string
- description: hashtag of the client (length 1-6, UPPERCASE)
in: query
name: hashtag
type: string
produces: produces:
- application/json - application/json
responses: responses:
"200": "201":
description: user created description: user created
schema: "400":
example: description: format is not correct
user_hastag: XVLBZG "422":
type: object description: username or/and email already already assigned
default:
description: unexpected error
schema:
$ref: '#/definitions/errorModel'
summary: Create new user summary: Create new user
tags: tags:
- user - user