diff --git a/README.md b/README.md index fc6e12a..6bab4e5 100644 --- a/README.md +++ b/README.md @@ -1,5 +1,5 @@ # 2024_2_VKatuny -![Coverage](https://img.shields.io/badge/Coverage-60.8%25-yellow) +![Coverage](https://img.shields.io/badge/Coverage-60.3%25-yellow) Данный репозиторий предназначен для хранения backend части проекта HeadHunter, разрабатываемого командой VKатуны. diff --git a/db/migrations/025_testdata.sql b/db/migrations/025_testdata.sql index 88fd6de..019ee98 100644 --- a/db/migrations/025_testdata.sql +++ b/db/migrations/025_testdata.sql @@ -43,24 +43,24 @@ insert into vacancy (employer_id , salary, position, vacancy_description, work_t values (1, 90000, 'Скульптор', 'Требуется скульптор без опыта работы', 1, 1); -insert into vacancy (employer_id , salary, position, vacancy_description, work_type_id, city_id) -values (1, 170000, 'Дизайнер витрины', 'Требуется оформить главный стенд нового офиса', 2, 2); -insert into vacancy (employer_id , salary, position, vacancy_description, work_type_id,city_id) -values (3, 80000, 'Младший дизайнер интерьера', 'Требуется специалист в области дизайна интерьера в дружный коллектив нашего бюро', 1, 2); -insert into vacancy (employer_id , salary, position, vacancy_description, work_type_id, city_id) -values (3, 100000, 'Художник оформитель', 'Требуется опытный специалист для оформления дизайнерской мебели', 1, 3); -insert into vacancy (employer_id , salary, position, vacancy_description, work_type_id, city_id) -values (2, 210000, 'Младший ландшафтный дизайнер', 'Нанимаем опытного специалиста для оформления нескольких парковых зон', 2, 1); -insert into vacancy (employer_id , salary, position, vacancy_description, work_type_id, city_id) -values (2, 120000, 'Младший ландшафтный дизайнер', 'Требуется специалист по живой изгороди', 1, 3); -insert into vacancy (employer_id , salary, position, vacancy_description, work_type_id,city_id) -values (2, 220000, 'Куратор', 'Требуется куратор в нашу программу повышения квалификации сотрудников', 1, 2); -insert into vacancy (employer_id , salary, position, vacancy_description, work_type_id, city_id) -values (1, 320000, 'Графитист', 'Нанимаем профессионального художника-гафитиста для временного графити приуроченному к празднику на высотном здании', 2, 2); -insert into vacancy (employer_id , salary, position, vacancy_description, work_type_id, city_id) -values (4, 110000, 'Хореограф', 'Ищем профессионального хареографа со стажем 15 лет', 1, 3); -insert into vacancy (employer_id , salary, position, vacancy_description, work_type_id, city_id) -values (4, 210000, 'Художник декоратор', 'Требуется декоратор для постановок', 1, 1); +insert into vacancy (employer_id , salary, position, vacancy_description, work_type_id, city_id, path_to_company_avatar) +values (1, 170000, 'Дизайнер витрины', 'Требуется оформить главный стенд нового офиса', 2, 2, "media/Uncompressed/1ahsdfhtrgtorhjertoldbtsdjgxsdfkg.PNG"); +insert into vacancy (employer_id , salary, position, vacancy_description, work_type_id, city_id, path_to_company_avatar) +values (3, 80000, 'Младший дизайнер интерьера', 'Требуется специалист в области дизайна интерьера в дружный коллектив нашего бюро', 1, 2, "media/Uncompressed/1ahsdfhtrgtaahjertoldbtsdjgxsdfkg.PNG"); +insert into vacancy (employer_id , salary, position, vacancy_description, work_type_id, city_id, path_to_company_avatar) +values (3, 100000, 'Художник оформитель', 'Требуется опытный специалист для оформления дизайнерской мебели', 1, 3, "media/Uncompressed/1ahsdfhtrgtaahjertoldbtsdjgxsdfkg.PNG"); +insert into vacancy (employer_id , salary, position, vacancy_description, work_type_id, city_id, path_to_company_avatar) +values (2, 210000, 'Младший ландшафтный дизайнер', 'Нанимаем опытного специалиста для оформления нескольких парковых зон', 2, 1, "media/Uncompressed/1ahsdfhtrgtorhjruooldbtsdjgxsdfkg.JPG"); +insert into vacancy (employer_id , salary, position, vacancy_description, work_type_id, city_id, path_to_company_avatar) +values (2, 120000, 'Младший ландшафтный дизайнер', 'Требуется специалист по живой изгороди', 1, 3, "media/Uncompressed/1ahsdfhtrgtorhjruooldbtsdjgxsdfkg.JPG"); +insert into vacancy (employer_id , salary, position, vacancy_description, work_type_id, city_id, path_to_company_avatar) +values (2, 220000, 'Куратор', 'Требуется куратор в нашу программу повышения квалификации сотрудников', 1, 2, "media/Uncompressed/1ahsdfhtrgtorhjruooldbtsdjgxsdfkg.JPG"); +insert into vacancy (employer_id , salary, position, vacancy_description, work_type_id, city_id, path_to_company_avatar) +values (1, 320000, 'Графитист', 'Нанимаем профессионального художника-гафитиста для временного графити приуроченному к празднику на высотном здании', 2, 2, "media/Uncompressed/1ahsdfhtrgtorhjertoldbtsdjgxsdfkg.PNG"); +insert into vacancy (employer_id , salary, position, vacancy_description, work_type_id, city_id, path_to_company_avatar) +values (4, 110000, 'Хореограф', 'Ищем профессионального хареографа со стажем 15 лет', 1, 3, "media/Uncompressed/1ahsdfhtrgtorhjruaaldbtsdjgxsdfkg.JPG"); +insert into vacancy (employer_id , salary, position, vacancy_description, work_type_id, city_id, path_to_company_avatar) +values (4, 210000, 'Художник декоратор', 'Требуется декоратор для постановок', 1, 1, "media/Uncompressed/1ahsdfhtrgtorhjruaaldbtsdjgxsdfkg.JPG"); insert into vacancy_subscriber (applicant_id , vacancy_id) values (1, 1); @@ -119,4 +119,4 @@ delete from vacancy_to_creation_tag where vacancy_id=1 and creation_tag_id=1; delete from employer_rate_to_applicant_creation where employer_id=1 and applicant_creation_id=1; delete from cv_to_creation_tag where cv_id=1 and creation_tag_id=1; delete from cv_to_creation_tag where applicant_creation_id=1 and creation_tag_id=1; -delete from cv_to_creation_tag where applicant_creation_id=1 and portfolio_id=1; \ No newline at end of file +delete from cv_to_creation_tag where applicant_creation_id=1 and portfolio_id=1; diff --git a/go.mod b/go.mod index 5359a70..b7955bd 100644 --- a/go.mod +++ b/go.mod @@ -8,6 +8,7 @@ require ( github.com/davidbyttow/govips/v2 v2.15.0 github.com/gorilla/websocket v1.5.3 github.com/jackc/pgx/v5 v5.7.1 + github.com/mailru/easyjson v0.9.0 github.com/rafaeljusto/redigomock/v3 v3.1.2 github.com/stretchr/testify v1.10.0 github.com/swaggo/swag v1.16.4 @@ -40,7 +41,6 @@ require ( github.com/gorilla/css v1.0.1 // indirect github.com/josharian/intern v1.0.0 // indirect github.com/klauspost/compress v1.17.11 // indirect - github.com/mailru/easyjson v0.9.0 // indirect github.com/munnerz/goautoneg v0.0.0-20191010083416-a7dc8b61c822 // indirect github.com/prometheus/client_model v0.6.1 // indirect github.com/prometheus/common v0.61.0 // indirect diff --git a/internal/pkg/applicant/delivery/applicant_profile.go b/internal/pkg/applicant/delivery/applicant_profile.go index 29a84bd..6fb61f6 100644 --- a/internal/pkg/applicant/delivery/applicant_profile.go +++ b/internal/pkg/applicant/delivery/applicant_profile.go @@ -1,6 +1,7 @@ package delivery import ( + "io" "net/http" "strconv" @@ -125,7 +126,7 @@ func (h *ApplicantHandlers) UpdateProfile(w http.ResponseWriter, r *http.Request }) return } - + r.ParseMultipartForm(25 << 20) // 25Mb newProfileData := &dto.JSONUpdateApplicantProfile{} newProfileData.FirstName = r.FormValue("firstName") newProfileData.LastName = r.FormValue("lastName") @@ -137,7 +138,16 @@ func (h *ApplicantHandlers) UpdateProfile(w http.ResponseWriter, r *http.Request file, header, err := r.FormFile("profile_avatar") if err == nil { defer file.Close() - fileAddress, compressedFileAddress, err := h.fileLoadingUsecase.WriteImage(file, header) + fileWasRead, err := io.ReadAll(file) + if err != nil { + h.logger.Errorf("function %s: got err %s", fn, err) + middleware.UniversalMarshal(w, http.StatusInternalServerError, dto.JSONResponse{ + HTTPStatus: http.StatusInternalServerError, + Error: err.Error(), + }) + return + } + fileAddress, compressedFileAddress, err := h.fileLoadingUsecase.WriteImage(fileWasRead, header) h.logger.Debugf("address %s compressed %s", fileAddress, compressedFileAddress) if err != nil { middleware.UniversalMarshal(w, http.StatusBadRequest, dto.JSONResponse{ diff --git a/internal/pkg/applicant/usecase/applicant_profile.go b/internal/pkg/applicant/usecase/applicant_profile.go index 93164bb..32e662e 100644 --- a/internal/pkg/applicant/usecase/applicant_profile.go +++ b/internal/pkg/applicant/usecase/applicant_profile.go @@ -69,12 +69,6 @@ func (au *ApplicantUsecase) UpdateApplicantProfile(ctx context.Context, applican return err } au.logger.Debug("compress") - // TODO: add microservice - - // if err != nil { - // au.logger.Errorf("fail compress microservice") - // return err - // } au.logger.Debugf("function: %s; successfully updated applicant profile", fn) return nil diff --git a/internal/pkg/cvs/delivery/cvs_handlers.go b/internal/pkg/cvs/delivery/cvs_handlers.go index 51d860f..f252c58 100644 --- a/internal/pkg/cvs/delivery/cvs_handlers.go +++ b/internal/pkg/cvs/delivery/cvs_handlers.go @@ -2,6 +2,7 @@ package delivery import ( "context" + "io" "net/http" "strconv" @@ -84,7 +85,16 @@ func (h *CVsHandler) CreateCV(w http.ResponseWriter, r *http.Request) { file, header, err := r.FormFile("profile_avatar") if err == nil { defer file.Close() - fileAddress, compressedFileAddress, err := h.fileLoadingUsecase.WriteImage(file, header) + fileWasRead, err := io.ReadAll(file) + if err != nil { + h.logger.Errorf("function %s: got err %s", fn, err) + middleware.UniversalMarshal(w, http.StatusInternalServerError, dto.JSONResponse{ + HTTPStatus: http.StatusInternalServerError, + Error: err.Error(), + }) + return + } + fileAddress, compressedFileAddress, err := h.fileLoadingUsecase.WriteImage(fileWasRead, header) if err != nil { middleware.UniversalMarshal(w, http.StatusBadRequest, dto.JSONResponse{ HTTPStatus: http.StatusBadRequest, @@ -164,7 +174,7 @@ func (h *CVsHandler) GetCV(w http.ResponseWriter, r *http.Request) { }) return } - CV.CompressedAvatar = h.fileLoadingUsecase.FindCompressedFile(CV.Avatar) + CV.CompressedAvatar = h.fileLoadingUsecase.FindCompressedFile(CV.Avatar) h.logger.Debugf("%s: success, got cv: %v", fn, CV) middleware.UniversalMarshal(w, http.StatusOK, dto.JSONResponse{ HTTPStatus: http.StatusOK, @@ -236,7 +246,16 @@ func (h *CVsHandler) UpdateCV(w http.ResponseWriter, r *http.Request) { file, header, err := r.FormFile("profile_avatar") if err == nil { defer file.Close() - fileAddress, compressedFileAddress, err := h.fileLoadingUsecase.WriteImage(file, header) + fileWasRead, err := io.ReadAll(file) + if err != nil { + h.logger.Errorf("function %s: got err %s", fn, err) + middleware.UniversalMarshal(w, http.StatusInternalServerError, dto.JSONResponse{ + HTTPStatus: http.StatusInternalServerError, + Error: err.Error(), + }) + return + } + fileAddress, compressedFileAddress, err := h.fileLoadingUsecase.WriteImage(fileWasRead, header) if err != nil { middleware.UniversalMarshal(w, http.StatusBadRequest, dto.JSONResponse{ HTTPStatus: http.StatusBadRequest, @@ -372,7 +391,7 @@ func (h *CVsHandler) CVtoPDF(w http.ResponseWriter, r *http.Request) { }) return } - name.FileName="/"+name.FileName + name.FileName = "/" + name.FileName middleware.UniversalMarshal(w, http.StatusOK, dto.JSONResponse{ HTTPStatus: http.StatusOK, Body: name, diff --git a/internal/pkg/employer/delivery/employer_profile.go b/internal/pkg/employer/delivery/employer_profile.go index b012fe0..dcf3ac7 100644 --- a/internal/pkg/employer/delivery/employer_profile.go +++ b/internal/pkg/employer/delivery/employer_profile.go @@ -1,6 +1,7 @@ package delivery import ( + "io" "net/http" "strconv" @@ -122,7 +123,7 @@ func (h *EmployerHandlers) UpdateProfile(w http.ResponseWriter, r *http.Request) }) return } - + r.ParseMultipartForm(25 << 20) // 25Mb newProfileData := &dto.JSONUpdateEmployerProfile{} newProfileData.FirstName = r.FormValue("firstName") newProfileData.LastName = r.FormValue("lastName") @@ -132,7 +133,16 @@ func (h *EmployerHandlers) UpdateProfile(w http.ResponseWriter, r *http.Request) file, header, err := r.FormFile("profile_avatar") if err == nil { defer file.Close() - fileAddress, compressedFileAddress, err := h.fileLoadingUsecase.WriteImage(file, header) + fileWasRead, err := io.ReadAll(file) + if err != nil { + h.logger.Errorf("function %s: got err %s", fn, err) + middleware.UniversalMarshal(w, http.StatusInternalServerError, dto.JSONResponse{ + HTTPStatus: http.StatusInternalServerError, + Error: err.Error(), + }) + return + } + fileAddress, compressedFileAddress, err := h.fileLoadingUsecase.WriteImage(fileWasRead, header) if err != nil { middleware.UniversalMarshal(w, http.StatusBadRequest, dto.JSONResponse{ HTTPStatus: http.StatusBadRequest, @@ -188,7 +198,7 @@ func (h *EmployerHandlers) GetEmployerVacancies(w http.ResponseWriter, r *http.R return } - vacancies, err := h.vacanciesUsecase.GetVacanciesByEmployerID(r.Context(),employerID) + vacancies, err := h.vacanciesUsecase.GetVacanciesByEmployerID(r.Context(), employerID) if err != nil { h.logger.Errorf("function %s: got err %s", fn, err) middleware.UniversalMarshal(w, http.StatusInternalServerError, dto.JSONResponse{ @@ -200,7 +210,7 @@ func (h *EmployerHandlers) GetEmployerVacancies(w http.ResponseWriter, r *http.R for _, vacancy := range vacancies { utils.EscapeHTMLStruct(vacancy) - } + } h.logger.Debugf("function %s: success, got vacancies: %d", fn, len(vacancies)) middleware.UniversalMarshal(w, http.StatusOK, dto.JSONResponse{ diff --git a/internal/pkg/file_loading/file_loading.go b/internal/pkg/file_loading/file_loading.go index f104a59..341f117 100644 --- a/internal/pkg/file_loading/file_loading.go +++ b/internal/pkg/file_loading/file_loading.go @@ -7,12 +7,12 @@ import ( ) type IFileLoadingRepository interface { - WriteFileOnDisk(filename string, header *multipart.FileHeader, file multipart.File) (string, string, error) + WriteFileOnDisk(filename string, header *multipart.FileHeader, file []byte) (string, string, error) CVtoPDF(CV *dto.JSONCv, applicant *dto.JSONGetApplicantProfile) (string, error) } type IFileLoadingUsecase interface { - WriteImage(file multipart.File, header *multipart.FileHeader) (string, string, error) + WriteImage(file []byte, header *multipart.FileHeader) (string, string, error) FindCompressedFile(filename string) string CVtoPDF(CV *dto.JSONCv, applicant *dto.JSONGetApplicantProfile) (*dto.CVPDFFile, error) } diff --git a/internal/pkg/file_loading/mock/file_loading.go b/internal/pkg/file_loading/mock/file_loading.go index 0215f7c..e869a74 100644 --- a/internal/pkg/file_loading/mock/file_loading.go +++ b/internal/pkg/file_loading/mock/file_loading.go @@ -57,7 +57,7 @@ func (mr *MockIFileLoadingRepositoryMockRecorder) CVtoPDF(CV, applicant any) *go } // WriteFileOnDisk mocks base method. -func (m *MockIFileLoadingRepository) WriteFileOnDisk(filename string, header *multipart.FileHeader, file multipart.File) (string, string, error) { +func (m *MockIFileLoadingRepository) WriteFileOnDisk(filename string, header *multipart.FileHeader, file []byte) (string, string, error) { m.ctrl.T.Helper() ret := m.ctrl.Call(m, "WriteFileOnDisk", filename, header, file) ret0, _ := ret[0].(string) @@ -126,7 +126,7 @@ func (mr *MockIFileLoadingUsecaseMockRecorder) FindCompressedFile(filename any) } // WriteImage mocks base method. -func (m *MockIFileLoadingUsecase) WriteImage(file multipart.File, header *multipart.FileHeader) (string, string, error) { +func (m *MockIFileLoadingUsecase) WriteImage(file []byte, header *multipart.FileHeader) (string, string, error) { m.ctrl.T.Helper() ret := m.ctrl.Call(m, "WriteImage", file, header) ret0, _ := ret[0].(string) diff --git a/internal/pkg/file_loading/repository/file_loading_file_storage.go b/internal/pkg/file_loading/repository/file_loading_file_storage.go index 75da8d4..ffc28f5 100644 --- a/internal/pkg/file_loading/repository/file_loading_file_storage.go +++ b/internal/pkg/file_loading/repository/file_loading_file_storage.go @@ -6,6 +6,7 @@ import ( "io" "mime/multipart" "os" + "slices" "strconv" "strings" "text/template" @@ -31,7 +32,7 @@ func NewFileLoadingStorage(logger *logrus.Logger, mediaDir, CVinPDFDir, template } } -func (s *FileLoadingStorage) WriteFileOnDisk(filename string, header *multipart.FileHeader, file multipart.File) (string, string, error) { +func (s *FileLoadingStorage) WriteFileOnDisk(filename string, header *multipart.FileHeader, file []byte) (string, string, error) { fn := "FileLoadingStorage.WriteFileOnDisk" s.logger.Debugf("%s: entering with name: %s", fn, s.mediaDir+filename+header.Filename) dst, err := os.Create(s.mediaDir + filename + header.Filename) @@ -40,32 +41,26 @@ func (s *FileLoadingStorage) WriteFileOnDisk(filename string, header *multipart. return "", "", fmt.Errorf("error creating file") } defer dst.Close() - if _, err := io.Copy(dst, file); err != nil { + reader := bytes.NewReader(file) + if _, err := io.Copy(dst, reader); err != nil { s.logger.Errorf("%s: got error copying file", fn) return "", "", fmt.Errorf("error copying file") } s.logger.Debugf("%s: done with name: %s and %s", fn, s.mediaDir, filename+header.Filename) - return s.mediaDir, filename + header.Filename, nil + dirList := strings.Split(s.mediaDir, "/") + dirList = dirList[slices.Index(dirList, "2024_2_VKatuny")+1:] + dirCut := strings.Join(dirList, "/") + "/" + return dirCut, filename + header.Filename, nil } func (s *FileLoadingStorage) CVtoPDF(CV *dto.JSONCv, applicant *dto.JSONGetApplicantProfile) (string, error) { fn := "FileLoadingStorage.CVtoPDF" s.logger.Debugf("%s: entering", fn) - pwd, _ := os.Getwd() - newPwd := "" - for _, i := range strings.Split(pwd, "/") { - newPwd += i + "/" - if i == "2024_2_VKatuny" { - break - } - } - tmpl := template.Must(template.ParseFiles(newPwd + s.templateDir + "template.html")) - pwd, err := os.Getwd() - if err != nil { - s.logger.Errorf("%s: got err %s", fn, err) - return "", err - } + templateDir := addslesh(s.templateDir) + mediaDir := addslesh(s.mediaDir) + cvinPDFdir := addslesh(s.cvinPDFdir) + tmpl := template.Must(template.ParseFiles(templateDir + "template.html")) type And struct { CV dto.JSONCv Applicant dto.JSONGetApplicantProfile @@ -73,28 +68,27 @@ func (s *FileLoadingStorage) CVtoPDF(CV *dto.JSONCv, applicant *dto.JSONGetAppli Template string } megaStruct := And{CV: *CV, Applicant: *applicant} - if len(megaStruct.Applicant.BirthDate) > 9 { - megaStruct.Applicant.BirthDate = megaStruct.Applicant.BirthDate[:9] + if len(megaStruct.Applicant.BirthDate) > 10 { + megaStruct.Applicant.BirthDate = megaStruct.Applicant.BirthDate[:10] } - if len(megaStruct.CV.CreatedAt) > 9 { - megaStruct.CV.CreatedAt = megaStruct.CV.CreatedAt[:9] + if len(megaStruct.CV.CreatedAt) > 10 { + megaStruct.CV.CreatedAt = megaStruct.CV.CreatedAt[:10] } - s.logger.Debugf("avatar: %s", pwd+CV.Avatar) - s.logger.Debugf("template: %s", pwd+"/"+s.templateDir+"template.css") - megaStruct.Template = pwd + "/" + s.templateDir + "template.css" - megaStruct.CV.Avatar = pwd + CV.Avatar + s.logger.Debugf("avatar: %s", mediaDir+CV.Avatar) + s.logger.Debugf("template: %s", templateDir+"template.css") + megaStruct.Template = templateDir + "template.css" + megaStruct.CV.Avatar = mediaDir + strings.Split(CV.Avatar, "/")[len(strings.Split(CV.Avatar, "/"))-1] if CV.Avatar != "" { megaStruct.IsImg = 1 } else { megaStruct.IsImg = 0 } var buf bytes.Buffer - err = tmpl.Execute(&buf, megaStruct) + err := tmpl.Execute(&buf, megaStruct) if err != nil { s.logger.Errorf("%s: got err %s", fn, err) return "", err } - //s.logger.Debugf(buf.String()) pdfg, err := wkhtml.NewPDFGenerator() if err != nil { @@ -109,8 +103,8 @@ func (s *FileLoadingStorage) CVtoPDF(CV *dto.JSONCv, applicant *dto.JSONGetAppli s.logger.Errorf("%s: got err %s", fn, err) return "", err } - name := s.cvinPDFdir + strconv.Itoa(int(CV.ID)) + "&&" + strconv.Itoa(int(CV.ApplicantID)) + ".pdf" - err = pdfg.WriteFile(newPwd+name) + name := cvinPDFdir + strconv.Itoa(int(CV.ID)) + "&&" + strconv.Itoa(int(CV.ApplicantID)) + ".pdf" + err = pdfg.WriteFile(name) if err != nil { s.logger.Errorf("%s: got err %s", fn, err) return "", err @@ -118,3 +112,10 @@ func (s *FileLoadingStorage) CVtoPDF(CV *dto.JSONCv, applicant *dto.JSONGetAppli s.logger.Debugf("%s: done with name: %s", fn, name) return name, nil } + +func addslesh(str string) string { + if str[len(str)-1] != '/' { + str += "/" + } + return str +} diff --git a/internal/pkg/file_loading/usecase/file_loading.go b/internal/pkg/file_loading/usecase/file_loading.go index e65ecc1..aee03ec 100644 --- a/internal/pkg/file_loading/usecase/file_loading.go +++ b/internal/pkg/file_loading/usecase/file_loading.go @@ -3,6 +3,7 @@ package usecase import ( "fmt" "mime/multipart" + "net/http" "os" "slices" "strings" @@ -32,43 +33,36 @@ func NewFileLoadingUsecase(logger *logrus.Logger, repositories *internal.Reposit } } -var allowedTypes = []string{"image/jpeg", "image/jpg", "image/svg", "image/svg+xml"} +var allowedTypes = []string{"image/jpeg", "image/svg+xml", "image/pjpeg", "image/webp"} -func (vu *FileLoadingUsecase) WriteImage(file multipart.File, header *multipart.FileHeader) (string, string, error) { - a := header.Header - vu.logger.Debug(a["Content-Type"][0]) - for _, i := range a["Content-Type"] { - if !slices.Contains(allowedTypes, i) { - return "", "", fmt.Errorf(dto.MsgInvalidFile) - } +func (vu *FileLoadingUsecase) WriteImage(file []byte, header *multipart.FileHeader) (string, string, error) { + ContentType := http.DetectContentType(file) + vu.logger.Debug(ContentType) + if !slices.Contains(allowedTypes, ContentType) || header.Size > 25<<21 || len(file) > 25<<21 { + return "", "", fmt.Errorf(dto.MsgInvalidFile) } filename := utils.GenerateSessionToken(utils.TokenLength+10, dto.UserTypeApplicant) - dir, fileAddress, err := vu.FileLoadingRepository.WriteFileOnDisk(filename, header, file) + dir, fileName, err := vu.FileLoadingRepository.WriteFileOnDisk(filename, header, file) if err != nil { return "", "", err } - return dir + fileAddress, vu.FindCompressedFile(fileAddress), nil + return dir + fileName, vu.FindCompressedFile(fileName), nil } func (vu *FileLoadingUsecase) FindCompressedFile(filename string) string { filename = strings.Split(filename, "/")[len(strings.Split(filename, "/"))-1] vu.logger.Debugf("filename: %s", filename) dir := vu.conf.CompressMicroservice.CompressedMediaDir - pwd, _ := os.Getwd() - newPwd := "" - for _, i := range strings.Split(pwd, "/") { - newPwd += i + "/" - if i == "2024_2_VKatuny" { - break - } - } - compressed, err := os.ReadDir(newPwd + dir) + compressed, err := os.ReadDir(dir) if err != nil { return "" } + dirList := strings.Split(dir, "/") + dirList = dirList[slices.Index(dirList, "2024_2_VKatuny")+1:] + dirCut := strings.Join(dirList, "/") + "/" for _, file := range compressed { if file.Name()[:strings.Index(file.Name(), ".")] == filename[:strings.Index(filename, ".")] { - return dir + file.Name() + return dirCut + file.Name() } } return "" diff --git a/internal/pkg/vacancies/delivery/vacancy.go b/internal/pkg/vacancies/delivery/vacancy.go index 7d116c2..243e195 100644 --- a/internal/pkg/vacancies/delivery/vacancy.go +++ b/internal/pkg/vacancies/delivery/vacancy.go @@ -2,6 +2,7 @@ package delivery import ( "context" + "io" "net/http" "strconv" @@ -80,7 +81,16 @@ func (h *VacanciesHandlers) CreateVacancy(w http.ResponseWriter, r *http.Request file, header, err := r.FormFile("company_avatar") if err == nil { defer file.Close() - fileAddress, compressedFileAddress, err := h.fileLoadingUsecase.WriteImage(file, header) + fileWasRead, err := io.ReadAll(file) + if err != nil { + h.logger.Errorf("function %s: got err %s", fn, err) + middleware.UniversalMarshal(w, http.StatusInternalServerError, dto.JSONResponse{ + HTTPStatus: http.StatusInternalServerError, + Error: err.Error(), + }) + return + } + fileAddress, compressedFileAddress, err := h.fileLoadingUsecase.WriteImage(fileWasRead, header) if err != nil { middleware.UniversalMarshal(w, http.StatusBadRequest, dto.JSONResponse{ HTTPStatus: http.StatusBadRequest, @@ -236,7 +246,16 @@ func (h *VacanciesHandlers) UpdateVacancy(w http.ResponseWriter, r *http.Request file, header, err := r.FormFile("company_avatar") if err == nil { defer file.Close() - fileAddress, compressedFileAddress, err := h.fileLoadingUsecase.WriteImage(file, header) + fileWasRead, err := io.ReadAll(file) + if err != nil { + h.logger.Errorf("function %s: got err %s", fn, err) + middleware.UniversalMarshal(w, http.StatusInternalServerError, dto.JSONResponse{ + HTTPStatus: http.StatusInternalServerError, + Error: err.Error(), + }) + return + } + fileAddress, compressedFileAddress, err := h.fileLoadingUsecase.WriteImage(fileWasRead, header) if err != nil { middleware.UniversalMarshal(w, http.StatusBadRequest, dto.JSONResponse{ HTTPStatus: http.StatusBadRequest, @@ -370,27 +389,27 @@ func (h *VacanciesHandlers) SubscribeVacancy(w http.ResponseWriter, r *http.Requ } h.logger.Debugf("user_ID: %d subscribed on vacancy_ID %d", currentUser.ID, vacancyID) // go func() { - vacancy, err := h.vacanciesUsecase.GetVacancy(r.Context(), vacancyID) - if err != nil { - h.logger.Errorf("while getting from db got err %s", err) - return - } - applicant, err := h.applicantUsecase.GetApplicantProfile(context.Background(), currentUser.ID) - if err != nil { - h.logger.Errorf("while getting from db got err %s", err) - return - } - h.logger.Debugf("Sending notification: %+v on vacancy: %+v", vacancy, applicant) - _, err = h.NotificationsGRPC.CreateEmployerNotification( - context.Background(), - ¬ificationsmicroservice.CreateEmployerNotificationInput{ - ApplicantID: currentUser.ID, - VacancyID: vacancyID, - EmployerID: vacancy.EmployerID, - ApplicantInfo: applicant.FirstName + " " + applicant.LastName, - VacancyInfo: vacancy.Position, - }, - ) + vacancy, err := h.vacanciesUsecase.GetVacancy(r.Context(), vacancyID) + if err != nil { + h.logger.Errorf("while getting from db got err %s", err) + return + } + applicant, err := h.applicantUsecase.GetApplicantProfile(context.Background(), currentUser.ID) + if err != nil { + h.logger.Errorf("while getting from db got err %s", err) + return + } + h.logger.Debugf("Sending notification: %+v on vacancy: %+v", vacancy, applicant) + _, err = h.NotificationsGRPC.CreateEmployerNotification( + context.Background(), + ¬ificationsmicroservice.CreateEmployerNotificationInput{ + ApplicantID: currentUser.ID, + VacancyID: vacancyID, + EmployerID: vacancy.EmployerID, + ApplicantInfo: applicant.FirstName + " " + applicant.LastName, + VacancyInfo: vacancy.Position, + }, + ) //}() middleware.UniversalMarshal(w, http.StatusOK, dto.JSONResponse{ diff --git a/templates/template.html b/templates/template.html index 95f4c7f..5b27716 100644 --- a/templates/template.html +++ b/templates/template.html @@ -7,28 +7,27 @@ {{if eq .IsImg 1}}Profile picture{{end}}
-
-

