Some kind of starting point
This commit is contained in:
11
src/db/types.go
Normal file
11
src/db/types.go
Normal file
@@ -0,0 +1,11 @@
|
||||
package db
|
||||
|
||||
import (
|
||||
"github.com/google/uuid"
|
||||
)
|
||||
|
||||
// User is a user in the system
|
||||
type User struct {
|
||||
ID uuid.UUID `json:"id"`
|
||||
Username string `json:"username"`
|
||||
}
|
||||
17
src/handlers/get.go
Normal file
17
src/handlers/get.go
Normal file
@@ -0,0 +1,17 @@
|
||||
package handlers
|
||||
|
||||
import (
|
||||
"github.com/gofiber/fiber/v2"
|
||||
log "github.com/sirupsen/logrus"
|
||||
)
|
||||
|
||||
// Hello handler
|
||||
func Hello(c *fiber.Ctx) error {
|
||||
return c.SendString("Hello, World!")
|
||||
}
|
||||
|
||||
// UserGet handler
|
||||
func UserGet(c *fiber.Ctx) error {
|
||||
log.WithFields(log.Fields{"userID": c.Params("userID")}).Debug("GETing user")
|
||||
return c.SendString("USER ffs")
|
||||
}
|
||||
20
src/handlers/middlewares.go
Normal file
20
src/handlers/middlewares.go
Normal file
@@ -0,0 +1,20 @@
|
||||
package handlers
|
||||
|
||||
import (
|
||||
"github.com/gofiber/fiber/v2"
|
||||
log "github.com/sirupsen/logrus"
|
||||
)
|
||||
|
||||
// RequireJSON is a middleware that makes sure the request content-type always is application/json (or nothing, defaulting to application/json)
|
||||
func RequireJSON(c *fiber.Ctx) error {
|
||||
c.Accepts("application/json")
|
||||
contentType := string(c.Request().Header.ContentType())
|
||||
|
||||
if contentType != "application/json" && contentType != "" {
|
||||
log.WithFields(log.Fields{"content-type": contentType}).Debug("Invalid content-type in request")
|
||||
return c.Status(415).JSON([]ResJSONError{{Error: "Invalid content-type"}})
|
||||
}
|
||||
|
||||
c.Next()
|
||||
return nil
|
||||
}
|
||||
44
src/handlers/post.go
Normal file
44
src/handlers/post.go
Normal file
@@ -0,0 +1,44 @@
|
||||
package handlers
|
||||
|
||||
import (
|
||||
"github.com/gofiber/fiber/v2"
|
||||
"github.com/google/uuid"
|
||||
|
||||
"gitlab.larvit.se/power-plan/api/src/db"
|
||||
)
|
||||
|
||||
// UserCreate creates a new user
|
||||
func UserCreate(c *fiber.Ctx) error {
|
||||
type UserInput struct {
|
||||
Username string `json:"username"`
|
||||
Password string `json:"password"`
|
||||
}
|
||||
|
||||
userInput := new(UserInput)
|
||||
|
||||
if err := c.BodyParser(userInput); err != nil {
|
||||
return c.Status(400).JSON([]ResJSONError{
|
||||
{Error: err.Error()},
|
||||
})
|
||||
}
|
||||
|
||||
var errors []ResJSONError
|
||||
|
||||
if userInput.Username == "" {
|
||||
errors = append(errors, ResJSONError{Error: "Can not be empty", Field: "username"})
|
||||
}
|
||||
if userInput.Password == "" {
|
||||
errors = append(errors, ResJSONError{Error: "Can not be empty", Field: "password"})
|
||||
}
|
||||
|
||||
if len(errors) != 0 {
|
||||
return c.Status(400).JSON(errors)
|
||||
}
|
||||
|
||||
createdUser := db.User{
|
||||
ID: uuid.New(),
|
||||
Username: userInput.Username,
|
||||
}
|
||||
|
||||
return c.Status(201).JSON(createdUser)
|
||||
}
|
||||
7
src/handlers/types.go
Normal file
7
src/handlers/types.go
Normal file
@@ -0,0 +1,7 @@
|
||||
package handlers
|
||||
|
||||
// ResJSONError is an error field that is used in JSON error responses
|
||||
type ResJSONError struct {
|
||||
Error string `json:"error"`
|
||||
Field string `json:"field,omitempty"`
|
||||
}
|
||||
62
src/utils.go
Normal file
62
src/utils.go
Normal file
@@ -0,0 +1,62 @@
|
||||
package utils
|
||||
|
||||
import (
|
||||
"encoding/json"
|
||||
"errors"
|
||||
"fmt"
|
||||
"io"
|
||||
"net/http"
|
||||
"strings"
|
||||
)
|
||||
|
||||
type malformedRequest struct {
|
||||
status int
|
||||
msg string
|
||||
}
|
||||
|
||||
func (mr *malformedRequest) Error() string {
|
||||
return mr.msg
|
||||
}
|
||||
|
||||
// ValidateJSONBody validates a JSON request body to a destionation interface
|
||||
func ValidateJSONBody(req *http.Request, dst interface{}) error {
|
||||
dec := json.NewDecoder(req.Body)
|
||||
dec.DisallowUnknownFields()
|
||||
|
||||
err := dec.Decode(&dst)
|
||||
if err != nil {
|
||||
var syntaxError *json.SyntaxError
|
||||
var unmarshalTypeError *json.UnmarshalTypeError
|
||||
|
||||
if errors.As(err, &syntaxError) {
|
||||
msg := fmt.Sprintf("Request body contains badly-formed JSON (at position %d)", syntaxError.Offset)
|
||||
return &malformedRequest{status: http.StatusBadRequest, msg: msg}
|
||||
} else if errors.Is(err, io.ErrUnexpectedEOF) {
|
||||
msg := fmt.Sprintf("Request body contains badly-formed JSON")
|
||||
return &malformedRequest{status: http.StatusBadRequest, msg: msg}
|
||||
} else if errors.As(err, &unmarshalTypeError) {
|
||||
msg := fmt.Sprintf("Request body contains an invalid value for the %q field (at position %d)", unmarshalTypeError.Field, unmarshalTypeError.Offset)
|
||||
return &malformedRequest{status: http.StatusBadRequest, msg: msg}
|
||||
} else if strings.HasPrefix(err.Error(), "json: unknown field ") {
|
||||
fieldName := strings.TrimPrefix(err.Error(), "json: unknown field ")
|
||||
msg := fmt.Sprintf("Request body contains unknown field %s", fieldName)
|
||||
return &malformedRequest{status: http.StatusBadRequest, msg: msg}
|
||||
} else if errors.Is(err, io.EOF) {
|
||||
msg := "Request body must not be empty"
|
||||
return &malformedRequest{status: http.StatusBadRequest, msg: msg}
|
||||
} else if err.Error() == "http: request body too large" {
|
||||
msg := "Request body must not be larger than 1MB"
|
||||
return &malformedRequest{status: http.StatusRequestEntityTooLarge, msg: msg}
|
||||
} else {
|
||||
return err
|
||||
}
|
||||
}
|
||||
|
||||
err = dec.Decode(&struct{}{})
|
||||
if err != io.EOF {
|
||||
msg := "Request body must only contain a single JSON object"
|
||||
return &malformedRequest{status: http.StatusBadRequest, msg: msg}
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
Reference in New Issue
Block a user