diff --git a/.gitignore b/.gitignore index 723ef36..1f64963 100644 --- a/.gitignore +++ b/.gitignore @@ -1 +1,3 @@ -.idea \ No newline at end of file +.idea +go.mod +2024-2-Zdes-budet-nazvanie-UykwHnIE.crt \ No newline at end of file diff --git a/build/main.Dockerfile b/build/main.Dockerfile index 1cde78e..474e8ad 100644 --- a/build/main.Dockerfile +++ b/build/main.Dockerfile @@ -8,6 +8,4 @@ FROM scratch AS runner WORKDIR /build COPY --from=builder /github.com/go-park-mail-ru/2024_2_ThereWillBeName/.bin . EXPOSE 8080 -ENTRYPOINT ["./.bin"] - - +ENTRYPOINT ["./.bin"] \ No newline at end of file diff --git a/cmd/main.go b/cmd/main.go index 9442103..160c84b 100644 --- a/cmd/main.go +++ b/cmd/main.go @@ -1,60 +1,64 @@ package main import ( - "TripAdvisor/pkg/middleware" + "TripAdvisor/internal/models" + "TripAdvisor/internal/pkg/middleware" + "TripAdvisor/internal/pkg/places/delivery" + "TripAdvisor/internal/pkg/places/repo" + "TripAdvisor/internal/pkg/places/usecase" "flag" "fmt" + "github.com/gorilla/mux" "log" "net/http" "os" "time" - "github.com/gorilla/mux" ) -type config struct { - port int - env string -} -type application struct { - config config - logger *log.Logger -} - func main() { - var cfg config - flag.IntVar(&cfg.port, "port", 8080, "API server port") - flag.StringVar(&cfg.env, "env", "development", "Environment") + newPlaceRepo := repo.NewPLaceRepository() + placeUsecase := usecase.NewPlaceUsecase(newPlaceRepo) + handler := delivery.NewPlacesHandler(placeUsecase) + + var cfg models.Config + flag.IntVar(&cfg.Port, "port", 8080, "API server port") + flag.StringVar(&cfg.Env, "env", "development", "Environment") + flag.StringVar(&cfg.AllowedOrigin, "allowed-origin", "*", "Allowed origin") flag.Parse() + + corsMiddleware := middleware.NewCORSMiddleware([]string{cfg.AllowedOrigin}) + logger := log.New(os.Stdout, "", log.Ldate|log.Ltime) - app := &application{ - config: cfg, - logger: logger, - } - r := mux.NewRouter().PathPrefix("/api").Subrouter() - r.Use(middleware.CORSMiddleware) + r := mux.NewRouter().PathPrefix("/api/v1").Subrouter() + r.Use(corsMiddleware.CorsMiddleware) r.NotFoundHandler = http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { http.Error(w, "Not Found", http.StatusNotFound) }) - r.HandleFunc("/healthcheck", app.healthcheckHandler).Methods(http.MethodGet) - + healthcheck := r.PathPrefix("/healthcheck").Subrouter() + healthcheck.HandleFunc("", healthcheckHandler).Methods(http.MethodGet) + placecheck := r.PathPrefix("/places").Subrouter() + placecheck.HandleFunc("", handler.GetPlaceHandler).Methods(http.MethodGet) srv := &http.Server{ - Addr: fmt.Sprintf(":%d", cfg.port), + Addr: fmt.Sprintf(":%d", cfg.Port), Handler: r, IdleTimeout: time.Minute, ReadTimeout: 10 * time.Second, WriteTimeout: 30 * time.Second, } - logger.Printf("Starting server on port %d in %s mode", cfg.port, srv.Addr) + + logger.Printf("starting %s server on %s", cfg.Env, srv.Addr) err := srv.ListenAndServe() if err != nil { - logger.Fatal(err) + fmt.Errorf("Failed to start server: %v", err) + os.Exit(1) } } -func (app *application) healthcheckHandler(w http.ResponseWriter, r *http.Request) { +func healthcheckHandler(w http.ResponseWriter, r *http.Request) { _, err := fmt.Fprintf(w, "STATUS: OK") if err != nil { - fmt.Printf("ERROR: healthcheckHandler: %s\n", err) + http.Error(w, "", http.StatusBadRequest) + fmt.Errorf("ERROR: healthcheckHandler: %s\n", err) } } diff --git a/go.mod b/go.mod index f2859b0..2374b82 100644 --- a/go.mod +++ b/go.mod @@ -1,5 +1,3 @@ module TripAdvisor - -go 1.22.7 - -require github.com/gorilla/mux v1.8.1 +go 1.23.0 +require github.com/gorilla/mux v1.8.1 \ No newline at end of file diff --git a/internal/.gitkeep b/internal/.gitkeep deleted file mode 100644 index e69de29..0000000 diff --git a/internal/models/config.go b/internal/models/config.go new file mode 100644 index 0000000..0609922 --- /dev/null +++ b/internal/models/config.go @@ -0,0 +1,7 @@ +package models + +type Config struct { + Port int + Env string + AllowedOrigin string +} diff --git a/internal/models/place.go b/internal/models/place.go new file mode 100644 index 0000000..b288e8f --- /dev/null +++ b/internal/models/place.go @@ -0,0 +1,7 @@ +package models + +type Place struct { + ID int `json:"id"` + Name string `json:"name""` + Image string `json:"image"` +} diff --git a/internal/pkg/middleware/cors.go b/internal/pkg/middleware/cors.go new file mode 100644 index 0000000..b4487e7 --- /dev/null +++ b/internal/pkg/middleware/cors.go @@ -0,0 +1,24 @@ +package middleware + +import "net/http" + +type CORSMiddleware struct { + AllowedOrigins []string +} + +func NewCORSMiddleware(allowedOrigins []string) *CORSMiddleware { + return &CORSMiddleware{ + AllowedOrigins: allowedOrigins, + } +} +func (c *CORSMiddleware) CorsMiddleware(next http.Handler) http.Handler { + return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { + w.Header().Set("Access-Control-Allow-Methods", "POST,PUT,DELETE,GET") + w.Header().Set("Access-Control-Allow-Headers", "Content-Type") + w.Header().Set("Access-Control-Allow-Credentials", "true") + if len(c.AllowedOrigins) > 0 { + w.Header().Set("Access-Control-Allow-Origin", c.AllowedOrigins[0]) + } + }, + ) +} diff --git a/internal/pkg/places/delivery/http.go b/internal/pkg/places/delivery/http.go new file mode 100644 index 0000000..61aab3c --- /dev/null +++ b/internal/pkg/places/delivery/http.go @@ -0,0 +1,29 @@ +package delivery + +import ( + "TripAdvisor/internal/pkg/places" + "encoding/json" + "net/http" +) + +type PlacesHandler struct { + uc places.PlaceUsecase +} + +func NewPlacesHandler(uc places.PlaceUsecase) *PlacesHandler { + return &PlacesHandler{uc} +} + +func (h *PlacesHandler) GetPlaceHandler(w http.ResponseWriter, r *http.Request) { + places, err := h.uc.GetPlaces(r.Context()) + if err != nil { + http.Error(w, "Не удалось получить список достопримечательностей", http.StatusInternalServerError) + return + } + w.Header().Set("Content-Type", "application/json") + err = json.NewEncoder(w).Encode(places) + if err != nil { + http.Error(w, "Не удалось преобразовать в json", http.StatusInternalServerError) + return + } +} diff --git a/internal/pkg/places/interfaces.go b/internal/pkg/places/interfaces.go new file mode 100644 index 0000000..c588ba6 --- /dev/null +++ b/internal/pkg/places/interfaces.go @@ -0,0 +1,14 @@ +package places + +import ( + "TripAdvisor/internal/models" + "context" +) + +type PlaceRepo interface { + GetPlaces(ctx context.Context) ([]models.Place, error) +} + +type PlaceUsecase interface { + GetPlaces(ctx context.Context) ([]models.Place, error) +} diff --git a/internal/pkg/places/repo/places.json b/internal/pkg/places/repo/places.json new file mode 100644 index 0000000..a27b5d0 --- /dev/null +++ b/internal/pkg/places/repo/places.json @@ -0,0 +1,33 @@ +[ + { + "id": 0, + "image": "/images/image1.png", + "name": "Московский Кремль" + }, + { + "id": 1, + "image": "/images/image2.png", + "name": "Красная Площадь" + }, + { + "id": 2, + "image": "/images/image3.png", + "name": "Храм Василия Блаженного" + }, + { + "id": 3, + "image": "/images/image4.png", + "name": "Большой театр" + }, + { + "id": 4, + "image": "/images/image5.png", + "name": "Государственная Третьяковская галерея" + }, + { + "id": 5, + "image": "/images/image6.png", + "name": "Государственный музей Петергоф" + } + ] + diff --git a/internal/pkg/places/repo/repo.go b/internal/pkg/places/repo/repo.go new file mode 100644 index 0000000..77110fb --- /dev/null +++ b/internal/pkg/places/repo/repo.go @@ -0,0 +1,29 @@ +package repo + +import ( + "TripAdvisor/internal/models" + "context" + _ "embed" + "encoding/json" + "log" +) + +type PlaceRepository struct { +} + +func NewPLaceRepository() *PlaceRepository { + return &PlaceRepository{} +} + +//go:embed places.json +var jsonFileData []byte + +func (r *PlaceRepository) GetPlaces(ctx context.Context) ([]models.Place, error) { + images := make([]models.Place, 0) + err := json.Unmarshal(jsonFileData, &images) + if err != nil { + log.Println(err) + return nil, err + } + return images, nil +} diff --git a/internal/pkg/places/usecase/usecase.go b/internal/pkg/places/usecase/usecase.go new file mode 100644 index 0000000..bcac9fa --- /dev/null +++ b/internal/pkg/places/usecase/usecase.go @@ -0,0 +1,23 @@ +package usecase + +import ( + "TripAdvisor/internal/models" + "TripAdvisor/internal/pkg/places" + "context" +) + +type PlaceUsecaseImpl struct { + repo places.PlaceRepo +} + +func NewPlaceUsecase(repo places.PlaceRepo) *PlaceUsecaseImpl { + return &PlaceUsecaseImpl{repo: repo} +} + +func (i *PlaceUsecaseImpl) GetPlaces(ctx context.Context) ([]models.Place, error) { + places, err := i.repo.GetPlaces(ctx) + if err != nil { + return nil, err + } + return places, nil +} diff --git a/pkg/.gitkeep b/pkg/.gitkeep deleted file mode 100644 index e69de29..0000000