{{.Applicant.FirstName}} {{.Applicant.LastName}}

-
Контакты:
-

{{.Applicant.Contacts}}

-

+
+

{{.Applicant.FirstName}} {{.Applicant.LastName}}

+ {{if eq .Applicant.Contacts ""}}{{else}}
Контакты:
+

{{.Applicant.Contacts}}

{{end}}
-
-

{{.CV.PositionRu}} ({{.CV.PositionEn}})

+
+

{{.CV.PositionRu}} ({{.CV.PositionEn}})

-
-

Опыт работы

-

{{.CV.WorkingExperience}}

+
+

Опыт работы

+

{{.CV.WorkingExperience}}

-
-

Образование

-

{{.Applicant.Education}}

-
-
-

Дополнительная информация

-

Дата рождения: {{.Applicant.BirthDate}}

-

Город поиска вакансии: {{.Applicant.City}}

-

Резюме создано в: {{.CV.CreatedAt}}

+ {{if eq .Applicant.Education ""}}{{else}}
+

Образование

+

{{.Applicant.Education}}

+
{{end}} +
+

Дополнительная информация

+

Дата рождения: {{.Applicant.BirthDate}}

+ {{if eq .Applicant.City ""}}{{else}}

Город поиска вакансии: {{.Applicant.City}}

{{end}} +

Резюме создано в: {{.CV.CreatedAt}}

\ No newline at end of file