From 0f5329cee8330d3fd6973a4485eadc87948e0b56 Mon Sep 17 00:00:00 2001 From: AkshatIITk Date: Thu, 27 Jun 2024 03:47:55 +0530 Subject: [PATCH 1/7] Added PVF routes and handlers (Student Side) --- application/config.go | 2 +- application/db.pvf.go | 27 ++++++++++++++++++ application/model.go | 15 ++++++++++ application/router.go | 3 ++ application/student.pvf.go | 57 ++++++++++++++++++++++++++++++++++++++ 5 files changed, 103 insertions(+), 1 deletion(-) create mode 100644 application/db.pvf.go create mode 100644 application/student.pvf.go diff --git a/application/config.go b/application/config.go index 7703b3e..d2b9c36 100644 --- a/application/config.go +++ b/application/config.go @@ -33,7 +33,7 @@ func openConnection() { db = database err = db.AutoMigrate(&Proforma{}, &ApplicationQuestion{}, &ApplicationQuestionAnswer{}, - &ProformaEvent{}, &EventCoordinator{}, &EventStudent{}, &ApplicationResume{}) + &ProformaEvent{}, &EventCoordinator{}, &EventStudent{}, &ApplicationResume{}, &PVF{}) if err != nil { logrus.Fatal("Failed to migrate application database: ", err) panic(err) diff --git a/application/db.pvf.go b/application/db.pvf.go new file mode 100644 index 0000000..83d7df7 --- /dev/null +++ b/application/db.pvf.go @@ -0,0 +1,27 @@ +package application + +import "github.com/gin-gonic/gin" + +func createPVF(ctx *gin.Context, pvf *PVF) error { + tx := db.WithContext(ctx).Create(pvf) + return tx.Error +} + +func fetchPvfForStudent(ctx *gin.Context, sid uint, rid uint, jps *[]PVF) error { + tx := db.WithContext(ctx). + // Where("student_recruitment_cycle_id = ? AND recruitment_cycle_id = ?", sid, rid). + Select( + "id", + "company_university_name", + "role", + "duration", + "description", + "mentor_name", + "mentor_designation", + "mentor_email", + "is_verified", + ). + Order("id ASC"). + Find(jps) + return tx.Error +} diff --git a/application/model.go b/application/model.go index 80fbd43..136804b 100644 --- a/application/model.go +++ b/application/model.go @@ -126,3 +126,18 @@ type ApplicationResume struct { ResumeID uint `json:"resume_id"` Resume string `json:"resume"` } + +type PVF struct { + gorm.Model + StudentRecruitmentCycleID uint `json:"student_recruitment_cycle_id" gorm:"index;->;<-:create"` + CompanyUniversityName string `json:"company_university_name"` + Role string `json:"role"` + Duration string `json:"duration"` + Description string `json:"description"` + MentorName string `json:"mentor_name"` + MentorDesignation string `json:"mentor_designation"` + MentorEmail string `json:"mentor_email"` + IsApproved sql.NullBool `json:"is_approved" gorm:"index;default:NULL"` + IsVerified sql.NullBool `json:"is_verified" gorm:"index;default:NULL"` + RecruitmentCycleID uint `json:"recruitment_cycle_id" gorm:"index;->;<-:create"` +} diff --git a/application/router.go b/application/router.go index 1824690..88942a7 100644 --- a/application/router.go +++ b/application/router.go @@ -61,6 +61,9 @@ func StudentRouter(mail_channel chan mail.Mail, r *gin.Engine) { student.GET("/proforma/:pid", getProformaForStudentHandler) student.GET("/proforma/:pid/event", getEventsByProformaForStudentHandler) + student.POST("/pvf", postPvfForStudentHandler) + student.GET("/pvf", getPvfForStudentHandler) + student.GET("/opening", getProformasForEligibleStudentHandler) student.GET("/opening/:pid", getApplicationHandler) student.POST("/opening/:pid", postApplicationHandler(mail_channel)) diff --git a/application/student.pvf.go b/application/student.pvf.go new file mode 100644 index 0000000..c2fb8e2 --- /dev/null +++ b/application/student.pvf.go @@ -0,0 +1,57 @@ +package application + +import ( + "net/http" + + "github.com/gin-gonic/gin" + "github.com/sirupsen/logrus" + "github.com/spo-iitk/ras-backend/middleware" + "github.com/spo-iitk/ras-backend/util" +) + +func postPvfForStudentHandler(ctx *gin.Context) { + sid := getStudentRCID(ctx) + if sid == 0 { + ctx.AbortWithStatusJSON(http.StatusBadRequest, gin.H{"error": "SRCID not found"}) + return + } + var pvf PVF + err := ctx.ShouldBindJSON(&pvf) + if err != nil { + ctx.AbortWithStatusJSON(http.StatusBadRequest, gin.H{"error": err.Error()}) + return + } + pvf.StudentRecruitmentCycleID = sid + err = createPVF(ctx, &pvf) + if err != nil { + ctx.AbortWithStatusJSON(http.StatusBadRequest, gin.H{"error": err.Error()}) + return + } + user := middleware.GetUserID(ctx) + + logrus.Infof("%v created \a proforma with id %d", user, pvf.ID) + ctx.JSON(http.StatusOK, gin.H{"pid": pvf.ID}) + +} + +func getPvfForStudentHandler(ctx *gin.Context) { + sid := getStudentRCID(ctx) + if sid == 0 { + ctx.AbortWithStatusJSON(http.StatusBadRequest, gin.H{"error": "SRCID not found"}) + return + } + rid, err := util.ParseUint(ctx.Param("rid")) + if err != nil { + ctx.AbortWithStatusJSON(http.StatusBadRequest, gin.H{"error": err.Error()}) + return + } + var jps []PVF + err = fetchPvfForStudent(ctx, sid, rid, &jps) + if err != nil { + ctx.AbortWithStatusJSON(http.StatusBadRequest, gin.H{"error": err.Error()}) + return + } + + ctx.JSON(http.StatusOK, jps) + +} From c1fabf644758f07e3a0dedcd26abeca27e56500b Mon Sep 17 00:00:00 2001 From: AkshatIITk Date: Wed, 3 Jul 2024 17:24:05 +0530 Subject: [PATCH 2/7] added new verification server, routes and its handlers --- application/db.pvf.go | 66 ++++++++++++++- application/model.go | 1 + application/router.go | 13 ++- application/student.pvf.go | 26 +++++- application/util.pvf_verification.go | 1 + application/verify.pvf.go | 120 +++++++++++++++++++++++++++ cmd/main.go | 3 + cmd/verification.go | 31 +++++++ config.yaml | 3 +- container/nginx.conf | 3 + middleware/jwt.go | 45 +++++++++- student/admin.update.go | 6 +- 12 files changed, 306 insertions(+), 12 deletions(-) create mode 100644 application/util.pvf_verification.go create mode 100644 application/verify.pvf.go create mode 100644 cmd/verification.go diff --git a/application/db.pvf.go b/application/db.pvf.go index 83d7df7..392d334 100644 --- a/application/db.pvf.go +++ b/application/db.pvf.go @@ -1,15 +1,28 @@ package application -import "github.com/gin-gonic/gin" +import ( + "github.com/gin-gonic/gin" + "gorm.io/gorm/clause" +) func createPVF(ctx *gin.Context, pvf *PVF) error { tx := db.WithContext(ctx).Create(pvf) return tx.Error } -func fetchPvfForStudent(ctx *gin.Context, sid uint, rid uint, jps *[]PVF) error { +func fetchPVF(ctx *gin.Context, pid uint, jp *PVF) error { + tx := db.WithContext(ctx).Where("id = ?", pid).First(jp) + return tx.Error +} + +func updatePVF(ctx *gin.Context, jp *PVF) error { + tx := db.WithContext(ctx).Where("id = ?", jp.ID).Updates(jp) + return tx.Error +} + +func fetchAllPvfForStudent(ctx *gin.Context, sid uint, rid uint, jps *[]PVF) error { tx := db.WithContext(ctx). - // Where("student_recruitment_cycle_id = ? AND recruitment_cycle_id = ?", sid, rid). + Where("student_recruitment_cycle_id = ? AND recruitment_cycle_id = ?", sid, rid). Select( "id", "company_university_name", @@ -20,8 +33,55 @@ func fetchPvfForStudent(ctx *gin.Context, sid uint, rid uint, jps *[]PVF) error "mentor_designation", "mentor_email", "is_verified", + "file_name", ). Order("id ASC"). Find(jps) return tx.Error } +func fetchPvfForStudent(ctx *gin.Context, sid uint, rid uint, pid uint, jps *[]PVF) error { + tx := db.WithContext(ctx). + Where("student_recruitment_cycle_id = ? AND recruitment_cycle_id = ? AND id = ?", sid, rid, pid). + Select( + "id", + "company_university_name", + "role", + "duration", + "description", + "mentor_name", + "mentor_designation", + "mentor_email", + "is_verified", + "file_name", + ). + Find(jps) + return tx.Error +} + +func fetchPvfForVerification(ctx *gin.Context, id uint, rid uint, jps *PVF) error { + tx := db.WithContext(ctx). + Where("id = ? AND recruitment_cycle_id = ?", id, rid). + Select( + "id", + "company_university_name", + "role", + "duration", + "description", + "mentor_name", + "mentor_designation", + "mentor_email", + "is_verified", + "is_approved", + "file_name", + ). + Find(jps) + return tx.Error +} + +func verifyPvf(ctx *gin.Context, pvf *PVF) (bool, error) { + tx := db.WithContext(ctx).Model(&pvf). + Clauses(clause.Returning{}). + Where("id = ?", pvf.ID). + Updates(map[string]interface{}{"is_verified": pvf.IsVerified}) + return tx.RowsAffected > 0, tx.Error +} diff --git a/application/model.go b/application/model.go index 136804b..bdd0abb 100644 --- a/application/model.go +++ b/application/model.go @@ -140,4 +140,5 @@ type PVF struct { IsApproved sql.NullBool `json:"is_approved" gorm:"index;default:NULL"` IsVerified sql.NullBool `json:"is_verified" gorm:"index;default:NULL"` RecruitmentCycleID uint `json:"recruitment_cycle_id" gorm:"index;->;<-:create"` + FileName string `json:"filename"` } diff --git a/application/router.go b/application/router.go index 88942a7..7d2d177 100644 --- a/application/router.go +++ b/application/router.go @@ -62,7 +62,8 @@ func StudentRouter(mail_channel chan mail.Mail, r *gin.Engine) { student.GET("/proforma/:pid/event", getEventsByProformaForStudentHandler) student.POST("/pvf", postPvfForStudentHandler) - student.GET("/pvf", getPvfForStudentHandler) + student.GET("/pvf", getAllPvfForStudentHandler) + student.GET("/pvf/:pid", getPvfForStudentHandler) student.GET("/opening", getProformasForEligibleStudentHandler) student.GET("/opening/:pid", getApplicationHandler) @@ -99,3 +100,13 @@ func CompanyRouter(r *gin.Engine) { company.GET("/event/:eid/student", getStudentsByEventForCompanyHandler) } } + +func PvfVerificationRouter(r *gin.Engine) { + pvf := r.Group("/api/verification/application/rc/:rid") + pvf.Use() + { + pvf.GET("/pvf/:pid", getPvfForVerificationHandler) + pvf.PUT("pvf/:pid/verify", verifyPvfHandler) + pvf.PUT("/pvf", putPVFHandler) + } +} diff --git a/application/student.pvf.go b/application/student.pvf.go index c2fb8e2..837c748 100644 --- a/application/student.pvf.go +++ b/application/student.pvf.go @@ -29,12 +29,12 @@ func postPvfForStudentHandler(ctx *gin.Context) { } user := middleware.GetUserID(ctx) - logrus.Infof("%v created \a proforma with id %d", user, pvf.ID) + logrus.Infof("%v created \a pvf with id %d", user, pvf.ID) ctx.JSON(http.StatusOK, gin.H{"pid": pvf.ID}) } -func getPvfForStudentHandler(ctx *gin.Context) { +func getAllPvfForStudentHandler(ctx *gin.Context) { sid := getStudentRCID(ctx) if sid == 0 { ctx.AbortWithStatusJSON(http.StatusBadRequest, gin.H{"error": "SRCID not found"}) @@ -46,7 +46,7 @@ func getPvfForStudentHandler(ctx *gin.Context) { return } var jps []PVF - err = fetchPvfForStudent(ctx, sid, rid, &jps) + err = fetchAllPvfForStudent(ctx, sid, rid, &jps) if err != nil { ctx.AbortWithStatusJSON(http.StatusBadRequest, gin.H{"error": err.Error()}) return @@ -55,3 +55,23 @@ func getPvfForStudentHandler(ctx *gin.Context) { ctx.JSON(http.StatusOK, jps) } +func getPvfForStudentHandler(ctx *gin.Context) { + rid, err := util.ParseUint(ctx.Param("rid")) + if err != nil { + ctx.AbortWithStatusJSON(http.StatusBadRequest, gin.H{"error": err.Error()}) + return + } + id, err := util.ParseUint(ctx.Param("pid")) + if err != nil { + ctx.AbortWithStatusJSON(http.StatusBadRequest, gin.H{"error": err.Error()}) + return + } + var jps PVF + err = fetchPvfForVerification(ctx, id, rid, &jps) + if err != nil { + ctx.AbortWithStatusJSON(http.StatusBadRequest, gin.H{"error": err.Error()}) + return + } + + ctx.JSON(http.StatusOK, jps) +} diff --git a/application/util.pvf_verification.go b/application/util.pvf_verification.go new file mode 100644 index 0000000..b584a8a --- /dev/null +++ b/application/util.pvf_verification.go @@ -0,0 +1 @@ +package application diff --git a/application/verify.pvf.go b/application/verify.pvf.go new file mode 100644 index 0000000..3574948 --- /dev/null +++ b/application/verify.pvf.go @@ -0,0 +1,120 @@ +package application + +import ( + "net/http" + + "github.com/gin-gonic/gin" + "github.com/spo-iitk/ras-backend/util" +) + +func getPvfForVerificationHandler(ctx *gin.Context) { + rid, err := util.ParseUint(ctx.Param("rid")) + if err != nil { + ctx.AbortWithStatusJSON(http.StatusBadRequest, gin.H{"error": err.Error()}) + return + } + id, err := util.ParseUint("1") // id to be upadated + if err != nil { + ctx.AbortWithStatusJSON(http.StatusBadRequest, gin.H{"error": err.Error()}) + return + } + var jps PVF + err = fetchPvfForVerification(ctx, id, rid, &jps) + if err != nil { + ctx.AbortWithStatusJSON(http.StatusBadRequest, gin.H{"error": err.Error()}) + return + } + + ctx.JSON(http.StatusOK, jps) +} + +func verifyPvfHandler(ctx *gin.Context) { + // var verifyPvfRequest PVF + // if err := ctx.ShouldBindJSON(&verifyPvfRequest); err != nil { + // ctx.AbortWithStatusJSON(http.StatusBadRequest, gin.H{"error": err.Error()}) + // return + // } + // pid, err := util.ParseUint(ctx.Param("pid")) + + // if err != nil { + // ctx.AbortWithStatusJSON(http.StatusBadRequest, gin.H{"error": err.Error()}) + // return + // } + + // verifyPvfRequest.ID = pid + // updated, err := verifyPvf(ctx, &verifyPvfRequest) + // if err != nil { + // ctx.AbortWithStatusJSON(http.StatusBadRequest, gin.H{"error": err.Error()}) + // return + // } + + // if !updated { + // ctx.AbortWithStatusJSON(http.StatusNotFound, gin.H{"error": "PVF not found"}) + // return + // } + + // if verifyPvfRequest.IsVerified { + // logrus.Infof("A PVF with id %d is verified", verifyPvfRequest.ID) + // ctx.JSON(http.StatusOK, gin.H{"status": "Successfully verified"}) + // } else { + // logrus.Infof("A PVF with id %d is unverified", verifyPvfRequest.ID) + // ctx.JSON(http.StatusOK, gin.H{"status": "Successfully unverified"}) + // } + +} + +func putPVFHandler(ctx *gin.Context) { + var jp PVF + + err := ctx.ShouldBindJSON(&jp) + if err != nil { + ctx.AbortWithStatusJSON(http.StatusBadRequest, gin.H{"error": err.Error()}) + return + } + + if jp.ID == 0 { + ctx.AbortWithStatusJSON(http.StatusBadRequest, gin.H{"error": "id is required"}) + return + } + + var oldJp PVF + err = fetchPVF(ctx, jp.ID, &oldJp) + if err != nil { + ctx.AbortWithStatusJSON(http.StatusBadRequest, gin.H{"error": err.Error()}) + return + } + + // jp.ActionTakenBy = middleware.GetUserID(ctx) + + // publishNotice := oldJp.Deadline == 0 && jp.Deadline != 0 + + err = updatePVF(ctx, &jp) + if err != nil { + ctx.AbortWithStatusJSON(http.StatusBadRequest, gin.H{"error": err.Error()}) + return + } + ctx.JSON(http.StatusOK, gin.H{"status": "Updated PVF with id " + util.ParseString(jp.ID)}) + // user := middleware.GetUserID(ctx) + + // logrus.Infof("%v edited a proforma with id %d", user, jp.ID) + + // if publishNotice { + // logrus.Infof("%v published a proforma with id %d", user, jp.ID) + + // err = rc.CreateNotice(ctx, oldJp.RecruitmentCycleID, &rc.Notice{ + // Title: fmt.Sprintf("[%s] | New Job Opening for %s", jp.CompanyName, jp.Profile), + // Description: fmt.Sprintf( + // "A new opening has been created for the profile of %s in the company %s", + // jp.Profile, jp.CompanyName), + // Tags: fmt.Sprintf("opening,%s,%s", jp.Role, jp.CompanyName), + // }) + // if err != nil { + // ctx.AbortWithStatusJSON(http.StatusBadRequest, gin.H{"error": err.Error()}) + // return + // } + + // ctx.JSON(http.StatusOK, gin.H{"status": "Proforma with id " + util.ParseString(jp.ID) + " has been published"}) + // } else { + // ctx.JSON(http.StatusOK, gin.H{"status": "Updated proforma with id " + util.ParseString(jp.ID)}) + // } +} diff --git a/cmd/main.go b/cmd/main.go index 197be7b..97c6a0a 100644 --- a/cmd/main.go +++ b/cmd/main.go @@ -54,6 +54,9 @@ func main() { g.Go(func() error { return adminCompanyServer().ListenAndServe() }) + g.Go(func() error { + return verificationServer().ListenAndServe() + }) log.Println("Starting Server...") if err := g.Wait(); err != nil { diff --git a/cmd/verification.go b/cmd/verification.go new file mode 100644 index 0000000..3b5e689 --- /dev/null +++ b/cmd/verification.go @@ -0,0 +1,31 @@ +package main + +import ( + "fmt" + "net/http" + + "github.com/gin-gonic/gin" + "github.com/spf13/viper" + "github.com/spo-iitk/ras-backend/application" + "github.com/spo-iitk/ras-backend/middleware" +) + +func verificationServer() *http.Server { + PORT := viper.GetString("PORT.VERIFICATION") + fmt.Print(PORT) + engine := gin.New() + engine.Use(middleware.CORS()) + engine.Use(gin.CustomRecovery(recoveryHandler)) + engine.Use(gin.Logger()) + + application.PvfVerificationRouter(engine) + + server := &http.Server{ + Addr: ":" + PORT, + Handler: engine, + ReadTimeout: readTimeout, + WriteTimeout: writeTimeout, + } + + return server +} diff --git a/config.yaml b/config.yaml index 976b211..eaaca43 100644 --- a/config.yaml +++ b/config.yaml @@ -24,7 +24,8 @@ PORT: AUTH: 3475 STUDENT: 3480 COMPANY: 3485 - ADMIN: + VERIFICATION: 3505 + ADMIN: RC: 3490 APP: 3492 COMPANY: 3495 diff --git a/container/nginx.conf b/container/nginx.conf index 639d06f..77ae74f 100644 --- a/container/nginx.conf +++ b/container/nginx.conf @@ -32,4 +32,7 @@ server{ location /api/admin/student { proxy_pass http://localhost:3500; } + location /api/verification { + proxy_pass http://localhost:3505; + } } \ No newline at end of file diff --git a/middleware/jwt.go b/middleware/jwt.go index 5f07f10..1aff5cb 100644 --- a/middleware/jwt.go +++ b/middleware/jwt.go @@ -6,7 +6,6 @@ import ( "github.com/golang-jwt/jwt" "github.com/spf13/viper" - _ "github.com/spo-iitk/ras-backend/config" ) var ( @@ -20,6 +19,11 @@ type CustomClaims struct { RoleID uint `json:"role_id"` jwt.StandardClaims } +type CustomPVFClaims struct { + email string `json:"email"` + pid uint `json:"pid"` + jwt.StandardClaims +} func init() { jwtExpirationLong = viper.GetInt("JWT.EXPIRATION.LONG") @@ -65,3 +69,42 @@ func validateToken(encodedToken string) (string, uint, error) { return claims.UserID, claims.RoleID, nil } + +func GeneratePVFToken(email string, pid uint, long bool) (string, error) { + var jwtExpiration int + if long { + jwtExpiration = jwtExpirationLong + } else { + jwtExpiration = jwtExpirationShort + } + + claims := CustomPVFClaims{ + email, + pid, + jwt.StandardClaims{ + ExpiresAt: time.Now().Add(time.Duration(jwtExpiration) * time.Minute).Unix(), + IssuedAt: jwt.TimeFunc().Unix(), + Issuer: "ras", + }, + } + token := jwt.NewWithClaims(jwt.SigningMethodHS256, claims) + tokenString, err := token.SignedString(signingKey) + return tokenString, err +} + +func validatePVFToken(encodedToken string) (string, uint, error) { + + claims := &CustomPVFClaims{} + _, err := jwt.ParseWithClaims(encodedToken, claims, func(token *jwt.Token) (interface{}, error) { + if _, isvalid := token.Method.(*jwt.SigningMethodHMAC); !isvalid { + return nil, fmt.Errorf("invalid token %s", token.Header["alg"]) + } + return []byte(signingKey), nil + }) + + if err != nil { + return "", 20, err + } + + return claims.email, claims.pid, nil +} diff --git a/student/admin.update.go b/student/admin.update.go index 625a003..6bdd204 100644 --- a/student/admin.update.go +++ b/student/admin.update.go @@ -97,9 +97,9 @@ func makeStudentEdiatableHandler(ctx *gin.Context) { ctx.AbortWithStatusJSON(http.StatusBadRequest, gin.H{"error": err.Error()}) return } - + user := middleware.GetUserID(ctx) - + logrus.Infof("A student with id %d is made editable by %v", editableStudentRequest.ID, user) ctx.JSON(http.StatusOK, gin.H{"status": "Successfully made student editable"}) -} \ No newline at end of file +} From 7d2a660d99c22a9505fa7237d1037168a7112541 Mon Sep 17 00:00:00 2001 From: AkshatIITk Date: Fri, 5 Jul 2024 04:38:20 +0530 Subject: [PATCH 3/7] added pvf routes and its handlers & made func for generate and validate token for pvf --- application/admin.pvf.go | 111 ++++++++++++++++++++++++++++++++++++ application/db.pvf.go | 23 +++++++- application/model.go | 2 +- application/router.go | 12 +++- application/student.pvf.go | 9 ++- application/verify.pvf.go | 57 +++++------------- cmd/verification.go | 1 + middleware/authenticator.go | 49 ++++++++++++++++ middleware/jwt.go | 22 +++---- 9 files changed, 224 insertions(+), 62 deletions(-) create mode 100644 application/admin.pvf.go diff --git a/application/admin.pvf.go b/application/admin.pvf.go new file mode 100644 index 0000000..2b158f7 --- /dev/null +++ b/application/admin.pvf.go @@ -0,0 +1,111 @@ +package application + +import ( + "net/http" + + "github.com/gin-gonic/gin" + "github.com/sirupsen/logrus" + "github.com/spo-iitk/ras-backend/mail" + "github.com/spo-iitk/ras-backend/middleware" + "github.com/spo-iitk/ras-backend/util" +) + +func getAllPvfForAdminHandler(ctx *gin.Context) { + rid, err := util.ParseUint(ctx.Param("rid")) + if err != nil { + ctx.AbortWithStatusJSON(http.StatusBadRequest, gin.H{"error": err.Error()}) + return + } + var jps []PVF + err = fetchAllPvfForAdmin(ctx, rid, &jps) + if err != nil { + ctx.AbortWithStatusJSON(http.StatusBadRequest, gin.H{"error": err.Error()}) + return + } + + ctx.JSON(http.StatusOK, jps) + +} + +func getPvfForAdminHandler(ctx *gin.Context) { + rid, err := util.ParseUint(ctx.Param("rid")) + if err != nil { + ctx.AbortWithStatusJSON(http.StatusBadRequest, gin.H{"error": err.Error()}) + return + } + pid, err := util.ParseUint(ctx.Param("pid")) + if err != nil { + ctx.AbortWithStatusJSON(http.StatusBadRequest, gin.H{"error": err.Error()}) + return + } + var jps PVF + err = fetchPvfForAdmin(ctx, rid, pid, &jps) + if err != nil { + ctx.AbortWithStatusJSON(http.StatusBadRequest, gin.H{"error": err.Error()}) + return + } + + ctx.JSON(http.StatusOK, jps) +} + +func sendVerificationLinkForPvfHandler(mail_channel chan mail.Mail) gin.HandlerFunc { + return func(ctx *gin.Context) { + pid, err := util.ParseUint(ctx.Param("pid")) + if err != nil { + ctx.AbortWithStatusJSON(http.StatusBadRequest, gin.H{"error": err.Error()}) + return + } + var pvf PVF + + rid, err := util.ParseUint(ctx.Param("rid")) + if err != nil { + ctx.AbortWithStatusJSON(http.StatusBadRequest, gin.H{"error": err.Error()}) + return + } + err = fetchPvfForAdmin(ctx, rid, pid, &pvf) + if err != nil { + ctx.AbortWithStatusJSON(http.StatusBadRequest, gin.H{"error": err.Error()}) + return + } + + token, err := middleware.GeneratePVFToken("akshat23@iitk.ac.in", pid, rid) + if err != nil { + ctx.AbortWithStatusJSON(http.StatusBadRequest, gin.H{"error": err.Error()}) + return + } + + logrus.Infof("A Token %s with id %d", token, pid) // to be removed + + // hardcode email + mail_channel <- mail.GenerateMail("iitkakshat@gmail.com", + "Verification requested on RAS", + "Dear "+pvf.MentorName+"PVF ID :"+util.ParseString(pid)+"Token : "+token+",\n\nWe got your request for registration on Recruitment Automation System, IIT Kanpur. We will get back to you soon. For any queries, please get in touch with us at spo@iitk.ac.in.") + + // mail_channel <- mail.GenerateMail("spo@iitk.ac.in", + // "Registration requested on RAS", + // "Company "+pvf.Duration+" has requested to be registered on RAS. The details are as follows:\n\n"+ + // "Name: "+pvf.CompanyUniversityName+"\n"+ + // "Designation: "+pvf.MentorDesignation+"\n"+ + // "Email: "+pvf.MentorEmail+"\n"+ + // "Phone: "+pvf.FileName+"\n"+ + // "Comments: "+pvf.Duration+"\n") + // ctx.JSON(http.StatusOK, gin.H{"pid": pid}) // to be removed + ctx.JSON(http.StatusOK, gin.H{"status": "Successfully Requested"}) + } +} + +func deletePVFHandler(ctx *gin.Context) { + pid, err := util.ParseUint(ctx.Param("pid")) + if err != nil { + ctx.AbortWithStatusJSON(http.StatusBadRequest, gin.H{"error": err.Error()}) + return + } + + err = deletePVF(ctx, pid) + if err != nil { + ctx.AbortWithStatusJSON(http.StatusBadRequest, gin.H{"error": err.Error()}) + return + } + + ctx.JSON(http.StatusOK, gin.H{"status": "deleted PVF"}) +} diff --git a/application/db.pvf.go b/application/db.pvf.go index 392d334..d1ac7c8 100644 --- a/application/db.pvf.go +++ b/application/db.pvf.go @@ -34,12 +34,13 @@ func fetchAllPvfForStudent(ctx *gin.Context, sid uint, rid uint, jps *[]PVF) err "mentor_email", "is_verified", "file_name", + "remarks", ). Order("id ASC"). Find(jps) return tx.Error } -func fetchPvfForStudent(ctx *gin.Context, sid uint, rid uint, pid uint, jps *[]PVF) error { +func fetchPvfForStudent(ctx *gin.Context, sid uint, rid uint, pid uint, jps *PVF) error { tx := db.WithContext(ctx). Where("student_recruitment_cycle_id = ? AND recruitment_cycle_id = ? AND id = ?", sid, rid, pid). Select( @@ -53,10 +54,17 @@ func fetchPvfForStudent(ctx *gin.Context, sid uint, rid uint, pid uint, jps *[]P "mentor_email", "is_verified", "file_name", + "remarks", ). Find(jps) return tx.Error } +func fetchPvfForAdmin(ctx *gin.Context, rid uint, pid uint, jps *PVF) error { + tx := db.WithContext(ctx). + Where("recruitment_cycle_id = ? AND id = ?", rid, pid). + Find(jps) + return tx.Error +} func fetchPvfForVerification(ctx *gin.Context, id uint, rid uint, jps *PVF) error { tx := db.WithContext(ctx). @@ -85,3 +93,16 @@ func verifyPvf(ctx *gin.Context, pvf *PVF) (bool, error) { Updates(map[string]interface{}{"is_verified": pvf.IsVerified}) return tx.RowsAffected > 0, tx.Error } + +func fetchAllPvfForAdmin(ctx *gin.Context, rid uint, jps *[]PVF) error { + tx := db.WithContext(ctx). + Where("recruitment_cycle_id = ?", rid). + Order("id DESC"). + Find(jps) + return tx.Error +} + +func deletePVF(ctx *gin.Context, pid uint) error { + tx := db.WithContext(ctx).Where("id = ?", pid).Delete(&PVF{}) + return tx.Error +} diff --git a/application/model.go b/application/model.go index bdd0abb..c49e1fd 100644 --- a/application/model.go +++ b/application/model.go @@ -133,7 +133,7 @@ type PVF struct { CompanyUniversityName string `json:"company_university_name"` Role string `json:"role"` Duration string `json:"duration"` - Description string `json:"description"` + Remarks string `json:"remarks"` MentorName string `json:"mentor_name"` MentorDesignation string `json:"mentor_designation"` MentorEmail string `json:"mentor_email"` diff --git a/application/router.go b/application/router.go index 7d2d177..72aa312 100644 --- a/application/router.go +++ b/application/router.go @@ -18,6 +18,11 @@ func AdminRouter(mail_channel chan mail.Mail, r *gin.Engine) { admin.DELETE("event/:eid/student", deleteAllStudentsFromEventHandler) admin.DELETE("event/:eid/student/:sid", deleteStudentFromEventHandler) + admin.GET("/pvf", getAllPvfForAdminHandler) + admin.GET("/pvf/:pid", getPvfForAdminHandler) + admin.GET("pvf/:pid/verification/send", sendVerificationLinkForPvfHandler(mail_channel)) + admin.DELETE("pvf/:pid", deletePVFHandler) + admin.GET("/company/:cid/proforma", getProformaByCompanyHandler) admin.GET("/proforma", getAllProformasHandler) @@ -64,6 +69,7 @@ func StudentRouter(mail_channel chan mail.Mail, r *gin.Engine) { student.POST("/pvf", postPvfForStudentHandler) student.GET("/pvf", getAllPvfForStudentHandler) student.GET("/pvf/:pid", getPvfForStudentHandler) + student.DELETE("pvf/:pid", deletePVFHandler) student.GET("/opening", getProformasForEligibleStudentHandler) student.GET("/opening/:pid", getApplicationHandler) @@ -102,11 +108,11 @@ func CompanyRouter(r *gin.Engine) { } func PvfVerificationRouter(r *gin.Engine) { - pvf := r.Group("/api/verification/application/rc/:rid") + pvf := r.Group("/api/verification") pvf.Use() { - pvf.GET("/pvf/:pid", getPvfForVerificationHandler) - pvf.PUT("pvf/:pid/verify", verifyPvfHandler) + pvf.GET("/pvf", getPvfForVerificationHandler) + // pvf.PUT("pvf/:pid/verify", verifyPvfHandler) pvf.PUT("/pvf", putPVFHandler) } } diff --git a/application/student.pvf.go b/application/student.pvf.go index 837c748..65e9f8c 100644 --- a/application/student.pvf.go +++ b/application/student.pvf.go @@ -56,18 +56,23 @@ func getAllPvfForStudentHandler(ctx *gin.Context) { } func getPvfForStudentHandler(ctx *gin.Context) { + sid := getStudentRCID(ctx) + if sid == 0 { + ctx.AbortWithStatusJSON(http.StatusBadRequest, gin.H{"error": "SRCID not found"}) + return + } rid, err := util.ParseUint(ctx.Param("rid")) if err != nil { ctx.AbortWithStatusJSON(http.StatusBadRequest, gin.H{"error": err.Error()}) return } - id, err := util.ParseUint(ctx.Param("pid")) + pid, err := util.ParseUint(ctx.Param("pid")) if err != nil { ctx.AbortWithStatusJSON(http.StatusBadRequest, gin.H{"error": err.Error()}) return } var jps PVF - err = fetchPvfForVerification(ctx, id, rid, &jps) + err = fetchPvfForStudent(ctx, sid, rid, pid, &jps) if err != nil { ctx.AbortWithStatusJSON(http.StatusBadRequest, gin.H{"error": err.Error()}) return diff --git a/application/verify.pvf.go b/application/verify.pvf.go index 3574948..5939747 100644 --- a/application/verify.pvf.go +++ b/application/verify.pvf.go @@ -4,63 +4,33 @@ import ( "net/http" "github.com/gin-gonic/gin" + "github.com/spo-iitk/ras-backend/middleware" "github.com/spo-iitk/ras-backend/util" ) func getPvfForVerificationHandler(ctx *gin.Context) { - rid, err := util.ParseUint(ctx.Param("rid")) - if err != nil { - ctx.AbortWithStatusJSON(http.StatusBadRequest, gin.H{"error": err.Error()}) - return - } - id, err := util.ParseUint("1") // id to be upadated - if err != nil { - ctx.AbortWithStatusJSON(http.StatusBadRequest, gin.H{"error": err.Error()}) - return - } - var jps PVF - err = fetchPvfForVerification(ctx, id, rid, &jps) - if err != nil { - ctx.AbortWithStatusJSON(http.StatusBadRequest, gin.H{"error": err.Error()}) - return - } + // ctx.JSON(http.StatusOK, gin.H{"pid": middleware.GetPVFID(ctx)}) + pid := middleware.GetPVFID(ctx) - ctx.JSON(http.StatusOK, jps) -} - -func verifyPvfHandler(ctx *gin.Context) { - // var verifyPvfRequest PVF - // if err := ctx.ShouldBindJSON(&verifyPvfRequest); err != nil { - // ctx.AbortWithStatusJSON(http.StatusBadRequest, gin.H{"error": err.Error()}) - // return - // } // pid, err := util.ParseUint(ctx.Param("pid")) - // if err != nil { // ctx.AbortWithStatusJSON(http.StatusBadRequest, gin.H{"error": err.Error()}) // return // } - - // verifyPvfRequest.ID = pid - // updated, err := verifyPvf(ctx, &verifyPvfRequest) + // rid, err := util.ParseUint(ctx.Param("rid")) // if err != nil { // ctx.AbortWithStatusJSON(http.StatusBadRequest, gin.H{"error": err.Error()}) // return // } + rid := middleware.GetRcID(ctx) + var jps PVF + err := fetchPvfForVerification(ctx, pid, rid, &jps) + if err != nil { + ctx.AbortWithStatusJSON(http.StatusBadRequest, gin.H{"error": err.Error()}) + return + } - // if !updated { - // ctx.AbortWithStatusJSON(http.StatusNotFound, gin.H{"error": "PVF not found"}) - // return - // } - - // if verifyPvfRequest.IsVerified { - // logrus.Infof("A PVF with id %d is verified", verifyPvfRequest.ID) - // ctx.JSON(http.StatusOK, gin.H{"status": "Successfully verified"}) - // } else { - // logrus.Infof("A PVF with id %d is unverified", verifyPvfRequest.ID) - // ctx.JSON(http.StatusOK, gin.H{"status": "Successfully unverified"}) - // } - + ctx.JSON(http.StatusOK, jps) } func putPVFHandler(ctx *gin.Context) { @@ -71,6 +41,9 @@ func putPVFHandler(ctx *gin.Context) { ctx.AbortWithStatusJSON(http.StatusBadRequest, gin.H{"error": err.Error()}) return } + pid := middleware.GetPVFID(ctx) + + jp.ID = pid if jp.ID == 0 { ctx.AbortWithStatusJSON(http.StatusBadRequest, gin.H{"error": "id is required"}) diff --git a/cmd/verification.go b/cmd/verification.go index 3b5e689..58c6f5c 100644 --- a/cmd/verification.go +++ b/cmd/verification.go @@ -15,6 +15,7 @@ func verificationServer() *http.Server { fmt.Print(PORT) engine := gin.New() engine.Use(middleware.CORS()) + engine.Use(middleware.PVFAuthenticator()) engine.Use(gin.CustomRecovery(recoveryHandler)) engine.Use(gin.Logger()) diff --git a/middleware/authenticator.go b/middleware/authenticator.go index bf061f2..c82cd84 100644 --- a/middleware/authenticator.go +++ b/middleware/authenticator.go @@ -65,3 +65,52 @@ func GetRoleID(ctx *gin.Context) constants.Role { return constants.Role(ctx.GetInt("roleID")) } + +func PVFAuthenticator() gin.HandlerFunc { + return func(ctx *gin.Context) { + authorizationHeader := ctx.GetHeader("authorization") + if len(authorizationHeader) == 0 { + ctx.AbortWithStatusJSON(http.StatusUnauthorized, + gin.H{"error": "authorization header is not provided"}) + return + } + + fields := strings.Fields(authorizationHeader) + if len(fields) < 2 { + ctx.AbortWithStatusJSON(http.StatusUnauthorized, + gin.H{"error": "invalid authorization header format"}) + return + } + + authorizationType := strings.ToLower(fields[0]) + if authorizationType != ("bearer") { + ctx.AbortWithStatusJSON(http.StatusUnauthorized, + gin.H{"error": "bearer not found"}) + return + } + + email, pid, rid, err := validatePVFToken(fields[1]) + // ctx.JSON(http.StatusAccepted, gin.H{"email": email, "pid": pid, "rid": rid}) // to be removed + if err != nil { + ctx.AbortWithStatusJSON(http.StatusUnauthorized, + gin.H{"error": "invalid token"}) + return + } + ctx.Set("email", email) + ctx.Set("pid", pid) + ctx.Set("rid", rid) + + ctx.Next() + } +} + +func GetEmail(ctx *gin.Context) string { + return ctx.GetString("email") +} + +func GetPVFID(ctx *gin.Context) uint { + return ctx.GetUint("pid") +} +func GetRcID(ctx *gin.Context) uint { + return ctx.GetUint("rid") +} diff --git a/middleware/jwt.go b/middleware/jwt.go index 1aff5cb..9900799 100644 --- a/middleware/jwt.go +++ b/middleware/jwt.go @@ -20,8 +20,9 @@ type CustomClaims struct { jwt.StandardClaims } type CustomPVFClaims struct { - email string `json:"email"` - pid uint `json:"pid"` + Email string `json:"email"` + Pid uint `json:"pid"` + Rid uint `json:"rid"` jwt.StandardClaims } @@ -70,17 +71,13 @@ func validateToken(encodedToken string) (string, uint, error) { return claims.UserID, claims.RoleID, nil } -func GeneratePVFToken(email string, pid uint, long bool) (string, error) { - var jwtExpiration int - if long { - jwtExpiration = jwtExpirationLong - } else { - jwtExpiration = jwtExpirationShort - } +func GeneratePVFToken(email string, pid uint, rid uint) (string, error) { + var jwtExpiration = 10080 // 7days claims := CustomPVFClaims{ email, pid, + rid, jwt.StandardClaims{ ExpiresAt: time.Now().Add(time.Duration(jwtExpiration) * time.Minute).Unix(), IssuedAt: jwt.TimeFunc().Unix(), @@ -92,7 +89,7 @@ func GeneratePVFToken(email string, pid uint, long bool) (string, error) { return tokenString, err } -func validatePVFToken(encodedToken string) (string, uint, error) { +func validatePVFToken(encodedToken string) (string, uint, uint, error) { claims := &CustomPVFClaims{} _, err := jwt.ParseWithClaims(encodedToken, claims, func(token *jwt.Token) (interface{}, error) { @@ -103,8 +100,7 @@ func validatePVFToken(encodedToken string) (string, uint, error) { }) if err != nil { - return "", 20, err + return "", 20, 20, err } - - return claims.email, claims.pid, nil + return claims.Email, claims.Pid, claims.Rid, nil } From c850a24f02def91e1de73ad962a07bebd6aee8de Mon Sep 17 00:00:00 2001 From: AkshatIITk Date: Sat, 6 Jul 2024 00:42:26 +0530 Subject: [PATCH 4/7] added edit pvf (student side ) route and handlers and minor changes --- application/db.pvf.go | 33 ++++++++++++++++++-------------- application/model.go | 3 ++- application/router.go | 1 + application/student.pvf.go | 39 ++++++++++++++++++++++++++++++++++++++ 4 files changed, 61 insertions(+), 15 deletions(-) diff --git a/application/db.pvf.go b/application/db.pvf.go index d1ac7c8..e839aa8 100644 --- a/application/db.pvf.go +++ b/application/db.pvf.go @@ -2,7 +2,6 @@ package application import ( "github.com/gin-gonic/gin" - "gorm.io/gorm/clause" ) func createPVF(ctx *gin.Context, pvf *PVF) error { @@ -28,12 +27,12 @@ func fetchAllPvfForStudent(ctx *gin.Context, sid uint, rid uint, jps *[]PVF) err "company_university_name", "role", "duration", - "description", "mentor_name", "mentor_designation", "mentor_email", "is_verified", - "file_name", + "filename_mentor", + "filename_student", "remarks", ). Order("id ASC"). @@ -48,12 +47,12 @@ func fetchPvfForStudent(ctx *gin.Context, sid uint, rid uint, pid uint, jps *PVF "company_university_name", "role", "duration", - "description", "mentor_name", "mentor_designation", "mentor_email", "is_verified", - "file_name", + "filename_mentor", + "filename_student", "remarks", ). Find(jps) @@ -74,25 +73,26 @@ func fetchPvfForVerification(ctx *gin.Context, id uint, rid uint, jps *PVF) erro "company_university_name", "role", "duration", - "description", "mentor_name", "mentor_designation", "mentor_email", "is_verified", "is_approved", - "file_name", + "filename_student", + "filename_mentor", + "remarks", ). Find(jps) return tx.Error } -func verifyPvf(ctx *gin.Context, pvf *PVF) (bool, error) { - tx := db.WithContext(ctx).Model(&pvf). - Clauses(clause.Returning{}). - Where("id = ?", pvf.ID). - Updates(map[string]interface{}{"is_verified": pvf.IsVerified}) - return tx.RowsAffected > 0, tx.Error -} +// func verifyPvf(ctx *gin.Context, pvf *PVF) (bool, error) { +// tx := db.WithContext(ctx).Model(&pvf). +// Clauses(clause.Returning{}). +// Where("id = ?", pvf.ID). +// Updates(map[string]interface{}{"is_verified": pvf.IsVerified}) +// return tx.RowsAffected > 0, tx.Error +// } func fetchAllPvfForAdmin(ctx *gin.Context, rid uint, jps *[]PVF) error { tx := db.WithContext(ctx). @@ -106,3 +106,8 @@ func deletePVF(ctx *gin.Context, pid uint) error { tx := db.WithContext(ctx).Where("id = ?", pid).Delete(&PVF{}) return tx.Error } + +func updatePVFForStudent(ctx *gin.Context, sid uint, jp *PVF) (bool, error) { + tx := db.WithContext(ctx).Where("id = ? AND student_recruitment_cycle_id = ?", jp.ID, sid).Updates(jp) + return tx.RowsAffected == 1, tx.Error +} diff --git a/application/model.go b/application/model.go index c49e1fd..f4b68e6 100644 --- a/application/model.go +++ b/application/model.go @@ -140,5 +140,6 @@ type PVF struct { IsApproved sql.NullBool `json:"is_approved" gorm:"index;default:NULL"` IsVerified sql.NullBool `json:"is_verified" gorm:"index;default:NULL"` RecruitmentCycleID uint `json:"recruitment_cycle_id" gorm:"index;->;<-:create"` - FileName string `json:"filename"` + FilenameStudent string `json:"filename_student"` + FilenameMentor string `json:"filename_mentor"` } diff --git a/application/router.go b/application/router.go index 72aa312..b5db4eb 100644 --- a/application/router.go +++ b/application/router.go @@ -67,6 +67,7 @@ func StudentRouter(mail_channel chan mail.Mail, r *gin.Engine) { student.GET("/proforma/:pid/event", getEventsByProformaForStudentHandler) student.POST("/pvf", postPvfForStudentHandler) + student.PUT("/pvf/:pid", putPVFForStudentHandler) student.GET("/pvf", getAllPvfForStudentHandler) student.GET("/pvf/:pid", getPvfForStudentHandler) student.DELETE("pvf/:pid", deletePVFHandler) diff --git a/application/student.pvf.go b/application/student.pvf.go index 65e9f8c..4548d00 100644 --- a/application/student.pvf.go +++ b/application/student.pvf.go @@ -80,3 +80,42 @@ func getPvfForStudentHandler(ctx *gin.Context) { ctx.JSON(http.StatusOK, jps) } + +func putPVFForStudentHandler(ctx *gin.Context) { + sid := getStudentRCID(ctx) + if sid == 0 { + ctx.AbortWithStatusJSON(http.StatusBadRequest, gin.H{"error": "SRCID not found"}) + return + } + var jp PVF + + err := ctx.ShouldBindJSON(&jp) + if err != nil { + ctx.AbortWithStatusJSON(http.StatusBadRequest, gin.H{"error": err.Error()}) + return + } + + if jp.ID == 0 { + ctx.AbortWithStatusJSON(http.StatusBadRequest, gin.H{"error": "id is required"}) + return + } + + var oldJp PVF + err = fetchPVF(ctx, jp.ID, &oldJp) + if err != nil { + ctx.AbortWithStatusJSON(http.StatusBadRequest, gin.H{"error": err.Error()}) + return + } + + ok, err := updatePVFForStudent(ctx, sid, &jp) + if err != nil { + ctx.AbortWithStatusJSON(http.StatusBadRequest, gin.H{"error": err.Error()}) + return + } + + if !ok { + ctx.AbortWithStatusJSON(http.StatusForbidden, gin.H{"error": "pvf not found or unauthorized"}) + return + } + ctx.JSON(http.StatusOK, gin.H{"status": "Updated PVF with id " + util.ParseString(jp.ID)}) +} From cb42436991d04c4cdc2fe1da93ea7ebfb442c74d Mon Sep 17 00:00:00 2001 From: AkshatIITk Date: Sat, 6 Jul 2024 00:50:38 +0530 Subject: [PATCH 5/7] removed commented code --- application/db.pvf.go | 8 -------- 1 file changed, 8 deletions(-) diff --git a/application/db.pvf.go b/application/db.pvf.go index e839aa8..46b8d8a 100644 --- a/application/db.pvf.go +++ b/application/db.pvf.go @@ -86,14 +86,6 @@ func fetchPvfForVerification(ctx *gin.Context, id uint, rid uint, jps *PVF) erro return tx.Error } -// func verifyPvf(ctx *gin.Context, pvf *PVF) (bool, error) { -// tx := db.WithContext(ctx).Model(&pvf). -// Clauses(clause.Returning{}). -// Where("id = ?", pvf.ID). -// Updates(map[string]interface{}{"is_verified": pvf.IsVerified}) -// return tx.RowsAffected > 0, tx.Error -// } - func fetchAllPvfForAdmin(ctx *gin.Context, rid uint, jps *[]PVF) error { tx := db.WithContext(ctx). Where("recruitment_cycle_id = ?", rid). From c35c1262e0ddfb3dc5e52c5393800fd7e865e403 Mon Sep 17 00:00:00 2001 From: AkshatIITk Date: Sat, 6 Jul 2024 03:20:26 +0530 Subject: [PATCH 6/7] added admin side pvf route --- application/admin.pvf.go | 40 +++++++++++++++++++++++++++++++++++++-- application/router.go | 1 + application/verify.pvf.go | 23 ---------------------- 3 files changed, 39 insertions(+), 25 deletions(-) diff --git a/application/admin.pvf.go b/application/admin.pvf.go index 2b158f7..e6ff5a1 100644 --- a/application/admin.pvf.go +++ b/application/admin.pvf.go @@ -68,7 +68,8 @@ func sendVerificationLinkForPvfHandler(mail_channel chan mail.Mail) gin.HandlerF return } - token, err := middleware.GeneratePVFToken("akshat23@iitk.ac.in", pid, rid) + token, err := middleware.GeneratePVFToken("akshat23@iitk.ac.in", pid, rid) // for testing + // token, err := middleware.GeneratePVFToken(pvf.MentorEmail, pid, rid) if err != nil { ctx.AbortWithStatusJSON(http.StatusBadRequest, gin.H{"error": err.Error()}) return @@ -76,7 +77,7 @@ func sendVerificationLinkForPvfHandler(mail_channel chan mail.Mail) gin.HandlerF logrus.Infof("A Token %s with id %d", token, pid) // to be removed - // hardcode email + // hardcoded email mail_channel <- mail.GenerateMail("iitkakshat@gmail.com", "Verification requested on RAS", "Dear "+pvf.MentorName+"PVF ID :"+util.ParseString(pid)+"Token : "+token+",\n\nWe got your request for registration on Recruitment Automation System, IIT Kanpur. We will get back to you soon. For any queries, please get in touch with us at spo@iitk.ac.in.") @@ -109,3 +110,38 @@ func deletePVFHandler(ctx *gin.Context) { ctx.JSON(http.StatusOK, gin.H{"status": "deleted PVF"}) } + +func putPVFHandlerForAdmin(ctx *gin.Context) { + var jp PVF + + rid, err := util.ParseUint(ctx.Param("rid")) + if err != nil { + ctx.AbortWithStatusJSON(http.StatusBadRequest, gin.H{"error": err.Error()}) + return + } + + err = ctx.ShouldBindJSON(&jp) + if err != nil { + ctx.AbortWithStatusJSON(http.StatusBadRequest, gin.H{"error": err.Error()}) + return + } + + if jp.ID == 0 { + ctx.AbortWithStatusJSON(http.StatusBadRequest, gin.H{"error": "id is required"}) + return + } + + var oldJp PVF + err = fetchPvfForAdmin(ctx, rid, jp.ID, &oldJp) + if err != nil { + ctx.AbortWithStatusJSON(http.StatusBadRequest, gin.H{"error": err.Error()}) + return + } + + err = updatePVF(ctx, &jp) + if err != nil { + ctx.AbortWithStatusJSON(http.StatusBadRequest, gin.H{"error": err.Error()}) + return + } + ctx.JSON(http.StatusOK, gin.H{"status": "Updated PVF with id " + util.ParseString(jp.ID)}) +} diff --git a/application/router.go b/application/router.go index 79f290e..0dabf20 100644 --- a/application/router.go +++ b/application/router.go @@ -19,6 +19,7 @@ func AdminRouter(mail_channel chan mail.Mail, r *gin.Engine) { admin.DELETE("event/:eid/student/:sid", deleteStudentFromEventHandler) admin.GET("/pvf", getAllPvfForAdminHandler) + admin.PUT("/pvf", putPVFHandlerForAdmin) admin.GET("/pvf/:pid", getPvfForAdminHandler) admin.GET("pvf/:pid/verification/send", sendVerificationLinkForPvfHandler(mail_channel)) admin.DELETE("pvf/:pid", deletePVFHandler) diff --git a/application/verify.pvf.go b/application/verify.pvf.go index 5939747..f5e4795 100644 --- a/application/verify.pvf.go +++ b/application/verify.pvf.go @@ -67,27 +67,4 @@ func putPVFHandler(ctx *gin.Context) { return } ctx.JSON(http.StatusOK, gin.H{"status": "Updated PVF with id " + util.ParseString(jp.ID)}) - // user := middleware.GetUserID(ctx) - - // logrus.Infof("%v edited a proforma with id %d", user, jp.ID) - - // if publishNotice { - // logrus.Infof("%v published a proforma with id %d", user, jp.ID) - - // err = rc.CreateNotice(ctx, oldJp.RecruitmentCycleID, &rc.Notice{ - // Title: fmt.Sprintf("[%s] | New Job Opening for %s", jp.CompanyName, jp.Profile), - // Description: fmt.Sprintf( - // "A new opening has been created for the profile of %s in the company %s", - // jp.Profile, jp.CompanyName), - // Tags: fmt.Sprintf("opening,%s,%s", jp.Role, jp.CompanyName), - // }) - // if err != nil { - // ctx.AbortWithStatusJSON(http.StatusBadRequest, gin.H{"error": err.Error()}) - // return - // } - - // ctx.JSON(http.StatusOK, gin.H{"status": "Proforma with id " + util.ParseString(jp.ID) + " has been published"}) - // } else { - // ctx.JSON(http.StatusOK, gin.H{"status": "Updated proforma with id " + util.ParseString(jp.ID)}) - // } } From d412ba99daadfdfc4f737c656477f4245e1261e6 Mon Sep 17 00:00:00 2001 From: AkshatIITk Date: Sun, 7 Jul 2024 04:32:12 +0530 Subject: [PATCH 7/7] added fields in pvf model and modied mail template and made new routes --- application/admin.pvf.go | 56 +++++++++++++++++++++++++++++++++------- application/db.pvf.go | 18 +++---------- application/model.go | 3 +++ application/router.go | 1 + middleware/jwt.go | 2 +- 5 files changed, 56 insertions(+), 24 deletions(-) diff --git a/application/admin.pvf.go b/application/admin.pvf.go index e6ff5a1..663c40f 100644 --- a/application/admin.pvf.go +++ b/application/admin.pvf.go @@ -4,7 +4,6 @@ import ( "net/http" "github.com/gin-gonic/gin" - "github.com/sirupsen/logrus" "github.com/spo-iitk/ras-backend/mail" "github.com/spo-iitk/ras-backend/middleware" "github.com/spo-iitk/ras-backend/util" @@ -67,20 +66,37 @@ func sendVerificationLinkForPvfHandler(mail_channel chan mail.Mail) gin.HandlerF ctx.AbortWithStatusJSON(http.StatusBadRequest, gin.H{"error": err.Error()}) return } + pvf.IsApproved.Valid = true + pvf.IsApproved.Bool = true - token, err := middleware.GeneratePVFToken("akshat23@iitk.ac.in", pid, rid) // for testing - // token, err := middleware.GeneratePVFToken(pvf.MentorEmail, pid, rid) + err = updatePVF(ctx, &pvf) + if err != nil { + ctx.AbortWithStatusJSON(http.StatusBadRequest, gin.H{"error": err.Error()}) + return + } + // token, err := middleware.GeneratePVFToken("akshat23@iitk.ac.in", pid, rid) // for testing + token, err := middleware.GeneratePVFToken(pvf.MentorEmail, pid, rid) if err != nil { ctx.AbortWithStatusJSON(http.StatusBadRequest, gin.H{"error": err.Error()}) return } - logrus.Infof("A Token %s with id %d", token, pid) // to be removed - - // hardcoded email - mail_channel <- mail.GenerateMail("iitkakshat@gmail.com", - "Verification requested on RAS", - "Dear "+pvf.MentorName+"PVF ID :"+util.ParseString(pid)+"Token : "+token+",\n\nWe got your request for registration on Recruitment Automation System, IIT Kanpur. We will get back to you soon. For any queries, please get in touch with us at spo@iitk.ac.in.") + // logrus.Infof("A Token %s with id %d", token, pid) // to be removed + message := "Dear " + pvf.MentorName + ",\n\n" + + pvf.Name + " (email: " + pvf.IITKEmail + ") has requested your verification for a project/internship they completed under your guidance.\n\n" + + "To verify the details and electronically sign the Project Verification Form (PVF), please click the link below (valid upto 3days):\n\n\n" + + "https://placement.iitk.ac.in/verify?token=" + token + "&rcid=" + util.ParseString(rid) + "\n\n" + + "Your prompt response is appreciated to ensure timely processing of " + pvf.Name + "'s placement applications.\n\n" + + "Please note:\n" + + "The PVF verifies the student's involvement and contributions to the project/internship. " + + "Only projects/internships conducted with IIT Kanpur faculty or external organizations require verification. " + + "If you have any questions regarding the PVF process, please don't hesitate to contact the Students' Placement Office at spo@iitk.ac.in.\n\n" + + "Thank you for your time and support." + + mail_channel <- mail.GenerateMail(pvf.MentorEmail, + "Project Verification Required for "+pvf.Name+"'s Internship/Project", + message, + ) // mail_channel <- mail.GenerateMail("spo@iitk.ac.in", // "Registration requested on RAS", @@ -145,3 +161,25 @@ func putPVFHandlerForAdmin(ctx *gin.Context) { } ctx.JSON(http.StatusOK, gin.H{"status": "Updated PVF with id " + util.ParseString(jp.ID)}) } + +func getAllStudentPvfHandler(ctx *gin.Context) { + rid, err := util.ParseUint(ctx.Param("rid")) + if err != nil { + ctx.AbortWithStatusJSON(http.StatusBadRequest, gin.H{"error": err.Error()}) + return + } + + sid, err := util.ParseUint(ctx.Param("sid")) + if err != nil { + ctx.AbortWithStatusJSON(http.StatusBadRequest, gin.H{"error": err.Error()}) + return + } + var pvfs []PVF + err = fetchAllPvfForStudent(ctx, sid, rid, &pvfs) + if err != nil { + ctx.AbortWithStatusJSON(http.StatusBadRequest, gin.H{"error": err.Error()}) + return + } + ctx.JSON(http.StatusOK, pvfs) + +} diff --git a/application/db.pvf.go b/application/db.pvf.go index 46b8d8a..72e2f5e 100644 --- a/application/db.pvf.go +++ b/application/db.pvf.go @@ -22,20 +22,7 @@ func updatePVF(ctx *gin.Context, jp *PVF) error { func fetchAllPvfForStudent(ctx *gin.Context, sid uint, rid uint, jps *[]PVF) error { tx := db.WithContext(ctx). Where("student_recruitment_cycle_id = ? AND recruitment_cycle_id = ?", sid, rid). - Select( - "id", - "company_university_name", - "role", - "duration", - "mentor_name", - "mentor_designation", - "mentor_email", - "is_verified", - "filename_mentor", - "filename_student", - "remarks", - ). - Order("id ASC"). + Order("id DESC"). Find(jps) return tx.Error } @@ -61,6 +48,7 @@ func fetchPvfForStudent(ctx *gin.Context, sid uint, rid uint, pid uint, jps *PVF func fetchPvfForAdmin(ctx *gin.Context, rid uint, pid uint, jps *PVF) error { tx := db.WithContext(ctx). Where("recruitment_cycle_id = ? AND id = ?", rid, pid). + Order("id DESC"). Find(jps) return tx.Error } @@ -99,6 +87,8 @@ func deletePVF(ctx *gin.Context, pid uint) error { return tx.Error } +// func fetchAllStudentPvfForAdmin(ctx *gin.Context) + func updatePVFForStudent(ctx *gin.Context, sid uint, jp *PVF) (bool, error) { tx := db.WithContext(ctx).Where("id = ? AND student_recruitment_cycle_id = ?", jp.ID, sid).Updates(jp) return tx.RowsAffected == 1, tx.Error diff --git a/application/model.go b/application/model.go index f4b68e6..b69ef63 100644 --- a/application/model.go +++ b/application/model.go @@ -129,6 +129,9 @@ type ApplicationResume struct { type PVF struct { gorm.Model + RollNo string `json:"roll_no"` + Name string `json:"name"` + IITKEmail string `json:"iitk_email"` StudentRecruitmentCycleID uint `json:"student_recruitment_cycle_id" gorm:"index;->;<-:create"` CompanyUniversityName string `json:"company_university_name"` Role string `json:"role"` diff --git a/application/router.go b/application/router.go index 0dabf20..6019510 100644 --- a/application/router.go +++ b/application/router.go @@ -23,6 +23,7 @@ func AdminRouter(mail_channel chan mail.Mail, r *gin.Engine) { admin.GET("/pvf/:pid", getPvfForAdminHandler) admin.GET("pvf/:pid/verification/send", sendVerificationLinkForPvfHandler(mail_channel)) admin.DELETE("pvf/:pid", deletePVFHandler) + admin.GET("pvf/student/:sid", getAllStudentPvfHandler) admin.GET("/company/:cid/proforma", getProformaByCompanyHandler) admin.GET("/company/:cid/stats", getCompanyRecruitStatsHandler) diff --git a/middleware/jwt.go b/middleware/jwt.go index 9900799..8adc867 100644 --- a/middleware/jwt.go +++ b/middleware/jwt.go @@ -72,7 +72,7 @@ func validateToken(encodedToken string) (string, uint, error) { } func GeneratePVFToken(email string, pid uint, rid uint) (string, error) { - var jwtExpiration = 10080 // 7days + var jwtExpiration = 4320 // 3days claims := CustomPVFClaims{ email,