chore: add user schema

This commit is contained in:
2025-06-05 02:25:09 +01:00
parent 0879aec750
commit b70bdba0dc
9 changed files with 570 additions and 70 deletions
+5 -1
View File
@@ -13,12 +13,16 @@ type API struct {
}
func (api *API) Initialize() {
validator := validator.New(validator.WithRequiredStructEnabled())
validator.RegisterValidation("password", PasswordValidator)
db := &models.DB{}
ech := echo.New()
ech.Validator = &CustomValidator{validator: validator.New(validator.WithRequiredStructEnabled())}
ech.Validator = &CustomValidator{validator: validator}
db.Connect()
db.MigrateClients()
db.MigrateUsers()
api.instance = ech
api.db = db
+9 -9
View File
@@ -6,12 +6,12 @@ import (
"net/http"
"strconv"
"github.com/hazemKrimi/crimson-vault/internal/models"
"github.com/hazemKrimi/crimson-vault/internal/types"
"github.com/labstack/echo/v4"
)
func (api *API) CreateClientHandler(context echo.Context) error {
var body models.CreateClientRequestBody
var body types.CreateClientRequestBody
if err := context.Bind(&body); err != nil {
log.Println(fmt.Sprintf("Error creating Client: %v.", err))
@@ -52,13 +52,13 @@ func (api *API) GetClientHandler(context echo.Context) error {
return context.String(http.StatusInternalServerError, "Unexpected error getting Client!")
}
var client models.Client
var client types.Client
if err := api.db.GetClient(id, &client); err != nil {
return context.String(http.StatusNotFound, "Client not found!")
}
log.Println(fmt.Sprintf("Got client with ID %d.", client.ID))
log.Println(fmt.Sprintf("Got User with ID %d.", client.ID))
return context.JSON(http.StatusOK, client)
}
@@ -75,7 +75,7 @@ func (api *API) UpdateClientHandler(context echo.Context) error {
return context.String(http.StatusInternalServerError, "Unexpected error updating Client!")
}
var body models.UpdateClientRequestBody
var body types.UpdateClientRequestBody
if err := context.Bind(&body); err != nil {
log.Println(fmt.Sprintf("Error updating Client: %v.", err))
@@ -86,13 +86,13 @@ func (api *API) UpdateClientHandler(context echo.Context) error {
return err
}
var client models.Client
var client types.Client
if err := api.db.UpdateClient(id, body, &client); err != nil {
return context.String(http.StatusNotFound, "Client not found!")
}
log.Println(fmt.Sprintf("Updated client with ID %d.", client.ID))
log.Println(fmt.Sprintf("Updated Client with ID %d.", client.ID))
return context.JSON(http.StatusOK, client)
}
@@ -109,12 +109,12 @@ func (api *API) DeleteClientHandler(context echo.Context) error {
return context.String(http.StatusInternalServerError, "Unexpected error deleting Client!")
}
var client models.Client
var client types.Client
if err := api.db.DeleteClient(id); err != nil {
return context.String(http.StatusNotFound, "Client not found!")
}
log.Println(fmt.Sprintf("Deleted client with ID %d.", client.ID))
log.Println(fmt.Sprintf("Deleted Client with ID %d.", client.ID))
return context.String(http.StatusOK, "Client deleted successfully!")
}
+18 -6
View File
@@ -1,11 +1,23 @@
package api
import "github.com/labstack/echo/v4/middleware"
func (api *API) ClientRoutes() {
group := api.instance.Group("/clients")
clients := api.instance.Group("/clients")
users := api.instance.Group("/users")
group.GET("/", api.GetAllClientsHandler)
group.POST("/", api.CreateClientHandler)
group.GET("/:id", api.GetClientHandler)
group.PUT("/:id", api.UpdateClientHandler)
group.DELETE("/:id", api.DeleteClientHandler)
clients.GET("/", api.GetAllClientsHandler)
clients.POST("/", api.CreateClientHandler)
clients.GET("/:id", api.GetClientHandler)
clients.PUT("/:id", api.UpdateClientHandler)
clients.DELETE("/:id", api.DeleteClientHandler)
users.GET("/", api.GetAllUsersHandler)
users.POST("/", api.CreateUserHandler)
users.GET("/:id", api.GetUserHandler)
users.PUT("/:id", api.UpdateUserHandler)
users.PUT("/:id/security", api.UpdateUserSecurityDetailsHandler)
users.PUT("/:id/logo", api.UpdateUserLogoHandler, middleware.BodyLimit("2M"))
users.DELETE("/:id", api.DeleteUserHandler)
users.DELETE("/:id/logo", api.DeleteUserLogoHandler)
}
+279
View File
@@ -0,0 +1,279 @@
package api
import (
"fmt"
"io"
"log"
"net/http"
"os"
"path/filepath"
"strconv"
"strings"
"github.com/hazemKrimi/crimson-vault/internal/types"
"github.com/labstack/echo/v4"
)
func (api *API) CreateUserHandler(context echo.Context) error {
var body types.CreateUserRequestBody
if err := context.Bind(&body); err != nil {
log.Println(fmt.Sprintf("Error creating User: %v.", err))
return context.String(http.StatusBadRequest, "Invalid JSON!")
}
if err := context.Validate(body); err != nil {
return err
}
user := api.db.CreateUser(body)
log.Println(fmt.Sprintf("User created with ID %d.", user.ID))
return context.JSON(http.StatusOK, user)
}
func (api *API) GetAllUsersHandler(context echo.Context) error {
users, err := api.db.GetUsers()
if err != nil {
return context.String(http.StatusInternalServerError, "Unexpected error getting User!")
}
log.Println("Got all Users.")
return context.JSON(http.StatusOK, users)
}
func (api *API) GetUserHandler(context echo.Context) error {
idString := context.Param("id")
if idString == "" {
return context.String(http.StatusBadRequest, "ID is required to get a User!")
}
id, err := strconv.Atoi(idString)
if err != nil {
return context.String(http.StatusInternalServerError, "Unexpected error getting User!")
}
var user types.User
if err := api.db.GetUser(id, &user); err != nil {
return context.String(http.StatusNotFound, "User not found!")
}
log.Println(fmt.Sprintf("Got User with ID %d.", user.ID))
return context.JSON(http.StatusOK, user)
}
func (api *API) UpdateUserHandler(context echo.Context) error {
idString := context.Param("id")
if idString == "" {
return context.String(http.StatusBadRequest, "ID is required to update a User!")
}
id, err := strconv.Atoi(idString)
if err != nil {
return context.String(http.StatusInternalServerError, "Unexpected error updating User!")
}
var body types.UpdateUserRequestBody
if err := context.Bind(&body); err != nil {
log.Println(fmt.Sprintf("Error updating User: %v.", err))
return context.String(http.StatusBadRequest, "Invalid JSON!")
}
if err := context.Validate(body); err != nil {
return err
}
var user types.User
if err := api.db.UpdateUser(id, body, &user); err != nil {
return context.String(http.StatusNotFound, "User not found!")
}
log.Println(fmt.Sprintf("Updated user with ID %d.", user.ID))
return context.JSON(http.StatusOK, user)
}
func (api *API) UpdateUserSecurityDetailsHandler(context echo.Context) error {
idString := context.Param("id")
if idString == "" {
return context.String(http.StatusBadRequest, "ID is required to create security details for a User!")
}
id, err := strconv.Atoi(idString)
if err != nil {
return context.String(http.StatusInternalServerError, "Unexpected error while creating security details for User!")
}
var body types.UpdateUserSecurityDetailsBody
if err := context.Bind(&body); err != nil {
log.Println(fmt.Sprintf("Error creating security details for User: %v.", err))
return context.String(http.StatusBadRequest, "Invalid JSON!")
}
if err := context.Validate(body); err != nil {
return err
}
var user types.User
if err := api.db.UpdateUserSecurityDetails(id, body, &user); err != nil {
return context.String(http.StatusNotFound, "User not found!")
}
log.Println(fmt.Sprintf("Updated security details of user with ID %d.", user.ID))
return context.JSON(http.StatusOK, user)
}
func (api *API) UpdateUserLogoHandler(context echo.Context) error {
idString := context.Param("id")
if idString == "" {
return context.String(http.StatusBadRequest, "ID is required to update logo for User!")
}
id, err := strconv.Atoi(idString)
if err != nil {
return context.String(http.StatusInternalServerError, "Unexpected error updating logo for User!")
}
var user types.User
if err := api.db.GetUser(id, &user); err != nil {
return context.String(http.StatusNotFound, "User not found!")
}
if user.Username == "" {
return context.String(http.StatusBadRequest, "You have to add a username first for this User!")
}
file, err := context.FormFile("logo")
if err != nil {
log.Println(fmt.Sprintf("Error updating logo for User: %v.", err))
return context.String(http.StatusInternalServerError, "Unexpected error while updating logo for User!")
}
ext := strings.ToLower(filepath.Ext(file.Filename))
allowedExtensions := map[string]bool{
".jpg": true,
".jpeg": true,
".png": true,
".gif": true,
".bmp": true,
".webp": true,
}
if !allowedExtensions[ext] {
return context.String(http.StatusBadRequest, "Invalid file type, only image files are allowed!")
}
src, err := file.Open()
if err != nil {
log.Println(fmt.Sprintf("Error updating logo for User: %v.", err))
return context.String(http.StatusInternalServerError, "Unexpected error while updating logo for User!")
}
defer src.Close()
data, err := io.ReadAll(src)
if err != nil {
return context.String(http.StatusInternalServerError, "Unexpected error while updating logo for User!")
}
filetype := http.DetectContentType(data)
if !strings.HasPrefix(filetype, "image/") {
return context.String(http.StatusBadRequest, "Uploaded file is not a valid image!")
}
err = os.MkdirAll(user.Username, os.ModePerm)
if err != nil {
log.Println(fmt.Sprintf("Error updating logo for User: %v.", err))
return context.String(http.StatusInternalServerError, "Unexpected error while updating logo for User!")
}
path, err := filepath.Abs(filepath.Join(user.Username, fmt.Sprintf("logo%s", ext)))
if err != nil {
log.Println(fmt.Sprintf("Error updating logo for User: %v.", err))
return context.String(http.StatusInternalServerError, "Unexpected error while updating logo for User!")
}
if err := os.WriteFile(path, data, 0644); err != nil {
log.Println(fmt.Sprintf("Error updating logo for User: %v.", err))
return context.String(http.StatusInternalServerError, "Unexpected error while updating logo for User!")
}
if err := api.db.UpdateUserLogo(path, &user); err != nil {
log.Println(fmt.Sprintf("Error updating logo for User: %v.", err))
return context.String(http.StatusInternalServerError, "Unexpected error while updating logo for User!")
}
return context.JSON(http.StatusOK, user)
}
func (api *API) DeleteUserHandler(context echo.Context) error {
idString := context.Param("id")
if idString == "" {
return context.String(http.StatusBadRequest, "ID is required to delete a User!")
}
id, err := strconv.Atoi(idString)
if err != nil {
return context.String(http.StatusInternalServerError, "Unexpected error deleting User!")
}
if err := api.db.DeleteUser(id); err != nil {
return context.String(http.StatusNotFound, "User not found!")
}
log.Println(fmt.Sprintf("Deleted User with ID %d.", id))
return context.String(http.StatusOK, "User deleted successfully!")
}
func (api *API) DeleteUserLogoHandler(context echo.Context) error {
idString := context.Param("id")
if idString == "" {
return context.String(http.StatusBadRequest, "ID is required to delete logo of User!")
}
id, err := strconv.Atoi(idString)
if err != nil {
log.Println(fmt.Sprintf("Error deleting logo of User: %v.", err))
return context.String(http.StatusInternalServerError, "Unexpected error deleting logo of User!")
}
var user types.User
if err := api.db.GetUser(id, &user); err != nil {
return context.String(http.StatusNotFound, "User not found!")
}
os.Remove(user.Logo)
if err := api.db.DeleteUserLogo(&user); err != nil {
log.Println(fmt.Sprintf("Error deleting logo of User: %v.", err))
return context.String(http.StatusInternalServerError, "Unexpected error deleting logo of User!")
}
log.Println(fmt.Sprintf("Deleted logo of User with ID %d.", user.ID))
return context.String(http.StatusOK, "User logo deleted successfully!")
}
+19
View File
@@ -2,6 +2,7 @@ package api
import (
"net/http"
"regexp"
"github.com/go-playground/validator/v10"
"github.com/labstack/echo/v4"
@@ -17,3 +18,21 @@ func (validator *CustomValidator) Validate(i any) error {
}
return nil
}
func PasswordValidator(fieldLevel validator.FieldLevel) bool {
password := fieldLevel.Field().String()
var (
upper = regexp.MustCompile(`[A-Z]`)
lower = regexp.MustCompile(`[a-z]`)
number = regexp.MustCompile(`[0-9]`)
special = regexp.MustCompile(`[!@#~$%^&*()+|_{}:<>?,./;'\[\]\\-]`)
minChars = 8
)
return len(password) >= minChars &&
upper.MatchString(password) &&
lower.MatchString(password) &&
number.MatchString(password) &&
special.MatchString(password)
}