From 349507456e897a331c3a028e4b1ae343e5a9527a Mon Sep 17 00:00:00 2001 From: Mark Albrand Date: Mon, 2 Oct 2023 21:16:50 -0600 Subject: [PATCH 01/18] Delete postulation --- backend/controllers/postulation.go | 40 ++++++++++++++++++++++++++++++ backend/routes/routes_handler.go | 1 + 2 files changed, 41 insertions(+) diff --git a/backend/controllers/postulation.go b/backend/controllers/postulation.go index 7a039207..bf7c3156 100644 --- a/backend/controllers/postulation.go +++ b/backend/controllers/postulation.go @@ -224,3 +224,43 @@ func GetPostulactionFromStudent(c *gin.Context) { }) } + +func RetirePostulation(c *gin.Context) { + input := c.Query("id_postulacion") + user, err := utils.ExtractTokenUsername(c) + + if input == "" { + c.JSON(http.StatusBadRequest, responses.StandardResponse{ + Status: http.StatusBadRequest, + Message: "ID postulation is required as a query parameter", + Data: nil, + }) + return + } + + if err != nil { + c.JSON(http.StatusBadRequest, responses.StandardResponse{ + Status: http.StatusBadRequest, + Message: "Unauthorized. Cannot get information from token. " + err.Error(), + Data: nil, + }) + return + } + + err = configs.DB.Where("id_postulacion = ? AND id_estudiante = ?", input, user).Delete(&models.Postulacion{}).Error + + if err != nil { + c.JSON(http.StatusBadRequest, responses.StandardResponse{ + Status: http.StatusBadRequest, + Message: "Error deleting postulation. " + err.Error(), + Data: nil, + }) + return + } + + c.JSON(http.StatusOK, responses.StandardResponse{ + Status: http.StatusOK, + Message: "Postulation deleted successfully", + Data: nil, + }) +} diff --git a/backend/routes/routes_handler.go b/backend/routes/routes_handler.go index a89924f5..39388644 100644 --- a/backend/routes/routes_handler.go +++ b/backend/routes/routes_handler.go @@ -83,4 +83,5 @@ func Routes(router *gin.Engine) { postulations.Use(middlewares.JwtAuthentication()) postulations.POST("/", controllers.NewPostulation) postulations.GET("/getFromStudent", controllers.GetPostulactionFromStudent) + postulations.DELETE("/", controllers.RetirePostulation) } From 1747a96100a5f7c4068897b5252c39c5af70da38 Mon Sep 17 00:00:00 2001 From: Mark Albrand Date: Mon, 2 Oct 2023 21:20:36 -0600 Subject: [PATCH 02/18] Delete postulation --- backend/README.md | 13 +++++++++++++ backend/controllers/postulation.go | 16 +++++++++++++++- 2 files changed, 28 insertions(+), 1 deletion(-) diff --git a/backend/README.md b/backend/README.md index 2f0a0b65..608e4e9b 100644 --- a/backend/README.md +++ b/backend/README.md @@ -670,6 +670,19 @@ Devuelve las postulaciones de un Estudiante. ``` +### [DELETE] api/postulations/?id_postulacion=1 +Elimina una postulación. El usuario se obtiene del token. Se pasa el id de la postulación como query param +> **Note** +> Auth required + +#### Response +``` json +{ + "status": 200, + "message": "Postulation deleted successfully", + "data": null +} +``` --- ## Administradores diff --git a/backend/controllers/postulation.go b/backend/controllers/postulation.go index bf7c3156..e55f3460 100644 --- a/backend/controllers/postulation.go +++ b/backend/controllers/postulation.go @@ -247,12 +247,26 @@ func RetirePostulation(c *gin.Context) { return } + // verify that the postulation exists + var postulation models.Postulacion + + err = configs.DB.Where("id_postulacion = ? AND id_estudiante = ?", input, user).First(&postulation).Error + + if err != nil { + c.JSON(http.StatusNotFound, responses.StandardResponse{ + Status: http.StatusNotFound, + Message: "Error getting postulation. " + err.Error(), + Data: nil, + }) + return + } + err = configs.DB.Where("id_postulacion = ? AND id_estudiante = ?", input, user).Delete(&models.Postulacion{}).Error if err != nil { c.JSON(http.StatusBadRequest, responses.StandardResponse{ Status: http.StatusBadRequest, - Message: "Error deleting postulation. " + err.Error(), + Message: "No matching postulation was found. " + err.Error(), Data: nil, }) return From 40b295443b33b919544de124d3d567840633d897 Mon Sep 17 00:00:00 2001 From: Mark Albrand Date: Tue, 3 Oct 2023 18:03:06 -0600 Subject: [PATCH 03/18] Verificaciones de archivos --- backend/controllers/files.go | 10 ++++++++++ backend/utils/file.go | 11 +++++++++++ 2 files changed, 21 insertions(+) create mode 100644 backend/utils/file.go diff --git a/backend/controllers/files.go b/backend/controllers/files.go index a55e9921..9ed73caa 100644 --- a/backend/controllers/files.go +++ b/backend/controllers/files.go @@ -14,6 +14,7 @@ import ( func UpdateProfilePicture() gin.HandlerFunc { return func(c *gin.Context) { user, err := utils.ExtractTokenUsername(c) + acceptedFileTypes := []string{"png", "jpg", "jpeg"} if err != nil { c.JSON(http.StatusUnauthorized, responses.StandardResponse{ @@ -34,6 +35,15 @@ func UpdateProfilePicture() gin.HandlerFunc { // get the file type from filename fileType := file.Filename[strings.LastIndex(file.Filename, ".")+1:] + if !utils.Contains(acceptedFileTypes, fileType) { + c.JSON(http.StatusBadRequest, responses.StandardResponse{ + Status: http.StatusBadRequest, + Message: "Invalid file type. Accepted file types are " + strings.Join(acceptedFileTypes, ", "), + Data: nil, + }) + return + } + newFileName := user_stripped + "." + fileType dst := "./uploads/" + newFileName diff --git a/backend/utils/file.go b/backend/utils/file.go new file mode 100644 index 00000000..5638317c --- /dev/null +++ b/backend/utils/file.go @@ -0,0 +1,11 @@ +package utils + +func Contains(slice []string, item string) bool { + for _, s := range slice { + if s == item { + return true + } + } + + return false +} From 7c3516a55cdc13cf554e104d75ac4822844106b5 Mon Sep 17 00:00:00 2001 From: Mark Albrand Date: Tue, 3 Oct 2023 22:03:44 -0600 Subject: [PATCH 04/18] Update files.go --- backend/controllers/files.go | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/backend/controllers/files.go b/backend/controllers/files.go index 9ed73caa..7f28453d 100644 --- a/backend/controllers/files.go +++ b/backend/controllers/files.go @@ -63,7 +63,7 @@ func UpdateProfilePicture() gin.HandlerFunc { if userType == "student" { err = configs.DB.Model(&models.Estudiante{}).Where("correo = ?", user).Updates(models.Estudiante{Foto: newFileName}).Error - } else if userType == "company" { + } else if userType == "enterprise" { err = configs.DB.Model(&models.Empresa{}).Where("correo = ?", user).Updates(models.Empresa{Foto: newFileName}).Error } From 6d3276a5a70baa8e58d9cc92b1211e20a95ded96 Mon Sep 17 00:00:00 2001 From: Mark Albrand Date: Wed, 4 Oct 2023 12:21:09 -0600 Subject: [PATCH 05/18] Unique files --- backend/controllers/files.go | 36 +++++++++++++++++++++++++++++++++++- 1 file changed, 35 insertions(+), 1 deletion(-) diff --git a/backend/controllers/files.go b/backend/controllers/files.go index 7f28453d..d4ac0420 100644 --- a/backend/controllers/files.go +++ b/backend/controllers/files.go @@ -7,7 +7,10 @@ import ( "backend/utils" "fmt" "github.com/gin-gonic/gin" + "math/rand" "net/http" + "os" + "path/filepath" "strings" ) @@ -44,11 +47,22 @@ func UpdateProfilePicture() gin.HandlerFunc { return } - newFileName := user_stripped + "." + fileType + randomNumber := rand.Intn(9999999999-1111111111) + 1111111111 + newFileName := user_stripped + "_" + fmt.Sprint(randomNumber) + "." + fileType dst := "./uploads/" + newFileName fmt.Println("File: " + dst) + // Eliminar archivos antiguos con el mismo prefijo de usuario + if err := deleteFilesWithPrefix("./uploads/", user_stripped); err != nil { + c.JSON(http.StatusInternalServerError, responses.StandardResponse{ + Status: http.StatusInternalServerError, + Message: "Failed to delete old files: " + err.Error(), + Data: nil, + }) + return + } + // Actualizar en base de datos userType, err := utils.ExtractTokenUserType(c) @@ -83,6 +97,26 @@ func UpdateProfilePicture() gin.HandlerFunc { } } +func deleteFilesWithPrefix(directory, prefix string) error { + files, err := os.ReadDir(directory) + if err != nil { + return err + } + + for _, file := range files { + fmt.Println(file.Name()) + if strings.HasPrefix(file.Name(), prefix) { + filePath := filepath.Join(directory, file.Name()) + fmt.Println("Deleting file: " + filePath) + if err := os.Remove(filePath); err != nil { + return err + } + } + } + + return nil +} + func GetFile() gin.HandlerFunc { return func(c *gin.Context) { filename := c.Param("filename") From 39b79fe8351e3453debd0a4b9d3e0f9e4b354598 Mon Sep 17 00:00:00 2001 From: Mark Albrand Date: Wed, 4 Oct 2023 17:13:17 -0600 Subject: [PATCH 06/18] Data update --- backend/controllers/offer.go | 22 ++++++---------------- 1 file changed, 6 insertions(+), 16 deletions(-) diff --git a/backend/controllers/offer.go b/backend/controllers/offer.go index 3b1cac0e..6a8499ed 100644 --- a/backend/controllers/offer.go +++ b/backend/controllers/offer.go @@ -399,7 +399,7 @@ func GetApplicants(c *gin.Context) { var results []map[string]interface{} - rows, err := configs.DB.Raw("SELECT p.id_estudiante, p.estado, e.dpi, e.nombre, e.apellido, e.nacimiento, e.correo, e.telefono, e.carrera, e.semestre, e.cv, e.foto, e.universidad FROM postulacion p JOIN estudiante e ON p.id_estudiante = e.id_estudiante WHERE id_oferta = ?", input.IdOferta).Rows() + rows, err := configs.DB.Raw("SELECT p.id_estudiante, p.estado, e.nombre, e.apellido, e.nacimiento, e.foto, e.carrera, e.universidad FROM postulacion p JOIN estudiante e ON p.id_estudiante = e.id_estudiante WHERE id_oferta = ?", input.IdOferta).Rows() if err != nil { c.JSON(400, responses.StandardResponse{ Status: 400, @@ -413,18 +413,14 @@ func GetApplicants(c *gin.Context) { for rows.Next() { var idEstudiante string var estado string - var dpi string var nombre string var apellido string var nacimiento string - var correo string - var telefono string - var carrera int - var semestre int - var cv string - var foto string + var foto sql.NullString + var carrera string var universidad string - err := rows.Scan(&idEstudiante, &estado, &dpi, &nombre, &apellido, &nacimiento, &correo, &telefono, &carrera, &semestre, &cv, &foto, &universidad) + + err := rows.Scan(&idEstudiante, &estado, &nombre, &apellido, &nacimiento, &foto, &carrera, &universidad) if err != nil { c.JSON(400, responses.StandardResponse{ Status: 400, @@ -437,16 +433,10 @@ func GetApplicants(c *gin.Context) { result := map[string]interface{}{ "id_estudiante": idEstudiante, "estado": estado, - "dpi": dpi, "nombre": nombre, "apellido": apellido, "nacimiento": nacimiento, - "correo": correo, - "telefono": telefono, - "carrera": carrera, - "semestre": semestre, - "cv": cv, - "foto": foto, + "foto": foto.String, "universidad": universidad, } results = append(results, result) From fcc26c394b37b7cdcfd39e9796bc645a1548aee9 Mon Sep 17 00:00:00 2001 From: Mark Albrand Date: Wed, 4 Oct 2023 17:14:07 -0600 Subject: [PATCH 07/18] Data update --- backend/README.md | 50 +++++++++++++++++++++++++++++------------------ 1 file changed, 31 insertions(+), 19 deletions(-) diff --git a/backend/README.md b/backend/README.md index 608e4e9b..573bd22d 100644 --- a/backend/README.md +++ b/backend/README.md @@ -558,25 +558,37 @@ Retorna los estudiantes que se han postulado a una oferta #### Response ``` json { - "Status": "200", - "Message": "Applicants returned successfully", - "Data": [ - { - "apellido": "Albrand", - "carrera": 1, - "correo": "alb21004@uvg.edu.gt", - "cv": "cv", - "dpi": "2806089930101", - "estado": "Enviada", - "foto": "foto", - "id_estudiante": "alb21004@uvg.edu.gt", - "nacimiento": "2002-05-06T00:00:00Z", - "nombre": "Mark", - "semestre": 5, - "telefono": "58748587", - "universidad": "Universidad del Valle de Guatemala" - } - ] + "status": 200, + "message": "Applicants returned successfully", + "data": [ + { + "apellido": "Albrand", + "estado": "Enviada", + "foto": "alb21004_1504802402.jpg", + "id_estudiante": "alb21004@uvg.edu.gt", + "nacimiento": "2002-05-06T00:00:00Z", + "nombre": "Mark", + "universidad": "Universidad del Valle de Guatemala" + }, + { + "apellido": "Contreras Arroyave", + "estado": "enviada", + "foto": "", + "id_estudiante": "contrerasmarce@gmail.com", + "nacimiento": "2002-10-24T00:00:00Z", + "nombre": "Marcela", + "universidad": "Universidad Francisco Marroquín" + }, + { + "apellido": "Morales", + "estado": "En revisión", + "foto": "", + "id_estudiante": "mor21146@uvg.edu.gt", + "nacimiento": "2002-10-24T00:00:00Z", + "nombre": "Diego", + "universidad": "UVG" + } + ] } ``` From 853b716b26d1f2300252319594a1f8d2bb05a0f8 Mon Sep 17 00:00:00 2001 From: Mark Albrand Date: Wed, 4 Oct 2023 17:16:52 -0600 Subject: [PATCH 08/18] Update README.md --- backend/README.md | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/backend/README.md b/backend/README.md index 573bd22d..5541b8ad 100644 --- a/backend/README.md +++ b/backend/README.md @@ -547,6 +547,8 @@ Elimina una oferta de trabajo. También elimina cualquier postulación asociada ### [POST] api/offers/applicants Retorna los estudiantes que se han postulado a una oferta +> **Note** +> Auth required ## Params ``` json @@ -789,4 +791,4 @@ Suspende un usuario "message": "User suspended successfully", "data": null } -``` \ No newline at end of file +``` From 0b38925a84ea49963913a799beab38410e3b4148 Mon Sep 17 00:00:00 2001 From: Mark Albrand Date: Wed, 4 Oct 2023 17:24:12 -0600 Subject: [PATCH 09/18] Security corrections --- backend/controllers/offer.go | 41 ++++++++++++++++++++++++++++++------ 1 file changed, 35 insertions(+), 6 deletions(-) diff --git a/backend/controllers/offer.go b/backend/controllers/offer.go index 6a8499ed..1aeca645 100644 --- a/backend/controllers/offer.go +++ b/backend/controllers/offer.go @@ -4,9 +4,11 @@ import ( "backend/configs" "backend/models" "backend/responses" + "backend/utils" "database/sql" "fmt" "github.com/gin-gonic/gin" + "net/http" ) type OfferInput struct { @@ -387,6 +389,7 @@ func DeleteOffer(c *gin.Context) { func GetApplicants(c *gin.Context) { var input GetPostulationInput + var tokenUsername string if err := c.ShouldBindJSON(&input); err != nil { c.JSON(400, responses.StandardResponse{ @@ -397,12 +400,37 @@ func GetApplicants(c *gin.Context) { return } + tokenUsername, err := utils.ExtractTokenUsername(c) + + if err != nil { + c.JSON(http.StatusBadRequest, responses.StandardResponse{ + Status: http.StatusBadRequest, + Message: "Error extracting information from token. " + err.Error(), + Data: nil, + }) + return + } + + // Verificación con el token para que no se pueda ver las postulaciones de otras empresas + var offer models.Oferta + err = configs.DB.Where("id_oferta = ? AND id_empresa = ?", input.IdOferta, tokenUsername).First(&offer).Error + + if err != nil { + c.JSON(http.StatusForbidden, responses.StandardResponse{ + Status: http.StatusForbidden, + Message: "Error verifying ownership of the offer. " + err.Error(), + Data: nil, + }) + return + } + var results []map[string]interface{} rows, err := configs.DB.Raw("SELECT p.id_estudiante, p.estado, e.nombre, e.apellido, e.nacimiento, e.foto, e.carrera, e.universidad FROM postulacion p JOIN estudiante e ON p.id_estudiante = e.id_estudiante WHERE id_oferta = ?", input.IdOferta).Rows() + if err != nil { - c.JSON(400, responses.StandardResponse{ - Status: 400, + c.JSON(http.StatusBadRequest, responses.StandardResponse{ + Status: http.StatusBadRequest, Message: "Error getting postulations. " + err.Error(), Data: nil, }) @@ -421,9 +449,10 @@ func GetApplicants(c *gin.Context) { var universidad string err := rows.Scan(&idEstudiante, &estado, &nombre, &apellido, &nacimiento, &foto, &carrera, &universidad) + if err != nil { - c.JSON(400, responses.StandardResponse{ - Status: 400, + c.JSON(http.StatusBadRequest, responses.StandardResponse{ + Status: http.StatusBadRequest, Message: "Error scanning postulation row. " + err.Error(), Data: nil, }) @@ -442,8 +471,8 @@ func GetApplicants(c *gin.Context) { results = append(results, result) } - c.JSON(200, responses.PostulationResponse{ - Status: 200, + c.JSON(http.StatusOK, responses.PostulationResponse{ + Status: http.StatusOK, Message: "Applicants returned successfully", Data: results, }) From cdab90f9217dfff448fb97489dd80f855d082ed7 Mon Sep 17 00:00:00 2001 From: Mark Albrand Date: Wed, 4 Oct 2023 17:25:18 -0600 Subject: [PATCH 10/18] Security corrections --- backend/controllers/offer.go | 24 ++++++++++++++++++++++-- 1 file changed, 22 insertions(+), 2 deletions(-) diff --git a/backend/controllers/offer.go b/backend/controllers/offer.go index 1aeca645..fc8ed92a 100644 --- a/backend/controllers/offer.go +++ b/backend/controllers/offer.go @@ -275,7 +275,17 @@ func GetOfferByCompany(c *gin.Context) { }) return } - defer rows.Close() + defer func(rows *sql.Rows) { + err := rows.Close() + if err != nil { + c.JSON(http.StatusBadRequest, responses.StandardResponse{ + Status: http.StatusBadRequest, + Message: "Error closing rows: " + err.Error(), + Data: nil, + }) + return + } + }(rows) // Create a map to store offer details and associated career IDs offerMap := make(map[int]GetOfferByCompanyResponse) @@ -436,7 +446,17 @@ func GetApplicants(c *gin.Context) { }) return } - defer rows.Close() + defer func(rows *sql.Rows) { + err := rows.Close() + if err != nil { + c.JSON(http.StatusBadRequest, responses.StandardResponse{ + Status: http.StatusBadRequest, + Message: "Error closing postulation rows. " + err.Error(), + Data: nil, + }) + return + } + }(rows) for rows.Next() { var idEstudiante string From bc30a84f38c84639a8efa8bc6629c82002a637f8 Mon Sep 17 00:00:00 2001 From: Mark Albrand Date: Thu, 5 Oct 2023 21:42:25 -0600 Subject: [PATCH 11/18] File server --- backend/controllers/files.go | 34 +++++++++++++++++++- backend/utils/file.go | 60 ++++++++++++++++++++++++++++++++++++ 2 files changed, 93 insertions(+), 1 deletion(-) diff --git a/backend/controllers/files.go b/backend/controllers/files.go index d4ac0420..21abe1a5 100644 --- a/backend/controllers/files.go +++ b/backend/controllers/files.go @@ -50,6 +50,8 @@ func UpdateProfilePicture() gin.HandlerFunc { randomNumber := rand.Intn(9999999999-1111111111) + 1111111111 newFileName := user_stripped + "_" + fmt.Sprint(randomNumber) + "." + fileType + file.Filename = newFileName + dst := "./uploads/" + newFileName fmt.Println("File: " + dst) @@ -81,9 +83,39 @@ func UpdateProfilePicture() gin.HandlerFunc { err = configs.DB.Model(&models.Empresa{}).Where("correo = ?", user).Updates(models.Empresa{Foto: newFileName}).Error } - // Upload the file to specific dst. + // if directory does not exist, create it + if _, err := os.Stat("./uploads"); os.IsNotExist(err) { + err := os.Mkdir("./uploads", 0755) + if err != nil { + c.JSON(http.StatusInternalServerError, responses.StandardResponse{ + Status: http.StatusInternalServerError, + Message: "Failed to create directory: " + err.Error(), + Data: nil, + }) + return + } + } + err = c.SaveUploadedFile(file, dst) if err != nil { + c.JSON(http.StatusInternalServerError, responses.StandardResponse{ + Status: http.StatusInternalServerError, + Message: "Failed to save file: " + err.Error(), + Data: nil, + }) + return + } + + // send the file via HTTP to the file server + url := "http://ec2-13-57-42-212.us-west-1.compute.amazonaws.com/upload/" + bearerToken := "Bearer " + utils.ExtractToken(c) + + if err := utils.UploadFileToServer(url, bearerToken, file, dst); err != nil { + c.JSON(http.StatusInternalServerError, responses.StandardResponse{ + Status: http.StatusInternalServerError, + Message: "Failed to upload file to server: " + err.Error(), + Data: nil, + }) return } diff --git a/backend/utils/file.go b/backend/utils/file.go index 5638317c..805db563 100644 --- a/backend/utils/file.go +++ b/backend/utils/file.go @@ -1,5 +1,14 @@ package utils +import ( + "bytes" + "fmt" + "io/ioutil" + "mime/multipart" + "net/http" + "path/filepath" +) + func Contains(slice []string, item string) bool { for _, s := range slice { if s == item { @@ -9,3 +18,54 @@ func Contains(slice []string, item string) bool { return false } + +func UploadFileToServer(url string, bearer string, file *multipart.FileHeader, dst string) error { + // open file + f, err := file.Open() + if err != nil { + return err + } + defer f.Close() + + // read file + fileBytes, err := ioutil.ReadAll(f) + if err != nil { + return err + } + + // create new request body + body := new(bytes.Buffer) + writer := multipart.NewWriter(body) + part, err := writer.CreateFormFile("file", filepath.Base(dst)) + if err != nil { + return err + } + part.Write(fileBytes) + writer.Close() + + // create new request + req, err := http.NewRequest("POST", url, body) + if err != nil { + return err + } + req.Header.Add("Authorization", bearer) + req.Header.Add("Content-Type", writer.FormDataContentType()) + + // send request + client := &http.Client{} + res, err := client.Do(req) + if err != nil { + return err + } + defer res.Body.Close() + + // read response + responseBody, err := ioutil.ReadAll(res.Body) + if err != nil { + return err + } + + fmt.Println(string(responseBody)) + + return nil +} From 676d86643808a1d9e6998405f35f6301f5dfeda5 Mon Sep 17 00:00:00 2001 From: Mark Albrand Date: Thu, 5 Oct 2023 21:48:50 -0600 Subject: [PATCH 12/18] File server --- backend/utils/file.go | 30 +++++++++++++++++++----------- 1 file changed, 19 insertions(+), 11 deletions(-) diff --git a/backend/utils/file.go b/backend/utils/file.go index 805db563..439ae5e9 100644 --- a/backend/utils/file.go +++ b/backend/utils/file.go @@ -3,7 +3,7 @@ package utils import ( "bytes" "fmt" - "io/ioutil" + "io" "mime/multipart" "net/http" "path/filepath" @@ -20,30 +20,38 @@ func Contains(slice []string, item string) bool { } func UploadFileToServer(url string, bearer string, file *multipart.FileHeader, dst string) error { - // open file + // Abrir el archivo f, err := file.Open() if err != nil { return err } defer f.Close() - // read file - fileBytes, err := ioutil.ReadAll(f) + // Leer el archivo + fileBytes, err := io.ReadAll(f) if err != nil { return err } - // create new request body + // Crear el body de la request body := new(bytes.Buffer) writer := multipart.NewWriter(body) part, err := writer.CreateFormFile("file", filepath.Base(dst)) if err != nil { return err } - part.Write(fileBytes) - writer.Close() - // create new request + _, err = part.Write(fileBytes) + if err != nil { + return err + } + + err = writer.Close() + if err != nil { + return err + } + + // Crear la request req, err := http.NewRequest("POST", url, body) if err != nil { return err @@ -51,7 +59,7 @@ func UploadFileToServer(url string, bearer string, file *multipart.FileHeader, d req.Header.Add("Authorization", bearer) req.Header.Add("Content-Type", writer.FormDataContentType()) - // send request + // Hacer la request client := &http.Client{} res, err := client.Do(req) if err != nil { @@ -59,8 +67,8 @@ func UploadFileToServer(url string, bearer string, file *multipart.FileHeader, d } defer res.Body.Close() - // read response - responseBody, err := ioutil.ReadAll(res.Body) + // Leer el body de la respuesta + responseBody, err := io.ReadAll(res.Body) if err != nil { return err } From aca2e50a9d05fb5c4b32a412c1e1cc3b83619957 Mon Sep 17 00:00:00 2001 From: Mark Albrand Date: Thu, 5 Oct 2023 22:02:50 -0600 Subject: [PATCH 13/18] File server --- backend/controllers/files.go | 2 ++ 1 file changed, 2 insertions(+) diff --git a/backend/controllers/files.go b/backend/controllers/files.go index 21abe1a5..3aaeedf4 100644 --- a/backend/controllers/files.go +++ b/backend/controllers/files.go @@ -85,6 +85,7 @@ func UpdateProfilePicture() gin.HandlerFunc { // if directory does not exist, create it if _, err := os.Stat("./uploads"); os.IsNotExist(err) { + fmt.Println("Creating directory") err := os.Mkdir("./uploads", 0755) if err != nil { c.JSON(http.StatusInternalServerError, responses.StandardResponse{ @@ -96,6 +97,7 @@ func UpdateProfilePicture() gin.HandlerFunc { } } + // Save locally err = c.SaveUploadedFile(file, dst) if err != nil { c.JSON(http.StatusInternalServerError, responses.StandardResponse{ From c6e964c8b409e6ef6247f2b5deea42a291f88791 Mon Sep 17 00:00:00 2001 From: Mark Albrand Date: Thu, 5 Oct 2023 22:13:30 -0600 Subject: [PATCH 14/18] Koyeb --- backend/configs/setup.go | 13 +++++++++++++ backend/controllers/files.go | 14 -------------- backend/main.go | 5 +++++ 3 files changed, 18 insertions(+), 14 deletions(-) diff --git a/backend/configs/setup.go b/backend/configs/setup.go index 4bdbbf45..2d856d63 100644 --- a/backend/configs/setup.go +++ b/backend/configs/setup.go @@ -4,6 +4,7 @@ import ( "fmt" "github.com/jinzhu/gorm" _ "github.com/jinzhu/gorm/dialects/postgres" + "os" ) var DB *gorm.DB @@ -19,3 +20,15 @@ func SetupDB() { fmt.Println("Connected to DB") DB = db } + +func CreateDirIfNotExist(path string) (bool, error) { + if _, err := os.Stat(path); os.IsNotExist(err) { + fmt.Println("Directory " + path + " does not exist. Creating...") + err := os.Mkdir(path, 0777) + if err != nil { + return false, err + } + return true, nil + } + return false, nil +} diff --git a/backend/controllers/files.go b/backend/controllers/files.go index 3aaeedf4..3509aeb7 100644 --- a/backend/controllers/files.go +++ b/backend/controllers/files.go @@ -83,20 +83,6 @@ func UpdateProfilePicture() gin.HandlerFunc { err = configs.DB.Model(&models.Empresa{}).Where("correo = ?", user).Updates(models.Empresa{Foto: newFileName}).Error } - // if directory does not exist, create it - if _, err := os.Stat("./uploads"); os.IsNotExist(err) { - fmt.Println("Creating directory") - err := os.Mkdir("./uploads", 0755) - if err != nil { - c.JSON(http.StatusInternalServerError, responses.StandardResponse{ - Status: http.StatusInternalServerError, - Message: "Failed to create directory: " + err.Error(), - Data: nil, - }) - return - } - } - // Save locally err = c.SaveUploadedFile(file, dst) if err != nil { diff --git a/backend/main.go b/backend/main.go index c78b2666..942d547e 100644 --- a/backend/main.go +++ b/backend/main.go @@ -7,6 +7,11 @@ import ( ) func main() { + // create directory for uploads + if _, err := configs.CreateDirIfNotExist("./uploads"); err != nil { + panic(err) + } + router := gin.Default() router.Use(CORS()) From 5b3bdbb58c77945fb085bbbdae0a45e93a38ad8c Mon Sep 17 00:00:00 2001 From: Mark Albrand Date: Sat, 7 Oct 2023 13:29:46 -0600 Subject: [PATCH 15/18] Debug --- backend/controllers/offer.go | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/backend/controllers/offer.go b/backend/controllers/offer.go index fc8ed92a..9cb216b7 100644 --- a/backend/controllers/offer.go +++ b/backend/controllers/offer.go @@ -422,6 +422,10 @@ func GetApplicants(c *gin.Context) { } // Verificación con el token para que no se pueda ver las postulaciones de otras empresas + // TODO Eliminar debug + fmt.Println("tokenUsername: ", tokenUsername) + fmt.Println("input.IdOferta: ", input.IdOferta) + var offer models.Oferta err = configs.DB.Where("id_oferta = ? AND id_empresa = ?", input.IdOferta, tokenUsername).First(&offer).Error From 977230ae16448b8df7ff1671ea14f909fff59d24 Mon Sep 17 00:00:00 2001 From: Mark Albrand Date: Sat, 7 Oct 2023 13:35:45 -0600 Subject: [PATCH 16/18] Debug --- backend/controllers/offer.go | 4 ---- 1 file changed, 4 deletions(-) diff --git a/backend/controllers/offer.go b/backend/controllers/offer.go index 9cb216b7..fc8ed92a 100644 --- a/backend/controllers/offer.go +++ b/backend/controllers/offer.go @@ -422,10 +422,6 @@ func GetApplicants(c *gin.Context) { } // Verificación con el token para que no se pueda ver las postulaciones de otras empresas - // TODO Eliminar debug - fmt.Println("tokenUsername: ", tokenUsername) - fmt.Println("input.IdOferta: ", input.IdOferta) - var offer models.Oferta err = configs.DB.Where("id_oferta = ? AND id_empresa = ?", input.IdOferta, tokenUsername).First(&offer).Error From eeed8dc9874d6bacd6a339097fc417eaa8292626 Mon Sep 17 00:00:00 2001 From: Mark Albrand Date: Sat, 7 Oct 2023 17:38:31 -0600 Subject: [PATCH 17/18] New file delivery --- backend/configs/setup.go | 1 + backend/controllers/files.go | 52 ++++++++++++++++++++++++++++++++++-- 2 files changed, 51 insertions(+), 2 deletions(-) diff --git a/backend/configs/setup.go b/backend/configs/setup.go index 2d856d63..a66ddcde 100644 --- a/backend/configs/setup.go +++ b/backend/configs/setup.go @@ -8,6 +8,7 @@ import ( ) var DB *gorm.DB +var FileServer = "http://ec2-13-57-42-212.us-west-1.compute.amazonaws.com/files/" func SetupDB() { envs := EnvPG() diff --git a/backend/controllers/files.go b/backend/controllers/files.go index 3509aeb7..9f7f3cfd 100644 --- a/backend/controllers/files.go +++ b/backend/controllers/files.go @@ -7,6 +7,7 @@ import ( "backend/utils" "fmt" "github.com/gin-gonic/gin" + "io" "math/rand" "net/http" "os" @@ -140,7 +141,54 @@ func deleteFilesWithPrefix(directory, prefix string) error { func GetFile() gin.HandlerFunc { return func(c *gin.Context) { filename := c.Param("filename") - filePath := "./uploads/" + filename - c.File(filePath) // Esto sirve el archivo al cliente + fileURL := configs.FileServer + filename + + // Realizar una solicitud GET al servidor de archivos externo + resp, err := http.Get(fileURL) + if err != nil { + c.JSON(http.StatusNotFound, responses.StandardResponse{ + Status: http.StatusNotFound, + Message: "Error al obtener el archivo: " + err.Error(), + Data: nil, + }) + return + } + + defer func(Body io.ReadCloser) { + err := Body.Close() + if err != nil { + c.JSON(http.StatusInternalServerError, responses.StandardResponse{ + Status: http.StatusInternalServerError, + Message: "Error al cerrar el cuerpo de la respuesta del servidor de archivos externo: " + err.Error(), + Data: nil, + }) + return + } + }(resp.Body) + + if resp.StatusCode != http.StatusOK { + c.JSON(http.StatusNotFound, responses.StandardResponse{ + Status: http.StatusNotFound, + Message: "Archivo no encontrado en el servidor de archivos externo", + Data: nil, + }) + return + } + + // Configurar las cabeceras de la respuesta para el cliente + c.Header("Content-Type", resp.Header.Get("Content-Type")) + c.Header("Content-Disposition", "inline; filename="+filename) + c.Header("Content-Length", resp.Header.Get("Content-Length")) + + // Copiar el cuerpo de la respuesta del servidor de archivos al cuerpo de la respuesta de Gin + _, err = io.Copy(c.Writer, resp.Body) + if err != nil { + c.JSON(http.StatusInternalServerError, responses.StandardResponse{ + Status: http.StatusInternalServerError, + Message: "Error al copiar el cuerpo de la respuesta del servidor de archivos externo: " + err.Error(), + Data: nil, + }) + return + } } } From 05f9dd4dfa46c48af1009d8d4635ab55dbe01c2b Mon Sep 17 00:00:00 2001 From: Mark Albrand Date: Sat, 7 Oct 2023 17:39:08 -0600 Subject: [PATCH 18/18] New file delivery --- backend/routes/routes_handler.go | 1 - 1 file changed, 1 deletion(-) diff --git a/backend/routes/routes_handler.go b/backend/routes/routes_handler.go index 39388644..4c10d9c8 100644 --- a/backend/routes/routes_handler.go +++ b/backend/routes/routes_handler.go @@ -50,7 +50,6 @@ func Routes(router *gin.Engine) { public.GET("/careers", controllers.GetCareers) // Empresas - // Ale: Use "company" porque el mamark quería que fuera en inglés :) companies := router.Group("api/companies") companies.Use(middlewares.JwtAuthentication())