Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Task/validation #43

Open
wants to merge 10 commits into
base: dev
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from 9 commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 0 additions & 2 deletions .env
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

файл надо убрать

This file was deleted.

1 change: 1 addition & 0 deletions .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -2,3 +2,4 @@
go.mod
2024-2-Zdes-budet-nazvanie-UykwHnIE.crt
data/
.env
7 changes: 3 additions & 4 deletions cmd/main.go
Original file line number Diff line number Diff line change
Expand Up @@ -109,12 +109,11 @@ func main() {

places := r.PathPrefix("/places").Subrouter()
places.HandleFunc("", placeHandler.GetPlacesHandler).Methods(http.MethodGet)
places.HandleFunc("", placeHandler.PostPlaceHandler).Methods(http.MethodPost)
places.Handle("", middleware.MiddlewareAuth(jwtHandler, http.HandlerFunc(placeHandler.PostPlaceHandler), logger)).Methods(http.MethodPost)
places.HandleFunc("/search/{placeName}", placeHandler.SearchPlacesHandler).Methods(http.MethodGet)
places.HandleFunc("/{id}", placeHandler.GetPlaceHandler).Methods(http.MethodGet)
places.HandleFunc("/{id}", placeHandler.PutPlaceHandler).Methods(http.MethodPut)
places.HandleFunc("/{id}", placeHandler.DeletePlaceHandler).Methods(http.MethodDelete)

places.Handle("/{id}", middleware.MiddlewareAuth(jwtHandler, http.HandlerFunc(placeHandler.PutPlaceHandler), logger)).Methods(http.MethodPut)
places.Handle("/{id}", middleware.MiddlewareAuth(jwtHandler, http.HandlerFunc(placeHandler.DeletePlaceHandler), logger)).Methods(http.MethodDelete)
r.PathPrefix("/swagger").Handler(httpSwagger.WrapHandler)

reviews := places.PathPrefix("/{placeID}/reviews").Subrouter()
Expand Down
1 change: 1 addition & 0 deletions go.mod
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,7 @@ require (

require (
github.com/fatih/color v1.18.0 // indirect
github.com/google/uuid v1.6.0 // indirect
github.com/mattn/go-colorable v0.1.13 // indirect
github.com/mattn/go-isatty v0.0.20 // indirect
golang.org/x/sys v0.26.0 // indirect
Expand Down
2 changes: 2 additions & 0 deletions go.sum
Original file line number Diff line number Diff line change
Expand Up @@ -20,6 +20,8 @@ github.com/go-openapi/swag v0.23.0 h1:vsEVJDUo2hPJ2tu0/Xc+4noaxyEffXNIs3cOULZ+Gr
github.com/go-openapi/swag v0.23.0/go.mod h1:esZ8ITTYEsH1V2trKHjAN8Ai7xHb8RV+YSZ577vPjgQ=
github.com/golang/mock v1.6.0 h1:ErTB+efbowRARo13NNdxyJji2egdxLGQhRaY+DUumQc=
github.com/golang/mock v1.6.0/go.mod h1:p6yTPP+5HYm5mzsMV8JkE6ZKdX+/wYM6Hr+LicevLPs=
github.com/google/uuid v1.6.0 h1:NIvaJDMOsjHA8n1jAhLSgzrAzy1Hgr+hNrb57e+94F0=
github.com/google/uuid v1.6.0/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo=
github.com/gorilla/mux v1.8.1 h1:TuBL49tXwgrFYWhqrNgrUNEY92u81SPhu7sTdzQEiWY=
github.com/gorilla/mux v1.8.1/go.mod h1:AKf9I4AEqPTmMytcMc0KkNouC66V3BtZ4qD5fmWSiMQ=
github.com/josharian/intern v1.0.0 h1:vlS4z54oSdjm0bgjRigI+G1HpF+tI+9rE5LLzOg8HmY=
Expand Down
26 changes: 26 additions & 0 deletions internal/models/place.go
Original file line number Diff line number Diff line change
@@ -1,5 +1,7 @@
package models

import "2024_2_ThereWillBeName/internal/validator"

type CreatePlace struct {
Name string `json:"name"`
ImagePath string `json:"imagePath"`
Expand Down Expand Up @@ -37,3 +39,27 @@ type UpdatePlace struct {
PhoneNumber string `json:"phoneNumber"`
CategoriesId []int `json:"categoriesId"`
}

func ValidateCreatePlace(v *validator.Validator, place *CreatePlace) {
v.Check(place.Name != "", "name", "must be provided")
v.Check(len(place.Name) <= 255, "name", "must not be more than 255 symbols")
v.Check(place.Description != "", "descriprion", "must be provided")
v.Check(len(place.Description) <= 255, "description", "must not be more than 255 symbols")
v.Check(place.ImagePath != "", "image path", "must be provided")
v.Check(len(place.ImagePath) <= 255, "image path", "must not be more than 255 symbols")
v.Check(place.Address != "", "address", "must be provided")
v.Check(len(place.Address) <= 255, "address", "must not be more than 255 symbols")
v.Check(place.CityId != 0, "city id", "must be provided")
}

func ValidateUpdatePlace(v *validator.Validator, place *UpdatePlace) {
v.Check(place.Name != "", "name", "must be provided")
v.Check(len(place.Name) <= 255, "name", "must not be more than 255 symbols")
v.Check(place.Description != "", "descriprion", "must be provided")
v.Check(len(place.Description) <= 255, "description", "must not be more than 255 symbols")
v.Check(place.ImagePath != "", "image path", "must be provided")
v.Check(len(place.ImagePath) <= 255, "image path", "must not be more than 255 symbols")
v.Check(place.Address != "", "address", "must be provided")
v.Check(len(place.Address) <= 255, "address", "must not be more than 255 symbols")
v.Check(place.CityId != 0, "city id", "must be provided")
}
13 changes: 12 additions & 1 deletion internal/models/review.go
Original file line number Diff line number Diff line change
@@ -1,6 +1,9 @@
package models

import "time"
import (
"2024_2_ThereWillBeName/internal/validator"
"time"
)

type Review struct {
ID uint `json:"id"`
Expand All @@ -25,3 +28,11 @@ type GetReviewByUserID struct {
Rating int `json:"rating"`
ReviewText string `json:"review_text"`
}

func ValidateReview(v *validator.Validator, review *Review) {
v.Check(review.ReviewText != "", "reviewText", "must be provided")
v.Check(len(review.ReviewText) <= 255, "reviewText", "must not be more than 255 symbols")
v.Check(review.Rating != 0, "rating", "must be provided")
v.Check(review.PlaceID != 0, "place id", "must be provided")
v.Check(review.UserID != 0, "user id", "must be provided")
}
25 changes: 24 additions & 1 deletion internal/models/trip.go
Original file line number Diff line number Diff line change
@@ -1,6 +1,9 @@
package models

import "time"
import (
"2024_2_ThereWillBeName/internal/validator"
"time"
)

type Trip struct {
ID uint `json:"id"`
Expand All @@ -13,3 +16,23 @@ type Trip struct {
Private bool `json:"private_trip"`
CreatedAt time.Time `json:"created_at"`
}

func ValidateTrip(v *validator.Validator, trip *Trip) {
v.Check(trip.Name != "", "name", "must be provided")
v.Check(len(trip.Name) <= 255, "name", "must not be more than 255 symbols")
v.Check(trip.UserID != 0, "user id", "must be provided")
v.Check(len(trip.Description) <= 255, "description", "must not be more than 255 symbols")
v.Check(trip.CityID != 0, "city id", "must be provided")

startDate, _ := time.Parse("2006-01-02", trip.StartDate)
endDate, _ := time.Parse("2006-01-02", trip.EndDate)

now := time.Now()
oneMonthAgo := now.AddDate(0, -1, 0)
twoYearsAhead := now.AddDate(2, 0, 0)
v.Check(startDate.Before(twoYearsAhead), "start date", "must not be too late")
v.Check(startDate.After(oneMonthAgo), "start date", "must not be too early")
v.Check(endDate.Before(twoYearsAhead), "end date", "must not be too late")
v.Check(endDate.After(oneMonthAgo), "end date", "must not be too early")
v.Check(endDate.After(startDate), "dates", "start must not be later than end")
}
12 changes: 11 additions & 1 deletion internal/models/user.go
Original file line number Diff line number Diff line change
@@ -1,6 +1,9 @@
package models

import "time"
import (
"2024_2_ThereWillBeName/internal/validator"
"time"
)

type User struct {
ID uint `json:"id"`
Expand All @@ -16,3 +19,10 @@ type UserProfile struct {
AvatarPath string `json:"avatar_path,omitempty"`
Email string `json:"email,omitempty"`
}

func ValidateUser(v *validator.Validator, user *User) {
v.Check(user.Login != "", "login", "must be provided")
v.Check(user.Password != "", "password", "must be provided")
v.Check(user.Email != "", "email", "must be provided")
v.Matches(user.Email, validator.EmailRX)
}
16 changes: 8 additions & 8 deletions internal/pkg/middleware/middleware.go
Original file line number Diff line number Diff line change
Expand Up @@ -18,19 +18,19 @@

func MiddlewareAuth(jwtService jwt.JWTInterface, next http.Handler, logger *slog.Logger) http.Handler {
return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
token := r.Header.Get("X-CSRF-Token")
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

CSRF тут уже не при чем, ты поменяла механизм передачи токена. Теперь это скорее вот эта схема https://swagger.io/docs/specification/v3_0/authentication/bearer-authentication/


cookie, err := r.Cookie("token")
if err != nil {
if token == "" {
response := httpresponse.ErrorResponse{
Message: "Cookie not found",
Message: "Token is missing",
}

logger.Error("Cookie not found", slog.Any("error", err.Error()))
httpresponse.SendJSONResponse(w, response, http.StatusUnauthorized, logger)
if logger != nil {
logger.Error("Token is missing")
}
httpresponse.SendJSONResponse(w, response, http.StatusForbidden, logger)
return
}

claims, err := jwtService.ParseToken(cookie.Value)
claims, err := jwtService.ParseToken(token)
if err != nil {
response := httpresponse.ErrorResponse{
Message: "Invalid token",
Expand All @@ -46,7 +46,7 @@
login := claims["login"].(string)
email := claims["email"].(string)
if logger != nil {
logger.Info("Token parsed", slog.Int("userID", int(userID)), slog.String("login", login), slog.String("email", email))

Check failure on line 49 in internal/pkg/middleware/middleware.go

View workflow job for this annotation

GitHub Actions / linters

G115: integer overflow conversion uint -> int (gosec)
}
ctx := context.WithValue(r.Context(), IdKey, userID)
ctx = context.WithValue(ctx, LoginKey, login)
Expand Down
35 changes: 35 additions & 0 deletions internal/pkg/places/delivery/http/handler.go
Original file line number Diff line number Diff line change
Expand Up @@ -5,9 +5,11 @@ import (
httpresponse "2024_2_ThereWillBeName/internal/pkg/httpresponses"
log "2024_2_ThereWillBeName/internal/pkg/logger"
"2024_2_ThereWillBeName/internal/pkg/places"
"2024_2_ThereWillBeName/internal/validator"
"encoding/json"
"errors"
"fmt"
"html/template"
"log/slog"
"net/http"
"strconv"
Expand Down Expand Up @@ -71,9 +73,11 @@ func (h *PlacesHandler) GetPlacesHandler(w http.ResponseWriter, r *http.Request)
// @Param place body models.CreatePlace true "Place data"
// @Success 201 {object} httpresponses.ErrorResponse "Place successfully created"
// @Failure 400 {object} httpresponses.ErrorResponse
// @Failure 422 {object} httpresponses.ErrorResponse
// @Failure 500 {object} httpresponses.ErrorResponse
// @Router /places [post]
func (h *PlacesHandler) PostPlaceHandler(w http.ResponseWriter, r *http.Request) {
w.Header().Set("Content-Security-Policy", "default-src 'self'; script-src 'self'; style-src 'self';")
logCtx := log.LogRequestStart(r.Context(), r.Method, r.RequestURI)
h.logger.DebugContext(logCtx, "Handling request for creating a place")

Expand All @@ -85,6 +89,18 @@ func (h *PlacesHandler) PostPlaceHandler(w http.ResponseWriter, r *http.Request)
slog.String("place_data", fmt.Sprintf("%+v", place)))
return
}
v := validator.New()
if models.ValidateCreatePlace(v, &place); !v.Valid() {
httpresponse.SendJSONResponse(w, nil, http.StatusUnprocessableEntity, h.logger)
return
}

place.Name = template.HTMLEscapeString(place.Name)
place.ImagePath = template.HTMLEscapeString(place.ImagePath)
place.Description = template.HTMLEscapeString(place.Description)
place.Address = template.HTMLEscapeString(place.Address)
place.PhoneNumber = template.HTMLEscapeString(place.PhoneNumber)

if err := h.uc.CreatePlace(r.Context(), place); err != nil {
httpresponse.SendJSONResponse(w, nil, http.StatusInternalServerError, h.logger)
h.logger.Error("Failed to create place",
Expand All @@ -106,9 +122,11 @@ func (h *PlacesHandler) PostPlaceHandler(w http.ResponseWriter, r *http.Request)
// @Param place body models.UpdatePlace true "Updated place data"
// @Success 200 {object} httpresponses.ErrorResponse "Place successfully updated"
// @Failure 400 {object} httpresponses.ErrorResponse
// @Failure 422 {object} httpresponses.ErrorResponse
// @Failure 500 {object} httpresponses.ErrorResponse
// @Router /places/{id} [put]
func (h *PlacesHandler) PutPlaceHandler(w http.ResponseWriter, r *http.Request) {
w.Header().Set("Content-Security-Policy", "default-src 'self'; script-src 'self'; style-src 'self';")
logCtx := log.LogRequestStart(r.Context(), r.Method, r.RequestURI)
h.logger.DebugContext(logCtx, "Handling request for updating a place")

Expand All @@ -121,6 +139,19 @@ func (h *PlacesHandler) PutPlaceHandler(w http.ResponseWriter, r *http.Request)
slog.String("place_data", fmt.Sprintf("%+v", place)))
return
}

v := validator.New()
if models.ValidateUpdatePlace(v, &place); !v.Valid() {
httpresponse.SendJSONResponse(w, nil, http.StatusUnprocessableEntity, h.logger)
return
}

place.Name = template.HTMLEscapeString(place.Name)
place.ImagePath = template.HTMLEscapeString(place.ImagePath)
place.Description = template.HTMLEscapeString(place.Description)
place.Address = template.HTMLEscapeString(place.Address)
place.PhoneNumber = template.HTMLEscapeString(place.PhoneNumber)

if err := h.uc.UpdatePlace(r.Context(), place); err != nil {
httpresponse.SendJSONResponse(w, nil, http.StatusInternalServerError, h.logger)
h.logger.Error("Failed to update place",
Expand Down Expand Up @@ -221,6 +252,7 @@ func (h *PlacesHandler) GetPlaceHandler(w http.ResponseWriter, r *http.Request)
// @Failure 500 {object} httpresponses.ErrorResponse
// @Router /places/search/{placeName} [get]
func (h *PlacesHandler) SearchPlacesHandler(w http.ResponseWriter, r *http.Request) {
w.Header().Set("Content-Security-Policy", "default-src 'self'; script-src 'self'; style-src 'self';")
placeName := mux.Vars(r)["placeName"]

logCtx := log.LogRequestStart(r.Context(), r.Method, r.RequestURI)
Expand All @@ -238,6 +270,9 @@ func (h *PlacesHandler) SearchPlacesHandler(w http.ResponseWriter, r *http.Reque
h.logger.Warn("Invalid limit parameter", slog.String("error", err.Error()))
return
}

placeName = template.HTMLEscapeString(placeName)

places, err := h.uc.SearchPlaces(r.Context(), placeName, limit, offset)
if err != nil {
httpresponse.SendJSONResponse(w, nil, http.StatusInternalServerError, h.logger)
Expand Down
17 changes: 17 additions & 0 deletions internal/pkg/reviews/delivery/http/handler.go
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,9 @@ import (
log "2024_2_ThereWillBeName/internal/pkg/logger"
"2024_2_ThereWillBeName/internal/pkg/middleware"
"2024_2_ThereWillBeName/internal/pkg/reviews"
"2024_2_ThereWillBeName/internal/validator"
"context"
"html/template"
"log/slog"

"encoding/json"
Expand Down Expand Up @@ -85,6 +87,13 @@ func (h *ReviewHandler) CreateReviewHandler(w http.ResponseWriter, r *http.Reque
httpresponse.SendJSONResponse(w, response, http.StatusBadRequest, h.logger)
return
}
v := validator.New()
if models.ValidateReview(v, &review); !v.Valid() {
httpresponse.SendJSONResponse(w, nil, http.StatusUnprocessableEntity, h.logger)
return
}

review.ReviewText = template.HTMLEscapeString(review.ReviewText)

if review.Rating < 1 || review.Rating > 5 {
h.logger.Warn("Invalid rating",
Expand Down Expand Up @@ -161,6 +170,14 @@ func (h *ReviewHandler) UpdateReviewHandler(w http.ResponseWriter, r *http.Reque
return
}

v := validator.New()
if models.ValidateReview(v, &review); !v.Valid() {
httpresponse.SendJSONResponse(w, nil, http.StatusUnprocessableEntity, h.logger)
return
}

review.ReviewText = template.HTMLEscapeString(review.ReviewText)

review.ID = uint(reviewID)
err = h.uc.UpdateReview(context.Background(), review)
if err != nil {
Expand Down
29 changes: 27 additions & 2 deletions internal/pkg/trips/delivery/http/handler.go
Original file line number Diff line number Diff line change
Expand Up @@ -6,10 +6,13 @@ import (
log "2024_2_ThereWillBeName/internal/pkg/logger"
"2024_2_ThereWillBeName/internal/pkg/middleware"
"2024_2_ThereWillBeName/internal/pkg/trips"
"2024_2_ThereWillBeName/internal/validator"

"context"
"encoding/json"
"errors"
"fmt"
"html/template"
"log/slog"
"net/http"
"strconv"
Expand Down Expand Up @@ -68,10 +71,11 @@ func ErrorCheck(err error, action string, logger *slog.Logger, ctx context.Conte
// @Success 201 {object} models.Trip "Trip created successfully"
// @Failure 400 {object} httpresponses.ErrorResponse "Invalid request"
// @Failure 404 {object} httpresponses.ErrorResponse "Invalid request"
// @Failure 422 {object} httpresponses.ErrorResponse
// @Failure 500 {object} httpresponses.ErrorResponse "Failed to create trip"
// @Router /trips [post]
func (h *TripHandler) CreateTripHandler(w http.ResponseWriter, r *http.Request) {

w.Header().Set("Content-Security-Policy", "default-src 'self'; script-src 'self'; style-src 'self';")
logCtx := log.LogRequestStart(r.Context(), r.Method, r.RequestURI)
h.logger.DebugContext(logCtx, "Handling request for creating a trip")

Expand Down Expand Up @@ -109,6 +113,16 @@ func (h *TripHandler) CreateTripHandler(w http.ResponseWriter, r *http.Request)
EndDate: tripData.EndDate,
Private: tripData.Private,
}

v := validator.New()
if models.ValidateTrip(v, &trip); !v.Valid() {
httpresponse.SendJSONResponse(w, nil, http.StatusUnprocessableEntity, h.logger)
return
}

trip.Name = template.HTMLEscapeString(trip.Name)
trip.Description = template.HTMLEscapeString(trip.Description)

err = h.uc.CreateTrip(context.Background(), trip)
if err != nil {
response, status := ErrorCheck(err, "create", h.logger, context.Background())
Expand All @@ -132,10 +146,11 @@ func (h *TripHandler) CreateTripHandler(w http.ResponseWriter, r *http.Request)
// @Failure 400 {object} httpresponses.ErrorResponse "Invalid trip ID"
// @Failure 400 {object} httpresponses.ErrorResponse "Invalid trip data"
// @Failure 404 {object} httpresponses.ErrorResponse "Trip not found"
// @Failure 422 {object} httpresponses.ErrorResponse
// @Failure 500 {object} httpresponses.ErrorResponse "Failed to update trip"
// @Router /trips/{id} [put]
func (h *TripHandler) UpdateTripHandler(w http.ResponseWriter, r *http.Request) {

w.Header().Set("Content-Security-Policy", "default-src 'self'; script-src 'self'; style-src 'self';")
logCtx := log.LogRequestStart(r.Context(), r.Method, r.RequestURI)
h.logger.DebugContext(logCtx, "Handling request for updating a trip")

Expand Down Expand Up @@ -182,6 +197,16 @@ func (h *TripHandler) UpdateTripHandler(w http.ResponseWriter, r *http.Request)
EndDate: tripData.EndDate,
Private: tripData.Private,
}

v := validator.New()
if models.ValidateTrip(v, &trip); !v.Valid() {
httpresponse.SendJSONResponse(w, nil, http.StatusUnprocessableEntity, h.logger)
return
}

trip.Name = template.HTMLEscapeString(trip.Name)
trip.Description = template.HTMLEscapeString(trip.Description)

err = h.uc.UpdateTrip(context.Background(), trip)
if err != nil {
logCtx := log.AppendCtx(context.Background(), slog.Int("tripID", tripID))
Expand Down
Loading
Loading