From eeaaddd21c1f7454f4b090256125ab638e86d6fc Mon Sep 17 00:00:00 2001 From: MrDzhofik Date: Sun, 28 Apr 2024 16:39:11 +0300 Subject: [PATCH 01/15] Lol --- ...0240401145225_create_and_insert_tables.sql | 5 ++ .../postgres/question/question_storage.go | 46 +++++-------------- 2 files changed, 17 insertions(+), 34 deletions(-) diff --git a/db/migrations/20240401145225_create_and_insert_tables.sql b/db/migrations/20240401145225_create_and_insert_tables.sql index 3d59dd3..33fec31 100644 --- a/db/migrations/20240401145225_create_and_insert_tables.sql +++ b/db/migrations/20240401145225_create_and_insert_tables.sql @@ -282,6 +282,11 @@ CREATE TABLE quiz created_at timestamptz ); +INSERT INTO question(text) +VALUES ('Насколько вы удовлетворены удобством КудаТуда?'), + ('Насколько интуитивно понятен интерфейс?'), + ('Оцените представленные достопримечательности'); + -- +goose Down -- +goose StatementBegin diff --git a/internal/storage/postgres/question/question_storage.go b/internal/storage/postgres/question/question_storage.go index 81f79dd..7e6c985 100644 --- a/internal/storage/postgres/question/question_storage.go +++ b/internal/storage/postgres/question/question_storage.go @@ -41,7 +41,7 @@ func (qs *QuestionStorage) AddReview(review entities.Review) error { func (qs *QuestionStorage) GetQuestions() ([]entities.QuestionResponse, error) { var questions []*entities.QuestionResponse ctx := context.Background() - err := pgxscan.Select(ctx, qs.db, &questions, `SELECT id AS question_id, text FROM question ORDER BY question_id`) + err := pgxscan.Select(ctx, qs.db, &questions, `SELECT * FROM question`) if err != nil { logger.Logger().Error(err.Error()) return []entities.QuestionResponse{}, err @@ -58,8 +58,7 @@ func (qs *QuestionStorage) GetQuestions() ([]entities.QuestionResponse, error) { func (qs *QuestionStorage) GetReview(userID int) ([]entities.Review, error) { var review []*entities.Review ctx := context.Background() - err := pgxscan.Select(ctx, qs.db, &review, `SELECT user_id, rating, question_id, created_at FROM quiz WHERE user_id = $1`, userID) - + err := pgxscan.Select(ctx, qs.db, &review, `SELECT * FROM quiz WHERE user_id = $1`, userID) if err != nil { logger.Logger().Error(err.Error()) return []entities.Review{}, err @@ -73,35 +72,14 @@ func (qs *QuestionStorage) GetReview(userID int) ([]entities.Review, error) { return reviewList, nil } -func (qs *QuestionStorage) SetStat(userID int) ([]entities.Statistic, error) { - var statistic []*entities.Statistic - ctx := context.Background() - err := pgxscan.Select(ctx, qs.db, &statistic, `SELECT q.text, r.rating AS user_grade, AVG(r.rating) AS average_grade - FROM quiz r - INNER JOIN question q ON r.question_id = q.id --- WHERE user_id = $1 - GROUP BY r.question_id, q.text, r.rating`, userID) - if err != nil { - logger.Logger().Error(err.Error()) - return []entities.Statistic{}, err - } - - var statisticList []entities.Statistic - for _, s := range statistic { - statisticList = append(statisticList, *s) - } - - return statisticList, nil -} - func (qs *QuestionStorage) GetAvgStat() ([]entities.Statistic, error) { var statistic []*entities.Statistic ctx := context.Background() - err := pgxscan.Select(ctx, qs.db, &statistic, `SELECT q.id, q.text, AVG(r.rating) as average_grade - FROM quiz r - INNER JOIN question q ON r.question_id = q.id - GROUP BY q.id, r.question_id, q.text - ORDER BY q.id`) + err := pgxscan.Select(ctx, qs.db, &statistic, `SELECT q.id, q.text, AVG(r.rating), + FROM quiz r + INNER JOIN question q ON r.question_id = q.id + GROUP BY r.question_id + ORDER BY q.id`) if err != nil { logger.Logger().Error(err.Error()) return []entities.Statistic{}, err @@ -119,11 +97,11 @@ func (qs *QuestionStorage) GetUserStat(userID int) ([]entities.Statistic, error) var statistic []*entities.Statistic ctx := context.Background() - err := pgxscan.Select(ctx, qs.db, &statistic, `SELECT q.id, q.text, r.rating AS user_grade - FROM question q - INNER JOIN quiz r ON q.id = r.question_id - WHERE user_id = $1 - ORDER BY q.id `, userID) + err := pgxscan.Select(ctx, qs.db, &statistic, `SELECT q.question_id, q.text, r.rating + FROM question q + INNER JOIN quiz r ON q.id = r.question_id + WHERE user_id = $1 + ORDER BY q.id `, userID) if err != nil { logger.Logger().Error(err.Error()) return []entities.Statistic{}, err From d292459942464d1c3f2633ce9f0cce31910fc50e Mon Sep 17 00:00:00 2001 From: MrDzhofik Date: Mon, 29 Apr 2024 11:24:06 +0300 Subject: [PATCH 02/15] Change migration and sightByID handler to work with longitude and latitude, failed attempt to change tests and upload --- ...0240401145225_create_and_insert_tables.sql | 128 +++++++++++------- internal/delivery/file.go | 32 ++--- .../delivery/handlers/comment/comment_test.go | 6 +- internal/delivery/handlers/journey/journey.go | 15 -- internal/delivery/handlers/sight/sight.go | 2 +- .../delivery/handlers/sight/sight_test.go | 24 ++-- .../delivery/initialization/storage_init.go | 11 +- internal/entities/sight.go | 4 +- .../storage/postgres/sight/sight_storage.go | 9 +- .../storage_interfaces/sight_interface.go | 1 - internal/usecase/journey_usecase.go | 5 - router/router.go | 10 +- utils/cors/cors.go | 2 +- 13 files changed, 117 insertions(+), 132 deletions(-) diff --git a/db/migrations/20240401145225_create_and_insert_tables.sql b/db/migrations/20240401145225_create_and_insert_tables.sql index 33fec31..37f13be 100644 --- a/db/migrations/20240401145225_create_and_insert_tables.sql +++ b/db/migrations/20240401145225_create_and_insert_tables.sql @@ -41,6 +41,8 @@ CREATE TABLE sight description text, city_id integer REFERENCES city (id), country_id integer REFERENCES country (id), + latitude double precision, + longitude double precision, UNIQUE (name, city_id) ); @@ -92,6 +94,21 @@ CREATE TABLE quiz created_at timestamptz ); +CREATE TABLE question +( + id integer PRIMARY KEY GENERATED ALWAYS AS IDENTITY, + text text NOT NULL +); + +CREATE TABLE quiz +( + id integer PRIMARY KEY GENERATED ALWAYS AS IDENTITY, + user_id integer REFERENCES user_data (id), + rating integer NOT NULL CHECK (rating > 0 AND rating <= 5), + question_id integer REFERENCES question (id), + created_at timestamptz +); + INSERT INTO country(country) VALUES ('Россия'), ('Беларусь'), @@ -118,89 +135,121 @@ VALUES ('Москва', 1), ('Сулакский каньон', 5); -INSERT INTO sight(name, description, city_id, country_id) +INSERT INTO sight(name, description, city_id, country_id, longitude, latitude) VALUES ('У дяди Вани', 'Ресторан с видом на Сталинскую высотку.', 1, - 1), + 1, + 55.768329, + 37.597468), ('Государственный музей изобразительных искусств имени А.С. Пушкина', 'Музей.', 1, - 1), + 1, + 55.747277, + 37.605194), ('МГТУ им. Н. Э. Баумана', 'Хороший вуз.', 1, - 1), + 1, + 55.766471, + 37.683446), ('Вкусно - и точка', 'Неплохое кафе, вызывает гастрит.', 1, - 1), + 1, + 55.771585, + 37.681730), ('Краеведческий музей', 'Один из самых больших провинциальных музеев краеведческого профиля.', 2, - 1), + 1, + 53.148541, + 48.456204), ('Спасо-Преображенский кафедральный собор', 'Спасо-Преображенский кафедральный собор расположен в центре города и является первым каменным храмом Тамбова и старейшим в Тамбовской обл.', 3, - 1), + 1, + 52.727371, + 41.459110), ('Мирский замок', 'Памятник архитектуры, внесён в список Всемирного наследия ЮНЕСКО.', 9, - 2), + 2, + 53.900162, + 27.551518), ('Чуфут-Кале', 'Пещерный город в Крыму. Топ.', 4, - 4), + 4, + 44.733255, + 33.934201), ('Сасык-Сиваш', 'Розовое озеро. Оно реально розовое.', 5, - 4), + 4, + 45.181475, + 33.576833), ('Крепость Чембело', 'Остатки крепости.', 6, - 4), + 4, + 44.512136, + 33.598321), ('Мечеть Кул Зариф', 'Главная джума-мечеть республики Татарстан и города Казани.', 7, - 3), + 3, + 55.798399, + 49.105147), ('Салтинский Подземный Водопад', 'Единственный в России подземный водопад.', 8, - 5), + 5, + 42.380378, + 47.042068), ('Озеро Рица', 'Рица — горное озеро ледниково-тектонического происхождения на Западном Кавказе, в Гудаутском районе Абхазии', 10, - 6) - , + 6, + 43.480130, + 40.542047), ('Архитектурный комплекс Цитадель Нарын-Кала', 'Древняя дербентская крепость, возведённая по повелению персидского правителя Хосрова I Ануширвана в VI веке, включена ЮНЕСКО в Список Всемирного наследия.', 11, - 5) - , + 5, + 42.055340, + 48.276883), ('Сторожевые башни Северного Кавказа', 'Хорошо сохранившиеся родовые башни XIV–XVI веков, которые выполняли роль жилища и защиты от врагов.', 13, - 5) - , + 5, + 42.086155, + 47.603964), ('Сулакский каньон', 'У истоков реки Сулак берёт начало уникальный каньон. Давным-давно бурная река расколола гору, разделив Салатавский и Гимринский хребты.', 14, - 5) - , + 5, + 43.017452, + 46.824505), ('Стрелка Волги и Оки', 'Место, где реки Ока и Волга, сливаясь, образуют живописный треугольный мыс, называют Стрелкой. Это природная достопримечательность Нижнего Новгорода.', 12, - 1) - , + 1, + 56.335336, + 43.967053), ('Чкаловская лестница', 'Чкаловская лестница - один из символов Нижнего Новгорода. Между Верхневолжской и Нижневолжской набережными находится интересная нижегородская достопримечательность, которая видна даже на космических снимках', 12, - 1) - , + 1, + 56.330091, + 44.008924), ('Нижегородский Кремль', 'Нижегородский кремль – древняя крепость и одновременно главная историческая достопримечательность Нижнего Новгорода', 12, - 1); + 1, + 56.328437, + 44.003111); INSERT INTO image_data(path, sight_id) @@ -226,7 +275,8 @@ VALUES ('public/1.jpg', 1), INSERT INTO question(text) VALUES ('Насколько вы удовлетворены удобством КудаТуда?'), - ('Насколько интуитивно понятен интерфейс?'); + ('Насколько интуитивно понятен интерфейс?'), + ('Оцените представленные достопримечательности'); CREATE OR REPLACE FUNCTION create_profile() @@ -267,26 +317,10 @@ AFTER UPDATE ON feedback FOR EACH ROW EXECUTE FUNCTION update_sight_rating(); -CREATE TABLE question -( - id integer PRIMARY KEY GENERATED ALWAYS AS IDENTITY, - text text NOT NULL -); - -CREATE TABLE quiz -( - id integer PRIMARY KEY GENERATED ALWAYS AS IDENTITY, - user_id integer REFERENCES user_data (id), - rating integer NOT NULL CHECK (rating > 0 AND rating <= 5), - question_id integer REFERENCES question (id), - created_at timestamptz -); - -INSERT INTO question(text) -VALUES ('Насколько вы удовлетворены удобством КудаТуда?'), - ('Насколько интуитивно понятен интерфейс?'), - ('Оцените представленные достопримечательности'); - +CREATE TRIGGER after_delete_feedback +AFTER DELETE ON feedback +FOR EACH ROW +EXECUTE FUNCTION update_sight_rating(); -- +goose Down -- +goose StatementBegin diff --git a/internal/delivery/file.go b/internal/delivery/file.go index 4bb94b2..13d05b1 100644 --- a/internal/delivery/file.go +++ b/internal/delivery/file.go @@ -22,35 +22,22 @@ type FileResponse struct { var ( maxFileSizeMB = 1 maxFileSizeBytes = int64(maxFileSizeMB) * 1024 * 1024 - magicTable = map[string]string{ - "\xff\xd8\xff": "image/jpeg", - "\x89PNG\r\n\x1a\n": "image/png", - "GIF87a": "image/gif", - "GIF89a": "image/gif", - } ) -func DetectType(b []byte) bool { - flag := false - s := string(b) - for key, val := range magicTable { - if strings.HasPrefix(s, key) { - fmt.Println(val) - flag = true - } - } - return flag -} - -func ValidateFileExtension(file multipart.File) bool { +func ValidateFileExtension(fileHeader multipart.FileHeader) bool { buff := make([]byte, 512) + file, err := fileHeader.Open() + if err != nil { + return false + } + defer file.Close() if _, err := file.Read(buff); err != nil { return false } - val := DetectType(buff) + fileType := strings.ToLower(http.DetectContentType(buff)) - return val + return fileType == "image/png" || fileType == "image/jpeg" || fileType == "image/jpg" } func ValidateFileSize(handler *multipart.FileHeader) bool { @@ -70,10 +57,11 @@ func SaveFile(r *http.Request) (string, error) { logger.Error("Error while retrieving file:", "error", err) return string(""), err } + fmt.Println("No") defer file.Close() // Validate - flag := ValidateFileExtension(file) + flag := ValidateFileExtension(*handler) if !flag { logger.Error("Not valid format!") return string(""), errors.New("Not valid format!") diff --git a/internal/delivery/handlers/comment/comment_test.go b/internal/delivery/handlers/comment/comment_test.go index 7f3ab03..6d5d7e9 100644 --- a/internal/delivery/handlers/comment/comment_test.go +++ b/internal/delivery/handlers/comment/comment_test.go @@ -4,7 +4,7 @@ import ( "context" "testing" - comment "github.com/go-park-mail-ru/2024_1_ResCogitans/internal/delivery/handlers/sight" + comment "github.com/go-park-mail-ru/2024_1_ResCogitans/internal/delivery/handlers/comment" "github.com/go-park-mail-ru/2024_1_ResCogitans/internal/entities" "github.com/go-park-mail-ru/2024_1_ResCogitans/utils/wrapper" "github.com/stretchr/testify/assert" @@ -12,14 +12,14 @@ import ( // Get comments func TestGetComments(t *testing.T) { - handler := &comment.SightsHandler{} + handler := &comment.CommentHandler{} ctx := context.Background() param := make(map[string]string) param["id"] = "1" ctx = wrapper.SetPathParamsToCtx(ctx, param) - resp, err := handler.GetSightByID(ctx, entities.Sight{}) + resp, err := handler.GetCommentsBySightID(ctx, entities.Sight{}) assert.NotEmpty(t, resp.Comms) diff --git a/internal/delivery/handlers/journey/journey.go b/internal/delivery/handlers/journey/journey.go index 4a213f2..e37b031 100644 --- a/internal/delivery/handlers/journey/journey.go +++ b/internal/delivery/handlers/journey/journey.go @@ -87,21 +87,6 @@ func (h *JourneyHandler) AddJourneySight(ctx context.Context, requestData entiti return entities.JourneySight{JourneyID: journeyID}, nil } -func (h *JourneyHandler) DeleteJourneySight(ctx context.Context, requestData entities.JourneySight) (entities.JourneySight, error) { - pathParams := httputils.GetPathParamsFromCtx(ctx) - journeyID, err := strconv.Atoi(pathParams["id"]) - if err != nil { - return entities.JourneySight{}, err - } - - err = h.JourneyUseCase.DeleteJourneySight(journeyID, requestData) - if err != nil { - return entities.JourneySight{}, err - } - - return entities.JourneySight{}, nil -} - func (h *JourneyHandler) GetJourneySights(ctx context.Context, _ entities.JourneySight) (entities.JourneySights, error) { pathParams := httputils.GetPathParamsFromCtx(ctx) journeyID, err := strconv.Atoi(pathParams["id"]) diff --git a/internal/delivery/handlers/sight/sight.go b/internal/delivery/handlers/sight/sight.go index 9532f2a..3f4c8ea 100644 --- a/internal/delivery/handlers/sight/sight.go +++ b/internal/delivery/handlers/sight/sight.go @@ -35,7 +35,7 @@ func (h *SightHandler) GetSights(_ context.Context, _ entities.Sight) (entities. return entities.Sights{Sight: sights}, nil } -// GetSights godoc +// GetSight godoc // @Summary Get sight by id // @Description get sight by id // @Accept json diff --git a/internal/delivery/handlers/sight/sight_test.go b/internal/delivery/handlers/sight/sight_test.go index 0ba2bf1..524ff08 100644 --- a/internal/delivery/handlers/sight/sight_test.go +++ b/internal/delivery/handlers/sight/sight_test.go @@ -4,26 +4,25 @@ import ( "context" "testing" + sight "github.com/go-park-mail-ru/2024_1_ResCogitans/internal/delivery/handlers/sight" "github.com/go-park-mail-ru/2024_1_ResCogitans/internal/entities" - "github.com/go-park-mail-ru/2024_1_ResCogitans/utils/wrapper" + utils "github.com/go-park-mail-ru/2024_1_ResCogitans/utils/httputils" "github.com/stretchr/testify/assert" ) func TestGetSights(t *testing.T) { - handler := &SightsHandler{} + h := &sight.SightHandler{} + h = sight.NewSightsHandler() ctx := context.Background() - resp, err := handler.GetSights(ctx, entities.Sight{}) + resp, err := h.GetSights(ctx, entities.Sight{}) if err != nil { t.Fatalf("Failed to get sights: %v", err) } - assert.NotEmpty(t, resp.Sight) - expectedFirstSight := entities.Sight{ ID: 1, - Rating: 2.1, Name: "У дяди Вани", Description: "Ресторан с видом на Сталинскую высотку.", CityID: 1, @@ -35,16 +34,16 @@ func TestGetSights(t *testing.T) { func TestGetSightsByID(t *testing.T) { comm := entities.Comments{} - handler := &SightsHandler{} + handler := &sight.SightHandler{} comm.Validate() ctx := context.Background() param := make(map[string]string) param["id"] = "1" - ctx = wrapper.SetPathParamsToCtx(ctx, param) + ctx = utils.SetPathParamsToCtx(ctx, param) - resp, err := handler.GetSightByID(ctx, entities.Sight{}) + resp, err := handler.GetSight(ctx, entities.Sight{}) if err != nil { t.Fatalf("Failed to get sights: %v", err) } @@ -53,7 +52,6 @@ func TestGetSightsByID(t *testing.T) { expectedSight := entities.Sight{ ID: 1, - Rating: 2.1, Name: "У дяди Вани", Description: "Ресторан с видом на Сталинскую высотку.", CityID: 1, @@ -66,14 +64,14 @@ func TestGetSightsByID(t *testing.T) { } func TestGetSightsByIDNotInt(t *testing.T) { - handler := &SightsHandler{} + handler := &sight.SightHandler{} ctx := context.Background() param := make(map[string]string) param["id"] = "id" - ctx = wrapper.SetPathParamsToCtx(ctx, param) + ctx = utils.SetPathParamsToCtx(ctx, param) - _, err := handler.GetSightByID(ctx, entities.Sight{}) + _, err := handler.GetSight(ctx, entities.Sight{}) assert.NotNil(t, err) } diff --git a/internal/delivery/initialization/storage_init.go b/internal/delivery/initialization/storage_init.go index e17a3a1..1316af7 100644 --- a/internal/delivery/initialization/storage_init.go +++ b/internal/delivery/initialization/storage_init.go @@ -1,7 +1,6 @@ package initialization import ( - "github.com/go-park-mail-ru/2024_1_ResCogitans/internal/storage/postgres/question" "github.com/go-park-mail-ru/2024_1_ResCogitans/internal/storage/postgres/sight" "github.com/go-park-mail-ru/2024_1_ResCogitans/internal/storage/postgres/user" "github.com/go-park-mail-ru/2024_1_ResCogitans/internal/storage/redis/session" @@ -20,10 +19,10 @@ type Storages struct { func StorageInit(pdb *pgxpool.Pool, rdb *redis.Client) *Storages { return &Storages{ - UserStorage: user.NewUserStorage(pdb), - ProfileStorage: user.NewUserProfileStorage(pdb), - SessionStorage: session.NewSessionStorage(rdb), - SightStorage: sight.NewSightStorage(pdb), - QuestionStorage: question.NewQuestionStorage(pdb), + UserStorage: user.NewUserStorage(pdb), + ProfileStorage: user.NewUserProfileStorage(pdb), + SessionStorage: session.NewSessionStorage(rdb), + SightStorage: sight.NewSightStorage(pdb), + // QuestionStorage: question.NewQuestionStorage(pdb), } } diff --git a/internal/entities/sight.go b/internal/entities/sight.go index bb6b0e3..37cce98 100644 --- a/internal/entities/sight.go +++ b/internal/entities/sight.go @@ -2,13 +2,15 @@ package entities type Sight struct { ID int `json:"id"` - Rating *float32 `json:"rating"` + Rating *float64 `json:"rating"` Name string `json:"name"` Description string `json:"description"` CityID int `json:"cityID"` CountryID int `json:"countryID"` City string `json:"city"` Country string `json:"country"` + Longitude *float64 `json:"longitude"` + Latitude *float64 `json:"latitude"` Path string `json:"url"` } diff --git a/internal/storage/postgres/sight/sight_storage.go b/internal/storage/postgres/sight/sight_storage.go index 53383de..d9a23e9 100644 --- a/internal/storage/postgres/sight/sight_storage.go +++ b/internal/storage/postgres/sight/sight_storage.go @@ -48,7 +48,7 @@ func (ss *SightStorage) GetSight(id int) (entities.Sight, error) { var sight []*entities.Sight ctx := context.Background() - err := pgxscan.Select(ctx, ss.db, &sight, `SELECT sight.id, COALESCE(rating, 0) AS rating, sight.name, description, city_id, sight.country_id, im.path, city.city, country.country + err := pgxscan.Select(ctx, ss.db, &sight, `SELECT sight.id, COALESCE(rating, 0) AS rating, sight.name, description, city_id, sight.country_id, im.path, city.city, country.country, longitude, latitude FROM sight INNER JOIN image_data AS im ON sight.id = im.sight_id @@ -265,13 +265,6 @@ func (ss *SightStorage) EditJourney(journeyID int, name, description string) err return nil } -func (ss *SightStorage) DeleteJourneySight(journeyID int, sight entities.JourneySight) error { - ctx := context.Background() - - _, err := ss.db.Exec(ctx, `DELETE FROM journey_sight WHERE journey_id = $1 AND sight_id = $2 `, journeyID, sight.SightID) - return err -} - func (ss *SightStorage) GetJourneySights(journeyID int) ([]entities.Sight, error) { var sights []entities.Sight var idList []*int diff --git a/internal/storage/storage_interfaces/sight_interface.go b/internal/storage/storage_interfaces/sight_interface.go index 699080a..58eaa23 100644 --- a/internal/storage/storage_interfaces/sight_interface.go +++ b/internal/storage/storage_interfaces/sight_interface.go @@ -18,7 +18,6 @@ type SightStorageInterface interface { GetJourneys(userID int) ([]entities.Journey, error) AddJourneySight(journeyID int, ids []int) error EditJourney(journeyID int, name, description string) error - DeleteJourneySight(journeyID int, sight entities.JourneySight) error GetJourneySights(journeyID int) ([]entities.Sight, error) GetJourney(journeyID int) (entities.Journey, error) } diff --git a/internal/usecase/journey_usecase.go b/internal/usecase/journey_usecase.go index a098ee0..188d7f4 100644 --- a/internal/usecase/journey_usecase.go +++ b/internal/usecase/journey_usecase.go @@ -11,7 +11,6 @@ type JourneyUseCaseInterface interface { GetJourneys(userID int) ([]entities.Journey, error) AddJourneySight(journeyID int, ids []int) error EditJourney(journeyID int, name, description string) error - DeleteJourneySight(journeyID int, sight entities.JourneySight) error GetJourneySights(journeyID int) ([]entities.Sight, error) GetJourney(journeyID int) (entities.Journey, error) CheckJourney(userID int) (bool, error) @@ -47,10 +46,6 @@ func (ju *JourneyUseCase) EditJourney(journeyID int, name, description string) e return ju.SightStorage.EditJourney(journeyID, name, description) } -func (ju *JourneyUseCase) DeleteJourneySight(journeyID int, sight entities.JourneySight) error { - return ju.SightStorage.DeleteJourneySight(journeyID, sight) -} - func (ju *JourneyUseCase) GetJourneySights(journeyID int) ([]entities.Sight, error) { return ju.SightStorage.GetJourneySights(journeyID) } diff --git a/router/router.go b/router/router.go index e3b50e4..e5fdeb8 100644 --- a/router/router.go +++ b/router/router.go @@ -36,7 +36,7 @@ func SetupRouter(_ *config.Config, handlers *initialization.Handlers) *chi.Mux { router.Use(middle.XSSMiddleware) // upload image - router.HandleFunc("/upload", user.Upload) + router.HandleFunc("/api/profile/{id}/upload", user.Upload) router.Mount("/api/sights", SightRoutes(handlers.SightHandler)) @@ -72,7 +72,6 @@ func SetupRouter(_ *config.Config, handlers *initialization.Handlers) *chi.Mux { router.Mount("/api/trip/{id}", JourneySightRoutes(handlers.JourneyHandler)) router.Mount("/api/trip/{id}/sight/add", AddJourneySightRoutes(handlers.JourneyHandler)) router.Mount("/api/trip/{id}/edit", EditJourney(handlers.JourneyHandler)) - router.Mount("/api/trip/{id}/sight/delete", DeleteJourneySightRoutes(handlers.JourneyHandler)) // quiz router.Mount("/api/review/create", CreateReviewRoutes(handlers.QuizHandler)) @@ -173,13 +172,6 @@ func EditJourney(handler *journey.JourneyHandler) chi.Router { return router } -func DeleteJourneySightRoutes(handler *journey.JourneyHandler) chi.Router { - router := chi.NewRouter() - wrapperInstance := &wrapper.Wrapper[entities.JourneySight, entities.JourneySight]{ServeHTTP: handler.DeleteJourneySight} - router.Post("/", wrapperInstance.HandlerWrapper) - return router -} - func JourneySightRoutes(handler *journey.JourneyHandler) chi.Router { router := chi.NewRouter() wrapperInstance := &wrapper.Wrapper[entities.JourneySight, entities.JourneySights]{ServeHTTP: handler.GetJourneySights} diff --git a/utils/cors/cors.go b/utils/cors/cors.go index 593c6de..a856ae8 100644 --- a/utils/cors/cors.go +++ b/utils/cors/cors.go @@ -4,7 +4,7 @@ import "net/http" func CorsMiddleware(next http.Handler) http.Handler { return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { - w.Header().Set("Access-Control-Allow-Origin", "http://127.0.0.1:3000") + w.Header().Set("Access-Control-Allow-Origin", "http://localhost:3000") w.Header().Set("Access-Control-Allow-Methods", "POST, GET, OPTIONS, PUT, DELETE") w.Header().Set("Access-Control-Allow-Headers", "Accept, Content-Type, Content-Length, Accept-Encoding, X-CSRF-Token, Authorization") w.Header().Set("Access-Control-Allow-Credentials", "true") From 31ad62d11ff900946e3c3766461cdc8c818e24f0 Mon Sep 17 00:00:00 2001 From: MrDzhofik Date: Tue, 30 Apr 2024 16:53:14 +0300 Subject: [PATCH 03/15] Fixed avatar uploads --- internal/delivery/file.go | 2 +- internal/delivery/handlers/avatar/avatar.go | 18 ++++++++++-------- 2 files changed, 11 insertions(+), 9 deletions(-) diff --git a/internal/delivery/file.go b/internal/delivery/file.go index 13d05b1..40c8d15 100644 --- a/internal/delivery/file.go +++ b/internal/delivery/file.go @@ -37,7 +37,7 @@ func ValidateFileExtension(fileHeader multipart.FileHeader) bool { fileType := strings.ToLower(http.DetectContentType(buff)) - return fileType == "image/png" || fileType == "image/jpeg" || fileType == "image/jpg" + return fileType == "image/png" || fileType == "image/jpeg" || fileType == "image/jpg" || fileType == "image/webp" } func ValidateFileSize(handler *multipart.FileHeader) bool { diff --git a/internal/delivery/handlers/avatar/avatar.go b/internal/delivery/handlers/avatar/avatar.go index 743be51..07a4e64 100644 --- a/internal/delivery/handlers/avatar/avatar.go +++ b/internal/delivery/handlers/avatar/avatar.go @@ -5,28 +5,30 @@ import ( "net/http" "os" "path/filepath" + + "github.com/go-park-mail-ru/2024_1_ResCogitans/internal/config" ) func Upload(w http.ResponseWriter, r *http.Request) { + cfg, err := config.LoadConfig() + if err != nil { + http.Error(w, "Failed to load config", http.StatusBadRequest) + return + } + if r.Method != http.MethodPost { http.Error(w, "Method not allowed", http.StatusMethodNotAllowed) return } - file, header, err := r.FormFile("image") + file, header, err := r.FormFile("file") if err != nil { http.Error(w, "Error retrieving the file", http.StatusBadRequest) return } defer file.Close() - cwd, err := os.Getwd() - if err != nil { - http.Error(w, "Error getting current directory", http.StatusInternalServerError) - return - } - - uploadPath := filepath.Join(cwd, "../../../Modules/public", header.Filename) + uploadPath := filepath.Join(cfg.FileUploadPath, header.Filename) out, err := os.Create(uploadPath) if err != nil { From 69a8d50aa77e2d66d7ec729a2b52e77309705921 Mon Sep 17 00:00:00 2001 From: MrDzhofik Date: Wed, 1 May 2024 00:36:54 +0300 Subject: [PATCH 04/15] Add creating, deleting and getting albums --- ...0240401145225_create_and_insert_tables.sql | 17 +++++ db/normalized/ERD_description.md | 16 ++++- internal/delivery/handlers/album/album.go | 71 +++++++++++++++++++ .../delivery/initialization/handler_init.go | 6 +- .../delivery/initialization/storage_init.go | 16 +++-- .../delivery/initialization/use_case_init.go | 2 + internal/entities/album.go | 20 ++++++ .../storage/postgres/album/album_storage.go | 69 ++++++++++++++++++ .../postgres/question/question_storage.go | 3 +- .../storage_interfaces/album_interface.go | 11 +++ .../storage_interfaces/question_interface.go | 3 +- internal/usecase/album_usecase.go | 43 +++++++++++ internal/usecase/review_usecase.go | 4 +- router/router.go | 27 +++++++ 14 files changed, 295 insertions(+), 13 deletions(-) create mode 100644 internal/delivery/handlers/album/album.go create mode 100644 internal/entities/album.go create mode 100644 internal/storage/postgres/album/album_storage.go create mode 100644 internal/storage/storage_interfaces/album_interface.go create mode 100644 internal/usecase/album_usecase.go diff --git a/db/migrations/20240401145225_create_and_insert_tables.sql b/db/migrations/20240401145225_create_and_insert_tables.sql index 37f13be..7cdb09b 100644 --- a/db/migrations/20240401145225_create_and_insert_tables.sql +++ b/db/migrations/20240401145225_create_and_insert_tables.sql @@ -109,6 +109,23 @@ CREATE TABLE quiz created_at timestamptz ); +CREATE TABLE album +( + id integer PRIMARY KEY GENERATED ALWAYS AS IDENTITY, + user_id integer REFERENCES user_data(id), + name text NOT NULL, + description text, + UNIQUE(user_id, name) +); + +CREATE TABLE album_photo +( + id integer PRIMARY KEY GENERATED ALWAYS AS IDENTITY, + album_id integer REFERENCES album(id), + path text UNIQUE, + description text +); + INSERT INTO country(country) VALUES ('Россия'), ('Беларусь'), diff --git a/db/normalized/ERD_description.md b/db/normalized/ERD_description.md index 0bf0c11..5f72cf7 100644 --- a/db/normalized/ERD_description.md +++ b/db/normalized/ERD_description.md @@ -53,15 +53,29 @@ integer rating text feedback } + ALBUM { + integer id PK + integer user_id FK + text name UK + text description + } + ALBUM_PHOTO { + integer id PK + integer album_id FK + text path UK + text description + } PROFILE ||--|| USER : has SIGHT }o--|| CITY: includes - CITY }0--|| COUNTRY: includes + CITY }o--|| COUNTRY: includes JOURNEY }o--|| USER : creates JOURNEY_SIGHT }|--|| JOURNEY: has JOURNEY_SIGHT }|--|| SIGHT : contains FEEDBACK }o--|| USER : writes FEEDBACK }o--|| SIGHT : belongs_to IMAGE }|--|| SIGHT : belongs_to + ALBUM }o--|| USER : creates + ALBUM_PHOTO }|--|| ALBUM : has ``` diff --git a/internal/delivery/handlers/album/album.go b/internal/delivery/handlers/album/album.go new file mode 100644 index 0000000..fdd71b3 --- /dev/null +++ b/internal/delivery/handlers/album/album.go @@ -0,0 +1,71 @@ +package album + +import ( + "context" + + "github.com/go-park-mail-ru/2024_1_ResCogitans/internal/entities" + "github.com/go-park-mail-ru/2024_1_ResCogitans/internal/usecase" + "github.com/go-park-mail-ru/2024_1_ResCogitans/utils/httputils" +) + +type AlbumHandler struct { + AlbumUseCase usecase.AlbumUseCaseInterface +} + +func NewAlbumHandler(usecase usecase.AlbumUseCaseInterface) *AlbumHandler { + return &AlbumHandler{ + AlbumUseCase: usecase, + } +} + +// CreateAlbum godoc +// @Summary Create new album +// @Description create new album +// @ID CreateAlbum +// @Accept json +// @Produce json +// @Success 200 Album +// @Router /sight [get] +func (h *AlbumHandler) CreateAlbum(ctx context.Context, album entities.Album) (entities.Album, error) { + userID, err := httputils.GetUserFromCtx(ctx) + if err != nil { + return entities.Album{}, err + } + + album.UserID = userID + + album, err = h.AlbumUseCase.CreateAlbum(album) + if err != nil { + return entities.Album{}, err + } + return album, nil +} + +// GetSights godoc +// @Summary Get all sights +// @Description get all sights +// @ID get-sights +// @Accept json +// @Produce json +// @Success 200 {array} sight.Sight +// @Router /sights [get] +func (h *AlbumHandler) DeleteAlbum(_ context.Context, album entities.Album) (entities.Album, error) { + _, err := h.AlbumUseCase.DeleteAlbum(album) + if err != nil { + return entities.Album{}, err + } + return entities.Album{}, nil +} + +func (h *AlbumHandler) GetAlbums(ctx context.Context, _ entities.Album) (entities.Albums, error) { + userID, err := httputils.GetUserFromCtx(ctx) + if err != nil { + return entities.Albums{}, err + } + + album, err := h.AlbumUseCase.GetAlbums(userID) + if err != nil { + return entities.Albums{}, err + } + return album, nil +} diff --git a/internal/delivery/initialization/handler_init.go b/internal/delivery/initialization/handler_init.go index e9d1b30..616e4d9 100644 --- a/internal/delivery/initialization/handler_init.go +++ b/internal/delivery/initialization/handler_init.go @@ -1,6 +1,7 @@ package initialization import ( + "github.com/go-park-mail-ru/2024_1_ResCogitans/internal/delivery/handlers/album" "github.com/go-park-mail-ru/2024_1_ResCogitans/internal/delivery/handlers/authorization" "github.com/go-park-mail-ru/2024_1_ResCogitans/internal/delivery/handlers/comment" "github.com/go-park-mail-ru/2024_1_ResCogitans/internal/delivery/handlers/deactivation" @@ -21,6 +22,7 @@ type Handlers struct { JourneyHandler *journey.JourneyHandler CommentHandler *comment.CommentHandler QuizHandler *quiz.QuizHandler + AlbumHandler *album.AlbumHandler AuthMiddleware *middle.AuthMiddleware } @@ -35,6 +37,8 @@ func HandlerInit(cases *UseCases) *Handlers { JourneyHandler: journey.NewJourneyHandler(cases.JourneyUseCase), CommentHandler: comment.NewCommentHandler(cases.CommentUseCase), QuizHandler: quiz.NewQuizHandler(cases.QuestionUseCase, cases.CommentUseCase, cases.JourneyUseCase), - AuthMiddleware: middle.NewAuthMiddleware(cases.SessionUseCase), + AlbumHandler: album.NewAlbumHandler(cases.AlbumUseCase), + + AuthMiddleware: middle.NewAuthMiddleware(cases.SessionUseCase), } } diff --git a/internal/delivery/initialization/storage_init.go b/internal/delivery/initialization/storage_init.go index 1316af7..a1ad4c9 100644 --- a/internal/delivery/initialization/storage_init.go +++ b/internal/delivery/initialization/storage_init.go @@ -1,6 +1,8 @@ package initialization import ( + "github.com/go-park-mail-ru/2024_1_ResCogitans/internal/storage/postgres/album" + "github.com/go-park-mail-ru/2024_1_ResCogitans/internal/storage/postgres/question" "github.com/go-park-mail-ru/2024_1_ResCogitans/internal/storage/postgres/sight" "github.com/go-park-mail-ru/2024_1_ResCogitans/internal/storage/postgres/user" "github.com/go-park-mail-ru/2024_1_ResCogitans/internal/storage/redis/session" @@ -14,15 +16,17 @@ type Storages struct { ProfileStorage storage.UserProfileStorageInterface SessionStorage storage.SessionStorageInterface SightStorage storage.SightStorageInterface - QuestionStorage storage.QuestionInterface + QuestionStorage storage.QuestionStorageInterface + AlbumStorage storage.AlbumStorageInterface } func StorageInit(pdb *pgxpool.Pool, rdb *redis.Client) *Storages { return &Storages{ - UserStorage: user.NewUserStorage(pdb), - ProfileStorage: user.NewUserProfileStorage(pdb), - SessionStorage: session.NewSessionStorage(rdb), - SightStorage: sight.NewSightStorage(pdb), - // QuestionStorage: question.NewQuestionStorage(pdb), + UserStorage: user.NewUserStorage(pdb), + ProfileStorage: user.NewUserProfileStorage(pdb), + SessionStorage: session.NewSessionStorage(rdb), + SightStorage: sight.NewSightStorage(pdb), + QuestionStorage: question.NewQuestionStorage(pdb), + AlbumStorage: album.NewAlbumStorage(pdb), } } diff --git a/internal/delivery/initialization/use_case_init.go b/internal/delivery/initialization/use_case_init.go index 668d7e9..16bc471 100644 --- a/internal/delivery/initialization/use_case_init.go +++ b/internal/delivery/initialization/use_case_init.go @@ -12,6 +12,7 @@ type UseCases struct { JourneyUseCase usecase.JourneyUseCaseInterface CommentUseCase usecase.CommentUseCaseInterface QuestionUseCase usecase.QuestionUseCaseInterface + AlbumUseCase usecase.AlbumUseCaseInterface } func UseCaseInit(storages *Storages) *UseCases { @@ -23,5 +24,6 @@ func UseCaseInit(storages *Storages) *UseCases { JourneyUseCase: usecase.NewJourneyUseCase(storages.SightStorage), CommentUseCase: usecase.NewCommentUseCase(storages.SightStorage), QuestionUseCase: usecase.NewQuestionUseCase(storages.QuestionStorage), + AlbumUseCase: usecase.NewAlbumUseCase(storages.AlbumStorage), } } diff --git a/internal/entities/album.go b/internal/entities/album.go new file mode 100644 index 0000000..180b0e7 --- /dev/null +++ b/internal/entities/album.go @@ -0,0 +1,20 @@ +package entities + +type Album struct { + ID int `json:"albumID"` + UserID int `json:"userID"` + Name string `json:"name"` + Description string `json:"description"` +} + +type Albums struct { + Albums []Album `json:"albums"` +} + +func (h Album) Validate() error { + return nil +} + +func (h Albums) Validate() error { + return nil +} diff --git a/internal/storage/postgres/album/album_storage.go b/internal/storage/postgres/album/album_storage.go new file mode 100644 index 0000000..0c2ccf6 --- /dev/null +++ b/internal/storage/postgres/album/album_storage.go @@ -0,0 +1,69 @@ +package album + +import ( + "context" + + "github.com/georgysavva/scany/v2/pgxscan" + "github.com/go-park-mail-ru/2024_1_ResCogitans/internal/entities" + storage "github.com/go-park-mail-ru/2024_1_ResCogitans/internal/storage/storage_interfaces" + "github.com/go-park-mail-ru/2024_1_ResCogitans/utils/logger" + "github.com/jackc/pgx/v5/pgxpool" +) + +// AlbunStorage struct +type AlbumStorage struct { + db *pgxpool.Pool +} + +// NewAlbumRepo creates sight repo +func NewAlbumStorage(db *pgxpool.Pool) storage.AlbumStorageInterface { + return &AlbumStorage{ + db: db, + } +} + +func (as *AlbumStorage) CreateAlbum(album entities.Album) (entities.Album, error) { + var albumID int + ctx := context.Background() + + err := as.db.QueryRow(ctx, `INSERT INTO album(user_id, name, description) + VALUES($1, $2, $3) RETURNING id`, album.UserID, album.Name, album.Description).Scan(&albumID) + if err != nil { + logger.Logger().Error(err.Error()) + return entities.Album{}, err + } + + return entities.Album{ID: albumID}, err +} + +func (as *AlbumStorage) GetAlbums(userID int) (entities.Albums, error) { + var albums []*entities.Album + + ctx := context.Background() + + err := pgxscan.Select(ctx, as.db, &albums, `SELECT id, name, description + FROM album + WHERE user_id = $1`, userID) + if err != nil { + logger.Logger().Error(err.Error()) + return entities.Albums{}, err + } + + var albumList []entities.Album + for _, a := range albums { + albumList = append(albumList, *a) + } + return entities.Albums{Albums: albumList}, nil +} + +func (as *AlbumStorage) DeleteAlbum(albumID int) error { + ctx := context.Background() + + _, err := as.db.Exec(ctx, `DELETE FROM album WHERE id = $1;`, albumID) + if err != nil { + logger.Logger().Error(err.Error()) + return err + } + + return nil +} diff --git a/internal/storage/postgres/question/question_storage.go b/internal/storage/postgres/question/question_storage.go index 1f9ceca..74bdd99 100644 --- a/internal/storage/postgres/question/question_storage.go +++ b/internal/storage/postgres/question/question_storage.go @@ -6,6 +6,7 @@ import ( "github.com/georgysavva/scany/v2/pgxscan" "github.com/go-park-mail-ru/2024_1_ResCogitans/internal/entities" + storage "github.com/go-park-mail-ru/2024_1_ResCogitans/internal/storage/storage_interfaces" "github.com/go-park-mail-ru/2024_1_ResCogitans/utils/logger" "github.com/jackc/pgx/v5/pgxpool" ) @@ -14,7 +15,7 @@ type QuestionStorage struct { db *pgxpool.Pool } -func NewQuestionStorage(db *pgxpool.Pool) *QuestionStorage { +func NewQuestionStorage(db *pgxpool.Pool) storage.QuestionStorageInterface { return &QuestionStorage{ db: db, } diff --git a/internal/storage/storage_interfaces/album_interface.go b/internal/storage/storage_interfaces/album_interface.go new file mode 100644 index 0000000..5e81e33 --- /dev/null +++ b/internal/storage/storage_interfaces/album_interface.go @@ -0,0 +1,11 @@ +package storage + +import ( + "github.com/go-park-mail-ru/2024_1_ResCogitans/internal/entities" +) + +type AlbumStorageInterface interface { + CreateAlbum(album entities.Album) (entities.Album, error) + DeleteAlbum(albumID int) error + GetAlbums(userID int) (entities.Albums, error) +} diff --git a/internal/storage/storage_interfaces/question_interface.go b/internal/storage/storage_interfaces/question_interface.go index cc2e1a1..a3b1a6e 100644 --- a/internal/storage/storage_interfaces/question_interface.go +++ b/internal/storage/storage_interfaces/question_interface.go @@ -4,9 +4,8 @@ import ( "github.com/go-park-mail-ru/2024_1_ResCogitans/internal/entities" ) -type QuestionInterface interface { +type QuestionStorageInterface interface { AddReview(userID int, review entities.Review) error - SetStat(userID int) ([]entities.Statistic, error) GetQuestions() ([]entities.QuestionResponse, error) GetReview(userID int) ([]entities.Review, error) GetAvgStat() ([]entities.Statistic, error) diff --git a/internal/usecase/album_usecase.go b/internal/usecase/album_usecase.go new file mode 100644 index 0000000..5b0b15a --- /dev/null +++ b/internal/usecase/album_usecase.go @@ -0,0 +1,43 @@ +package usecase + +import ( + "github.com/go-park-mail-ru/2024_1_ResCogitans/internal/entities" + storage "github.com/go-park-mail-ru/2024_1_ResCogitans/internal/storage/storage_interfaces" +) + +type AlbumUseCaseInterface interface { + CreateAlbum(album entities.Album) (entities.Album, error) + DeleteAlbum(album entities.Album) (entities.Album, error) + GetAlbums(userID int) (entities.Albums, error) +} + +type AlbumUseCase struct { + AlbumStorage storage.AlbumStorageInterface +} + +func NewAlbumUseCase(storage storage.AlbumStorageInterface) AlbumUseCaseInterface { + return &AlbumUseCase{ + AlbumStorage: storage, + } +} + +func (au *AlbumUseCase) CreateAlbum(album entities.Album) (entities.Album, error) { + row, err := au.AlbumStorage.CreateAlbum(album) + return row, err +} + +func (au *AlbumUseCase) DeleteAlbum(album entities.Album) (entities.Album, error) { + err := au.AlbumStorage.DeleteAlbum(album.ID) + if err != nil { + return entities.Album{}, err + } + return entities.Album{ID: album.ID}, nil +} + +func (au *AlbumUseCase) GetAlbums(userID int) (entities.Albums, error) { + albums, err := au.AlbumStorage.GetAlbums(userID) + if err != nil { + return entities.Albums{}, err + } + return albums, nil +} diff --git a/internal/usecase/review_usecase.go b/internal/usecase/review_usecase.go index 4678deb..0659bdc 100644 --- a/internal/usecase/review_usecase.go +++ b/internal/usecase/review_usecase.go @@ -13,10 +13,10 @@ type QuestionUseCaseInterface interface { } type QuestionUseCase struct { - QuestionStorage storage.QuestionInterface + QuestionStorage storage.QuestionStorageInterface } -func NewQuestionUseCase(storage storage.QuestionInterface) QuestionUseCaseInterface { +func NewQuestionUseCase(storage storage.QuestionStorageInterface) QuestionUseCaseInterface { return &QuestionUseCase{ QuestionStorage: storage, } diff --git a/router/router.go b/router/router.go index e5fdeb8..36f5e62 100644 --- a/router/router.go +++ b/router/router.go @@ -6,6 +6,7 @@ import ( "github.com/go-chi/chi/middleware" "github.com/go-chi/chi/v5" "github.com/go-park-mail-ru/2024_1_ResCogitans/internal/config" + "github.com/go-park-mail-ru/2024_1_ResCogitans/internal/delivery/handlers/album" "github.com/go-park-mail-ru/2024_1_ResCogitans/internal/delivery/handlers/authorization" user "github.com/go-park-mail-ru/2024_1_ResCogitans/internal/delivery/handlers/avatar" "github.com/go-park-mail-ru/2024_1_ResCogitans/internal/delivery/handlers/comment" @@ -78,6 +79,11 @@ func SetupRouter(_ *config.Config, handlers *initialization.Handlers) *chi.Mux { router.Mount("/api/review/check", CheckUserReviewRoutes(handlers.QuizHandler)) router.Mount("/api/review/get", GetStatistic(handlers.QuizHandler)) + // album + router.Mount("/api/profile/{id}/album/create", CreateAlbumRoutes(handlers.AlbumHandler)) + router.Mount("/api/profile/{id}/album/delete", DeleteAlbumRoutes(handlers.AlbumHandler)) + router.Mount("/api/profile/{id}/albums", GetAlbumsRoutes(handlers.AlbumHandler)) + return router } @@ -235,3 +241,24 @@ func GetStatistic(handler *quiz.QuizHandler) chi.Router { router.Get("/", wrapperInstance.HandlerWrapper) return router } + +func CreateAlbumRoutes(handler *album.AlbumHandler) chi.Router { + router := chi.NewRouter() + wrapperInstance := &wrapper.Wrapper[entities.Album, entities.Album]{ServeHTTP: handler.CreateAlbum} + router.Post("/", wrapperInstance.HandlerWrapper) + return router +} + +func DeleteAlbumRoutes(handler *album.AlbumHandler) chi.Router { + router := chi.NewRouter() + wrapperInstance := &wrapper.Wrapper[entities.Album, entities.Album]{ServeHTTP: handler.DeleteAlbum} + router.Post("/", wrapperInstance.HandlerWrapper) + return router +} + +func GetAlbumsRoutes(handler *album.AlbumHandler) chi.Router { + router := chi.NewRouter() + wrapperInstance := &wrapper.Wrapper[entities.Album, entities.Albums]{ServeHTTP: handler.GetAlbums} + router.Get("/", wrapperInstance.HandlerWrapper) + return router +} From 25ceb1b0dda8274d8e1d6356f0c1b186293a11cb Mon Sep 17 00:00:00 2001 From: MrDzhofik Date: Fri, 3 May 2024 14:46:05 +0300 Subject: [PATCH 05/15] Addind photo with Yandex Drive was added --- internal/config/config.go | 5 + internal/delivery/handlers/album/album.go | 15 ++ .../handlers/album/yandex_download.go | 198 ++++++++++++++++++ .../delivery/handlers/album/yandex_get.go | 41 ++++ internal/delivery/handlers/avatar/avatar.go | 44 ++++ internal/entities/album.go | 11 + .../storage/postgres/album/album_storage.go | 11 + .../storage_interfaces/album_interface.go | 1 + internal/usecase/album_usecase.go | 6 + router/router.go | 18 +- 10 files changed, 346 insertions(+), 4 deletions(-) create mode 100644 internal/delivery/handlers/album/yandex_download.go create mode 100644 internal/delivery/handlers/album/yandex_get.go diff --git a/internal/config/config.go b/internal/config/config.go index 4eb2a0b..2f6c188 100644 --- a/internal/config/config.go +++ b/internal/config/config.go @@ -16,6 +16,7 @@ type Config struct { Dsn `yaml:"dsn"` Redis `yaml:"redis"` FileUploadPath string `env:"FILE_UPLOAD_PATH"` + Drive `yaml:"token"` } type HTTPServer struct { @@ -41,6 +42,10 @@ type Redis struct { Password string `yaml:"password" env:"DB_REDIS_PASSWORD"` } +type Drive struct { + Token string `yaml:"token" env:"YANDEX_TOKEN"` +} + func LoadConfig() (*Config, error) { if err := godotenv.Load(); err != nil { return nil, errors.Wrap(err, "error loading .env file") diff --git a/internal/delivery/handlers/album/album.go b/internal/delivery/handlers/album/album.go index fdd71b3..3a5bfb5 100644 --- a/internal/delivery/handlers/album/album.go +++ b/internal/delivery/handlers/album/album.go @@ -2,6 +2,7 @@ package album import ( "context" + "strconv" "github.com/go-park-mail-ru/2024_1_ResCogitans/internal/entities" "github.com/go-park-mail-ru/2024_1_ResCogitans/internal/usecase" @@ -69,3 +70,17 @@ func (h *AlbumHandler) GetAlbums(ctx context.Context, _ entities.Album) (entitie } return album, nil } + +func (h *AlbumHandler) AddPhoto(ctx context.Context, photo entities.AlbumPhoto) (entities.AlbumPhoto, error) { + params := httputils.GetPathParamsFromCtx(ctx) + albumID, err := strconv.Atoi(params["albumID"]) + if err != nil { + return entities.AlbumPhoto{}, err + } + path := photo.Path + err = h.AlbumUseCase.AddPhoto(albumID, path) + if err != nil { + return entities.AlbumPhoto{}, err + } + return entities.AlbumPhoto{}, nil +} diff --git a/internal/delivery/handlers/album/yandex_download.go b/internal/delivery/handlers/album/yandex_download.go new file mode 100644 index 0000000..a5c2adf --- /dev/null +++ b/internal/delivery/handlers/album/yandex_download.go @@ -0,0 +1,198 @@ +package album + +import ( + "bytes" + "crypto/sha256" + "encoding/hex" + "encoding/json" + "fmt" + "io" + "mime/multipart" + "net/http" + "strconv" + "strings" + + "github.com/go-chi/chi/v5" + "github.com/go-park-mail-ru/2024_1_ResCogitans/internal/config" + "github.com/go-park-mail-ru/2024_1_ResCogitans/utils/logger" + "github.com/pkg/errors" +) + +type UploadResponse struct { + Href string `json:"href"` +} + +var ( + maxFileSizeMB = 1 + maxFileSizeBytes = int64(maxFileSizeMB) * 1024 * 1024 +) + +func ValidateFileExtension(fileHeader multipart.FileHeader) bool { + buff := make([]byte, 512) + file, err := fileHeader.Open() + if err != nil { + return false + } + defer file.Close() + if _, err := file.Read(buff); err != nil { + return false + } + + fileType := strings.ToLower(http.DetectContentType(buff)) + + return fileType == "image/png" || fileType == "image/jpeg" || fileType == "image/jpg" || fileType == "image/webp" +} + +func ValidateFileSize(handler *multipart.FileHeader) bool { + // Get file size + fileSize := handler.Size + + // Check if file size exceeds a certain limit + return fileSize <= maxFileSizeBytes +} + +func getURL(path, token string) (string, error) { + uploadPath := "jantugan/album/" + path + url := "https://cloud-api.yandex.net/v1/disk/resources/upload?path=" + uploadPath + + request, err := http.NewRequest("GET", url, nil) + if err != nil { + return "", err + } + request.Header.Set("Authorization", "OAuth "+token) + + client := &http.Client{} + response, err := client.Do(request) + if err != nil { + return "", err + } + defer response.Body.Close() + + if response.StatusCode != http.StatusOK { + return "", fmt.Errorf("getting URL failed with status: %d", response.StatusCode) + } + + var uploadResponse UploadResponse + if err := json.NewDecoder(response.Body).Decode(&uploadResponse); err != nil { + return "", err + } + + return uploadResponse.Href, nil +} + +func uploadFile(file multipart.File, handler *multipart.FileHeader) (string, error) { + logger := logger.Logger() + + // Валидация + flag := ValidateFileExtension(*handler) + if !flag { + logger.Error("Not valid format!") + return "", errors.New("Not valid format!") + } + + flag = ValidateFileSize(handler) + if !flag { + logger.Error("Error while checking file size:", "error") + return "", errors.New("Error while checking file size!") + } + + // Берем хэш из названия файла + sh := sha256.New() + sh.Write([]byte(handler.Filename)) + hashBytes1 := sh.Sum(nil) + newPath := hex.EncodeToString(hashBytes1) + + cfg, _ := config.LoadConfig() + + uploadURL, err := getURL(newPath, cfg.Drive.Token) + if err != nil { + return "", err + } + + // Создаем HTTP запрос для загрузки файла + request, err := http.NewRequest("PUT", uploadURL, file) + if err != nil { + return "", err + } + request.ContentLength = handler.Size + + // Отправляем запрос + client := &http.Client{} + response, err := client.Do(request) + if err != nil { + return "", err + } + defer response.Body.Close() + + if response.StatusCode != http.StatusCreated { + logger.Error("upload failed with status: %d", response.StatusCode) + } + + logger.Info("File uploaded successfully!") + + return newPath, nil +} + +func insertDataToDB(albumID int, path string) error { + log := logger.Logger() + url := fmt.Sprintf("http://localhost:8080/album/%d/add", albumID) // Предполагаем, что ваше приложение слушает на порту 8080 + + jsonData, err := json.Marshal(path) + if err != nil { + log.Error("Ошибка при преобразовании в JSON:", err) + return err + } + + // Отправка POST-запроса + resp, err := http.Post(url, "application/json", bytes.NewBuffer(jsonData)) + if err != nil { + log.Error("Ошибка при отправке запроса:", err) + return err + } + defer resp.Body.Close() + + // Чтение ответа + body, err := io.ReadAll(resp.Body) + if err != nil { + log.Error("Ошибка при чтении ответа:", err) + return err + } + + // Вывод ответа + fmt.Println("Ответ от сервера:", string(body)) + return nil +} + +func DoAll(w http.ResponseWriter, r *http.Request) { + logger := logger.Logger() + + r.ParseMultipartForm(10 << 20) + file, handler, err := r.FormFile("file") + if err != nil { + logger.Error("Error while retrieving file:", "error", err) + http.Error(w, "Error while retrieving file", http.StatusBadRequest) + return + } + defer file.Close() + + albumID, err := strconv.Atoi(chi.URLParam(r, "id")) + if err != nil { + logger.Error("Cannot convert to int", err) + http.Error(w, "Cannot convert to int", http.StatusBadRequest) + return + } + + path, err := uploadFile(file, handler) + if err != nil { + logger.Error("Error while uploading file:", "error", err) + http.Error(w, "Error while uploading file", http.StatusBadRequest) + return + } + + err = insertDataToDB(albumID, path) + if err != nil { + logger.Error("Error updating DB", "error", err) + http.Error(w, "Error updating DB", http.StatusBadRequest) + return + } +} diff --git a/internal/delivery/handlers/album/yandex_get.go b/internal/delivery/handlers/album/yandex_get.go new file mode 100644 index 0000000..7d3ed5d --- /dev/null +++ b/internal/delivery/handlers/album/yandex_get.go @@ -0,0 +1,41 @@ +package album + +import ( + "encoding/json" + "fmt" + "net/http" +) + +type DownloadResponse struct { + Href string `json:"href"` +} + +func getDownloadLink(filePath, token string) (DownloadResponse, error) { + path := "jantugan/album/" + filePath + url := "https://cloud-api.yandex.net/v1/disk/resources/download?path=" + path + + request, err := http.NewRequest("GET", url, nil) + if err != nil { + + return DownloadResponse{}, err + } + request.Header.Set("Authorization", "OAuth "+token) + + client := &http.Client{} + response, err := client.Do(request) + if err != nil { + return DownloadResponse{}, err + } + defer response.Body.Close() + + if response.StatusCode != http.StatusOK { + return DownloadResponse{}, fmt.Errorf("download link request failed with status: %d", response.StatusCode) + } + + var downloadResponse DownloadResponse + if err := json.NewDecoder(response.Body).Decode(&downloadResponse); err != nil { + return downloadResponse, err + } + + return downloadResponse, nil +} diff --git a/internal/delivery/handlers/avatar/avatar.go b/internal/delivery/handlers/avatar/avatar.go index 07a4e64..eef987c 100644 --- a/internal/delivery/handlers/avatar/avatar.go +++ b/internal/delivery/handlers/avatar/avatar.go @@ -2,13 +2,44 @@ package avatar import ( "io" + "mime/multipart" "net/http" "os" "path/filepath" + "strings" "github.com/go-park-mail-ru/2024_1_ResCogitans/internal/config" ) +var ( + maxFileSizeMB = 1 + maxFileSizeBytes = int64(maxFileSizeMB) * 1024 * 1024 +) + +func ValidateFileExtension(fileHeader multipart.FileHeader) bool { + buff := make([]byte, 512) + file, err := fileHeader.Open() + if err != nil { + return false + } + defer file.Close() + if _, err := file.Read(buff); err != nil { + return false + } + + fileType := strings.ToLower(http.DetectContentType(buff)) + + return fileType == "image/png" || fileType == "image/jpeg" || fileType == "image/jpg" || fileType == "image/webp" +} + +func ValidateFileSize(handler *multipart.FileHeader) bool { + // Get file size + fileSize := handler.Size + + // Check if file size exceeds a certain limit + return fileSize <= maxFileSizeBytes +} + func Upload(w http.ResponseWriter, r *http.Request) { cfg, err := config.LoadConfig() if err != nil { @@ -28,6 +59,19 @@ func Upload(w http.ResponseWriter, r *http.Request) { } defer file.Close() + // Validate + flag := ValidateFileExtension(*header) + if !flag { + http.Error(w, "Not valid format!", http.StatusBadRequest) + return + } + + flag = ValidateFileSize(header) + if !flag { + http.Error(w, "Not valid format!", http.StatusBadRequest) + return + } + uploadPath := filepath.Join(cfg.FileUploadPath, header.Filename) out, err := os.Create(uploadPath) diff --git a/internal/entities/album.go b/internal/entities/album.go index 180b0e7..f358665 100644 --- a/internal/entities/album.go +++ b/internal/entities/album.go @@ -11,6 +11,13 @@ type Albums struct { Albums []Album `json:"albums"` } +type AlbumPhoto struct { + ID int `json:"photoID` + AlbumID int `json:"albumID"` + Path string `json:"path"` + Description string `json:"description"` +} + func (h Album) Validate() error { return nil } @@ -18,3 +25,7 @@ func (h Album) Validate() error { func (h Albums) Validate() error { return nil } + +func (h AlbumPhoto) Validate() error { + return nil +} diff --git a/internal/storage/postgres/album/album_storage.go b/internal/storage/postgres/album/album_storage.go index 0c2ccf6..0b548ec 100644 --- a/internal/storage/postgres/album/album_storage.go +++ b/internal/storage/postgres/album/album_storage.go @@ -67,3 +67,14 @@ func (as *AlbumStorage) DeleteAlbum(albumID int) error { return nil } + +func (as *AlbumStorage) AddPhoto(albumID int, path string) error { + ctx := context.Background() + _, err := as.db.Exec(ctx, `INSERT INTO album_photo(album_id, path) VALUES ($1, $2)`, albumID, path) + if err != nil { + logger.Logger().Error(err.Error()) + return err + } + + return nil +} diff --git a/internal/storage/storage_interfaces/album_interface.go b/internal/storage/storage_interfaces/album_interface.go index 5e81e33..f319b3e 100644 --- a/internal/storage/storage_interfaces/album_interface.go +++ b/internal/storage/storage_interfaces/album_interface.go @@ -8,4 +8,5 @@ type AlbumStorageInterface interface { CreateAlbum(album entities.Album) (entities.Album, error) DeleteAlbum(albumID int) error GetAlbums(userID int) (entities.Albums, error) + AddPhoto(albumID int, path string) error } diff --git a/internal/usecase/album_usecase.go b/internal/usecase/album_usecase.go index 5b0b15a..b097e6d 100644 --- a/internal/usecase/album_usecase.go +++ b/internal/usecase/album_usecase.go @@ -9,6 +9,7 @@ type AlbumUseCaseInterface interface { CreateAlbum(album entities.Album) (entities.Album, error) DeleteAlbum(album entities.Album) (entities.Album, error) GetAlbums(userID int) (entities.Albums, error) + AddPhoto(albumID int, path string) error } type AlbumUseCase struct { @@ -41,3 +42,8 @@ func (au *AlbumUseCase) GetAlbums(userID int) (entities.Albums, error) { } return albums, nil } + +func (au *AlbumUseCase) AddPhoto(albumID int, path string) error { + err := au.AlbumStorage.AddPhoto(albumID, path) + return err +} diff --git a/router/router.go b/router/router.go index 36f5e62..12c597c 100644 --- a/router/router.go +++ b/router/router.go @@ -6,7 +6,7 @@ import ( "github.com/go-chi/chi/middleware" "github.com/go-chi/chi/v5" "github.com/go-park-mail-ru/2024_1_ResCogitans/internal/config" - "github.com/go-park-mail-ru/2024_1_ResCogitans/internal/delivery/handlers/album" + album "github.com/go-park-mail-ru/2024_1_ResCogitans/internal/delivery/handlers/album" "github.com/go-park-mail-ru/2024_1_ResCogitans/internal/delivery/handlers/authorization" user "github.com/go-park-mail-ru/2024_1_ResCogitans/internal/delivery/handlers/avatar" "github.com/go-park-mail-ru/2024_1_ResCogitans/internal/delivery/handlers/comment" @@ -38,6 +38,7 @@ func SetupRouter(_ *config.Config, handlers *initialization.Handlers) *chi.Mux { // upload image router.HandleFunc("/api/profile/{id}/upload", user.Upload) + router.HandleFunc("/api/album/{id}/upload", album.DoAll) router.Mount("/api/sights", SightRoutes(handlers.SightHandler)) @@ -80,9 +81,10 @@ func SetupRouter(_ *config.Config, handlers *initialization.Handlers) *chi.Mux { router.Mount("/api/review/get", GetStatistic(handlers.QuizHandler)) // album - router.Mount("/api/profile/{id}/album/create", CreateAlbumRoutes(handlers.AlbumHandler)) - router.Mount("/api/profile/{id}/album/delete", DeleteAlbumRoutes(handlers.AlbumHandler)) - router.Mount("/api/profile/{id}/albums", GetAlbumsRoutes(handlers.AlbumHandler)) + router.Mount("/api/album/create", CreateAlbumRoutes(handlers.AlbumHandler)) + router.Mount("/api/album/delete", DeleteAlbumRoutes(handlers.AlbumHandler)) + router.Mount("/api/albums", GetAlbumsRoutes(handlers.AlbumHandler)) + router.Mount("/api/album/{albumID}/add", AddPhotoAlbumRoutes(handlers.AlbumHandler)) return router } @@ -242,6 +244,7 @@ func GetStatistic(handler *quiz.QuizHandler) chi.Router { return router } +// album routes func CreateAlbumRoutes(handler *album.AlbumHandler) chi.Router { router := chi.NewRouter() wrapperInstance := &wrapper.Wrapper[entities.Album, entities.Album]{ServeHTTP: handler.CreateAlbum} @@ -262,3 +265,10 @@ func GetAlbumsRoutes(handler *album.AlbumHandler) chi.Router { router.Get("/", wrapperInstance.HandlerWrapper) return router } + +func AddPhotoAlbumRoutes(handler *album.AlbumHandler) chi.Router { + router := chi.NewRouter() + wrapperInstance := &wrapper.Wrapper[entities.Album, entities.Albums]{ServeHTTP: handler.GetAlbums} + router.Get("/", wrapperInstance.HandlerWrapper) + return router +} From 353b45322a81ea8187690eda0e6703d6cfe730dd Mon Sep 17 00:00:00 2001 From: MrDzhofik Date: Sat, 4 May 2024 13:38:57 +0300 Subject: [PATCH 06/15] Album view and album delete photo functions were added --- internal/delivery/handlers/album/album.go | 25 +++++++- .../handlers/album/yandex_download.go | 4 +- internal/entities/album.go | 11 +++- .../storage/postgres/album/album_storage.go | 58 +++++++++++++++++++ .../storage_interfaces/album_interface.go | 3 + internal/usecase/album_usecase.go | 42 ++++++++++++++ internal/usecase/yandex_delete.go | 33 +++++++++++ .../handlers/album => usecase}/yandex_get.go | 20 ++++--- router/router.go | 20 ++++++- 9 files changed, 201 insertions(+), 15 deletions(-) create mode 100644 internal/usecase/yandex_delete.go rename internal/{delivery/handlers/album => usecase}/yandex_get.go (58%) diff --git a/internal/delivery/handlers/album/album.go b/internal/delivery/handlers/album/album.go index 3a5bfb5..fe622f0 100644 --- a/internal/delivery/handlers/album/album.go +++ b/internal/delivery/handlers/album/album.go @@ -77,10 +77,31 @@ func (h *AlbumHandler) AddPhoto(ctx context.Context, photo entities.AlbumPhoto) if err != nil { return entities.AlbumPhoto{}, err } - path := photo.Path - err = h.AlbumUseCase.AddPhoto(albumID, path) + + err = h.AlbumUseCase.AddPhoto(albumID, photo.Path) + if err != nil { + return entities.AlbumPhoto{}, err + } + return entities.AlbumPhoto{}, nil +} + +func (h *AlbumHandler) DeletePhoto(ctx context.Context, photo entities.AlbumPhoto) (entities.AlbumPhoto, error) { + err := h.AlbumUseCase.DeletePhoto(photo.ID) if err != nil { return entities.AlbumPhoto{}, err } return entities.AlbumPhoto{}, nil } + +func (h *AlbumHandler) GetAlbumByID(ctx context.Context, photo entities.AlbumAndPhoto) (entities.AlbumAndPhoto, error) { + params := httputils.GetPathParamsFromCtx(ctx) + albumID, err := strconv.Atoi(params["albumID"]) + if err != nil { + return entities.AlbumAndPhoto{}, err + } + albumAndPhotos, err := h.AlbumUseCase.GetAlbumByID(albumID) + if err != nil { + return entities.AlbumAndPhoto{}, err + } + return albumAndPhotos, nil +} diff --git a/internal/delivery/handlers/album/yandex_download.go b/internal/delivery/handlers/album/yandex_download.go index a5c2adf..c309eef 100644 --- a/internal/delivery/handlers/album/yandex_download.go +++ b/internal/delivery/handlers/album/yandex_download.go @@ -163,7 +163,7 @@ func insertDataToDB(albumID int, path string) error { return nil } -func DoAll(w http.ResponseWriter, r *http.Request) { +func UploadImageAndInsert(w http.ResponseWriter, r *http.Request) { logger := logger.Logger() r.ParseMultipartForm(10 << 20) @@ -175,7 +175,7 @@ func DoAll(w http.ResponseWriter, r *http.Request) { } defer file.Close() - albumID, err := strconv.Atoi(chi.URLParam(r, "id")) + albumID, err := strconv.Atoi(chi.URLParam(r, "albumID")) if err != nil { logger.Error("Cannot convert to int", err) http.Error(w, "Cannot convert to int", http.StatusBadRequest) diff --git a/internal/entities/album.go b/internal/entities/album.go index f358665..6b742ca 100644 --- a/internal/entities/album.go +++ b/internal/entities/album.go @@ -12,12 +12,17 @@ type Albums struct { } type AlbumPhoto struct { - ID int `json:"photoID` + ID int `json:"photoID"` AlbumID int `json:"albumID"` Path string `json:"path"` Description string `json:"description"` } +type AlbumAndPhoto struct { + Info Album `json:"albumInfo"` + Photos []AlbumPhoto `json:"albumPhotos"` +} + func (h Album) Validate() error { return nil } @@ -29,3 +34,7 @@ func (h Albums) Validate() error { func (h AlbumPhoto) Validate() error { return nil } + +func (h AlbumAndPhoto) Validate() error { + return nil +} diff --git a/internal/storage/postgres/album/album_storage.go b/internal/storage/postgres/album/album_storage.go index 0b548ec..d286fdf 100644 --- a/internal/storage/postgres/album/album_storage.go +++ b/internal/storage/postgres/album/album_storage.go @@ -78,3 +78,61 @@ func (as *AlbumStorage) AddPhoto(albumID int, path string) error { return nil } + +func (as *AlbumStorage) DeletePhoto(photoID int) (entities.AlbumPhoto, error) { + var photo []*entities.AlbumPhoto + ctx := context.Background() + + err := pgxscan.Select(ctx, as.db, &photo, `SELECT path + FROM album_photo + WHERE id = $1`, photoID) + if err != nil { + logger.Logger().Error(err.Error()) + return entities.AlbumPhoto{}, err + } + + _, err = as.db.Exec(ctx, `DELETE album_photo WHERE id = $1`, photoID) + if err != nil { + logger.Logger().Error(err.Error()) + return entities.AlbumPhoto{}, err + } + + return *photo[0], nil +} + +func (as *AlbumStorage) GetAlbumInfo(albumID int) (entities.Album, error) { + var albums []*entities.Album + + ctx := context.Background() + + err := pgxscan.Select(ctx, as.db, &albums, `SELECT id, name, description + FROM album + WHERE id = $1`, albumID) + if err != nil { + logger.Logger().Error(err.Error()) + return entities.Album{}, err + } + + return *albums[0], nil +} + +func (as *AlbumStorage) GetAlbumPhotos(albumID int) ([]entities.AlbumPhoto, error) { + var albumPhotos []*entities.AlbumPhoto + + ctx := context.Background() + + err := pgxscan.Select(ctx, as.db, &albumPhotos, `SELECT id, path, description + FROM album_photo + WHERE album_id = $1`, albumID) + if err != nil { + logger.Logger().Error(err.Error()) + return nil, err + } + + var photoList []entities.AlbumPhoto + for _, p := range albumPhotos { + photoList = append(photoList, *p) + } + + return photoList, nil +} diff --git a/internal/storage/storage_interfaces/album_interface.go b/internal/storage/storage_interfaces/album_interface.go index f319b3e..79ec1e4 100644 --- a/internal/storage/storage_interfaces/album_interface.go +++ b/internal/storage/storage_interfaces/album_interface.go @@ -9,4 +9,7 @@ type AlbumStorageInterface interface { DeleteAlbum(albumID int) error GetAlbums(userID int) (entities.Albums, error) AddPhoto(albumID int, path string) error + DeletePhoto(photoID int) (entities.AlbumPhoto, error) + GetAlbumInfo(albumID int) (entities.Album, error) + GetAlbumPhotos(albumID int) ([]entities.AlbumPhoto, error) } diff --git a/internal/usecase/album_usecase.go b/internal/usecase/album_usecase.go index b097e6d..48c25a8 100644 --- a/internal/usecase/album_usecase.go +++ b/internal/usecase/album_usecase.go @@ -1,6 +1,8 @@ package usecase import ( + "fmt" + "github.com/go-park-mail-ru/2024_1_ResCogitans/internal/entities" storage "github.com/go-park-mail-ru/2024_1_ResCogitans/internal/storage/storage_interfaces" ) @@ -10,6 +12,8 @@ type AlbumUseCaseInterface interface { DeleteAlbum(album entities.Album) (entities.Album, error) GetAlbums(userID int) (entities.Albums, error) AddPhoto(albumID int, path string) error + DeletePhoto(photoID int) error + GetAlbumByID(albumID int) (entities.AlbumAndPhoto, error) } type AlbumUseCase struct { @@ -47,3 +51,41 @@ func (au *AlbumUseCase) AddPhoto(albumID int, path string) error { err := au.AlbumStorage.AddPhoto(albumID, path) return err } + +func (au *AlbumUseCase) DeletePhoto(photoID int) error { + photo, err := au.AlbumStorage.DeletePhoto(photoID) + if err != nil { + return err + } + + err = deleteResource(photo.Path) + if err != nil { + fmt.Printf("Error deleting resource: %s\n", err) + return err + } + return nil +} + +func (au *AlbumUseCase) GetAlbumByID(albumID int) (entities.AlbumAndPhoto, error) { + var albumAndPhotos entities.AlbumAndPhoto + + albumInfo, err := au.AlbumStorage.GetAlbumInfo(albumID) + if err != nil { + return entities.AlbumAndPhoto{}, err + } + albumPhotos, err := au.AlbumStorage.GetAlbumPhotos(albumID) + if err != nil { + return entities.AlbumAndPhoto{}, err + } + + for _, photo := range albumPhotos { + photo.Path, err = GetDownloadLink(photo.Path) + if err != nil { + return entities.AlbumAndPhoto{}, err + } + } + + albumAndPhotos.Info = albumInfo + albumAndPhotos.Photos = albumPhotos + return albumAndPhotos, err +} diff --git a/internal/usecase/yandex_delete.go b/internal/usecase/yandex_delete.go new file mode 100644 index 0000000..bcc3408 --- /dev/null +++ b/internal/usecase/yandex_delete.go @@ -0,0 +1,33 @@ +package usecase + +import ( + "fmt" + "net/http" + + "github.com/go-park-mail-ru/2024_1_ResCogitans/internal/config" +) + +func deleteResource(path string) error { + url := fmt.Sprintf("https://cloud-api.yandex.net/v1/disk/resources?path=%s&permanently=true", path) + + request, err := http.NewRequest("DELETE", url, nil) + if err != nil { + return err + } + + cfg, _ := config.LoadConfig() + request.Header.Set("Authorization", "OAuth "+cfg.Drive.Token) + + client := &http.Client{} + response, err := client.Do(request) + if err != nil { + return err + } + defer response.Body.Close() + + if response.StatusCode != http.StatusOK { + return fmt.Errorf("delete resource request failed with status: %d", response.StatusCode) + } + + return nil +} diff --git a/internal/delivery/handlers/album/yandex_get.go b/internal/usecase/yandex_get.go similarity index 58% rename from internal/delivery/handlers/album/yandex_get.go rename to internal/usecase/yandex_get.go index 7d3ed5d..060f1fb 100644 --- a/internal/delivery/handlers/album/yandex_get.go +++ b/internal/usecase/yandex_get.go @@ -1,41 +1,45 @@ -package album +package usecase import ( "encoding/json" "fmt" "net/http" + + "github.com/go-park-mail-ru/2024_1_ResCogitans/internal/config" ) type DownloadResponse struct { Href string `json:"href"` } -func getDownloadLink(filePath, token string) (DownloadResponse, error) { +func GetDownloadLink(filePath string) (string, error) { path := "jantugan/album/" + filePath url := "https://cloud-api.yandex.net/v1/disk/resources/download?path=" + path request, err := http.NewRequest("GET", url, nil) if err != nil { - return DownloadResponse{}, err + return "", err } - request.Header.Set("Authorization", "OAuth "+token) + cfg, _ := config.LoadConfig() + + request.Header.Set("Authorization", "OAuth "+cfg.Drive.Token) client := &http.Client{} response, err := client.Do(request) if err != nil { - return DownloadResponse{}, err + return "", err } defer response.Body.Close() if response.StatusCode != http.StatusOK { - return DownloadResponse{}, fmt.Errorf("download link request failed with status: %d", response.StatusCode) + return "", fmt.Errorf("download link request failed with status: %d", response.StatusCode) } var downloadResponse DownloadResponse if err := json.NewDecoder(response.Body).Decode(&downloadResponse); err != nil { - return downloadResponse, err + return "", err } - return downloadResponse, nil + return downloadResponse.Href, nil } diff --git a/router/router.go b/router/router.go index 12c597c..2760f17 100644 --- a/router/router.go +++ b/router/router.go @@ -38,7 +38,7 @@ func SetupRouter(_ *config.Config, handlers *initialization.Handlers) *chi.Mux { // upload image router.HandleFunc("/api/profile/{id}/upload", user.Upload) - router.HandleFunc("/api/album/{id}/upload", album.DoAll) + router.HandleFunc("/api/album/{albumID}/upload", album.UploadImageAndInsert) router.Mount("/api/sights", SightRoutes(handlers.SightHandler)) @@ -85,6 +85,8 @@ func SetupRouter(_ *config.Config, handlers *initialization.Handlers) *chi.Mux { router.Mount("/api/album/delete", DeleteAlbumRoutes(handlers.AlbumHandler)) router.Mount("/api/albums", GetAlbumsRoutes(handlers.AlbumHandler)) router.Mount("/api/album/{albumID}/add", AddPhotoAlbumRoutes(handlers.AlbumHandler)) + router.Mount("/api/album/{albumID}/delete", DeletePhotoAlbumRoutes(handlers.AlbumHandler)) + router.Mount("/api/album/{albumID}", GetAlbumPhotosRoutes(handlers.AlbumHandler)) return router } @@ -268,7 +270,21 @@ func GetAlbumsRoutes(handler *album.AlbumHandler) chi.Router { func AddPhotoAlbumRoutes(handler *album.AlbumHandler) chi.Router { router := chi.NewRouter() - wrapperInstance := &wrapper.Wrapper[entities.Album, entities.Albums]{ServeHTTP: handler.GetAlbums} + wrapperInstance := &wrapper.Wrapper[entities.AlbumPhoto, entities.AlbumPhoto]{ServeHTTP: handler.AddPhoto} + router.Post("/", wrapperInstance.HandlerWrapper) + return router +} + +func DeletePhotoAlbumRoutes(handler *album.AlbumHandler) chi.Router { + router := chi.NewRouter() + wrapperInstance := &wrapper.Wrapper[entities.AlbumPhoto, entities.AlbumPhoto]{ServeHTTP: handler.DeletePhoto} + router.Post("/", wrapperInstance.HandlerWrapper) + return router +} + +func GetAlbumPhotosRoutes(handler *album.AlbumHandler) chi.Router { + router := chi.NewRouter() + wrapperInstance := &wrapper.Wrapper[entities.AlbumAndPhoto, entities.AlbumAndPhoto]{ServeHTTP: handler.GetAlbumByID} router.Get("/", wrapperInstance.HandlerWrapper) return router } From 515e14cd543ed585f093e279ccc6464eaa5ce013 Mon Sep 17 00:00:00 2001 From: MrDzhofik Date: Sun, 5 May 2024 00:59:11 +0300 Subject: [PATCH 07/15] Fixed router --- router/router.go | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/router/router.go b/router/router.go index 2760f17..5d78106 100644 --- a/router/router.go +++ b/router/router.go @@ -81,9 +81,9 @@ func SetupRouter(_ *config.Config, handlers *initialization.Handlers) *chi.Mux { router.Mount("/api/review/get", GetStatistic(handlers.QuizHandler)) // album - router.Mount("/api/album/create", CreateAlbumRoutes(handlers.AlbumHandler)) - router.Mount("/api/album/delete", DeleteAlbumRoutes(handlers.AlbumHandler)) - router.Mount("/api/albums", GetAlbumsRoutes(handlers.AlbumHandler)) + router.Mount("/api/profile/{id}/album/create", CreateAlbumRoutes(handlers.AlbumHandler)) + router.Mount("/api/profile/{id}/album/delete", DeleteAlbumRoutes(handlers.AlbumHandler)) + router.Mount("/api/profile/{id}/albums", GetAlbumsRoutes(handlers.AlbumHandler)) router.Mount("/api/album/{albumID}/add", AddPhotoAlbumRoutes(handlers.AlbumHandler)) router.Mount("/api/album/{albumID}/delete", DeletePhotoAlbumRoutes(handlers.AlbumHandler)) router.Mount("/api/album/{albumID}", GetAlbumPhotosRoutes(handlers.AlbumHandler)) From e1a8efc33b3c82050abf42e124a9f5ca8cc5d6b7 Mon Sep 17 00:00:00 2001 From: MrDzhofik Date: Sun, 5 May 2024 19:38:36 +0300 Subject: [PATCH 08/15] Fixed getting albums --- internal/delivery/handlers/album/album.go | 5 +- .../handlers/album/yandex_download.go | 80 ++++++++++++------- .../storage/postgres/album/album_storage.go | 2 +- 3 files changed, 55 insertions(+), 32 deletions(-) diff --git a/internal/delivery/handlers/album/album.go b/internal/delivery/handlers/album/album.go index fe622f0..a593d5f 100644 --- a/internal/delivery/handlers/album/album.go +++ b/internal/delivery/handlers/album/album.go @@ -2,6 +2,7 @@ package album import ( "context" + "fmt" "strconv" "github.com/go-park-mail-ru/2024_1_ResCogitans/internal/entities" @@ -59,7 +60,8 @@ func (h *AlbumHandler) DeleteAlbum(_ context.Context, album entities.Album) (ent } func (h *AlbumHandler) GetAlbums(ctx context.Context, _ entities.Album) (entities.Albums, error) { - userID, err := httputils.GetUserFromCtx(ctx) + params := httputils.GetPathParamsFromCtx(ctx) + userID, err := strconv.Atoi(params["id"]) if err != nil { return entities.Albums{}, err } @@ -74,6 +76,7 @@ func (h *AlbumHandler) GetAlbums(ctx context.Context, _ entities.Album) (entitie func (h *AlbumHandler) AddPhoto(ctx context.Context, photo entities.AlbumPhoto) (entities.AlbumPhoto, error) { params := httputils.GetPathParamsFromCtx(ctx) albumID, err := strconv.Atoi(params["albumID"]) + fmt.Println(photo) if err != nil { return entities.AlbumPhoto{}, err } diff --git a/internal/delivery/handlers/album/yandex_download.go b/internal/delivery/handlers/album/yandex_download.go index c309eef..a6a8306 100644 --- a/internal/delivery/handlers/album/yandex_download.go +++ b/internal/delivery/handlers/album/yandex_download.go @@ -14,6 +14,7 @@ import ( "github.com/go-chi/chi/v5" "github.com/go-park-mail-ru/2024_1_ResCogitans/internal/config" + "github.com/go-park-mail-ru/2024_1_ResCogitans/internal/entities" "github.com/go-park-mail-ru/2024_1_ResCogitans/utils/logger" "github.com/pkg/errors" ) @@ -68,7 +69,7 @@ func getURL(path, token string) (string, error) { } defer response.Body.Close() - if response.StatusCode != http.StatusOK { + if response.StatusCode != http.StatusCreated { return "", fmt.Errorf("getting URL failed with status: %d", response.StatusCode) } @@ -135,9 +136,12 @@ func uploadFile(file multipart.File, handler *multipart.FileHeader) (string, err func insertDataToDB(albumID int, path string) error { log := logger.Logger() - url := fmt.Sprintf("http://localhost:8080/album/%d/add", albumID) // Предполагаем, что ваше приложение слушает на порту 8080 + url := fmt.Sprintf("http://localhost:8080/api/album/%d/add", albumID) + + var data entities.AlbumPhoto + data.Path = path - jsonData, err := json.Marshal(path) + jsonData, err := json.Marshal(data) if err != nil { log.Error("Ошибка при преобразовании в JSON:", err) return err @@ -166,33 +170,49 @@ func insertDataToDB(albumID int, path string) error { func UploadImageAndInsert(w http.ResponseWriter, r *http.Request) { logger := logger.Logger() - r.ParseMultipartForm(10 << 20) - file, handler, err := r.FormFile("file") - if err != nil { - logger.Error("Error while retrieving file:", "error", err) - http.Error(w, "Error while retrieving file", http.StatusBadRequest) - return - } - defer file.Close() - - albumID, err := strconv.Atoi(chi.URLParam(r, "albumID")) - if err != nil { - logger.Error("Cannot convert to int", err) - http.Error(w, "Cannot convert to int", http.StatusBadRequest) - return - } - - path, err := uploadFile(file, handler) - if err != nil { - logger.Error("Error while uploading file:", "error", err) - http.Error(w, "Error while uploading file", http.StatusBadRequest) - return + r.ParseMultipartForm(100 << 20) + + // Получение списка имен файлов + fileMap := r.MultipartForm.File + + // Обработка каждого файла + for fieldName, files := range fileMap { + for _, fileHeader := range files { + file, err := fileHeader.Open() + if err != nil { + http.Error(w, fmt.Sprintf("Error opening uploaded file: %s", err), http.StatusInternalServerError) + return + } + defer file.Close() + + file, handler, err := r.FormFile(fieldName) + if err != nil { + logger.Error("Error while retrieving file:", "error", err) + http.Error(w, "Error while retrieving file", http.StatusBadRequest) + return + } + defer file.Close() + + albumID, err := strconv.Atoi(chi.URLParam(r, "albumID")) + if err != nil { + logger.Error("Cannot convert to int", err) + http.Error(w, "Cannot convert to int", http.StatusBadRequest) + return + } + + path, err := uploadFile(file, handler) + if err != nil { + logger.Error("Error while uploading file:", "error", err) + http.Error(w, "Error while uploading file", http.StatusBadRequest) + return + } + err = insertDataToDB(albumID, path) + if err != nil { + logger.Error("Error updating DB", "error", err) + http.Error(w, "Error updating DB", http.StatusBadRequest) + return + } + } } - err = insertDataToDB(albumID, path) - if err != nil { - logger.Error("Error updating DB", "error", err) - http.Error(w, "Error updating DB", http.StatusBadRequest) - return - } } diff --git a/internal/storage/postgres/album/album_storage.go b/internal/storage/postgres/album/album_storage.go index d286fdf..0af24e9 100644 --- a/internal/storage/postgres/album/album_storage.go +++ b/internal/storage/postgres/album/album_storage.go @@ -105,7 +105,7 @@ func (as *AlbumStorage) GetAlbumInfo(albumID int) (entities.Album, error) { ctx := context.Background() - err := pgxscan.Select(ctx, as.db, &albums, `SELECT id, name, description + err := pgxscan.Select(ctx, as.db, &albums, `SELECT id, user_id, name, description FROM album WHERE id = $1`, albumID) if err != nil { From 183ce0ca9e5d9abf01c6050c6285ea86d82204c0 Mon Sep 17 00:00:00 2001 From: MrDzhofik Date: Sun, 5 May 2024 20:23:00 +0300 Subject: [PATCH 09/15] Uploading images now is working! --- internal/delivery/handlers/album/album.go | 4 +- .../handlers/album/yandex_download.go | 55 ++++++++++++------- .../storage/postgres/album/album_storage.go | 4 +- .../storage_interfaces/album_interface.go | 2 +- internal/usecase/album_usecase.go | 6 +- 5 files changed, 43 insertions(+), 28 deletions(-) diff --git a/internal/delivery/handlers/album/album.go b/internal/delivery/handlers/album/album.go index a593d5f..c77f1d9 100644 --- a/internal/delivery/handlers/album/album.go +++ b/internal/delivery/handlers/album/album.go @@ -2,7 +2,6 @@ package album import ( "context" - "fmt" "strconv" "github.com/go-park-mail-ru/2024_1_ResCogitans/internal/entities" @@ -76,12 +75,11 @@ func (h *AlbumHandler) GetAlbums(ctx context.Context, _ entities.Album) (entitie func (h *AlbumHandler) AddPhoto(ctx context.Context, photo entities.AlbumPhoto) (entities.AlbumPhoto, error) { params := httputils.GetPathParamsFromCtx(ctx) albumID, err := strconv.Atoi(params["albumID"]) - fmt.Println(photo) if err != nil { return entities.AlbumPhoto{}, err } - err = h.AlbumUseCase.AddPhoto(albumID, photo.Path) + err = h.AlbumUseCase.AddPhoto(albumID, photo.Path, photo.Description) if err != nil { return entities.AlbumPhoto{}, err } diff --git a/internal/delivery/handlers/album/yandex_download.go b/internal/delivery/handlers/album/yandex_download.go index a6a8306..c6d7107 100644 --- a/internal/delivery/handlers/album/yandex_download.go +++ b/internal/delivery/handlers/album/yandex_download.go @@ -69,8 +69,8 @@ func getURL(path, token string) (string, error) { } defer response.Body.Close() - if response.StatusCode != http.StatusCreated { - return "", fmt.Errorf("getting URL failed with status: %d", response.StatusCode) + if response.StatusCode != http.StatusOK { + return "", fmt.Errorf("получение URL не удалось со статусом: %d", response.StatusCode) } var uploadResponse UploadResponse @@ -87,14 +87,14 @@ func uploadFile(file multipart.File, handler *multipart.FileHeader) (string, err // Валидация flag := ValidateFileExtension(*handler) if !flag { - logger.Error("Not valid format!") - return "", errors.New("Not valid format!") + logger.Error("Неправильное расширение!") + return "", errors.New("Неправильное расширение!") } flag = ValidateFileSize(handler) if !flag { - logger.Error("Error while checking file size:", "error") - return "", errors.New("Error while checking file size!") + logger.Error("Ошибка при проверки размера файла:", "error") + return "", errors.New("Ошибка при проверки размера файла!") } // Берем хэш из названия файла @@ -126,20 +126,21 @@ func uploadFile(file multipart.File, handler *multipart.FileHeader) (string, err defer response.Body.Close() if response.StatusCode != http.StatusCreated { - logger.Error("upload failed with status: %d", response.StatusCode) + logger.Error("Загрузка не удалась со статусом: %d", response.StatusCode) } - logger.Info("File uploaded successfully!") + logger.Info("Загрузка успешна!") return newPath, nil } -func insertDataToDB(albumID int, path string) error { +func insertDataToDB(albumID int, path, description string) error { log := logger.Logger() url := fmt.Sprintf("http://localhost:8080/api/album/%d/add", albumID) var data entities.AlbumPhoto data.Path = path + data.Description = description jsonData, err := json.Marshal(data) if err != nil { @@ -175,41 +176,57 @@ func UploadImageAndInsert(w http.ResponseWriter, r *http.Request) { // Получение списка имен файлов fileMap := r.MultipartForm.File + // Получение списка описаний к фото + descr := r.FormValue("descriptions") + + var descriptions []map[string]string + err := json.Unmarshal([]byte(descr), &descriptions) + + if err != nil { + http.Error(w, fmt.Sprintf("Error decoding descriptions JSON: %s", err), http.StatusInternalServerError) + return + } + // Обработка каждого файла for fieldName, files := range fileMap { for _, fileHeader := range files { file, err := fileHeader.Open() if err != nil { - http.Error(w, fmt.Sprintf("Error opening uploaded file: %s", err), http.StatusInternalServerError) + http.Error(w, fmt.Sprintf("Ошибка при открытии файла: %s", err), http.StatusInternalServerError) return } defer file.Close() file, handler, err := r.FormFile(fieldName) if err != nil { - logger.Error("Error while retrieving file:", "error", err) - http.Error(w, "Error while retrieving file", http.StatusBadRequest) + logger.Error("Ошибка получения файла:", "error", err) + http.Error(w, "Ошибка получения файла", http.StatusBadRequest) return } defer file.Close() albumID, err := strconv.Atoi(chi.URLParam(r, "albumID")) if err != nil { - logger.Error("Cannot convert to int", err) - http.Error(w, "Cannot convert to int", http.StatusBadRequest) + logger.Error("Ошибка перевода в число", err) + http.Error(w, "Ошибка перевода в число", http.StatusBadRequest) return } path, err := uploadFile(file, handler) if err != nil { - logger.Error("Error while uploading file:", "error", err) - http.Error(w, "Error while uploading file", http.StatusBadRequest) + logger.Error("Ошибка загрузки файла:", "error", err) + http.Error(w, "Ошибка загрузки файла", http.StatusBadRequest) return } - err = insertDataToDB(albumID, path) + + index, _ := strconv.Atoi(fieldName) + + description := descriptions[index-1][fieldName] + + err = insertDataToDB(albumID, path, description) if err != nil { - logger.Error("Error updating DB", "error", err) - http.Error(w, "Error updating DB", http.StatusBadRequest) + logger.Error("Ошибка обновления базы данных", "error", err) + http.Error(w, "Ошибка обновления базы данных", http.StatusBadRequest) return } } diff --git a/internal/storage/postgres/album/album_storage.go b/internal/storage/postgres/album/album_storage.go index 0af24e9..4ff39c9 100644 --- a/internal/storage/postgres/album/album_storage.go +++ b/internal/storage/postgres/album/album_storage.go @@ -68,9 +68,9 @@ func (as *AlbumStorage) DeleteAlbum(albumID int) error { return nil } -func (as *AlbumStorage) AddPhoto(albumID int, path string) error { +func (as *AlbumStorage) AddPhoto(albumID int, path, description string) error { ctx := context.Background() - _, err := as.db.Exec(ctx, `INSERT INTO album_photo(album_id, path) VALUES ($1, $2)`, albumID, path) + _, err := as.db.Exec(ctx, `INSERT INTO album_photo(album_id, path, description) VALUES ($1, $2, $3)`, albumID, path, description) if err != nil { logger.Logger().Error(err.Error()) return err diff --git a/internal/storage/storage_interfaces/album_interface.go b/internal/storage/storage_interfaces/album_interface.go index 79ec1e4..2df5b1e 100644 --- a/internal/storage/storage_interfaces/album_interface.go +++ b/internal/storage/storage_interfaces/album_interface.go @@ -8,7 +8,7 @@ type AlbumStorageInterface interface { CreateAlbum(album entities.Album) (entities.Album, error) DeleteAlbum(albumID int) error GetAlbums(userID int) (entities.Albums, error) - AddPhoto(albumID int, path string) error + AddPhoto(albumID int, path, description string) error DeletePhoto(photoID int) (entities.AlbumPhoto, error) GetAlbumInfo(albumID int) (entities.Album, error) GetAlbumPhotos(albumID int) ([]entities.AlbumPhoto, error) diff --git a/internal/usecase/album_usecase.go b/internal/usecase/album_usecase.go index 48c25a8..89a639e 100644 --- a/internal/usecase/album_usecase.go +++ b/internal/usecase/album_usecase.go @@ -11,7 +11,7 @@ type AlbumUseCaseInterface interface { CreateAlbum(album entities.Album) (entities.Album, error) DeleteAlbum(album entities.Album) (entities.Album, error) GetAlbums(userID int) (entities.Albums, error) - AddPhoto(albumID int, path string) error + AddPhoto(albumID int, path, description string) error DeletePhoto(photoID int) error GetAlbumByID(albumID int) (entities.AlbumAndPhoto, error) } @@ -47,8 +47,8 @@ func (au *AlbumUseCase) GetAlbums(userID int) (entities.Albums, error) { return albums, nil } -func (au *AlbumUseCase) AddPhoto(albumID int, path string) error { - err := au.AlbumStorage.AddPhoto(albumID, path) +func (au *AlbumUseCase) AddPhoto(albumID int, path, description string) error { + err := au.AlbumStorage.AddPhoto(albumID, path, description) return err } From f21b03f1151e3257aa987bc70a2e59776d782787 Mon Sep 17 00:00:00 2001 From: MrDzhofik Date: Sun, 5 May 2024 20:57:26 +0300 Subject: [PATCH 10/15] Fixed photo path --- internal/usecase/album_usecase.go | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/internal/usecase/album_usecase.go b/internal/usecase/album_usecase.go index 89a639e..f20af30 100644 --- a/internal/usecase/album_usecase.go +++ b/internal/usecase/album_usecase.go @@ -78,8 +78,8 @@ func (au *AlbumUseCase) GetAlbumByID(albumID int) (entities.AlbumAndPhoto, error return entities.AlbumAndPhoto{}, err } - for _, photo := range albumPhotos { - photo.Path, err = GetDownloadLink(photo.Path) + for index, photo := range albumPhotos { + albumPhotos[index].Path, err = GetDownloadLink(photo.Path) if err != nil { return entities.AlbumAndPhoto{}, err } From 230e2fdae3aa60169ae938e1383f0e1f9cb9a49d Mon Sep 17 00:00:00 2001 From: MrDzhofik Date: Mon, 6 May 2024 00:52:09 +0300 Subject: [PATCH 11/15] Fixed album delete --- internal/delivery/handlers/album/yandex_download.go | 2 +- internal/storage/postgres/album/album_storage.go | 8 +++++++- 2 files changed, 8 insertions(+), 2 deletions(-) diff --git a/internal/delivery/handlers/album/yandex_download.go b/internal/delivery/handlers/album/yandex_download.go index c6d7107..d7f3450 100644 --- a/internal/delivery/handlers/album/yandex_download.go +++ b/internal/delivery/handlers/album/yandex_download.go @@ -24,7 +24,7 @@ type UploadResponse struct { } var ( - maxFileSizeMB = 1 + maxFileSizeMB = 8 maxFileSizeBytes = int64(maxFileSizeMB) * 1024 * 1024 ) diff --git a/internal/storage/postgres/album/album_storage.go b/internal/storage/postgres/album/album_storage.go index 4ff39c9..40ca051 100644 --- a/internal/storage/postgres/album/album_storage.go +++ b/internal/storage/postgres/album/album_storage.go @@ -59,7 +59,13 @@ func (as *AlbumStorage) GetAlbums(userID int) (entities.Albums, error) { func (as *AlbumStorage) DeleteAlbum(albumID int) error { ctx := context.Background() - _, err := as.db.Exec(ctx, `DELETE FROM album WHERE id = $1;`, albumID) + _, err := as.db.Exec(ctx, `DELETE FROM album_photo WHERE album_id = $1`, albumID) + if err != nil { + logger.Logger().Error(err.Error()) + return err + } + + _, err = as.db.Exec(ctx, `DELETE FROM album WHERE id = $1;`, albumID) if err != nil { logger.Logger().Error(err.Error()) return err From edc6630242c7db4cc2035a6d66f4db635e159523 Mon Sep 17 00:00:00 2001 From: MrDzhofik Date: Mon, 6 May 2024 01:41:12 +0300 Subject: [PATCH 12/15] Fixed removing photo --- internal/storage/postgres/album/album_storage.go | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/internal/storage/postgres/album/album_storage.go b/internal/storage/postgres/album/album_storage.go index 40ca051..fff25d0 100644 --- a/internal/storage/postgres/album/album_storage.go +++ b/internal/storage/postgres/album/album_storage.go @@ -97,7 +97,7 @@ func (as *AlbumStorage) DeletePhoto(photoID int) (entities.AlbumPhoto, error) { return entities.AlbumPhoto{}, err } - _, err = as.db.Exec(ctx, `DELETE album_photo WHERE id = $1`, photoID) + _, err = as.db.Exec(ctx, `DELETE FROM album_photo WHERE id = $1`, photoID) if err != nil { logger.Logger().Error(err.Error()) return entities.AlbumPhoto{}, err From 6aab34521a5f7313e939defce97d85f34f7be648 Mon Sep 17 00:00:00 2001 From: MrDzhofik Date: Mon, 6 May 2024 01:57:30 +0300 Subject: [PATCH 13/15] Fixed path for deleting photo --- internal/usecase/yandex_delete.go | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/internal/usecase/yandex_delete.go b/internal/usecase/yandex_delete.go index bcc3408..482cede 100644 --- a/internal/usecase/yandex_delete.go +++ b/internal/usecase/yandex_delete.go @@ -8,7 +8,8 @@ import ( ) func deleteResource(path string) error { - url := fmt.Sprintf("https://cloud-api.yandex.net/v1/disk/resources?path=%s&permanently=true", path) + uploadPath := "jantugan/album/" + path + url := "https://cloud-api.yandex.net/v1/disk/resources?path=" + uploadPath + "&permanently=true" request, err := http.NewRequest("DELETE", url, nil) if err != nil { @@ -25,7 +26,7 @@ func deleteResource(path string) error { } defer response.Body.Close() - if response.StatusCode != http.StatusOK { + if response.StatusCode != http.StatusNoContent { return fmt.Errorf("delete resource request failed with status: %d", response.StatusCode) } From 3592d66141149d11142643b5129ffd5d8d353fe8 Mon Sep 17 00:00:00 2001 From: MrDzhofik Date: Fri, 10 May 2024 17:53:52 +0300 Subject: [PATCH 14/15] Make storages more beautiful --- ...0240401145225_create_and_insert_tables.sql | 2 +- internal/entities/sight.go | 22 +-- .../storage/postgres/album/album_storage.go | 76 ++++---- .../storage/postgres/sight/sight_storage.go | 164 ++++++++++++------ 4 files changed, 169 insertions(+), 95 deletions(-) diff --git a/db/migrations/20240401145225_create_and_insert_tables.sql b/db/migrations/20240401145225_create_and_insert_tables.sql index 7cdb09b..3ddadec 100644 --- a/db/migrations/20240401145225_create_and_insert_tables.sql +++ b/db/migrations/20240401145225_create_and_insert_tables.sql @@ -106,7 +106,7 @@ CREATE TABLE quiz user_id integer REFERENCES user_data (id), rating integer NOT NULL CHECK (rating > 0 AND rating <= 5), question_id integer REFERENCES question (id), - created_at timestamptz + created_at timestamptz DEFAULT NOW() ); CREATE TABLE album diff --git a/internal/entities/sight.go b/internal/entities/sight.go index 37cce98..ae08abd 100644 --- a/internal/entities/sight.go +++ b/internal/entities/sight.go @@ -1,17 +1,17 @@ package entities type Sight struct { - ID int `json:"id"` - Rating *float64 `json:"rating"` - Name string `json:"name"` - Description string `json:"description"` - CityID int `json:"cityID"` - CountryID int `json:"countryID"` - City string `json:"city"` - Country string `json:"country"` - Longitude *float64 `json:"longitude"` - Latitude *float64 `json:"latitude"` - Path string `json:"url"` + ID int `json:"id"` + Rating float64 `json:"rating"` + Name string `json:"name"` + Description string `json:"description"` + CityID int `json:"cityID"` + CountryID int `json:"countryID"` + City string `json:"city"` + Country string `json:"country"` + Longitude float64 `json:"longitude"` + Latitude float64 `json:"latitude"` + Path string `json:"url"` } func (h Sight) Validate() error { diff --git a/internal/storage/postgres/album/album_storage.go b/internal/storage/postgres/album/album_storage.go index fff25d0..418aeb3 100644 --- a/internal/storage/postgres/album/album_storage.go +++ b/internal/storage/postgres/album/album_storage.go @@ -26,8 +26,10 @@ func (as *AlbumStorage) CreateAlbum(album entities.Album) (entities.Album, error var albumID int ctx := context.Background() - err := as.db.QueryRow(ctx, `INSERT INTO album(user_id, name, description) - VALUES($1, $2, $3) RETURNING id`, album.UserID, album.Name, album.Description).Scan(&albumID) + query := `INSERT INTO album(user_id, name, description) + VALUES($1, $2, $3) RETURNING id` + + err := as.db.QueryRow(ctx, query, album.UserID, album.Name, album.Description).Scan(&albumID) if err != nil { logger.Logger().Error(err.Error()) return entities.Album{}, err @@ -37,35 +39,36 @@ func (as *AlbumStorage) CreateAlbum(album entities.Album) (entities.Album, error } func (as *AlbumStorage) GetAlbums(userID int) (entities.Albums, error) { - var albums []*entities.Album - + var albums []entities.Album ctx := context.Background() - err := pgxscan.Select(ctx, as.db, &albums, `SELECT id, name, description + query := `SELECT id, name, description FROM album - WHERE user_id = $1`, userID) + WHERE user_id = $1` + + err := pgxscan.Select(ctx, as.db, &albums, query, userID) if err != nil { logger.Logger().Error(err.Error()) return entities.Albums{}, err } - var albumList []entities.Album - for _, a := range albums { - albumList = append(albumList, *a) - } - return entities.Albums{Albums: albumList}, nil + return entities.Albums{Albums: albums}, nil } func (as *AlbumStorage) DeleteAlbum(albumID int) error { ctx := context.Background() - _, err := as.db.Exec(ctx, `DELETE FROM album_photo WHERE album_id = $1`, albumID) + query := `DELETE FROM album_photo WHERE album_id = $1` + + _, err := as.db.Exec(ctx, query, albumID) if err != nil { logger.Logger().Error(err.Error()) return err } - _, err = as.db.Exec(ctx, `DELETE FROM album WHERE id = $1;`, albumID) + query = `DELETE FROM album WHERE id = $1;` + + _, err = as.db.Exec(ctx, query, albumID) if err != nil { logger.Logger().Error(err.Error()) return err @@ -76,7 +79,10 @@ func (as *AlbumStorage) DeleteAlbum(albumID int) error { func (as *AlbumStorage) AddPhoto(albumID int, path, description string) error { ctx := context.Background() - _, err := as.db.Exec(ctx, `INSERT INTO album_photo(album_id, path, description) VALUES ($1, $2, $3)`, albumID, path, description) + + query := `INSERT INTO album_photo(album_id, path, description) VALUES ($1, $2, $3)` + + _, err := as.db.Exec(ctx, query, albumID, path, description) if err != nil { logger.Logger().Error(err.Error()) return err @@ -86,59 +92,65 @@ func (as *AlbumStorage) AddPhoto(albumID int, path, description string) error { } func (as *AlbumStorage) DeletePhoto(photoID int) (entities.AlbumPhoto, error) { - var photo []*entities.AlbumPhoto + var photo entities.AlbumPhoto ctx := context.Background() - err := pgxscan.Select(ctx, as.db, &photo, `SELECT path + query := `SELECT path FROM album_photo - WHERE id = $1`, photoID) + WHERE id = $1` + + err := pgxscan.Get(ctx, as.db, &photo, query, photoID) if err != nil { logger.Logger().Error(err.Error()) return entities.AlbumPhoto{}, err } - _, err = as.db.Exec(ctx, `DELETE FROM album_photo WHERE id = $1`, photoID) + query = `DELETE FROM album_photo WHERE id = $1` + + _, err = as.db.Exec(ctx, query, photoID) if err != nil { logger.Logger().Error(err.Error()) return entities.AlbumPhoto{}, err } - return *photo[0], nil + return photo, nil } func (as *AlbumStorage) GetAlbumInfo(albumID int) (entities.Album, error) { - var albums []*entities.Album + var album entities.Album ctx := context.Background() - err := pgxscan.Select(ctx, as.db, &albums, `SELECT id, user_id, name, description + query := `SELECT id, user_id, name, description FROM album - WHERE id = $1`, albumID) + WHERE id = $1` + + err := pgxscan.Get(ctx, as.db, &album, query, albumID) if err != nil { logger.Logger().Error(err.Error()) return entities.Album{}, err } - return *albums[0], nil + return album, nil } func (as *AlbumStorage) GetAlbumPhotos(albumID int) ([]entities.AlbumPhoto, error) { - var albumPhotos []*entities.AlbumPhoto + var albumPhotos []entities.AlbumPhoto ctx := context.Background() - err := pgxscan.Select(ctx, as.db, &albumPhotos, `SELECT id, path, description + query := `SELECT + id, + path, + description FROM album_photo - WHERE album_id = $1`, albumID) + WHERE album_id = $1` + + err := pgxscan.Select(ctx, as.db, &albumPhotos, query, albumID) if err != nil { logger.Logger().Error(err.Error()) return nil, err } - var photoList []entities.AlbumPhoto - for _, p := range albumPhotos { - photoList = append(photoList, *p) - } - - return photoList, nil + return albumPhotos, nil } diff --git a/internal/storage/postgres/sight/sight_storage.go b/internal/storage/postgres/sight/sight_storage.go index d9a23e9..2c1fd17 100644 --- a/internal/storage/postgres/sight/sight_storage.go +++ b/internal/storage/postgres/sight/sight_storage.go @@ -25,30 +25,46 @@ func NewSightStorage(db *pgxpool.Pool) storage.SightStorageInterface { } func (ss *SightStorage) GetSightsList() ([]entities.Sight, error) { - var sights []*entities.Sight + var sights []entities.Sight ctx := context.Background() - err := pgxscan.Select(ctx, ss.db, &sights, `SELECT sight.id, COALESCE(rating, 0) AS rating, name, description, city_id, country_id, im.path + query := `SELECT + sight.id, + COALESCE(rating, 0) AS rating, + name, + description, + city_id, + country_id, + im.path FROM sight INNER JOIN image_data AS im - ON sight.id = im.sight_id `) + ON sight.id = im.sight_id ` + + err := pgxscan.Select(ctx, ss.db, &sights, query) if err != nil { logger.Logger().Error(err.Error()) return nil, err } - var sightList []entities.Sight - for _, s := range sights { - sightList = append(sightList, *s) - } - return sightList, nil + return sights, nil } func (ss *SightStorage) GetSight(id int) (entities.Sight, error) { - var sight []*entities.Sight + var sight entities.Sight ctx := context.Background() - err := pgxscan.Select(ctx, ss.db, &sight, `SELECT sight.id, COALESCE(rating, 0) AS rating, sight.name, description, city_id, sight.country_id, im.path, city.city, country.country, longitude, latitude + query := `SELECT + sight.id, + COALESCE(rating, 0) AS rating, + sight.name, + description, + city_id, + sight.country_id, + im.path, + city.city, + country.country, + longitude, + latitude FROM sight INNER JOIN image_data AS im ON sight.id = im.sight_id @@ -56,12 +72,14 @@ func (ss *SightStorage) GetSight(id int) (entities.Sight, error) { ON sight.city_id = city.id INNER JOIN country ON sight.country_id = country.id - WHERE sight.id = $1`, id) + WHERE sight.id = $1` + + err := pgxscan.Get(ctx, ss.db, &sight, query, id) if err != nil { return entities.Sight{}, err } - return *sight[0], nil + return sight, nil } func (ss *SightStorage) SearchSights(str string) (entities.Sights, error) { @@ -94,45 +112,52 @@ func (ss *SightStorage) SearchSights(str string) (entities.Sights, error) { } func (ss *SightStorage) GetCommentsBySightID(id int) ([]entities.Comment, error) { - var comments []*entities.Comment + var comments []entities.Comment ctx := context.Background() - err := pgxscan.Select(ctx, ss.db, &comments, `SELECT f.id, f.user_id, p.username, p.avatar, f.sight_id, f.rating, f.feedback FROM feedback AS f INNER JOIN profile_data AS p ON f.user_id = p.user_id WHERE sight_id = $1 `, id) + query := `SELECT + f.id, + f.user_id, + p.username, + p.avatar, + f.sight_id, + f.rating, + f.feedback + FROM feedback AS f + INNER JOIN profile_data AS p + ON f.user_id = p.user_id + WHERE sight_id = $1` + + err := pgxscan.Select(ctx, ss.db, &comments, query, id) if err != nil { logger.Logger().Error(err.Error()) return nil, err } - var commentsList []entities.Comment - for _, s := range comments { - commentsList = append(commentsList, *s) - } - - return commentsList, nil + return comments, nil } func (ss *SightStorage) GetCommentsByUserID(userID int) ([]entities.Comment, error) { - var comments []*entities.Comment + var comments []entities.Comment ctx := context.Background() - err := pgxscan.Select(ctx, ss.db, &comments, `SELECT f.id, f.user_id, f.sight_id, f.rating, f.feedback FROM feedback AS f WHERE user_id = $1 `, userID) + query := `SELECT f.id, f.user_id, f.sight_id, f.rating, f.feedback FROM feedback AS f WHERE user_id = $1 ` + + err := pgxscan.Select(ctx, ss.db, &comments, query, userID) if err != nil { logger.Logger().Error(err.Error()) return nil, err } - var commentsList []entities.Comment - for _, s := range comments { - commentsList = append(commentsList, *s) - } - - return commentsList, nil + return comments, nil } func (ss *SightStorage) CreateCommentBySightID(sightID int, comment entities.Comment) error { ctx := context.Background() - _, err := ss.db.Exec(ctx, `INSERT INTO feedback(user_id, sight_id, rating, feedback) VALUES($1, $2, $3, $4)`, comment.UserID, sightID, comment.Rating, comment.Feedback) + query := `INSERT INTO feedback(user_id, sight_id, rating, feedback) VALUES($1, $2, $3, $4)` + + _, err := ss.db.Exec(ctx, query, comment.UserID, sightID, comment.Rating, comment.Feedback) if err != nil { logger.Logger().Error(err.Error()) return err @@ -144,7 +169,9 @@ func (ss *SightStorage) CreateCommentBySightID(sightID int, comment entities.Com func (ss *SightStorage) EditComment(commentID int, comment entities.Comment) error { ctx := context.Background() - _, err := ss.db.Exec(ctx, `UPDATE feedback SET rating = $1, feedback = $2 WHERE id = $3`, comment.Rating, comment.Feedback, commentID) + query := `UPDATE feedback SET rating = $1, feedback = $2 WHERE id = $3` + + _, err := ss.db.Exec(ctx, query, comment.Rating, comment.Feedback, commentID) if err != nil { logger.Logger().Error(err.Error()) return err @@ -156,7 +183,9 @@ func (ss *SightStorage) EditComment(commentID int, comment entities.Comment) err func (ss *SightStorage) DeleteComment(commentID int) error { ctx := context.Background() - _, err := ss.db.Exec(ctx, `DELETE FROM feedback WHERE id = $1`, commentID) + query := `DELETE FROM feedback WHERE id = $1` + + _, err := ss.db.Exec(ctx, query, commentID) if err != nil { logger.Logger().Error(err.Error()) return err @@ -169,7 +198,9 @@ func (ss *SightStorage) DeleteComment(commentID int) error { func (ss *SightStorage) CreateJourney(journey entities.Journey) (entities.Journey, error) { ctx := context.Background() - row := ss.db.QueryRow(ctx, `INSERT INTO journey(name, user_id, description) VALUES ($1, $2, $3) RETURNING id, name, user_id, description;`, journey.Name, journey.UserID, journey.Description) + query := `INSERT INTO journey(name, user_id, description) VALUES ($1, $2, $3) RETURNING id, name, user_id, description;` + + row := ss.db.QueryRow(ctx, query, journey.Name, journey.UserID, journey.Description) err := row.Scan(&journey.ID, &journey.Name, &journey.UserID, &journey.Description) if err != nil { return entities.Journey{}, err @@ -180,13 +211,17 @@ func (ss *SightStorage) CreateJourney(journey entities.Journey) (entities.Journe func (ss *SightStorage) DeleteJourney(journeyID int) error { ctx := context.Background() - _, err := ss.db.Exec(ctx, `DELETE FROM journey_sight WHERE journey_id = $1`, journeyID) + query := `DELETE FROM journey_sight WHERE journey_id = $1` + + _, err := ss.db.Exec(ctx, query, journeyID) if err != nil { logger.Logger().Error(err.Error()) return err } - _, err = ss.db.Exec(ctx, `DELETE FROM journey WHERE id = $1`, journeyID) + query = `DELETE FROM journey WHERE id = $1` + + _, err = ss.db.Exec(ctx, query, journeyID) if err != nil { logger.Logger().Error(err.Error()) return err @@ -196,29 +231,37 @@ func (ss *SightStorage) DeleteJourney(journeyID int) error { } func (ss *SightStorage) GetJourneys(userID int) ([]entities.Journey, error) { - var journey []*entities.Journey + var journey []entities.Journey ctx := context.Background() - err := pgxscan.Select(ctx, ss.db, &journey, `SELECT j.id, j.name, j.description, p.username FROM journey AS j INNER JOIN profile_data AS p ON p.user_id = $1 WHERE j.user_id = $1;`, userID) + query := `SELECT + j.id, + j.name, + j.description, + p.username + FROM journey AS j + INNER JOIN profile_data AS p + ON p.user_id = $1 + WHERE j.user_id = $1;` + + err := pgxscan.Select(ctx, ss.db, &journey, query, userID) if err != nil { logger.Logger().Error(err.Error()) return nil, err } - var journeyList []entities.Journey - for _, j := range journey { - journeyList = append(journeyList, *j) - } - return journeyList, nil + return journey, nil } // AddJourneySight добавляет достопримечательности в существующую поездку. func (ss *SightStorage) AddJourneySight(journeyID int, sightIDs []int) error { ctx := context.Background() + query := `SELECT EXISTS(SELECT 1 FROM journey WHERE id = $1)` + // Проверяем, существует ли journeyID в таблице journey var journeyExists bool - err := ss.db.QueryRow(ctx, `SELECT EXISTS(SELECT 1 FROM journey WHERE id = $1)`, journeyID).Scan(&journeyExists) + err := ss.db.QueryRow(ctx, query, journeyID).Scan(&journeyExists) if err != nil { logger.Logger().Error(err.Error()) return err @@ -228,9 +271,11 @@ func (ss *SightStorage) AddJourneySight(journeyID int, sightIDs []int) error { return fmt.Errorf("journey with id %d does not exist", journeyID) } + query = `INSERT INTO journey_sight(journey_id, sight_id, priority) VALUES ($1, $2, $3)` + // Добавляем достопримечательности в journey_sight for _, sightID := range sightIDs { - _, err := ss.db.Exec(ctx, `INSERT INTO journey_sight(journey_id, sight_id, priority) VALUES ($1, $2, $3)`, journeyID, sightID, 0) + _, err := ss.db.Exec(ctx, query, journeyID, sightID, 0) if err != nil { logger.Logger().Error(err.Error()) return err @@ -243,9 +288,11 @@ func (ss *SightStorage) AddJourneySight(journeyID int, sightIDs []int) error { func (ss *SightStorage) EditJourney(journeyID int, name, description string) error { ctx := context.Background() + query := `SELECT EXISTS(SELECT 1 FROM journey WHERE id = $1)` + // Проверяем, существует ли journeyID в таблице journey var journeyExists bool - err := ss.db.QueryRow(ctx, `SELECT EXISTS(SELECT 1 FROM journey WHERE id = $1)`, journeyID).Scan(&journeyExists) + err := ss.db.QueryRow(ctx, query, journeyID).Scan(&journeyExists) if err != nil { logger.Logger().Error(err.Error()) return err @@ -255,8 +302,10 @@ func (ss *SightStorage) EditJourney(journeyID int, name, description string) err return fmt.Errorf("journey with id %d does not exist", journeyID) } + query = `UPDATE journey SET name = $1, description = $2 WHERE id = $3` + // Обновляем имя и описание поездки - _, err = ss.db.Exec(ctx, `UPDATE journey SET name = $1, description = $2 WHERE id = $3`, name, description, journeyID) + _, err = ss.db.Exec(ctx, query, name, description, journeyID) if err != nil { logger.Logger().Error(err.Error()) return err @@ -267,17 +316,19 @@ func (ss *SightStorage) EditJourney(journeyID int, name, description string) err func (ss *SightStorage) GetJourneySights(journeyID int) ([]entities.Sight, error) { var sights []entities.Sight - var idList []*int + var idList []int ctx := context.Background() - err := pgxscan.Select(ctx, ss.db, &idList, `SELECT js.sight_id FROM journey_sight AS js WHERE js.journey_id = $1`, journeyID) + query := `SELECT js.sight_id FROM journey_sight AS js WHERE js.journey_id = $1` + + err := pgxscan.Select(ctx, ss.db, &idList, query, journeyID) if err != nil { logger.Logger().Error(err.Error()) return nil, err } for _, id := range idList { - sight, err := ss.GetSight(*id) + sight, err := ss.GetSight(id) if err != nil { return nil, err } @@ -288,13 +339,24 @@ func (ss *SightStorage) GetJourneySights(journeyID int) ([]entities.Sight, error } func (ss *SightStorage) GetJourney(journeyID int) (entities.Journey, error) { - var journey []*entities.Journey + var journey entities.Journey ctx := context.Background() - err := pgxscan.Select(ctx, ss.db, &journey, `SELECT j.id, j.name, j.description, p.username, p.user_id FROM journey AS j INNER JOIN profile_data AS p ON p.user_id = j.user_id WHERE j.id = $1;`, journeyID) + query := `SELECT + j.id, + j.name, + j.description, + p.username, + p.user_id + FROM journey AS j + INNER JOIN profile_data AS p + ON p.user_id = j.user_id + WHERE j.id = $1;` + + err := pgxscan.Get(ctx, ss.db, &journey, query, journeyID) if err != nil { logger.Logger().Error(err.Error()) return entities.Journey{}, err } - return *journey[0], nil + return journey, nil } From 3a04840031dbfd5054306908d7ee7c6f98b46125 Mon Sep 17 00:00:00 2001 From: MrDzhofik Date: Fri, 10 May 2024 18:52:06 +0300 Subject: [PATCH 15/15] Fixed profile avatars --- images/arg_messi.jpeg | Bin 129689 -> 0 bytes ...{yandex_download.go => download_photos.go} | 0 internal/delivery/handlers/avatar/avatar.go | 75 +++++++++++++++--- internal/usecase/album_usecase.go | 7 -- 4 files changed, 66 insertions(+), 16 deletions(-) delete mode 100644 images/arg_messi.jpeg rename internal/delivery/handlers/album/{yandex_download.go => download_photos.go} (100%) diff --git a/images/arg_messi.jpeg b/images/arg_messi.jpeg deleted file mode 100644 index 3865ea4b9af4e246eb6049c3db176081259f10b5..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 129689 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