diff --git a/go.mod b/go.mod index f0c63d3..ea29448 100644 --- a/go.mod +++ b/go.mod @@ -4,6 +4,7 @@ go 1.24.3 require ( github.com/go-playground/validator/v10 v10.26.0 + github.com/google/uuid v1.6.0 github.com/gorilla/sessions v1.4.0 github.com/labstack/echo-contrib v0.17.4 github.com/labstack/echo/v4 v4.13.4 @@ -17,7 +18,6 @@ require ( github.com/gabriel-vasile/mimetype v1.4.8 // indirect github.com/go-playground/locales v0.14.1 // indirect github.com/go-playground/universal-translator v0.18.1 // indirect - github.com/google/uuid v1.6.0 // indirect github.com/gorilla/context v1.1.2 // indirect github.com/gorilla/securecookie v1.1.2 // indirect github.com/inconshreveable/mousetrap v1.1.0 // indirect diff --git a/internal/api/client.go b/internal/api/client.go index ad01fe2..4d6ce40 100644 --- a/internal/api/client.go +++ b/internal/api/client.go @@ -4,13 +4,26 @@ import ( "fmt" "log" "net/http" - "strconv" + + "github.com/google/uuid" + "github.com/labstack/echo/v4" "github.com/hazemKrimi/crimson-vault/internal/types" - "github.com/labstack/echo/v4" ) func (api *API) CreateClientHandler(context echo.Context) error { + userId, ok := context.Get("id").(string) + + if !ok { + return context.String(http.StatusInternalServerError, "Unexpected error getting User!") + } + + id, err := uuid.Parse(userId) + + if err != nil { + return context.String(http.StatusInternalServerError, "Unexpected error getting User!") + } + var body types.CreateClientRequestBody if err := context.Bind(&body); err != nil { @@ -22,14 +35,26 @@ func (api *API) CreateClientHandler(context echo.Context) error { return err } - client := api.db.CreateClient(body) + client := api.db.CreateClient(id, body) - log.Println(fmt.Sprintf("Client created with ID %d.", client.ID)) + log.Println(fmt.Sprintf("Client created with ID %s.", client.ID)) return context.JSON(http.StatusOK, client) } func (api *API) GetAllClientsHandler(context echo.Context) error { - clients, err := api.db.GetClients() + userId, ok := context.Get("id").(string) + + if !ok { + return context.String(http.StatusInternalServerError, "Unexpected error getting User!") + } + + id, err := uuid.Parse(userId) + + if err != nil { + return context.String(http.StatusInternalServerError, "Unexpected error getting User!") + } + + clients, err := api.db.GetClients(id) if err != nil { return context.String(http.StatusInternalServerError, "Unexpected error getting Clients!") @@ -40,39 +65,51 @@ func (api *API) GetAllClientsHandler(context echo.Context) error { } func (api *API) GetClientHandler(context echo.Context) error { - idString := context.Param("id") + userIdString, ok := context.Get("id").(string) - if idString == "" { - return context.String(http.StatusBadRequest, "ID is required to get a Client!") + if !ok { + return context.String(http.StatusInternalServerError, "Unexpected error getting User!") } - id, err := strconv.ParseUint(idString, 10, 32) + userId, err := uuid.Parse(userIdString) if err != nil { - return context.String(http.StatusInternalServerError, "Unexpected error getting Client!") + return context.String(http.StatusInternalServerError, "Unexpected error getting User!") + } + + id, err := uuid.Parse(context.Param("id")) + + if err != nil { + return context.String(http.StatusBadRequest, "ID is required to get a Client!") } var client types.Client - if err := api.db.GetClient(uint32(id), &client); err != nil { + if err := api.db.GetClientById(userId, id, &client); err != nil { return context.String(http.StatusNotFound, "Client not found!") } - log.Println(fmt.Sprintf("Got User with ID %d.", client.ID)) + log.Println(fmt.Sprintf("Got User with ID %s.", client.ID)) return context.JSON(http.StatusOK, client) } func (api *API) UpdateClientHandler(context echo.Context) error { - idString := context.Param("id") + userIdString, ok := context.Get("id").(string) - if idString == "" { - return context.String(http.StatusBadRequest, "ID is required to update a Client!") + if !ok { + return context.String(http.StatusInternalServerError, "Unexpected error getting User!") } - id, err := strconv.ParseUint(idString, 10, 32) + userId, err := uuid.Parse(userIdString) if err != nil { - return context.String(http.StatusInternalServerError, "Unexpected error updating Client!") + return context.String(http.StatusInternalServerError, "Unexpected error getting User!") + } + + id, err := uuid.Parse(context.Param("id")) + + if err != nil { + return context.String(http.StatusBadRequest, "ID is required to update a Client!") } var body types.UpdateClientRequestBody @@ -88,33 +125,37 @@ func (api *API) UpdateClientHandler(context echo.Context) error { var client types.Client - if err := api.db.UpdateClient(uint32(id), body, &client); err != nil { + if err := api.db.UpdateClient(userId, 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 %s.", client.ID)) return context.JSON(http.StatusOK, client) } func (api *API) DeleteClientHandler(context echo.Context) error { - idString := context.Param("id") + userIdString, ok := context.Get("id").(string) - if idString == "" { + if !ok { + return context.String(http.StatusInternalServerError, "Unexpected error getting User!") + } + + userId, err := uuid.Parse(userIdString) + + if err != nil { + return context.String(http.StatusInternalServerError, "Unexpected error getting User!") + } + + id, err := uuid.Parse(context.Param("id")) + + if err != nil { return context.String(http.StatusBadRequest, "ID is required to delete a Client!") } - id, err := strconv.ParseUint(idString, 10, 32) - - if err != nil { - return context.String(http.StatusInternalServerError, "Unexpected error deleting Client!") - } - - var client types.Client - - if err := api.db.DeleteClient(uint32(id)); err != nil { + if err := api.db.DeleteClient(userId, 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 %s.", id)) return context.String(http.StatusOK, "Client deleted successfully!") } diff --git a/internal/api/middleware.go b/internal/api/middleware.go index 2779210..6097a11 100644 --- a/internal/api/middleware.go +++ b/internal/api/middleware.go @@ -38,7 +38,7 @@ func (api *API) AuthSessionMiddleware(next echo.HandlerFunc) echo.HandlerFunc { context.Set("id", sess.Values["id"]) context.Set("sessionId", sess.Values["sessionId"]) - context.Set("username", sess.Values["username"]) + context.Set("name", sess.Values["name"]) return next(context) } diff --git a/internal/api/routes.go b/internal/api/routes.go index bd38c06..e4cc797 100644 --- a/internal/api/routes.go +++ b/internal/api/routes.go @@ -15,14 +15,13 @@ func (api *API) ClientRoutes() { func (api *API) UserRoutes() { users := api.instance.Group("/api/users") - users.GET("/", api.GetAllUsersHandler) users.POST("/", api.CreateUserHandler) - users.GET("/:id/", api.GetUserHandler) - users.PUT("/:id/", api.UpdateUserHandler, api.AuthSessionMiddleware) - users.PUT("/:id/security/", api.UpdateUserSecurityDetailsHandler) - users.PUT("/:id/logo/", api.UpdateUserLogoHandler, middleware.BodyLimit("2M")) - users.DELETE("/:id/", api.DeleteUserHandler, api.AuthSessionMiddleware) - users.DELETE("/:id/logo/", api.DeleteUserLogoHandler, api.AuthSessionMiddleware) + users.GET("/", api.GetUserHandler, api.AuthSessionMiddleware) + users.PUT("/", api.UpdateUserHandler, api.AuthSessionMiddleware) + users.PUT("/security/", api.UpdateUserSecurityCredentialsHandler, api.AuthSessionMiddleware) + users.PUT("/logo/", api.UpdateUserLogoHandler, middleware.BodyLimit("2M"), api.AuthSessionMiddleware) + users.DELETE("/", api.DeleteUserHandler, api.AuthSessionMiddleware) + users.DELETE("/logo/", api.DeleteUserLogoHandler, api.AuthSessionMiddleware) } func (api *API) AuthRoutes() { diff --git a/internal/api/user.go b/internal/api/user.go index 1759473..532fa8e 100644 --- a/internal/api/user.go +++ b/internal/api/user.go @@ -10,10 +10,11 @@ import ( "strings" "github.com/google/uuid" - "github.com/hazemKrimi/crimson-vault/internal/lib" - "github.com/hazemKrimi/crimson-vault/internal/types" "github.com/labstack/echo-contrib/session" "github.com/labstack/echo/v4" + + "github.com/hazemKrimi/crimson-vault/internal/lib" + "github.com/hazemKrimi/crimson-vault/internal/types" ) func (api *API) CreateUserHandler(context echo.Context) error { @@ -63,10 +64,16 @@ func (api *API) GetAllUsersHandler(context echo.Context) error { } func (api *API) GetUserHandler(context echo.Context) error { - id, err := uuid.Parse(context.Param("id")) + userId, ok := context.Get("id").(string) + + if !ok { + return context.String(http.StatusInternalServerError, "Unexpected error getting User!") + } + + id, err := uuid.Parse(userId) if err != nil { - return context.String(http.StatusBadRequest, "ID is required to get a User!") + return context.String(http.StatusInternalServerError, "Unexpected error getting User!") } var user types.User @@ -80,10 +87,16 @@ func (api *API) GetUserHandler(context echo.Context) error { } func (api *API) UpdateUserHandler(context echo.Context) error { - id, err := uuid.Parse(context.Param("id")) + userId, ok := context.Get("id").(string) + + if !ok { + return context.String(http.StatusInternalServerError, "Unexpected error updating User!") + } + + id, err := uuid.Parse(userId) if err != nil { - return context.String(http.StatusBadRequest, "ID is required to update a User!") + return context.String(http.StatusInternalServerError, "Unexpected error updating User!") } var body types.UpdateUserRequestBody @@ -107,14 +120,20 @@ func (api *API) UpdateUserHandler(context echo.Context) error { return context.JSON(http.StatusOK, user) } -func (api *API) UpdateUserSecurityDetailsHandler(context echo.Context) error { - id, err := uuid.Parse(context.Param("id")) +func (api *API) UpdateUserSecurityCredentialsHandler(context echo.Context) error { + userId, ok := context.Get("id").(string) - if err != nil { - return context.String(http.StatusBadRequest, "ID is required to create security details for a User!") + if !ok { + return context.String(http.StatusInternalServerError, "Unexpected error updating User security credentials!") } - var body types.UpdateUserSecurityDetailsBody + id, err := uuid.Parse(userId) + + if err != nil { + return context.String(http.StatusInternalServerError, "Unexpected error updating User security credentials!") + } + + var body types.UpdateUserSecurityCredentialsBody if err := context.Bind(&body); err != nil { log.Println(fmt.Sprintf("Error creating security details for User: %v.", err)) @@ -127,7 +146,7 @@ func (api *API) UpdateUserSecurityDetailsHandler(context echo.Context) error { var user types.User - if err := api.db.UpdateUserSecurityDetails(id, body, &user); err != nil { + if err := api.db.UpdateUserSecurityCredentials(id, body, &user); err != nil { return context.String(http.StatusNotFound, "User not found!") } @@ -136,10 +155,16 @@ func (api *API) UpdateUserSecurityDetailsHandler(context echo.Context) error { } func (api *API) UpdateUserLogoHandler(context echo.Context) error { - id, err := uuid.Parse(context.Param("id")) + userId, ok := context.Get("id").(string) + + if !ok { + return context.String(http.StatusInternalServerError, "Unexpected error updating User logo!") + } + + id, err := uuid.Parse(userId) if err != nil { - return context.String(http.StatusBadRequest, "ID is required to update logo for User!") + return context.String(http.StatusInternalServerError, "Unexpected error updating User logo!") } var user types.User @@ -156,7 +181,7 @@ func (api *API) UpdateUserLogoHandler(context echo.Context) error { 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!") + return context.String(http.StatusBadRequest, "No image has been uploaded!") } ext := strings.ToLower(filepath.Ext(file.Filename)) @@ -220,10 +245,16 @@ func (api *API) UpdateUserLogoHandler(context echo.Context) error { } func (api *API) DeleteUserHandler(context echo.Context) error { - id, err := uuid.Parse(context.Param("id")) + userId, ok := context.Get("id").(string) + + if !ok { + return context.String(http.StatusInternalServerError, "Unexpected error deleting User!") + } + + id, err := uuid.Parse(userId) if err != nil { - return context.String(http.StatusBadRequest, "ID is required to delete a User!") + return context.String(http.StatusInternalServerError, "Unexpected error deleting User!") } if err := api.db.DeleteUser(id); err != nil { @@ -235,10 +266,16 @@ func (api *API) DeleteUserHandler(context echo.Context) error { } func (api *API) DeleteUserLogoHandler(context echo.Context) error { - id, err := uuid.Parse(context.Param("id")) + userId, ok := context.Get("id").(string) + + if !ok { + return context.String(http.StatusInternalServerError, "Unexpected error deleting User logo!") + } + + id, err := uuid.Parse(userId) if err != nil { - return context.String(http.StatusBadRequest, "ID is required to delete logo of User!") + return context.String(http.StatusInternalServerError, "Unexpected error deleting User logo!") } var user types.User diff --git a/internal/models/client.go b/internal/models/client.go index fd735cc..55d724e 100644 --- a/internal/models/client.go +++ b/internal/models/client.go @@ -1,6 +1,8 @@ package models import ( + "github.com/google/uuid" + "github.com/hazemKrimi/crimson-vault/internal/types" ) @@ -8,8 +10,10 @@ func (db *DB) MigrateClients() { db.instance.AutoMigrate(&types.Client{}) } -func (db *DB) CreateClient(body types.CreateClientRequestBody) types.Client { +func (db *DB) CreateClient(userId uuid.UUID, body types.CreateClientRequestBody) types.Client { client := types.Client{ + ID: uuid.New().String(), + UserID: userId.String(), Name: body.Name, FiscalCode: body.FiscalCode, Address: body.Address, @@ -23,10 +27,10 @@ func (db *DB) CreateClient(body types.CreateClientRequestBody) types.Client { return client } -func (db *DB) GetClients() ([]types.Client, error) { +func (db *DB) GetClients(userId uuid.UUID) ([]types.Client, error) { var clients []types.Client - result := db.instance.Find(&clients) + result := db.instance.Where("user_id = ?", userId).Find(&clients) if result.Error != nil { return nil, result.Error @@ -35,8 +39,8 @@ func (db *DB) GetClients() ([]types.Client, error) { return clients, nil } -func (db *DB) GetClient(id uint32, client *types.Client) error { - result := db.instance.Where("id = ?", id).First(client, id) +func (db *DB) GetClientById(userId, id uuid.UUID, client *types.Client) error { + result := db.instance.Where("user_id = ?", userId).Where("id = ?", id).First(client) if result.Error != nil { return result.Error @@ -45,8 +49,8 @@ func (db *DB) GetClient(id uint32, client *types.Client) error { return nil } -func (db *DB) UpdateClient(id uint32, body types.UpdateClientRequestBody, client *types.Client) error { - result := db.instance.Where("id = ?", id).First(client, id) +func (db *DB) UpdateClient(userId, id uuid.UUID, body types.UpdateClientRequestBody, client *types.Client) error { + result := db.instance.Where("user_id = ?", userId).Where("id = ?", id).First(client) if result.Error != nil { return result.Error @@ -69,8 +73,8 @@ func (db *DB) UpdateClient(id uint32, body types.UpdateClientRequestBody, client return nil } -func (db *DB) DeleteClient(id uint32) error { - result := db.instance.Delete(&types.Client{}, id) +func (db *DB) DeleteClient(userId, id uuid.UUID) error { + result := db.instance.Where("user_id = ?", userId).Where("id = ?", id).Delete(&types.Client{}) if result.Error != nil { return result.Error diff --git a/internal/models/user.go b/internal/models/user.go index 54bd84d..6bed818 100644 --- a/internal/models/user.go +++ b/internal/models/user.go @@ -4,6 +4,7 @@ import ( "strings" "github.com/google/uuid" + "github.com/hazemKrimi/crimson-vault/internal/lib" "github.com/hazemKrimi/crimson-vault/internal/types" ) @@ -91,6 +92,7 @@ func (db *DB) UpdateUser(id uuid.UUID, body types.UpdateUserRequestBody, user *t Country: body.Country, Phone: body.Phone, Email: body.Email, + Username: strings.ToLower(body.Username), }) if result.Error != nil { @@ -100,7 +102,7 @@ func (db *DB) UpdateUser(id uuid.UUID, body types.UpdateUserRequestBody, user *t return nil } -func (db *DB) UpdateUserSecurityDetails(id uuid.UUID, body types.UpdateUserSecurityDetailsBody, user *types.User) error { +func (db *DB) UpdateUserSecurityCredentials(id uuid.UUID, body types.UpdateUserSecurityCredentialsBody, user *types.User) error { result := db.instance.Where("id = ?", id).First(user, id) if result.Error != nil { @@ -114,7 +116,6 @@ func (db *DB) UpdateUserSecurityDetails(id uuid.UUID, body types.UpdateUserSecur } result = db.instance.Model(user).Updates(types.User{ - Username: strings.ToLower(body.Username), Password: hashedPassword, }) diff --git a/internal/types/client.go b/internal/types/client.go index 913c2b8..9c71135 100644 --- a/internal/types/client.go +++ b/internal/types/client.go @@ -7,7 +7,8 @@ import ( ) type Client struct { - ID uint32 `json:"id" gorm:"primaryKey"` + ID string `json:"id" gorm:"primaryKey"` + UserID string `json:"userId"` CreatedAt time.Time `json:"createAt"` UpdatedAt time.Time `json:"updatedAt"` DeletedAt gorm.DeletedAt `json:"deletedAt" gorm:"index"` diff --git a/internal/types/user.go b/internal/types/user.go index f1c9161..2a60d45 100644 --- a/internal/types/user.go +++ b/internal/types/user.go @@ -22,6 +22,7 @@ type User struct { Email string `json:"email"` Username string `json:"username" gorm:"unique"` Password string `json:"-"` + Clients []Client `json:"clients" gorm:"constraint:OnUpdate:CASCADE,onDelete:CASCADE"` } type CreateUserRequestBody struct { @@ -42,10 +43,10 @@ type UpdateUserRequestBody struct { Country string `json:"country" validate:"omitempty,alpha"` Phone string `json:"phone" validate:"omitempty,e164"` Email string `json:"email" validate:"omitempty,email"` + Username string `json:"username"` } -type UpdateUserSecurityDetailsBody struct { - Username string `json:"username"` +type UpdateUserSecurityCredentialsBody struct { Password string `json:"password" validate:"password"` ConfirmPassword string `json:"confirmPassword" validate:"password,eqcsfield=Password"` }