diff --git a/controllers/authController.go b/controllers/authController.go index 34ca888..9852c21 100644 --- a/controllers/authController.go +++ b/controllers/authController.go @@ -21,10 +21,21 @@ import ( "go.mongodb.org/mongo-driver/mongo" ) -var teacherCollection *mongo.Collection = database.OpenCollection(database.Client, "teachers") -var studentCollection *mongo.Collection = database.OpenCollection(database.Client, "students") -var contactCollection *mongo.Collection = database.OpenCollection(database.Client, "contacts") -var adminCollection *mongo.Collection = database.OpenCollection(database.Client, "admins") +/* + The auth controller handles the following: + - default admin settings + - creating new users + - authenticating users + - retreiving users + - removeing users +*/ + +var TeacherCollection *mongo.Collection = database.OpenCollection(database.Client, "teachers") +var StudentCollection *mongo.Collection = database.OpenCollection(database.Client, "students") +var ContactCollection *mongo.Collection = database.OpenCollection(database.Client, "contacts") +var AdminCollection *mongo.Collection = database.OpenCollection(database.Client, "admins") +var ImageCollection *mongo.Collection = database.OpenCollection(database.Client, "images") +var LockerCollection *mongo.Collection = database.OpenCollection(database.Client, "lockers") func confirm(s string) bool { r := bufio.NewReader(os.Stdin) @@ -86,7 +97,7 @@ func CreateDefaultAdmin() models.Admin { } func NewSystem() { - count, err := adminCollection.CountDocuments(context.Background(), bson.D{}) + count, err := AdminCollection.CountDocuments(context.Background(), bson.D{}) if err != nil { fmt.Println("Unable to detect new system") } @@ -98,7 +109,7 @@ func NewSystem() { defaultAdmin := CreateDefaultAdmin() if confirm("Are the above credentials correct?") { - _, insertErr := adminCollection.InsertOne(context.Background(), defaultAdmin) + _, insertErr := AdminCollection.InsertOne(context.Background(), defaultAdmin) if insertErr != nil { log.Printf("Failed to create an admin\n") } @@ -130,7 +141,7 @@ func AuthenticateUser(c *fiber.Ctx, userType int) (bool, string) { claims := token.Claims.(*jwt.StandardClaims) var userID models.Id - findErr := idCollection.FindOne(context.TODO(), bson.M{"cid": claims.Issuer}).Decode(&userID) + findErr := IdCollection.FindOne(context.TODO(), bson.M{"cid": claims.Issuer}).Decode(&userID) if findErr != nil { return false, "" } @@ -268,7 +279,7 @@ func Enroll(c *fiber.Ctx) error { student.Updated_at, _ = time.Parse(time.RFC3339, time.Now().Format(time.RFC3339)) student.ID = primitive.NewObjectID() - _, insertErr := studentCollection.InsertOne(ctx, student) + _, insertErr := StudentCollection.InsertOne(ctx, student) if insertErr != nil { cancel() return c.Status(fiber.StatusInternalServerError).JSON(fiber.Map{ @@ -278,7 +289,7 @@ func Enroll(c *fiber.Ctx) error { }) } - _, insertErr = imageCollection.InsertOne(ctx, photo) + _, insertErr = ImageCollection.InsertOne(ctx, photo) if insertErr != nil { cancel() return c.Status(fiber.StatusInternalServerError).JSON(fiber.Map{ @@ -406,7 +417,7 @@ func RegisterTeacher(c *fiber.Ctx) error { teacher.Updated_at, _ = time.Parse(time.RFC3339, time.Now().Format(time.RFC3339)) teacher.ID = primitive.NewObjectID() - _, insertErr := teacherCollection.InsertOne(ctx, teacher) + _, insertErr := TeacherCollection.InsertOne(ctx, teacher) if insertErr != nil { cancel() return c.Status(fiber.StatusInternalServerError).JSON(fiber.Map{ @@ -503,7 +514,7 @@ func CreateAdmin(c *fiber.Ctx) error { admin.Updated_at, _ = time.Parse(time.RFC3339, time.Now().Format(time.RFC3339)) admin.ID = primitive.NewObjectID() - _, insertErr := adminCollection.InsertOne(ctx, admin) + _, insertErr := AdminCollection.InsertOne(ctx, admin) if insertErr != nil { cancel() return c.Status(fiber.StatusInternalServerError).JSON(fiber.Map{ @@ -543,7 +554,7 @@ func StudentLogin(c *fiber.Ctx) error { } var student models.Student - err := studentCollection.FindOne(ctx, bson.M{"schooldata.sid": data["sid"]}).Decode(&student) + err := StudentCollection.FindOne(ctx, bson.M{"schooldata.sid": data["sid"]}).Decode(&student) if err != nil { cancel() @@ -574,7 +585,7 @@ func StudentLogin(c *fiber.Ctx) error { }, } - _, updateErr := studentCollection.UpdateOne( + _, updateErr := StudentCollection.UpdateOne( ctx, bson.M{"schooldata.sid": data["sid"]}, update, @@ -623,7 +634,7 @@ func StudentLogin(c *fiber.Ctx) error { }, } - _, updateErr := studentCollection.UpdateOne( + _, updateErr := StudentCollection.UpdateOne( ctx, bson.M{"schooldata.sid": data["sid"]}, update, @@ -650,7 +661,7 @@ func StudentLogin(c *fiber.Ctx) error { }, } - _, updateErr := studentCollection.UpdateOne( + _, updateErr := StudentCollection.UpdateOne( ctx, bson.M{"schooldata.sid": data["sid"]}, update, @@ -715,7 +726,7 @@ func TeacherLogin(c *fiber.Ctx) error { } var teacher models.Teacher - err := teacherCollection.FindOne(ctx, bson.M{"schooldata.tid": data["tid"]}).Decode(&teacher) + err := TeacherCollection.FindOne(ctx, bson.M{"schooldata.tid": data["tid"]}).Decode(&teacher) defer cancel() if err != nil { @@ -785,7 +796,7 @@ func AdminLogin(c *fiber.Ctx) error { } var admin models.Admin - err := adminCollection.FindOne(ctx, bson.M{"aid": data["aid"]}).Decode(&admin) + err := AdminCollection.FindOne(ctx, bson.M{"aid": data["aid"]}).Decode(&admin) defer cancel() if err != nil { @@ -878,7 +889,7 @@ func Student(c *fiber.Ctx) error { responseData["photo"] = nil var student models.Student - findErr := studentCollection.FindOne(context.TODO(), bson.M{"schooldata.sid": sid}).Decode(&student) + findErr := StudentCollection.FindOne(context.TODO(), bson.M{"schooldata.sid": sid}).Decode(&student) if findErr != nil { return c.Status(fiber.StatusBadRequest).JSON(fiber.Map{ "success": false, @@ -897,14 +908,14 @@ func Student(c *fiber.Ctx) error { var locker models.Locker if student.SchoolData.Locker != "" { - lockerCollection.FindOne(context.TODO(), bson.M{"ID": student.SchoolData.Locker}).Decode(&locker) + LockerCollection.FindOne(context.TODO(), bson.M{"ID": student.SchoolData.Locker}).Decode(&locker) responseData["locker"] = locker } var contacts []models.Contact var contact models.Contact for i := range student.PersonalData.Contacts { - findErr := contactCollection.FindOne(context.TODO(), bson.M{"_id": student.PersonalData.Contacts[i]}).Decode(&contact) + findErr := ContactCollection.FindOne(context.TODO(), bson.M{"_id": student.PersonalData.Contacts[i]}).Decode(&contact) if findErr != nil { responseData["error"] = "Error! There was an error finding some contacts" } @@ -915,7 +926,7 @@ func Student(c *fiber.Ctx) error { } var photo models.Photo - findErr = imageCollection.FindOne(context.TODO(), bson.M{"name": student.SchoolData.PhotoName}).Decode(&photo) + findErr = ImageCollection.FindOne(context.TODO(), bson.M{"name": student.SchoolData.PhotoName}).Decode(&photo) if findErr != nil { responseData["error"] = "Error! There was an error finding the student photo" } @@ -943,7 +954,7 @@ func Teacher(c *fiber.Ctx) error { claims := token.Claims.(*jwt.StandardClaims) var teacher models.Teacher - findErr := teacherCollection.FindOne(context.TODO(), bson.M{"schooldata.tid": claims.Issuer}).Decode(&teacher) + findErr := TeacherCollection.FindOne(context.TODO(), bson.M{"schooldata.tid": claims.Issuer}).Decode(&teacher) if findErr != nil { return c.Status(fiber.StatusBadRequest).JSON(fiber.Map{ "success": false, @@ -974,7 +985,7 @@ func Admin(c *fiber.Ctx) error { claims := token.Claims.(*jwt.StandardClaims) var admin models.Admin - findErr := adminCollection.FindOne(context.TODO(), bson.M{"aid": claims.Issuer}).Decode(&admin) + findErr := AdminCollection.FindOne(context.TODO(), bson.M{"aid": claims.Issuer}).Decode(&admin) if findErr != nil { return c.Status(fiber.StatusBadRequest).JSON(fiber.Map{ "success": false, @@ -1054,7 +1065,7 @@ func CreateContact(c *fiber.Ctx) error { contact.Updated_at, _ = time.Parse(time.RFC3339, time.Now().Format(time.RFC3339)) contact.ID = primitive.NewObjectID() - _, insertErr := contactCollection.InsertOne(ctx, contact) + _, insertErr := ContactCollection.InsertOne(ctx, contact) if insertErr != nil { cancel() return c.Status(fiber.StatusInternalServerError).JSON(fiber.Map{ @@ -1069,7 +1080,7 @@ func CreateContact(c *fiber.Ctx) error { "contacts": contact.ID, }, } - _, updateErr := studentCollection.UpdateOne( + _, updateErr := StudentCollection.UpdateOne( ctx, bson.M{"sid": data["sid"]}, update, @@ -1121,7 +1132,7 @@ func DeleteContact(c *fiber.Ctx) error { }) } - _, err := contactCollection.DeleteOne(ctx, bson.M{"_id": data["_id"]}) + _, err := ContactCollection.DeleteOne(ctx, bson.M{"_id": data["_id"]}) if err != nil { cancel() return c.Status(fiber.StatusInternalServerError).JSON(fiber.Map{ @@ -1137,3 +1148,177 @@ func DeleteContact(c *fiber.Ctx) error { "message": "Successfully deleted contact", }) } + +func RemoveStudent(c *fiber.Ctx) error { + var data map[string]string + ctx, cancel := context.WithTimeout(context.Background(), 30*time.Second) + + if err := c.BodyParser(&data); err != nil { + cancel() + return c.Status(fiber.StatusInternalServerError).JSON(fiber.Map{ + "success": false, + "message": "Failed to parse body", + "error": err, + }) + } + + // Ensure Authenticated admin sent request + if verified, _ := AuthenticateUser(c, 3); !verified { + cancel() + return c.Status(fiber.StatusUnauthorized).JSON(fiber.Map{ + "success": false, + "message": "Unauthorized: only an admin can perform this action", + }) + } + + // Check student id is included + if data["sid"] == "" { + cancel() + return c.Status(fiber.StatusBadRequest).JSON(fiber.Map{ + "success": false, + "message": "missing required fields", + }) + } + + _, deleteErr := IdCollection.DeleteOne(ctx, bson.M{"cid": data["sid"]}) + if deleteErr != nil { + cancel() + return c.Status(fiber.StatusInternalServerError).JSON(fiber.Map{ + "success": false, + "message": "the identification number could not be deleted", + "error": deleteErr, + }) + } + + _, deleteErr = StudentCollection.DeleteOne(ctx, bson.M{"schooldata.sid": data["sid"]}) + if deleteErr != nil { + cancel() + return c.Status(fiber.StatusInternalServerError).JSON(fiber.Map{ + "success": false, + "message": "the student could not be deleted", + "error": deleteErr, + }) + } + defer cancel() + + return c.Status(fiber.StatusOK).JSON(fiber.Map{ + "success": true, + "message": "successfully deleted student", + }) +} + +func RemoveTeacher(c *fiber.Ctx) error { + var data map[string]string + ctx, cancel := context.WithTimeout(context.Background(), 30*time.Second) + + if err := c.BodyParser(&data); err != nil { + cancel() + return c.Status(fiber.StatusInternalServerError).JSON(fiber.Map{ + "success": false, + "message": "Failed to parse body", + "error": err, + }) + } + + // Ensure Authenticated admin sent request + if verified, _ := AuthenticateUser(c, 3); !verified { + cancel() + return c.Status(fiber.StatusUnauthorized).JSON(fiber.Map{ + "success": false, + "message": "Unauthorized: only an admin can perform this action", + }) + } + + // Check student id is included + if data["tid"] == "" { + cancel() + return c.Status(fiber.StatusBadRequest).JSON(fiber.Map{ + "success": false, + "message": "missing required fields", + }) + } + + _, deleteErr := IdCollection.DeleteOne(ctx, bson.M{"cid": data["tid"]}) + if deleteErr != nil { + cancel() + return c.Status(fiber.StatusInternalServerError).JSON(fiber.Map{ + "success": false, + "message": "the identification number could not be deleted", + "error": deleteErr, + }) + } + + _, deleteErr = TeacherCollection.DeleteOne(ctx, bson.M{"schooldata.tid": data["tid"]}) + if deleteErr != nil { + cancel() + return c.Status(fiber.StatusInternalServerError).JSON(fiber.Map{ + "success": false, + "message": "the teacher could not be deleted", + "error": deleteErr, + }) + } + defer cancel() + + return c.Status(fiber.StatusOK).JSON(fiber.Map{ + "success": true, + "message": "successfully deleted teacher", + }) +} + +func RemoveAdmin(c *fiber.Ctx) error { + var data map[string]string + ctx, cancel := context.WithTimeout(context.Background(), 30*time.Second) + + if err := c.BodyParser(&data); err != nil { + cancel() + return c.Status(fiber.StatusInternalServerError).JSON(fiber.Map{ + "success": false, + "message": "Failed to parse body", + "error": err, + }) + } + + // Ensure Authenticated admin sent request + if verified, _ := AuthenticateUser(c, 3); !verified { + cancel() + return c.Status(fiber.StatusUnauthorized).JSON(fiber.Map{ + "success": false, + "message": "Unauthorized: only an admin can perform this action", + }) + } + + // Check student id is included + if data["aid"] == "" { + cancel() + return c.Status(fiber.StatusBadRequest).JSON(fiber.Map{ + "success": false, + "message": "missing required fields", + }) + } + + _, deleteErr := IdCollection.DeleteOne(ctx, bson.M{"cid": data["aid"]}) + if deleteErr != nil { + cancel() + return c.Status(fiber.StatusInternalServerError).JSON(fiber.Map{ + "success": false, + "message": "the identification number could not be deleted", + "error": deleteErr, + }) + } + + _, deleteErr = AdminCollection.DeleteOne(ctx, bson.M{"aid": data["aid"]}) + if deleteErr != nil { + cancel() + return c.Status(fiber.StatusInternalServerError).JSON(fiber.Map{ + "success": false, + "message": "the admin could not be deleted", + "error": deleteErr, + }) + } + defer cancel() + + return c.Status(fiber.StatusOK).JSON(fiber.Map{ + "success": true, + "message": "successfully deleted admin", + }) +} diff --git a/controllers/idController.go b/controllers/idController.go index 2f8b349..625247f 100644 --- a/controllers/idController.go +++ b/controllers/idController.go @@ -14,21 +14,21 @@ import ( "go.mongodb.org/mongo-driver/mongo" ) -var idCollection *mongo.Collection = database.OpenCollection(database.Client, "cids") +var IdCollection *mongo.Collection = database.OpenCollection(database.Client, "cids") var table = [...]byte{'1', '2', '3', '4', '5', '6', '7', '8', '9', '0'} func ValidateID(id string, userType int) bool { // true: valid id, false: id already in use ctx, cancel := context.WithTimeout(context.Background(), 30*time.Second) var foundID models.Id - err := idCollection.FindOne(ctx, bson.M{"cid": id}).Decode(&foundID) + err := IdCollection.FindOne(ctx, bson.M{"cid": id}).Decode(&foundID) cancel() if err != nil { // If there is no id found create new ID object to be stored and return true (unless insert error then try again) var newID models.Id newID.CID = id newID.ParentType = userType newID.ID = primitive.NewObjectID() - _, insertErr := idCollection.InsertOne(context.Background(), newID) + _, insertErr := IdCollection.InsertOne(context.Background(), newID) return insertErr == nil } return false @@ -37,7 +37,7 @@ func ValidateID(id string, userType int) bool { // true: valid id, false: id alr func ValidatePEN(pen string) bool { // true: valid pen, false: pen already in use ctx, cancel := context.WithTimeout(context.Background(), 30*time.Second) var foundID models.Id - err := studentCollection.FindOne(ctx, bson.M{"schooldata.pen": pen}).Decode(&foundID) + err := StudentCollection.FindOne(ctx, bson.M{"schooldata.pen": pen}).Decode(&foundID) cancel() return err != nil } diff --git a/controllers/update/adminController.go b/controllers/update/adminController.go new file mode 100644 index 0000000..dfe4446 --- /dev/null +++ b/controllers/update/adminController.go @@ -0,0 +1,461 @@ +package update + +import ( + "context" + "time" + + . "github.com/SowinskiBraeden/school-management-api/controllers" + "github.com/SowinskiBraeden/school-management-api/models" + "github.com/gofiber/fiber/v2" + "github.com/golang-jwt/jwt" + "go.mongodb.org/mongo-driver/bson" +) + +/* + Several of these functions aren't directly for updating admin + information, but for admins only to update information of other + object types such as lockers, enabling user accounts after being + disbaled, etc. +*/ + +func UpdateLockerCombo(c *fiber.Ctx) error { + var data map[string]string + ctx, cancel := context.WithTimeout(context.Background(), 30*time.Second) + + if err := c.BodyParser(&data); err != nil { + cancel() + return c.Status(fiber.StatusInternalServerError).JSON(fiber.Map{ + "success": false, + "message": "Failed to parse body", + "error": err, + }) + } + + // Ensure Authenticated admin sent request + if verified, _ := AuthenticateUser(c, 3); !verified { + cancel() + return c.Status(fiber.StatusUnauthorized).JSON(fiber.Map{ + "success": false, + "message": "Unauthorized: only an admin can perform this action", + }) + } + + // Check locker number is included + if data["lockernumber"] == "" || data["newlockercombo"] == "" { + cancel() + return c.Status(fiber.StatusBadRequest).JSON(fiber.Map{ + "success": false, + "message": "missing required fields", + }) + } + + update_time, _ := time.Parse(time.RFC3339, time.Now().Format(time.RFC3339)) + update := bson.M{ + "$set": bson.M{ + "lockercombo": data["newlockercombo"], + "updated_at": update_time, + }, + } + + _, updateErr := LockerCollection.UpdateOne( + ctx, + bson.M{"lockernumber": data["lockernumber"]}, + update, + ) + if updateErr != nil { + cancel() + return c.Status(fiber.StatusInternalServerError).JSON(fiber.Map{ + "success": false, + "message": "the locker could not be updated", + "error": updateErr, + }) + } + defer cancel() + + return c.Status(fiber.StatusOK).JSON(fiber.Map{ + "success": true, + "message": "successfully updated locker", + }) +} + +func UpdateAdminName(c *fiber.Ctx) error { + var data map[string]string + ctx, cancel := context.WithTimeout(context.Background(), 30*time.Second) + + if err := c.BodyParser(&data); err != nil { + cancel() + return c.Status(fiber.StatusInternalServerError).JSON(fiber.Map{ + "success": false, + "message": "Failed to parse body", + "error": err, + }) + } + + cookie := c.Cookies("jwt") + + token, err := jwt.ParseWithClaims(cookie, &jwt.StandardClaims{}, func(token *jwt.Token) (interface{}, error) { + return []byte(SecretKey), nil + }) + if err != nil { + cancel() + return c.Status(fiber.StatusUnauthorized).JSON(fiber.Map{ + "success": false, + "message": "not authorized", + }) + } + + claims := token.Claims.(*jwt.StandardClaims) + + var admin models.Admin + findErr := AdminCollection.FindOne(ctx, bson.M{"aid": claims.Issuer}).Decode(&admin) + if findErr != nil { + cancel() + return c.Status(fiber.StatusBadRequest).JSON(fiber.Map{ + "success": false, + "message": "admin not found", + }) + } + + // Check required fields are included + if data["firstname"] == "" || data["lastname"] == "" { + cancel() + return c.Status(fiber.StatusBadRequest).JSON(fiber.Map{ + "success": false, + "message": "missing required fields", + }) + } + + update_time, _ := time.Parse(time.RFC3339, time.Now().Format(time.RFC3339)) + update := bson.M{ + "$set": bson.M{ + "firstname": data["firstname"], + "lastname": data["lastname"], + "updated_at": update_time, + }, + } + + _, updateErr := TeacherCollection.UpdateOne( + ctx, + bson.M{"aid": claims.Issuer}, + update, + ) + if updateErr != nil { + cancel() + return c.Status(fiber.StatusInternalServerError).JSON(fiber.Map{ + "success": false, + "message": "the admin could not be updated", + "error": updateErr, + }) + } + defer cancel() + + return c.Status(fiber.StatusOK).JSON(fiber.Map{ + "success": true, + "message": "successfully updated admin", + }) +} + +func UpdateAdminEmail(c *fiber.Ctx) error { + var data map[string]string + ctx, cancel := context.WithTimeout(context.Background(), 30*time.Second) + + if err := c.BodyParser(&data); err != nil { + cancel() + return c.Status(fiber.StatusInternalServerError).JSON(fiber.Map{ + "success": false, + "message": "Failed to parse body", + "error": err, + }) + } + + cookie := c.Cookies("jwt") + + token, err := jwt.ParseWithClaims(cookie, &jwt.StandardClaims{}, func(token *jwt.Token) (interface{}, error) { + return []byte(SecretKey), nil + }) + if err != nil { + cancel() + return c.Status(fiber.StatusUnauthorized).JSON(fiber.Map{ + "success": false, + "message": "not authorized", + }) + } + + claims := token.Claims.(*jwt.StandardClaims) + + var admin models.Admin + findErr := AdminCollection.FindOne(ctx, bson.M{"aid": claims.Issuer}).Decode(&admin) + if findErr != nil { + cancel() + return c.Status(fiber.StatusBadRequest).JSON(fiber.Map{ + "success": false, + "message": "admin not found", + }) + } + + // Check required fields are included + if data["email"] == "" { + cancel() + return c.Status(fiber.StatusBadRequest).JSON(fiber.Map{ + "success": false, + "message": "missing required fields", + }) + } + + update_time, _ := time.Parse(time.RFC3339, time.Now().Format(time.RFC3339)) + update := bson.M{ + "$set": bson.M{ + "email": data["email"], + "updated_at": update_time, + }, + } + + _, updateErr := TeacherCollection.UpdateOne( + ctx, + bson.M{"aid": claims.Issuer}, + update, + ) + if updateErr != nil { + cancel() + return c.Status(fiber.StatusInternalServerError).JSON(fiber.Map{ + "success": false, + "message": "the admin could not be updated", + "error": updateErr, + }) + } + defer cancel() + + return c.Status(fiber.StatusOK).JSON(fiber.Map{ + "success": true, + "message": "successfully updated admin", + }) +} + +func UpdateAdminPassword(c *fiber.Ctx) error { + var data map[string]string + ctx, cancel := context.WithTimeout(context.Background(), 30*time.Second) + + if err := c.BodyParser(&data); err != nil { + cancel() + return c.Status(fiber.StatusInternalServerError).JSON(fiber.Map{ + "success": false, + "message": "Failed to parse body", + "error": err, + }) + } + + cookie := c.Cookies("jwt") + + token, err := jwt.ParseWithClaims(cookie, &jwt.StandardClaims{}, func(token *jwt.Token) (interface{}, error) { + return []byte(SecretKey), nil + }) + if err != nil { + cancel() + return c.Status(fiber.StatusUnauthorized).JSON(fiber.Map{ + "success": false, + "message": "not authorized", + }) + } + + claims := token.Claims.(*jwt.StandardClaims) + + var admin models.Admin + findErr := AdminCollection.FindOne(ctx, bson.M{"aid": claims.Issuer}).Decode(&admin) + if findErr != nil { + cancel() + return c.Status(fiber.StatusBadRequest).JSON(fiber.Map{ + "success": false, + "message": "admin not found", + }) + } + + // Check required fields are included + if data["password"] == "" || data["newpassword1"] == "" || data["newpassword2"] == "" { + cancel() + return c.Status(fiber.StatusBadRequest).JSON(fiber.Map{ + "success": false, + "message": "missing required fields", + }) + } + + if !admin.ComparePasswords(data["password"]) { + cancel() + return c.Status(fiber.StatusOK).JSON(fiber.Map{ + "success": false, + "message": "Your password is incorrect", + }) + } + + if data["newpassword1"] != data["newpassword2"] { + cancel() + return c.Status(fiber.StatusBadRequest).JSON(fiber.Map{ + "success": false, + "message": "Your new passwords must match", + }) + } + + update_time, _ := time.Parse(time.RFC3339, time.Now().Format(time.RFC3339)) + update := bson.M{ + "$set": bson.M{ + "password": admin.HashPassword(data["newpassword1"]), + "temppassword": false, // If it were a temp password, its not now + "updated_at": update_time, + }, + } + + _, updateErr := TeacherCollection.UpdateOne( + ctx, + bson.M{"aid": claims.Issuer}, + update, + ) + if updateErr != nil { + cancel() + return c.Status(fiber.StatusInternalServerError).JSON(fiber.Map{ + "success": false, + "message": "the admin password could not be updated", + "error": updateErr, + }) + } + defer cancel() + + subject := "Password Changed" + receiver := admin.Email + r := NewRequest([]string{receiver}, subject) + + if sent := r.Send("./templates/selfPasswordChanged.html", map[string]string{"username": admin.FirstName}); !sent { + return c.Status(fiber.StatusInternalServerError).JSON(fiber.Map{ + "success": false, + "message": "Could not send password to admins email", + }) + } + + return c.Status(fiber.StatusOK).JSON(fiber.Map{ + "success": true, + "message": "successfully updated admin password", + }) +} + +func RemoveStudentsDisabled(c *fiber.Ctx) error { + var data map[string]string + ctx, cancel := context.WithTimeout(context.Background(), 30*time.Second) + + if err := c.BodyParser(&data); err != nil { + cancel() + return c.Status(fiber.StatusInternalServerError).JSON(fiber.Map{ + "success": false, + "message": "Failed to parse body", + "error": err, + }) + } + + // Ensure Authorized admin sent request + if verified, _ := AuthenticateUser(c, 3); !verified { + cancel() + return c.Status(fiber.StatusUnauthorized).JSON(fiber.Map{ + "success": false, + "message": "Unauthorized: only an admin can perform this action", + }) + } + + // Check required fields are included + if data["sid"] == "" { + cancel() + return c.Status(fiber.StatusBadRequest).JSON(fiber.Map{ + "success": false, + "message": "missing required fields", + }) + } + + update_time, _ := time.Parse(time.RFC3339, time.Now().Format(time.RFC3339)) + update := bson.M{ + "$set": bson.M{ + "accountdata.accountdisabled": false, + "accountdata.alerted": false, + "accountdata.attempts": 0, + "updated_at": update_time, + }, + } + + result, updateErr := StudentCollection.UpdateOne( + ctx, + bson.M{"schooldata.sid": data["sid"]}, + update, + ) + if updateErr != nil { + cancel() + return c.Status(fiber.StatusInternalServerError).JSON(fiber.Map{ + "success": false, + "message": "the student account could not be enabled", + "error": updateErr, + }) + } + defer cancel() + + return c.Status(fiber.StatusOK).JSON(fiber.Map{ + "success": true, + "message": "successfully enabled student account", + "result": result, + }) +} + +func RemoveTeachersDisabled(c *fiber.Ctx) error { + var data map[string]string + ctx, cancel := context.WithTimeout(context.Background(), 30*time.Second) + + if err := c.BodyParser(&data); err != nil { + cancel() + return c.Status(fiber.StatusInternalServerError).JSON(fiber.Map{ + "success": false, + "message": "Failed to parse body", + "error": err, + }) + } + + // Ensure Authorized admin sent request + if verified, _ := AuthenticateUser(c, 3); !verified { + cancel() + return c.Status(fiber.StatusUnauthorized).JSON(fiber.Map{ + "success": false, + "message": "Unauthorized: only an admin can perform this action", + }) + } + + // Check required fields are included + if data["tid"] == "" { + cancel() + return c.Status(fiber.StatusBadRequest).JSON(fiber.Map{ + "success": false, + "message": "missing required fields", + }) + } + + update_time, _ := time.Parse(time.RFC3339, time.Now().Format(time.RFC3339)) + update := bson.M{ + "$set": bson.M{ + "accountdata.accountdisabled": false, + "accountdata.attempts": 0, + "updated_at": update_time, + }, + } + + result, updateErr := TeacherCollection.UpdateOne( + ctx, + bson.M{"schooldata.tid": data["tid"]}, + update, + ) + if updateErr != nil { + cancel() + return c.Status(fiber.StatusInternalServerError).JSON(fiber.Map{ + "success": false, + "message": "the teacher account could not be enabled", + "error": updateErr, + }) + } + defer cancel() + + return c.Status(fiber.StatusOK).JSON(fiber.Map{ + "success": true, + "message": "successfully enabled teacher account", + "result": result, + }) +} diff --git a/controllers/update/contactController.go b/controllers/update/contactController.go new file mode 100644 index 0000000..b8bdf46 --- /dev/null +++ b/controllers/update/contactController.go @@ -0,0 +1,392 @@ +package update + +import ( + "context" + "time" + + . "github.com/SowinskiBraeden/school-management-api/controllers" + + "github.com/gofiber/fiber/v2" + "go.mongodb.org/mongo-driver/bson" +) + +func UpdateContactName(c *fiber.Ctx) error { + var data map[string]string + ctx, cancel := context.WithTimeout(context.Background(), 30*time.Second) + + if err := c.BodyParser(&data); err != nil { + cancel() + return c.Status(fiber.StatusInternalServerError).JSON(fiber.Map{ + "success": false, + "message": "Failed to parse body", + "error": err, + }) + } + + // Ensure Authenticated admin sent request + if verified, _ := AuthenticateUser(c, 3); !verified { + cancel() + return c.Status(fiber.StatusUnauthorized).JSON(fiber.Map{ + "success": false, + "message": "Unauthorized: only an admin can perform this action", + }) + } + + // Check required fields are included + if data["_id"] == "" || data["firstname"] == "" || data["lastname"] == "" { + cancel() + return c.Status(fiber.StatusBadRequest).JSON(fiber.Map{ + "success": false, + "message": "missing required fields", + }) + } + + var middlename string = "" + + if data["middlename"] != "" { + middlename = data["middlename"] + } + + update_time, _ := time.Parse(time.RFC3339, time.Now().Format(time.RFC3339)) + update := bson.M{ + "$set": bson.M{ + "firstname": data["firstname"], + "middlename": middlename, + "lastname": data["lastname"], + "updated_at": update_time, + }, + } + + _, updateErr := ContactCollection.UpdateOne( + ctx, + bson.M{"_id": data["_id"]}, + update, + ) + if updateErr != nil { + cancel() + return c.Status(fiber.StatusInternalServerError).JSON(fiber.Map{ + "success": false, + "message": "the contact could not be updated", + "error": updateErr, + }) + } + defer cancel() + + return c.Status(fiber.StatusOK).JSON(fiber.Map{ + "success": true, + "message": "successfully updated contact", + }) +} + +func UpdateContactAddress(c *fiber.Ctx) error { + var data map[string]string + ctx, cancel := context.WithTimeout(context.Background(), 30*time.Second) + + if err := c.BodyParser(&data); err != nil { + cancel() + return c.Status(fiber.StatusInternalServerError).JSON(fiber.Map{ + "success": false, + "message": "Failed to parse body", + "error": err, + }) + } + + // Ensure Authenticated admin sent request + if verified, _ := AuthenticateUser(c, 3); !verified { + cancel() + return c.Status(fiber.StatusUnauthorized).JSON(fiber.Map{ + "success": false, + "message": "Unauthorized: only an admin can perform this action", + }) + } + + // Check required fields are included + if data["_id"] == "" || data["address"] == "" || data["city"] == "" || data["province"] == "" || data["postal"] == "" { + cancel() + return c.Status(fiber.StatusBadRequest).JSON(fiber.Map{ + "success": false, + "message": "missing required fields", + }) + } + + update_time, _ := time.Parse(time.RFC3339, time.Now().Format(time.RFC3339)) + update := bson.M{ + "$set": bson.M{ + "address": data["address"], + "city": data["city"], + "province": data["province"], + "postal": data["postal"], + "updated_at": update_time, + }, + } + + _, updateErr := ContactCollection.UpdateOne( + ctx, + bson.M{"_id": data["_id"]}, + update, + ) + if updateErr != nil { + cancel() + return c.Status(fiber.StatusInternalServerError).JSON(fiber.Map{ + "success": false, + "message": "the contact could not be updated", + "error": updateErr, + }) + } + defer cancel() + + return c.Status(fiber.StatusOK).JSON(fiber.Map{ + "success": true, + "message": "successfully updated contact", + }) +} + +func UpdateContactHomePhone(c *fiber.Ctx) error { + var data map[string]string + ctx, cancel := context.WithTimeout(context.Background(), 30*time.Second) + + if err := c.BodyParser(&data); err != nil { + cancel() + return c.Status(fiber.StatusInternalServerError).JSON(fiber.Map{ + "success": false, + "message": "Failed to parse body", + "error": err, + }) + } + + // Ensure Authenticated admin sent request + if verified, _ := AuthenticateUser(c, 3); !verified { + cancel() + return c.Status(fiber.StatusUnauthorized).JSON(fiber.Map{ + "success": false, + "message": "Unauthorized: only an admin can perform this action", + }) + } + + // Check id of contact and new priority number is included + if data["_id"] == "" || data["newnumber"] == "" { + cancel() + return c.Status(fiber.StatusBadRequest).JSON(fiber.Map{ + "success": false, + "message": "missing required fields", + }) + } + + update_time, _ := time.Parse(time.RFC3339, time.Now().Format(time.RFC3339)) + update := bson.M{ + "$set": bson.M{ + "homephone": data["newnumber"], + "updated_at": update_time, + }, + } + + _, updateErr := ContactCollection.UpdateOne( + ctx, + bson.M{"_id": data["_id"]}, + update, + ) + if updateErr != nil { + cancel() + return c.Status(fiber.StatusInternalServerError).JSON(fiber.Map{ + "success": false, + "message": "contact could not be updated", + "error": updateErr, + }) + } + defer cancel() + + return c.Status(fiber.StatusOK).JSON(fiber.Map{ + "success": true, + "message": "successfully updated contact", + }) +} + +func UpdateContactWorkPhone(c *fiber.Ctx) error { + var data map[string]string + ctx, cancel := context.WithTimeout(context.Background(), 30*time.Second) + + if err := c.BodyParser(&data); err != nil { + cancel() + return c.Status(fiber.StatusInternalServerError).JSON(fiber.Map{ + "success": false, + "message": "Failed to parse body", + "error": err, + }) + } + + // Ensure Authenticated admin sent request + if verified, _ := AuthenticateUser(c, 3); !verified { + cancel() + return c.Status(fiber.StatusUnauthorized).JSON(fiber.Map{ + "success": false, + "message": "Unauthorized: only an admin can perform this action", + }) + } + + // Check id of contact and new priority number is included + if data["_id"] == "" || data["newnumber"] == "" { + cancel() + return c.Status(fiber.StatusBadRequest).JSON(fiber.Map{ + "success": false, + "message": "missing required fields", + }) + } + + update_time, _ := time.Parse(time.RFC3339, time.Now().Format(time.RFC3339)) + update := bson.M{ + "$set": bson.M{ + "workphone": data["newnumber"], + "updated_at": update_time, + }, + } + + _, updateErr := ContactCollection.UpdateOne( + ctx, + bson.M{"_id": data["_id"]}, + update, + ) + if updateErr != nil { + cancel() + return c.Status(fiber.StatusInternalServerError).JSON(fiber.Map{ + "success": false, + "message": "contact could not be updated", + "error": updateErr, + }) + } + defer cancel() + + return c.Status(fiber.StatusOK).JSON(fiber.Map{ + "success": true, + "message": "successfully updated contact", + }) +} + +func UpdateContactEmail(c *fiber.Ctx) error { + var data map[string]string + ctx, cancel := context.WithTimeout(context.Background(), 30*time.Second) + + if err := c.BodyParser(&data); err != nil { + cancel() + return c.Status(fiber.StatusInternalServerError).JSON(fiber.Map{ + "success": false, + "message": "Failed to parse body", + "error": err, + }) + } + + // Ensure Authenticated admin sent request + if verified, _ := AuthenticateUser(c, 3); !verified { + cancel() + return c.Status(fiber.StatusUnauthorized).JSON(fiber.Map{ + "success": false, + "message": "Unauthorized: only an admin can perform this action", + }) + } + + // Check id of contact and new priority number is included + if data["_id"] == "" || data["email"] == "" { + cancel() + return c.Status(fiber.StatusBadRequest).JSON(fiber.Map{ + "success": false, + "message": "missing required fields", + }) + } + + update_time, _ := time.Parse(time.RFC3339, time.Now().Format(time.RFC3339)) + update := bson.M{ + "$set": bson.M{ + "email": data["email"], + "updated_at": update_time, + }, + } + + _, updateErr := ContactCollection.UpdateOne( + ctx, + bson.M{"_id": data["_id"]}, + update, + ) + if updateErr != nil { + cancel() + return c.Status(fiber.StatusInternalServerError).JSON(fiber.Map{ + "success": false, + "message": "contact could not be updated", + "error": updateErr, + }) + } + defer cancel() + + return c.Status(fiber.StatusOK).JSON(fiber.Map{ + "success": true, + "message": "successfully updated contact", + }) +} + +func UpdateContactPriority(c *fiber.Ctx) error { + var data map[string]interface{} + ctx, cancel := context.WithTimeout(context.Background(), 30*time.Second) + + if err := c.BodyParser(&data); err != nil { + cancel() + return c.Status(fiber.StatusInternalServerError).JSON(fiber.Map{ + "success": false, + "message": "Failed to parse body", + "error": err, + }) + } + + // Ensure Authenticated admin sent request + if verified, _ := AuthenticateUser(c, 3); !verified { + cancel() + return c.Status(fiber.StatusUnauthorized).JSON(fiber.Map{ + "success": false, + "message": "Unauthorized: only an admin can perform this action", + }) + } + + // Check id of contact and new priority number is included + if data["_id"] == nil || data["priority"] == nil { + cancel() + return c.Status(fiber.StatusBadRequest).JSON(fiber.Map{ + "success": false, + "message": "missing required fields", + }) + } + + var priority int = data["priority"].(int) + + if priority > 10 || priority < 1 { + cancel() + return c.Status(fiber.StatusBadRequest).JSON(fiber.Map{ + "success": false, + "message": "invalid priority", + }) + } + + update_time, _ := time.Parse(time.RFC3339, time.Now().Format(time.RFC3339)) + update := bson.M{ + "$set": bson.M{ + "priority": priority, + "updated_at": update_time, + }, + } + + _, updateErr := ContactCollection.UpdateOne( + ctx, + bson.M{"_id": data["_id"]}, + update, + ) + if updateErr != nil { + cancel() + return c.Status(fiber.StatusInternalServerError).JSON(fiber.Map{ + "success": false, + "message": "contact could not be updated", + "error": updateErr, + }) + } + defer cancel() + + return c.Status(fiber.StatusOK).JSON(fiber.Map{ + "success": true, + "message": "successfully updated contact", + }) +} diff --git a/controllers/update/studentController.go b/controllers/update/studentController.go new file mode 100644 index 0000000..3d92000 --- /dev/null +++ b/controllers/update/studentController.go @@ -0,0 +1,980 @@ +package update + +import ( + "context" + "encoding/base64" + "fmt" + "io/ioutil" + "os" + "strings" + "time" + + . "github.com/SowinskiBraeden/school-management-api/controllers" + "github.com/SowinskiBraeden/school-management-api/models" + + "github.com/gofiber/fiber/v2" + "github.com/golang-jwt/jwt" + "github.com/google/uuid" + "go.mongodb.org/mongo-driver/bson" +) + +func toBase64(b []byte) string { + return base64.StdEncoding.EncodeToString(b) +} + +func UpdateStudentName(c *fiber.Ctx) error { + var data map[string]string + ctx, cancel := context.WithTimeout(context.Background(), 30*time.Second) + + if err := c.BodyParser(&data); err != nil { + cancel() + return c.Status(fiber.StatusInternalServerError).JSON(fiber.Map{ + "success": false, + "message": "Failed to parse body", + "error": err, + }) + } + + // Ensure Authenticated admin sent request + if verified, _ := AuthenticateUser(c, 3); !verified { + cancel() + return c.Status(fiber.StatusUnauthorized).JSON(fiber.Map{ + "success": false, + "message": "Unauthorized: only an admin can perform this action", + }) + } + + // Check id and names are included + // Middle name is optional + if data["sid"] == "" || data["firstname"] == "" || data["lastname"] == "" { + cancel() + return c.Status(fiber.StatusBadRequest).JSON(fiber.Map{ + "success": false, + "message": "missing required fields", + }) + } + + var updateMiddle bool = false + if data["middlename"] != "" { + updateMiddle = true + } + + update_time, _ := time.Parse(time.RFC3339, time.Now().Format(time.RFC3339)) + var update bson.M + if updateMiddle { + update = bson.M{ + "$set": bson.M{ + "personaldata.firstname": data["firstname"], + "personaldata.middlename": data["middlename"], + "personaldata.lastname": data["lastname"], + "updated_at": update_time, + }, + } + } else { + update = bson.M{ + "$set": bson.M{ + "personaldata.firstname": data["firstname"], + "personaldata.lastname": data["lastname"], + "updated_at": update_time, + }, + } + } + + result, updateErr := StudentCollection.UpdateOne( + ctx, + bson.M{"schooldata.sid": data["sid"]}, + update, + ) + if updateErr != nil { + cancel() + return c.Status(fiber.StatusInternalServerError).JSON(fiber.Map{ + "success": false, + "message": "the student could not be updated", + "error": updateErr, + }) + } + defer cancel() + + return c.Status(fiber.StatusOK).JSON(fiber.Map{ + "success": true, + "message": "successfully updated student", + "result": result, + }) +} + +func UpdateStudentGradeLevel(c *fiber.Ctx) error { + var data map[string]interface{} + ctx, cancel := context.WithTimeout(context.Background(), 30*time.Second) + + if err := c.BodyParser(&data); err != nil { + cancel() + return c.Status(fiber.StatusInternalServerError).JSON(fiber.Map{ + "success": false, + "message": "Failed to parse body", + "error": err, + }) + } + + // Ensure Authorized admin sent request + if verified, _ := AuthenticateUser(c, 3); !verified { + cancel() + return c.Status(fiber.StatusUnauthorized).JSON(fiber.Map{ + "success": false, + "message": "Unauthorized: only an admin can perform this action", + }) + } + + // Check required fields are included + if data["sid"] == nil || data["gradelevel"] == nil { + cancel() + return c.Status(fiber.StatusBadRequest).JSON(fiber.Map{ + "success": false, + "message": "missing required fields", + }) + } + + update_time, _ := time.Parse(time.RFC3339, time.Now().Format(time.RFC3339)) + update := bson.M{ + "$set": bson.M{ + "schooldata.gradelevel": data["gradelevel"].(float64), + "updated_at": update_time, + }, + } + + _, updateErr := StudentCollection.UpdateOne( + ctx, + bson.M{"schooldata.sid": data["sid"].(string)}, + update, + ) + if updateErr != nil { + cancel() + return c.Status(fiber.StatusInternalServerError).JSON(fiber.Map{ + "success": false, + "message": "the student could not be updated", + "error": updateErr, + }) + } + defer cancel() + + return c.Status(fiber.StatusOK).JSON(fiber.Map{ + "success": true, + "message": "successfully updated student", + }) +} + +/* + Far later on this function is going to be completely automated. + Instead of an admin sending a request to update the homeroom of + a student or teacher, the system will take the room number of + the teacher's or student's Block 2 class from their schedule. + + Though this function would remain for students only, for example + a student requests a course change, if its their block 2 the + admin would have to alter their homeroom to be the new class + number. +*/ +func UpdateStudentHomeroom(c *fiber.Ctx) error { + var data map[string]string + ctx, cancel := context.WithTimeout(context.Background(), 30*time.Second) + + if err := c.BodyParser(&data); err != nil { + cancel() + return c.Status(fiber.StatusInternalServerError).JSON(fiber.Map{ + "success": false, + "message": "Failed to parse body", + "error": err, + }) + } + + // Ensure Authenticated admin sent request + if verified, _ := AuthenticateUser(c, 3); !verified { + cancel() + return c.Status(fiber.StatusUnauthorized).JSON(fiber.Map{ + "success": false, + "message": "Unauthorized: only an admin can perform this action", + }) + } + + // Check required fields are included + if data["sid"] == "" || data["homeroom"] == "" { + cancel() + return c.Status(fiber.StatusBadRequest).JSON(fiber.Map{ + "success": false, + "message": "missing required fields", + }) + } + + update_time, _ := time.Parse(time.RFC3339, time.Now().Format(time.RFC3339)) + update := bson.M{ + "$set": bson.M{ + "schooldata.homeroom": data["homeroom"], + "updated_at": update_time, + }, + } + + _, updateErr := StudentCollection.UpdateOne( + ctx, + bson.M{"schooldata.sid": data["sid"]}, + update, + ) + if updateErr != nil { + cancel() + return c.Status(fiber.StatusInternalServerError).JSON(fiber.Map{ + "success": false, + "message": "the student could not be updated", + "error": updateErr, + }) + } + defer cancel() + + return c.Status(fiber.StatusOK).JSON(fiber.Map{ + "success": true, + "message": "successfully updated student", + }) +} + +func UpdateStudentPassword(c *fiber.Ctx) error { + var data map[string]string + ctx, cancel := context.WithTimeout(context.Background(), 30*time.Second) + + if err := c.BodyParser(&data); err != nil { + cancel() + return c.Status(fiber.StatusInternalServerError).JSON(fiber.Map{ + "success": false, + "message": "Failed to parse body", + "error": err, + }) + } + + cookie := c.Cookies("jwt") + + token, err := jwt.ParseWithClaims(cookie, &jwt.StandardClaims{}, func(token *jwt.Token) (interface{}, error) { + return []byte(SecretKey), nil + }) + if err != nil { + cancel() + return c.Status(fiber.StatusUnauthorized).JSON(fiber.Map{ + "success": false, + "message": "not authorized", + }) + } + + claims := token.Claims.(*jwt.StandardClaims) + + var student models.Student + findErr := StudentCollection.FindOne(ctx, bson.M{"schooldata.sid": claims.Issuer}).Decode(&student) + if findErr != nil { + cancel() + return c.Status(fiber.StatusBadRequest).JSON(fiber.Map{ + "success": false, + "message": "student not found", + }) + } + + // Check required fields are included + if data["password"] == "" || data["newpassword1"] == "" || data["newpassword2"] == "" { + cancel() + return c.Status(fiber.StatusBadRequest).JSON(fiber.Map{ + "success": false, + "message": "missing required fields", + }) + } + + if !student.ComparePasswords(data["password"]) { + cancel() + return c.Status(fiber.StatusOK).JSON(fiber.Map{ + "success": false, + "message": "Your password is incorrect", + }) + } + + if data["newpassword1"] != data["newpassword2"] { + cancel() + return c.Status(fiber.StatusBadRequest).JSON(fiber.Map{ + "success": false, + "message": "Your new password must match", + }) + } + + if student.UsedPassword(data["newpassword1"]) { + cancel() + return c.Status(fiber.StatusBadRequest).JSON(fiber.Map{ + "success": false, + "message": "Your new password cannot be the same as a previous password", + }) + } + + update_time, _ := time.Parse(time.RFC3339, time.Now().Format(time.RFC3339)) + update := bson.M{ + "$set": bson.M{ + "accountdata.password": student.HashPassword(data["newpassword1"]), + "accountdata.temppassword": false, // If it were a temp password, its not now + "updated_at": update_time, + }, + "$push": bson.M{ + "accountdata.hashhistory": student.HashPassword(data["newpassword1"]), + }, + } + + _, updateErr := StudentCollection.UpdateOne( + ctx, + bson.M{"schooldata.sid": claims.Issuer}, + update, + ) + if updateErr != nil { + cancel() + return c.Status(fiber.StatusInternalServerError).JSON(fiber.Map{ + "success": false, + "message": "the student password could not be updated", + "error": updateErr, + }) + } + defer cancel() + + // Alert email the password has changed + subject := "Password Changed" + receiver := student.PersonalData.Email + r := NewRequest([]string{receiver}, subject) + + if sent := r.Send("./templates/selfPasswordChanged.html", map[string]string{"username": student.PersonalData.FirstName}); !sent { + return c.Status(fiber.StatusInternalServerError).JSON(fiber.Map{ + "success": false, + "message": "failed to send email to student", + }) + } + + return c.Status(fiber.StatusOK).JSON(fiber.Map{ + "success": true, + "message": "successfully updated student password", + }) +} + +// This is for students to reset their password if they are unable to login +func ResetStudentPassword(c *fiber.Ctx) error { + var data map[string]string + ctx, cancel := context.WithTimeout(context.Background(), 30*time.Second) + + if err := c.BodyParser(&data); err != nil { + cancel() + return c.Status(fiber.StatusInternalServerError).JSON(fiber.Map{ + "success": false, + "message": "Failed to parse body", + "error": err, + }) + } + + // Check required fields are included (email must be personal email) + if data["sid"] == "" || data["email"] == "" { + cancel() + return c.Status(fiber.StatusBadRequest).JSON(fiber.Map{ + "success": false, + "message": "missing required fields", + }) + } + + var student models.Student + findErr := StudentCollection.FindOne(context.TODO(), bson.M{"schooldata.sid": data["sid"]}).Decode(&student) + if findErr != nil { + cancel() + return c.Status(fiber.StatusBadRequest).JSON(fiber.Map{ + "success": false, + "message": "student not found", + }) + } + + if student.PersonalData.Email != data["email"] { + cancel() + return c.Status(fiber.StatusOK).JSON(fiber.Map{ + "success": false, + "message": "Your personal email is incorrect", + }) + } + + tempPass := student.GeneratePassword(12, 1, 1, 1) + update_time, _ := time.Parse(time.RFC3339, time.Now().Format(time.RFC3339)) + update := bson.M{ + "$set": bson.M{ + "accountdata.password": student.HashPassword(tempPass), + "accountdata.temppassword": true, + "updated_at": update_time, + }, + } + + result, updateErr := StudentCollection.UpdateOne( + ctx, + bson.M{"schooldata.sid": data["sid"]}, + update, + ) + if updateErr != nil { + cancel() + return c.Status(fiber.StatusInternalServerError).JSON(fiber.Map{ + "success": false, + "message": "the student password could not be updated", + "error": updateErr, + }) + } + defer cancel() + + // Send student personal email temp password + subject := "Password Changed" + receiver := student.PersonalData.Email + r := NewRequest([]string{receiver}, subject) + + if sent := r.Send("./templates/passwordChanged.html", map[string]string{"username": student.PersonalData.FirstName, "password": tempPass}); !sent { + return c.Status(fiber.StatusInternalServerError).JSON(fiber.Map{ + "success": false, + "message": "Could not send password to students email", + }) + } + + return c.Status(fiber.StatusOK).JSON(fiber.Map{ + "success": true, + "message": "successfully updated student password", + "result": result, + }) +} + +func UpdateStudentLocker(c *fiber.Ctx) error { + var data map[string]string + ctx, cancel := context.WithTimeout(context.Background(), 30*time.Second) + + if err := c.BodyParser(&data); err != nil { + cancel() + return c.Status(fiber.StatusInternalServerError).JSON(fiber.Map{ + "success": false, + "message": "Failed to parse body", + "error": err, + }) + } + + // Ensure Authenticated admin sent request + if verified, _ := AuthenticateUser(c, 3); !verified { + cancel() + return c.Status(fiber.StatusUnauthorized).JSON(fiber.Map{ + "success": false, + "message": "Unauthorized: only an admin can perform this action", + }) + } + + // Check id and locker are included + if data["sid"] == "" || data["lockernumber"] == "" { + cancel() + return c.Status(fiber.StatusBadRequest).JSON(fiber.Map{ + "success": false, + "message": "missing required fields", + }) + } + + var locker models.Locker + err := LockerCollection.FindOne(ctx, bson.M{"lockernumber": data["lockernumber"]}).Decode(&locker) + if err != nil { + cancel() + return c.Status(fiber.StatusBadRequest).JSON(fiber.Map{ + "success": false, + "message": "locker not found", + "error": err, + }) + } + + update_time, _ := time.Parse(time.RFC3339, time.Now().Format(time.RFC3339)) + update := bson.M{ + "$set": bson.M{ + "schooldata.locker": locker.ID, + "updated_at": update_time, + }, + } + + _, updateErr := StudentCollection.UpdateOne( + ctx, + bson.M{"schooldata.sid": data["sid"]}, + update, + ) + if updateErr != nil { + cancel() + return c.Status(fiber.StatusInternalServerError).JSON(fiber.Map{ + "success": false, + "message": "the student could not be updated", + "error": updateErr, + }) + } + defer cancel() + + return c.Status(fiber.StatusOK).JSON(fiber.Map{ + "success": true, + "message": "successfully updated student", + }) +} + +func UpdateStudentAddress(c *fiber.Ctx) error { + var data map[string]string + ctx, cancel := context.WithTimeout(context.Background(), 30*time.Second) + + if err := c.BodyParser(&data); err != nil { + cancel() + return c.Status(fiber.StatusInternalServerError).JSON(fiber.Map{ + "success": false, + "message": "Failed to parse body", + "error": err, + }) + } + + // Ensure Authenticated admin sent request + if verified, _ := AuthenticateUser(c, 3); !verified { + cancel() + return c.Status(fiber.StatusUnauthorized).JSON(fiber.Map{ + "success": false, + "message": "Unauthorized: only an admin can perform this action", + }) + } + + // Check required fields are included + if data["sid"] == "" || data["address"] == "" || data["city"] == "" || data["province"] == "" || data["postal"] == "" { + cancel() + return c.Status(fiber.StatusBadRequest).JSON(fiber.Map{ + "success": false, + "message": "missing required fields", + }) + } + + update_time, _ := time.Parse(time.RFC3339, time.Now().Format(time.RFC3339)) + update := bson.M{ + "$set": bson.M{ + "personaldata.address": data["address"], + "personaldata.city": data["city"], + "personaldata.province": data["province"], + "personaldata.postal": data["postal"], + "updated_at": update_time, + }, + } + + _, updateErr := StudentCollection.UpdateOne( + ctx, + bson.M{"sid": data["sid"]}, + update, + ) + if updateErr != nil { + cancel() + return c.Status(fiber.StatusInternalServerError).JSON(fiber.Map{ + "success": false, + "message": "the student could not be updated", + "error": updateErr, + }) + } + defer cancel() + + return c.Status(fiber.StatusOK).JSON(fiber.Map{ + "success": true, + "message": "successfully updated student", + }) +} + +// In the case a student gets held back a grade, we need to update their YOG (Year of Graduation) +func UpdateStudentYOG(c *fiber.Ctx) error { + var data map[string]interface{} + ctx, cancel := context.WithTimeout(context.Background(), 30*time.Second) + + if err := c.BodyParser(&data); err != nil { + cancel() + return c.Status(fiber.StatusInternalServerError).JSON(fiber.Map{ + "success": false, + "message": "Failed to parse body", + "error": err, + }) + } + + // Ensure Authenticated admin sent request + if verified, _ := AuthenticateUser(c, 3); !verified { + cancel() + return c.Status(fiber.StatusUnauthorized).JSON(fiber.Map{ + "success": false, + "message": "Unauthorized: only an admin can perform this action", + }) + } + + // Check required fields are included + if data["sid"] == "" || data["yog"] == nil { + cancel() + return c.Status(fiber.StatusBadRequest).JSON(fiber.Map{ + "success": false, + "message": "missing required fields", + }) + } + + var student models.Student + findErr := StudentCollection.FindOne(context.TODO(), bson.M{"schooldata.sid": data["sid"].(string)}).Decode(&student) + if findErr != nil { + cancel() + return c.Status(fiber.StatusBadRequest).JSON(fiber.Map{ + "success": false, + "message": "student not found", + }) + } + + update_time, _ := time.Parse(time.RFC3339, time.Now().Format(time.RFC3339)) + update := bson.M{ + "$set": bson.M{ + "schooldata.yog": data["yog"].(int), + "updated_at": update_time, + }, + } + + result, updateErr := StudentCollection.UpdateOne( + ctx, + bson.M{"schooldata.sid": data["sid"].(string)}, + update, + ) + if updateErr != nil { + cancel() + return c.Status(fiber.StatusInternalServerError).JSON(fiber.Map{ + "success": false, + "message": "the student could not be updated", + "error": updateErr, + }) + } + defer cancel() + + return c.Status(fiber.StatusOK).JSON(fiber.Map{ + "success": true, + "message": "successfully updated student", + "result": result, + }) +} + +func RemoveStudentContact(c *fiber.Ctx) error { + var data map[string]string + ctx, cancel := context.WithTimeout(context.Background(), 30*time.Second) + + if err := c.BodyParser(&data); err != nil { + cancel() + return c.Status(fiber.StatusInternalServerError).JSON(fiber.Map{ + "success": false, + "message": "Failed to parse body", + "error": err, + }) + } + + // Ensure Authenticated admin sent request + if verified, _ := AuthenticateUser(c, 3); !verified { + cancel() + return c.Status(fiber.StatusUnauthorized).JSON(fiber.Map{ + "success": false, + "message": "Unauthorized: only an admin can perform this action", + }) + } + + // Check id and contact id are included + if data["sid"] == "" || data["contactid"] == "" { + cancel() + return c.Status(fiber.StatusBadRequest).JSON(fiber.Map{ + "success": false, + "message": "missing required fields", + }) + } + + var contact models.Contact + err := ContactCollection.FindOne(ctx, bson.M{"_id": data["contactid"]}).Decode(&contact) + if err != nil { + cancel() + return c.Status(fiber.StatusBadRequest).JSON(fiber.Map{ + "success": false, + "message": "contact not found", + "error": err, + }) + } + + update_time, _ := time.Parse(time.RFC3339, time.Now().Format(time.RFC3339)) + update := bson.M{ + "$set": bson.M{ + "updated_at": update_time, + }, + "$pull": bson.M{ + "personaldata.contacts": contact.ID, + }, + } + + result, updateErr := StudentCollection.UpdateOne( + ctx, + bson.M{"schooldata.sid": data["sid"]}, + update, + ) + if updateErr != nil { + cancel() + return c.Status(fiber.StatusInternalServerError).JSON(fiber.Map{ + "success": false, + "message": "the contact could not be added", + "error": updateErr, + }) + } + defer cancel() + + return c.Status(fiber.StatusOK).JSON(fiber.Map{ + "success": true, + "message": "successfully added contact", + "result": result, + }) +} + +func AddStudentContact(c *fiber.Ctx) error { + var data map[string]string + ctx, cancel := context.WithTimeout(context.Background(), 30*time.Second) + + if err := c.BodyParser(&data); err != nil { + cancel() + return c.Status(fiber.StatusInternalServerError).JSON(fiber.Map{ + "success": false, + "message": "Failed to parse body", + "error": err, + }) + } + + // Ensure Authenticated admin sent request + if verified, _ := AuthenticateUser(c, 3); !verified { + cancel() + return c.Status(fiber.StatusUnauthorized).JSON(fiber.Map{ + "success": false, + "message": "Unauthorized: only an admin can perform this action", + }) + } + + // Check id and contact id are included + if data["sid"] == "" || data["contactid"] == "" { + cancel() + return c.Status(fiber.StatusBadRequest).JSON(fiber.Map{ + "success": false, + "message": "missing required fields", + }) + } + + var contact models.Contact + err := ContactCollection.FindOne(ctx, bson.M{"_id": data["contactid"]}).Decode(&contact) + if err != nil { + cancel() + return c.Status(fiber.StatusBadRequest).JSON(fiber.Map{ + "success": false, + "message": "contact not found", + "error": err, + }) + } + + update_time, _ := time.Parse(time.RFC3339, time.Now().Format(time.RFC3339)) + update := bson.M{ + "$set": bson.M{ + "updated_at": update_time, + }, + "$push": bson.M{ + "personaldata.contacts": contact.ID, + }, + } + + result, updateErr := StudentCollection.UpdateOne( + ctx, + bson.M{"schooldata.sid": data["sid"]}, + update, + ) + if updateErr != nil { + cancel() + return c.Status(fiber.StatusInternalServerError).JSON(fiber.Map{ + "success": false, + "message": "the contact could not be added", + "error": updateErr, + }) + } + defer cancel() + + return c.Status(fiber.StatusOK).JSON(fiber.Map{ + "success": true, + "message": "successfully added contact", + "result": result, + }) +} + +func UpdateStudentPhoto(c *fiber.Ctx) error { + ctx, cancel := context.WithTimeout(context.Background(), 30*time.Second) + + //Ensure Authenticated admin sent request + if verified, _ := AuthenticateUser(c, 3); !verified { + cancel() + return c.Status(fiber.StatusUnauthorized).JSON(fiber.Map{ + "success": false, + "message": "Unauthorized: only an admin can perform this action", + }) + } + + sid := c.FormValue("sid") + if sid == "" { + cancel() + return c.Status(fiber.StatusBadRequest).JSON(fiber.Map{ + "success": false, + "message": "missing required fields", + }) + } + + // Get student + var student models.Student + findErr := StudentCollection.FindOne(context.TODO(), bson.M{"schooldata.sid": sid}).Decode(&student) + if findErr != nil { + cancel() + return c.Status(fiber.StatusBadRequest).JSON(fiber.Map{ + "success": false, + "message": "the student could not be found", + "error": findErr, + }) + } + + // Collect image + file, err := c.FormFile("image") + if err != nil { + cancel() + return c.Status(fiber.StatusInternalServerError).JSON(fiber.Map{ + "success": false, + "message": "the image could not be retrieved", + "error": err, + }) + } + + // Get student photo + var photo models.Photo + findErr = ImageCollection.FindOne(context.TODO(), bson.M{"name": student.SchoolData.PhotoName}).Decode(&photo) + if findErr != nil { + cancel() + return c.Status(fiber.StatusInternalServerError).JSON(fiber.Map{ + "success": false, + "message": "the student image could not be found", + "error": findErr, + }) + } + + // Save image to local + uniqueId := uuid.New() + filename := strings.Replace(uniqueId.String(), "-", "", -1) + fileExt := strings.Split(file.Filename, ".")[1] + image := fmt.Sprintf("%s.%s", filename, fileExt) + err = c.SaveFile(file, fmt.Sprintf("./database/images/%s", image)) + if err != nil { + cancel() + return c.Status(fiber.StatusInternalServerError).JSON(fiber.Map{ + "success": false, + "message": "the image could not be saved", + "error": err, + }) + } + + // Read the entire file into a byte slice + bytes, err := ioutil.ReadFile(fmt.Sprintf("./database/images/%s", image)) + if err != nil { + cancel() + return c.Status(fiber.StatusInternalServerError).JSON(fiber.Map{ + "success": false, + "message": "the image could not be read", + "error": err, + }) + } + + var base64Encoding string = toBase64(bytes) + + // Update image name and base64 data + update_time, _ := time.Parse(time.RFC3339, time.Now().Format(time.RFC3339)) + update := bson.M{ + "$set": bson.M{ + "base64": base64Encoding, + "updated_at": update_time, + }, + } + _, updateErr := ImageCollection.UpdateOne( + ctx, + bson.M{"_id": photo.ID}, + update, + ) + if updateErr != nil { + cancel() + return c.Status(fiber.StatusInternalServerError).JSON(fiber.Map{ + "success": false, + "message": "the image could not be updated", + "error": updateErr, + }) + } + defer cancel() + + // Remove local image + os.Remove(fmt.Sprintf("./database/images/%s", image)) + + return c.Status(fiber.StatusOK).JSON(fiber.Map{ + "success": true, + "message": "successfully updated student photo", + }) +} + +func UpdateStudentEmail(c *fiber.Ctx) error { + var data map[string]string + ctx, cancel := context.WithTimeout(context.Background(), 30*time.Second) + + if err := c.BodyParser(&data); err != nil { + cancel() + return c.Status(fiber.StatusInternalServerError).JSON(fiber.Map{ + "success": false, + "message": "Failed to parse body", + "error": err, + }) + } + + var sid string + var verifiedStudent bool + verifiedAdmin, _ := AuthenticateUser(c, 3) + verifiedStudent, sid = AuthenticateUser(c, 1) + // Ensure Authenticated admin sent request + if !verifiedAdmin && !verifiedStudent { + cancel() + return c.Status(fiber.StatusUnauthorized).JSON(fiber.Map{ + "success": false, + "message": "Unauthorized: only an admin or teacher can perform this action", + }) + } + + if verifiedAdmin && data["sid"] == "" { + cancel() + return c.Status(fiber.StatusBadRequest).JSON(fiber.Map{ + "success": false, + "message": "missing required fields", + }) + } else if verifiedAdmin { + sid = data["sid"] + } + + // Check required fields are included + if data["email"] == "" { + cancel() + return c.Status(fiber.StatusBadRequest).JSON(fiber.Map{ + "success": false, + "message": "missing required fields", + }) + } + + update_time, _ := time.Parse(time.RFC3339, time.Now().Format(time.RFC3339)) + update := bson.M{ + "$set": bson.M{ + "personaldata.email": data["email"], + "updated_at": update_time, + }, + } + + result, updateErr := StudentCollection.UpdateOne( + ctx, + bson.M{"schooldata.sid": sid}, + update, + ) + if updateErr != nil { + cancel() + return c.Status(fiber.StatusInternalServerError).JSON(fiber.Map{ + "success": false, + "message": "the student could not be updated", + "error": updateErr, + }) + } + defer cancel() + + return c.Status(fiber.StatusOK).JSON(fiber.Map{ + "success": true, + "message": "successfully updated student", + "result": result, + }) +} diff --git a/controllers/update/teacherController.go b/controllers/update/teacherController.go new file mode 100644 index 0000000..979c190 --- /dev/null +++ b/controllers/update/teacherController.go @@ -0,0 +1,622 @@ +package update + +import ( + "context" + "fmt" + "io/ioutil" + "os" + "strings" + "time" + + . "github.com/SowinskiBraeden/school-management-api/controllers" + + "github.com/SowinskiBraeden/school-management-api/models" + "github.com/gofiber/fiber/v2" + "github.com/golang-jwt/jwt" + "github.com/google/uuid" + "go.mongodb.org/mongo-driver/bson" +) + +/* + Far later on this function is going to be completely automated. + Instead of an admin sending a request to update the homeroom of + a student or teacher, the system will take the room number of + the teacher's or student's Block 2 class from their schedule. + + Though this function would remain for students only, for example + a student requests a course change, if its their block 2 the + admin would have to alter their homeroom to be the new class + number. +*/ +func UpdateTeacherHomeroom(c *fiber.Ctx) error { + var data map[string]string + ctx, cancel := context.WithTimeout(context.Background(), 30*time.Second) + + if err := c.BodyParser(&data); err != nil { + cancel() + return c.Status(fiber.StatusInternalServerError).JSON(fiber.Map{ + "success": false, + "message": "Failed to parse body", + "error": err, + }) + } + + // Ensure Authenticated admin sent request + if verified, _ := AuthenticateUser(c, 3); !verified { + cancel() + return c.Status(fiber.StatusUnauthorized).JSON(fiber.Map{ + "success": false, + "message": "Unauthorized: only an admin can perform this action", + }) + } + + // Check required fields are included + if data["tid"] == "" || data["homeroom"] == "" { + cancel() + return c.Status(fiber.StatusBadRequest).JSON(fiber.Map{ + "success": false, + "message": "missing required fields", + }) + } + + update_time, _ := time.Parse(time.RFC3339, time.Now().Format(time.RFC3339)) + update := bson.M{ + "$set": bson.M{ + "schooldata.homeroom": data["homeroom"], + "updated_at": update_time, + }, + } + + _, updateErr := TeacherCollection.UpdateOne( + ctx, + bson.M{"schooldata.tid": data["tid"]}, + update, + ) + if updateErr != nil { + cancel() + return c.Status(fiber.StatusInternalServerError).JSON(fiber.Map{ + "success": false, + "message": "the teacher could not be updated", + "error": updateErr, + }) + } + defer cancel() + + return c.Status(fiber.StatusOK).JSON(fiber.Map{ + "success": true, + "message": "successfully updated teacher", + }) +} + +func UpdateTeacherPassword(c *fiber.Ctx) error { + var data map[string]string + ctx, cancel := context.WithTimeout(context.Background(), 30*time.Second) + + if err := c.BodyParser(&data); err != nil { + cancel() + return c.Status(fiber.StatusInternalServerError).JSON(fiber.Map{ + "success": false, + "message": "Failed to parse body", + "error": err, + }) + } + + cookie := c.Cookies("jwt") + + token, err := jwt.ParseWithClaims(cookie, &jwt.StandardClaims{}, func(token *jwt.Token) (interface{}, error) { + return []byte(SecretKey), nil + }) + if err != nil { + cancel() + return c.Status(fiber.StatusUnauthorized).JSON(fiber.Map{ + "success": false, + "message": "not authorized", + }) + } + + claims := token.Claims.(*jwt.StandardClaims) + + var teacher models.Teacher + findErr := TeacherCollection.FindOne(ctx, bson.M{"schooldata.tid": claims.Issuer}).Decode(&teacher) + if findErr != nil { + cancel() + return c.Status(fiber.StatusBadRequest).JSON(fiber.Map{ + "success": false, + "message": "teacher not found", + }) + } + + // Check required fields are included + if data["password"] == "" || data["newpassword1"] == "" || data["newpassword2"] == "" { + cancel() + return c.Status(fiber.StatusBadRequest).JSON(fiber.Map{ + "success": false, + "message": "missing required fields", + }) + } + + if !teacher.ComparePasswords(data["password"]) { + cancel() + return c.Status(fiber.StatusOK).JSON(fiber.Map{ + "success": false, + "message": "Your password is incorrect", + }) + } + + if data["newpassword1"] != data["newpassword2"] { + cancel() + return c.Status(fiber.StatusBadRequest).JSON(fiber.Map{ + "success": false, + "message": "Your new passwords must match", + }) + } + + if teacher.UsedPassword(data["newpassword1"]) { + cancel() + return c.Status(fiber.StatusBadRequest).JSON(fiber.Map{ + "success": false, + "message": "Your new password cannot be the same as a previous password", + }) + } + + update_time, _ := time.Parse(time.RFC3339, time.Now().Format(time.RFC3339)) + update := bson.M{ + "$set": bson.M{ + "accountdata.password": teacher.HashPassword(data["newpassword1"]), + "accountdata.temppassword": false, // If it were a temp password, its not now + "updated_at": update_time, + }, + "$push": bson.M{ + "accountdata.hashhistory": teacher.HashPassword(data["newpassword1"]), + }, + } + + _, updateErr := TeacherCollection.UpdateOne( + ctx, + bson.M{"schooldata.tid": claims.Issuer}, + update, + ) + if updateErr != nil { + cancel() + return c.Status(fiber.StatusInternalServerError).JSON(fiber.Map{ + "success": false, + "message": "the teacher password could not be updated", + "error": updateErr, + }) + } + defer cancel() + + subject := "Password Changed" + receiver := teacher.PersonalData.Email + r := NewRequest([]string{receiver}, subject) + + if sent := r.Send("./templates/selfPasswordChanged.html", map[string]string{"username": teacher.PersonalData.FirstName}); !sent { + return c.Status(fiber.StatusInternalServerError).JSON(fiber.Map{ + "success": false, + "message": "Could not send password to teachers email", + }) + } + + return c.Status(fiber.StatusOK).JSON(fiber.Map{ + "success": true, + "message": "successfully updated teacher password", + }) +} + +func ResetTeacherPassword(c *fiber.Ctx) error { + var data map[string]string + ctx, cancel := context.WithTimeout(context.Background(), 30*time.Second) + + if err := c.BodyParser(&data); err != nil { + cancel() + return c.Status(fiber.StatusInternalServerError).JSON(fiber.Map{ + "success": false, + "message": "Failed to parse body", + "error": err, + }) + } + + // Check required fields are included (email must be personal email) + if data["tid"] == "" || data["email"] == "" { + cancel() + return c.Status(fiber.StatusBadRequest).JSON(fiber.Map{ + "success": false, + "message": "missing required fields", + }) + } + + var teacher models.Teacher + findErr := TeacherCollection.FindOne(context.TODO(), bson.M{"schooldata.tid": data["tid"]}).Decode(&teacher) + if findErr != nil { + cancel() + return c.Status(fiber.StatusBadRequest).JSON(fiber.Map{ + "success": false, + "message": "teacher not found", + }) + } + + if teacher.PersonalData.Email != data["email"] { + cancel() + return c.Status(fiber.StatusOK).JSON(fiber.Map{ + "success": false, + "message": "Your personal email is incorrect", + }) + } + + tempPass := teacher.GeneratePassword(12, 1, 1, 1) + update_time, _ := time.Parse(time.RFC3339, time.Now().Format(time.RFC3339)) + update := bson.M{ + "$set": bson.M{ + "accountdata.password": teacher.HashPassword(tempPass), + "accountdata.temppassword": true, + "updated_at": update_time, + }, + } + + result, updateErr := StudentCollection.UpdateOne( + ctx, + bson.M{"schooldata.tid": data["tid"]}, + update, + ) + if updateErr != nil { + cancel() + return c.Status(fiber.StatusInternalServerError).JSON(fiber.Map{ + "success": false, + "message": "the teacher password could not be updated", + "error": updateErr, + }) + } + defer cancel() + + // Send teacher personal email temp password + subject := "Password Changed" + receiver := teacher.PersonalData.Email + r := NewRequest([]string{receiver}, subject) + + if sent := r.Send("./templates/passwordChanged.html", map[string]string{"username": teacher.PersonalData.FirstName, "password": tempPass}); !sent { + return c.Status(fiber.StatusInternalServerError).JSON(fiber.Map{ + "success": false, + "message": "Could not send password to teachers email", + }) + } + + return c.Status(fiber.StatusOK).JSON(fiber.Map{ + "success": true, + "message": "successfully updated teacher password", + "result": result, + }) +} + +func UpdateTeacherAddress(c *fiber.Ctx) error { + var data map[string]string + ctx, cancel := context.WithTimeout(context.Background(), 30*time.Second) + + if err := c.BodyParser(&data); err != nil { + cancel() + return c.Status(fiber.StatusInternalServerError).JSON(fiber.Map{ + "success": false, + "message": "Failed to parse body", + "error": err, + }) + } + + // Ensure Authenticated admin sent request + if verified, _ := AuthenticateUser(c, 3); !verified { + cancel() + return c.Status(fiber.StatusUnauthorized).JSON(fiber.Map{ + "success": false, + "message": "Unauthorized: only an admin can perform this action", + }) + } + + // Check required fields are included + if data["tid"] == "" || data["address"] == "" || data["city"] == "" || data["province"] == "" || data["postal"] == "" { + cancel() + return c.Status(fiber.StatusBadRequest).JSON(fiber.Map{ + "success": false, + "message": "missing required fields", + }) + } + + update_time, _ := time.Parse(time.RFC3339, time.Now().Format(time.RFC3339)) + update := bson.M{ + "$set": bson.M{ + "personaldata.address": data["address"], + "personaldata.city": data["city"], + "personaldata.province": data["province"], + "personaldata.postal": data["postal"], + "updated_at": update_time, + }, + } + + _, updateErr := TeacherCollection.UpdateOne( + ctx, + bson.M{"schooldata.tid": data["tid"]}, + update, + ) + if updateErr != nil { + cancel() + return c.Status(fiber.StatusInternalServerError).JSON(fiber.Map{ + "success": false, + "message": "the teacher could not be updated", + "error": updateErr, + }) + } + defer cancel() + + return c.Status(fiber.StatusOK).JSON(fiber.Map{ + "success": true, + "message": "successfully updated teacher", + }) +} + +func UpdateTeacherPhoto(c *fiber.Ctx) error { + ctx, cancel := context.WithTimeout(context.Background(), 30*time.Second) + + //Ensure Authenticated admin sent request + if verified, _ := AuthenticateUser(c, 3); !verified { + cancel() + return c.Status(fiber.StatusUnauthorized).JSON(fiber.Map{ + "success": false, + "message": "Unauthorized: only an admin can perform this action", + }) + } + + tid := c.FormValue("tid") + if tid == "" { + cancel() + return c.Status(fiber.StatusBadRequest).JSON(fiber.Map{ + "success": false, + "message": "missing required fields", + }) + } + + // Get teacher + var teacher models.Teacher + findErr := TeacherCollection.FindOne(context.TODO(), bson.M{"schooldata.tid": tid}).Decode(&teacher) + if findErr != nil { + cancel() + return c.Status(fiber.StatusBadRequest).JSON(fiber.Map{ + "success": false, + "message": "the teacher could not be found", + "error": findErr, + }) + } + + // Collect image + file, err := c.FormFile("image") + if err != nil { + cancel() + return c.Status(fiber.StatusInternalServerError).JSON(fiber.Map{ + "success": false, + "message": "the image could not be retrieved", + "error": err, + }) + } + + // Get student photo + var photo models.Photo + findErr = ImageCollection.FindOne(context.TODO(), bson.M{"name": teacher.SchoolData.PhotoName}).Decode(&photo) + if findErr != nil { + cancel() + return c.Status(fiber.StatusInternalServerError).JSON(fiber.Map{ + "success": false, + "message": "the student image could not be found", + "error": findErr, + }) + } + + // Save image to local + uniqueId := uuid.New() + filename := strings.Replace(uniqueId.String(), "-", "", -1) + fileExt := strings.Split(file.Filename, ".")[1] + image := fmt.Sprintf("%s.%s", filename, fileExt) + err = c.SaveFile(file, fmt.Sprintf("./database/images/%s", image)) + if err != nil { + cancel() + return c.Status(fiber.StatusInternalServerError).JSON(fiber.Map{ + "success": false, + "message": "the image could not be saved", + "error": err, + }) + } + + // Read the entire file into a byte slice + bytes, err := ioutil.ReadFile(fmt.Sprintf("./database/images/%s", image)) + if err != nil { + cancel() + return c.Status(fiber.StatusInternalServerError).JSON(fiber.Map{ + "success": false, + "message": "the image could not be read", + "error": err, + }) + } + + var base64Encoding string = toBase64(bytes) + + // Update image name and base64 data + update_time, _ := time.Parse(time.RFC3339, time.Now().Format(time.RFC3339)) + update := bson.M{ + "$set": bson.M{ + "base64": base64Encoding, + "updated_at": update_time, + }, + } + _, updateErr := ImageCollection.UpdateOne( + ctx, + bson.M{"_id": photo.ID}, + update, + ) + if updateErr != nil { + cancel() + return c.Status(fiber.StatusInternalServerError).JSON(fiber.Map{ + "success": false, + "message": "the image could not be updated", + "error": updateErr, + }) + } + defer cancel() + + // Remove local image + os.Remove(fmt.Sprintf("./database/images/%s", image)) + + return c.Status(fiber.StatusOK).JSON(fiber.Map{ + "success": true, + "message": "successfully updated teacher photo", + }) +} + +func UpdateTeacherEmail(c *fiber.Ctx) error { + var data map[string]string + ctx, cancel := context.WithTimeout(context.Background(), 30*time.Second) + + if err := c.BodyParser(&data); err != nil { + cancel() + return c.Status(fiber.StatusInternalServerError).JSON(fiber.Map{ + "success": false, + "message": "Failed to parse body", + "error": err, + }) + } + + var tid string + var verifiedTeacher bool + + verifiedAdmin, _ := AuthenticateUser(c, 3) + verifiedTeacher, tid = AuthenticateUser(c, 2) + // Ensure Authenticated admin sent request + if !verifiedAdmin && !verifiedTeacher { + cancel() + return c.Status(fiber.StatusUnauthorized).JSON(fiber.Map{ + "success": false, + "message": "Unauthorized: only an admin or teacher can perform this action", + }) + } + + if verifiedAdmin && data["tid"] == "" { + cancel() + return c.Status(fiber.StatusBadRequest).JSON(fiber.Map{ + "success": false, + "message": "missing required fields", + }) + } else if verifiedAdmin { + tid = data["tid"] + } + + // Check required fields are included + if data["email"] == "" { + cancel() + return c.Status(fiber.StatusBadRequest).JSON(fiber.Map{ + "success": false, + "message": "missing required fields", + }) + } + + update_time, _ := time.Parse(time.RFC3339, time.Now().Format(time.RFC3339)) + update := bson.M{ + "$set": bson.M{ + "personaldata.email": data["email"], + "updated_at": update_time, + }, + } + + _, updateErr := TeacherCollection.UpdateOne( + ctx, + bson.M{"schooldata.tid": tid}, + update, + ) + if updateErr != nil { + cancel() + return c.Status(fiber.StatusInternalServerError).JSON(fiber.Map{ + "success": false, + "message": "the teacher could not be updated", + "error": updateErr, + }) + } + defer cancel() + + return c.Status(fiber.StatusOK).JSON(fiber.Map{ + "success": true, + "message": "successfully updated teacher", + }) +} + +func UpdateTeacherName(c *fiber.Ctx) error { + var data map[string]string + ctx, cancel := context.WithTimeout(context.Background(), 30*time.Second) + + if err := c.BodyParser(&data); err != nil { + cancel() + return c.Status(fiber.StatusInternalServerError).JSON(fiber.Map{ + "success": false, + "message": "Failed to parse body", + "error": err, + }) + } + + // Ensure Authenticated admin sent request + if verified, _ := AuthenticateUser(c, 3); !verified { + cancel() + return c.Status(fiber.StatusUnauthorized).JSON(fiber.Map{ + "success": false, + "message": "Unauthorized: only an admin can perform this action", + }) + } + + // Check id and names are included + if data["tid"] == "" || data["firstname"] == "" || data["lastname"] == "" { + cancel() + return c.Status(fiber.StatusBadRequest).JSON(fiber.Map{ + "success": false, + "message": "missing required fields", + }) + } + + // Get teacher + var teacher models.Teacher + findErr := TeacherCollection.FindOne(ctx, bson.M{"schooldata.tid": data["tid"]}).Decode(&teacher) + if findErr != nil { + cancel() + return c.Status(fiber.StatusBadRequest).JSON(fiber.Map{ + "success": false, + "message": "the teacher could not be found", + "error": findErr, + }) + } + + var middlename string = "" + + if data["middlename"] != "" { + middlename = data["middlename"] + } + + update_time, _ := time.Parse(time.RFC3339, time.Now().Format(time.RFC3339)) + update := bson.M{ + "$set": bson.M{ + "personaldata.firstname": data["firstname"], + "personaldata.middlename": middlename, + "personaldata.lastname": data["lastname"], + "updated_at": update_time, + }, + } + + _, updateErr := TeacherCollection.UpdateOne( + ctx, + bson.M{"schooldata.tid": data["tid"]}, + update, + ) + if updateErr != nil { + cancel() + return c.Status(fiber.StatusInternalServerError).JSON(fiber.Map{ + "success": false, + "message": "the teacher could not be updated", + "error": updateErr, + }) + } + defer cancel() + + return c.Status(fiber.StatusOK).JSON(fiber.Map{ + "success": true, + "message": "successfully updated teacher", + }) +} diff --git a/controllers/updateController.go b/controllers/updateController.go deleted file mode 100644 index b3e4d82..0000000 --- a/controllers/updateController.go +++ /dev/null @@ -1,2585 +0,0 @@ -package controllers - -import ( - "context" - "encoding/base64" - "fmt" - "io/ioutil" - "os" - "strings" - "time" - - "github.com/SowinskiBraeden/school-management-api/database" - "github.com/SowinskiBraeden/school-management-api/models" - - "github.com/gofiber/fiber/v2" - "github.com/golang-jwt/jwt" - "github.com/google/uuid" - "go.mongodb.org/mongo-driver/bson" - "go.mongodb.org/mongo-driver/mongo" -) - -var lockerCollection *mongo.Collection = database.OpenCollection(database.Client, "lockers") -var imageCollection *mongo.Collection = database.OpenCollection(database.Client, "images") - -func toBase64(b []byte) string { - return base64.StdEncoding.EncodeToString(b) -} - -func UpdateStudentName(c *fiber.Ctx) error { - var data map[string]string - ctx, cancel := context.WithTimeout(context.Background(), 30*time.Second) - - if err := c.BodyParser(&data); err != nil { - cancel() - return c.Status(fiber.StatusInternalServerError).JSON(fiber.Map{ - "success": false, - "message": "Failed to parse body", - "error": err, - }) - } - - // Ensure Authenticated admin sent request - if verified, _ := AuthenticateUser(c, 3); !verified { - cancel() - return c.Status(fiber.StatusUnauthorized).JSON(fiber.Map{ - "success": false, - "message": "Unauthorized: only an admin can perform this action", - }) - } - - // Check id and names are included - // Middle name is optional - if data["sid"] == "" || data["firstname"] == "" || data["lastname"] == "" { - cancel() - return c.Status(fiber.StatusBadRequest).JSON(fiber.Map{ - "success": false, - "message": "missing required fields", - }) - } - - var updateMiddle bool = false - if data["middlename"] != "" { - updateMiddle = true - } - - update_time, _ := time.Parse(time.RFC3339, time.Now().Format(time.RFC3339)) - var update bson.M - if updateMiddle { - update = bson.M{ - "$set": bson.M{ - "personaldata.firstname": data["firstname"], - "personaldata.middlename": data["middlename"], - "personaldata.lastname": data["lastname"], - "updated_at": update_time, - }, - } - } else { - update = bson.M{ - "$set": bson.M{ - "personaldata.firstname": data["firstname"], - "personaldata.lastname": data["lastname"], - "updated_at": update_time, - }, - } - } - - result, updateErr := studentCollection.UpdateOne( - ctx, - bson.M{"schooldata.sid": data["sid"]}, - update, - ) - if updateErr != nil { - cancel() - return c.Status(fiber.StatusInternalServerError).JSON(fiber.Map{ - "success": false, - "message": "the student could not be updated", - "error": updateErr, - }) - } - defer cancel() - - return c.Status(fiber.StatusOK).JSON(fiber.Map{ - "success": true, - "message": "successfully updated student", - "result": result, - }) -} - -func UpdateStudentGradeLevel(c *fiber.Ctx) error { - var data map[string]interface{} - ctx, cancel := context.WithTimeout(context.Background(), 30*time.Second) - - if err := c.BodyParser(&data); err != nil { - cancel() - return c.Status(fiber.StatusInternalServerError).JSON(fiber.Map{ - "success": false, - "message": "Failed to parse body", - "error": err, - }) - } - - // Ensure Authorized admin sent request - if verified, _ := AuthenticateUser(c, 3); !verified { - cancel() - return c.Status(fiber.StatusUnauthorized).JSON(fiber.Map{ - "success": false, - "message": "Unauthorized: only an admin can perform this action", - }) - } - - // Check required fields are included - if data["sid"] == nil || data["gradelevel"] == nil { - cancel() - return c.Status(fiber.StatusBadRequest).JSON(fiber.Map{ - "success": false, - "message": "missing required fields", - }) - } - - update_time, _ := time.Parse(time.RFC3339, time.Now().Format(time.RFC3339)) - update := bson.M{ - "$set": bson.M{ - "schooldata.gradelevel": data["gradelevel"].(float64), - "updated_at": update_time, - }, - } - - _, updateErr := studentCollection.UpdateOne( - ctx, - bson.M{"schooldata.sid": data["sid"].(string)}, - update, - ) - if updateErr != nil { - cancel() - return c.Status(fiber.StatusInternalServerError).JSON(fiber.Map{ - "success": false, - "message": "the student could not be updated", - "error": updateErr, - }) - } - defer cancel() - - return c.Status(fiber.StatusOK).JSON(fiber.Map{ - "success": true, - "message": "successfully updated student", - }) -} - -/* - Far later on this function is going to be completely automated. - Instead of an admin sending a request to update the homeroom of - a student or teacher, the system will take the room number of - the teacher's or student's Block 2 class from their schedule. - - Though this function would remain for students only, for example - a student requests a course change, if its their block 2 the - admin would have to alter their homeroom to be the new class - number. -*/ -func UpdateStudentHomeroom(c *fiber.Ctx) error { - var data map[string]string - ctx, cancel := context.WithTimeout(context.Background(), 30*time.Second) - - if err := c.BodyParser(&data); err != nil { - cancel() - return c.Status(fiber.StatusInternalServerError).JSON(fiber.Map{ - "success": false, - "message": "Failed to parse body", - "error": err, - }) - } - - // Ensure Authenticated admin sent request - if verified, _ := AuthenticateUser(c, 3); !verified { - cancel() - return c.Status(fiber.StatusUnauthorized).JSON(fiber.Map{ - "success": false, - "message": "Unauthorized: only an admin can perform this action", - }) - } - - // Check required fields are included - if data["sid"] == "" || data["homeroom"] == "" { - cancel() - return c.Status(fiber.StatusBadRequest).JSON(fiber.Map{ - "success": false, - "message": "missing required fields", - }) - } - - update_time, _ := time.Parse(time.RFC3339, time.Now().Format(time.RFC3339)) - update := bson.M{ - "$set": bson.M{ - "schooldata.homeroom": data["homeroom"], - "updated_at": update_time, - }, - } - - _, updateErr := studentCollection.UpdateOne( - ctx, - bson.M{"schooldata.sid": data["sid"]}, - update, - ) - if updateErr != nil { - cancel() - return c.Status(fiber.StatusInternalServerError).JSON(fiber.Map{ - "success": false, - "message": "the student could not be updated", - "error": updateErr, - }) - } - defer cancel() - - return c.Status(fiber.StatusOK).JSON(fiber.Map{ - "success": true, - "message": "successfully updated student", - }) -} - -func UpdateStudentPassword(c *fiber.Ctx) error { - var data map[string]string - ctx, cancel := context.WithTimeout(context.Background(), 30*time.Second) - - if err := c.BodyParser(&data); err != nil { - cancel() - return c.Status(fiber.StatusInternalServerError).JSON(fiber.Map{ - "success": false, - "message": "Failed to parse body", - "error": err, - }) - } - - cookie := c.Cookies("jwt") - - token, err := jwt.ParseWithClaims(cookie, &jwt.StandardClaims{}, func(token *jwt.Token) (interface{}, error) { - return []byte(SecretKey), nil - }) - if err != nil { - cancel() - return c.Status(fiber.StatusUnauthorized).JSON(fiber.Map{ - "success": false, - "message": "not authorized", - }) - } - - claims := token.Claims.(*jwt.StandardClaims) - - var student models.Student - findErr := studentCollection.FindOne(ctx, bson.M{"schooldata.sid": claims.Issuer}).Decode(&student) - if findErr != nil { - cancel() - return c.Status(fiber.StatusBadRequest).JSON(fiber.Map{ - "success": false, - "message": "student not found", - }) - } - - // Check required fields are included - if data["password"] == "" || data["newpassword1"] == "" || data["newpassword2"] == "" { - cancel() - return c.Status(fiber.StatusBadRequest).JSON(fiber.Map{ - "success": false, - "message": "missing required fields", - }) - } - - if !student.ComparePasswords(data["password"]) { - cancel() - return c.Status(fiber.StatusOK).JSON(fiber.Map{ - "success": false, - "message": "Your password is incorrect", - }) - } - - if data["newpassword1"] != data["newpassword2"] { - cancel() - return c.Status(fiber.StatusBadRequest).JSON(fiber.Map{ - "success": false, - "message": "Your new password must match", - }) - } - - if student.UsedPassword(data["newpassword1"]) { - cancel() - return c.Status(fiber.StatusBadRequest).JSON(fiber.Map{ - "success": false, - "message": "Your new password cannot be the same as a previous password", - }) - } - - update_time, _ := time.Parse(time.RFC3339, time.Now().Format(time.RFC3339)) - update := bson.M{ - "$set": bson.M{ - "accountdata.password": student.HashPassword(data["newpassword1"]), - "accountdata.temppassword": false, // If it were a temp password, its not now - "updated_at": update_time, - }, - "$push": bson.M{ - "accountdata.hashhistory": student.HashPassword(data["newpassword1"]), - }, - } - - _, updateErr := studentCollection.UpdateOne( - ctx, - bson.M{"schooldata.sid": claims.Issuer}, - update, - ) - if updateErr != nil { - cancel() - return c.Status(fiber.StatusInternalServerError).JSON(fiber.Map{ - "success": false, - "message": "the student password could not be updated", - "error": updateErr, - }) - } - defer cancel() - - // Alert email the password has changed - subject := "Password Changed" - receiver := student.PersonalData.Email - r := NewRequest([]string{receiver}, subject) - - if sent := r.Send("./templates/selfPasswordChanged.html", map[string]string{"username": student.PersonalData.FirstName}); !sent { - return c.Status(fiber.StatusInternalServerError).JSON(fiber.Map{ - "success": false, - "message": "failed to send email to student", - }) - } - - return c.Status(fiber.StatusOK).JSON(fiber.Map{ - "success": true, - "message": "successfully updated student password", - }) -} - -// This is for students to reset their password if they are unable to login -func ResetStudentPassword(c *fiber.Ctx) error { - var data map[string]string - ctx, cancel := context.WithTimeout(context.Background(), 30*time.Second) - - if err := c.BodyParser(&data); err != nil { - cancel() - return c.Status(fiber.StatusInternalServerError).JSON(fiber.Map{ - "success": false, - "message": "Failed to parse body", - "error": err, - }) - } - - // Check required fields are included (email must be personal email) - if data["sid"] == "" || data["email"] == "" { - cancel() - return c.Status(fiber.StatusBadRequest).JSON(fiber.Map{ - "success": false, - "message": "missing required fields", - }) - } - - var student models.Student - findErr := studentCollection.FindOne(context.TODO(), bson.M{"schooldata.sid": data["sid"]}).Decode(&student) - if findErr != nil { - cancel() - return c.Status(fiber.StatusBadRequest).JSON(fiber.Map{ - "success": false, - "message": "student not found", - }) - } - - if student.PersonalData.Email != data["email"] { - cancel() - return c.Status(fiber.StatusOK).JSON(fiber.Map{ - "success": false, - "message": "Your personal email is incorrect", - }) - } - - tempPass := student.GeneratePassword(12, 1, 1, 1) - update_time, _ := time.Parse(time.RFC3339, time.Now().Format(time.RFC3339)) - update := bson.M{ - "$set": bson.M{ - "accountdata.password": student.HashPassword(tempPass), - "accountdata.temppassword": true, - "updated_at": update_time, - }, - } - - result, updateErr := studentCollection.UpdateOne( - ctx, - bson.M{"schooldata.sid": data["sid"]}, - update, - ) - if updateErr != nil { - cancel() - return c.Status(fiber.StatusInternalServerError).JSON(fiber.Map{ - "success": false, - "message": "the student password could not be updated", - "error": updateErr, - }) - } - defer cancel() - - // Send student personal email temp password - subject := "Password Changed" - receiver := student.PersonalData.Email - r := NewRequest([]string{receiver}, subject) - - if sent := r.Send("./templates/passwordChanged.html", map[string]string{"username": student.PersonalData.FirstName, "password": tempPass}); !sent { - return c.Status(fiber.StatusInternalServerError).JSON(fiber.Map{ - "success": false, - "message": "Could not send password to students email", - }) - } - - return c.Status(fiber.StatusOK).JSON(fiber.Map{ - "success": true, - "message": "successfully updated student password", - "result": result, - }) -} - -func UpdateStudentLocker(c *fiber.Ctx) error { - var data map[string]string - ctx, cancel := context.WithTimeout(context.Background(), 30*time.Second) - - if err := c.BodyParser(&data); err != nil { - cancel() - return c.Status(fiber.StatusInternalServerError).JSON(fiber.Map{ - "success": false, - "message": "Failed to parse body", - "error": err, - }) - } - - // Ensure Authenticated admin sent request - if verified, _ := AuthenticateUser(c, 3); !verified { - cancel() - return c.Status(fiber.StatusUnauthorized).JSON(fiber.Map{ - "success": false, - "message": "Unauthorized: only an admin can perform this action", - }) - } - - // Check id and locker are included - if data["sid"] == "" || data["lockernumber"] == "" { - cancel() - return c.Status(fiber.StatusBadRequest).JSON(fiber.Map{ - "success": false, - "message": "missing required fields", - }) - } - - var locker models.Locker - err := lockerCollection.FindOne(ctx, bson.M{"lockernumber": data["lockernumber"]}).Decode(&locker) - if err != nil { - cancel() - return c.Status(fiber.StatusBadRequest).JSON(fiber.Map{ - "success": false, - "message": "locker not found", - "error": err, - }) - } - - update_time, _ := time.Parse(time.RFC3339, time.Now().Format(time.RFC3339)) - update := bson.M{ - "$set": bson.M{ - "schooldata.locker": locker.ID, - "updated_at": update_time, - }, - } - - _, updateErr := studentCollection.UpdateOne( - ctx, - bson.M{"schooldata.sid": data["sid"]}, - update, - ) - if updateErr != nil { - cancel() - return c.Status(fiber.StatusInternalServerError).JSON(fiber.Map{ - "success": false, - "message": "the student could not be updated", - "error": updateErr, - }) - } - defer cancel() - - return c.Status(fiber.StatusOK).JSON(fiber.Map{ - "success": true, - "message": "successfully updated student", - }) -} - -func UpdateStudentAddress(c *fiber.Ctx) error { - var data map[string]string - ctx, cancel := context.WithTimeout(context.Background(), 30*time.Second) - - if err := c.BodyParser(&data); err != nil { - cancel() - return c.Status(fiber.StatusInternalServerError).JSON(fiber.Map{ - "success": false, - "message": "Failed to parse body", - "error": err, - }) - } - - // Ensure Authenticated admin sent request - if verified, _ := AuthenticateUser(c, 3); !verified { - cancel() - return c.Status(fiber.StatusUnauthorized).JSON(fiber.Map{ - "success": false, - "message": "Unauthorized: only an admin can perform this action", - }) - } - - // Check required fields are included - if data["sid"] == "" || data["address"] == "" || data["city"] == "" || data["province"] == "" || data["postal"] == "" { - cancel() - return c.Status(fiber.StatusBadRequest).JSON(fiber.Map{ - "success": false, - "message": "missing required fields", - }) - } - - update_time, _ := time.Parse(time.RFC3339, time.Now().Format(time.RFC3339)) - update := bson.M{ - "$set": bson.M{ - "personaldata.address": data["address"], - "personaldata.city": data["city"], - "personaldata.province": data["province"], - "personaldata.postal": data["postal"], - "updated_at": update_time, - }, - } - - _, updateErr := studentCollection.UpdateOne( - ctx, - bson.M{"sid": data["sid"]}, - update, - ) - if updateErr != nil { - cancel() - return c.Status(fiber.StatusInternalServerError).JSON(fiber.Map{ - "success": false, - "message": "the student could not be updated", - "error": updateErr, - }) - } - defer cancel() - - return c.Status(fiber.StatusOK).JSON(fiber.Map{ - "success": true, - "message": "successfully updated student", - }) -} - -// In the case a student gets held back a grade, we need to update their YOG (Year of Graduation) -func UpdateStudentYOG(c *fiber.Ctx) error { - var data map[string]interface{} - ctx, cancel := context.WithTimeout(context.Background(), 30*time.Second) - - if err := c.BodyParser(&data); err != nil { - cancel() - return c.Status(fiber.StatusInternalServerError).JSON(fiber.Map{ - "success": false, - "message": "Failed to parse body", - "error": err, - }) - } - - // Ensure Authenticated admin sent request - if verified, _ := AuthenticateUser(c, 3); !verified { - cancel() - return c.Status(fiber.StatusUnauthorized).JSON(fiber.Map{ - "success": false, - "message": "Unauthorized: only an admin can perform this action", - }) - } - - // Check required fields are included - if data["sid"] == "" || data["yog"] == nil { - cancel() - return c.Status(fiber.StatusBadRequest).JSON(fiber.Map{ - "success": false, - "message": "missing required fields", - }) - } - - var student models.Student - findErr := studentCollection.FindOne(context.TODO(), bson.M{"schooldata.sid": data["sid"].(string)}).Decode(&student) - if findErr != nil { - cancel() - return c.Status(fiber.StatusBadRequest).JSON(fiber.Map{ - "success": false, - "message": "student not found", - }) - } - - update_time, _ := time.Parse(time.RFC3339, time.Now().Format(time.RFC3339)) - update := bson.M{ - "$set": bson.M{ - "schooldata.yog": data["yog"].(int), - "updated_at": update_time, - }, - } - - result, updateErr := studentCollection.UpdateOne( - ctx, - bson.M{"schooldata.sid": data["sid"].(string)}, - update, - ) - if updateErr != nil { - cancel() - return c.Status(fiber.StatusInternalServerError).JSON(fiber.Map{ - "success": false, - "message": "the student could not be updated", - "error": updateErr, - }) - } - defer cancel() - - return c.Status(fiber.StatusOK).JSON(fiber.Map{ - "success": true, - "message": "successfully updated student", - "result": result, - }) -} - -func RemoveStudentContact(c *fiber.Ctx) error { - var data map[string]string - ctx, cancel := context.WithTimeout(context.Background(), 30*time.Second) - - if err := c.BodyParser(&data); err != nil { - cancel() - return c.Status(fiber.StatusInternalServerError).JSON(fiber.Map{ - "success": false, - "message": "Failed to parse body", - "error": err, - }) - } - - // Ensure Authenticated admin sent request - if verified, _ := AuthenticateUser(c, 3); !verified { - cancel() - return c.Status(fiber.StatusUnauthorized).JSON(fiber.Map{ - "success": false, - "message": "Unauthorized: only an admin can perform this action", - }) - } - - // Check id and contact id are included - if data["sid"] == "" || data["contactid"] == "" { - cancel() - return c.Status(fiber.StatusBadRequest).JSON(fiber.Map{ - "success": false, - "message": "missing required fields", - }) - } - - var contact models.Contact - err := contactCollection.FindOne(ctx, bson.M{"_id": data["contactid"]}).Decode(&contact) - if err != nil { - cancel() - return c.Status(fiber.StatusBadRequest).JSON(fiber.Map{ - "success": false, - "message": "contact not found", - "error": err, - }) - } - - update_time, _ := time.Parse(time.RFC3339, time.Now().Format(time.RFC3339)) - update := bson.M{ - "$set": bson.M{ - "updated_at": update_time, - }, - "$pull": bson.M{ - "personaldata.contacts": contact.ID, - }, - } - - result, updateErr := studentCollection.UpdateOne( - ctx, - bson.M{"schooldata.sid": data["sid"]}, - update, - ) - if updateErr != nil { - cancel() - return c.Status(fiber.StatusInternalServerError).JSON(fiber.Map{ - "success": false, - "message": "the contact could not be added", - "error": updateErr, - }) - } - defer cancel() - - return c.Status(fiber.StatusOK).JSON(fiber.Map{ - "success": true, - "message": "successfully added contact", - "result": result, - }) -} - -func AddStudentContact(c *fiber.Ctx) error { - var data map[string]string - ctx, cancel := context.WithTimeout(context.Background(), 30*time.Second) - - if err := c.BodyParser(&data); err != nil { - cancel() - return c.Status(fiber.StatusInternalServerError).JSON(fiber.Map{ - "success": false, - "message": "Failed to parse body", - "error": err, - }) - } - - // Ensure Authenticated admin sent request - if verified, _ := AuthenticateUser(c, 3); !verified { - cancel() - return c.Status(fiber.StatusUnauthorized).JSON(fiber.Map{ - "success": false, - "message": "Unauthorized: only an admin can perform this action", - }) - } - - // Check id and contact id are included - if data["sid"] == "" || data["contactid"] == "" { - cancel() - return c.Status(fiber.StatusBadRequest).JSON(fiber.Map{ - "success": false, - "message": "missing required fields", - }) - } - - var contact models.Contact - err := contactCollection.FindOne(ctx, bson.M{"_id": data["contactid"]}).Decode(&contact) - if err != nil { - cancel() - return c.Status(fiber.StatusBadRequest).JSON(fiber.Map{ - "success": false, - "message": "contact not found", - "error": err, - }) - } - - update_time, _ := time.Parse(time.RFC3339, time.Now().Format(time.RFC3339)) - update := bson.M{ - "$set": bson.M{ - "updated_at": update_time, - }, - "$push": bson.M{ - "personaldata.contacts": contact.ID, - }, - } - - result, updateErr := studentCollection.UpdateOne( - ctx, - bson.M{"schooldata.sid": data["sid"]}, - update, - ) - if updateErr != nil { - cancel() - return c.Status(fiber.StatusInternalServerError).JSON(fiber.Map{ - "success": false, - "message": "the contact could not be added", - "error": updateErr, - }) - } - defer cancel() - - return c.Status(fiber.StatusOK).JSON(fiber.Map{ - "success": true, - "message": "successfully added contact", - "result": result, - }) -} - -func UpdateStudentPhoto(c *fiber.Ctx) error { - ctx, cancel := context.WithTimeout(context.Background(), 30*time.Second) - - //Ensure Authenticated admin sent request - if verified, _ := AuthenticateUser(c, 3); !verified { - cancel() - return c.Status(fiber.StatusUnauthorized).JSON(fiber.Map{ - "success": false, - "message": "Unauthorized: only an admin can perform this action", - }) - } - - sid := c.FormValue("sid") - if sid == "" { - cancel() - return c.Status(fiber.StatusBadRequest).JSON(fiber.Map{ - "success": false, - "message": "missing required fields", - }) - } - - // Get student - var student models.Student - findErr := studentCollection.FindOne(context.TODO(), bson.M{"schooldata.sid": sid}).Decode(&student) - if findErr != nil { - cancel() - return c.Status(fiber.StatusBadRequest).JSON(fiber.Map{ - "success": false, - "message": "the student could not be found", - "error": findErr, - }) - } - - // Collect image - file, err := c.FormFile("image") - if err != nil { - cancel() - return c.Status(fiber.StatusInternalServerError).JSON(fiber.Map{ - "success": false, - "message": "the image could not be retrieved", - "error": err, - }) - } - - // Get student photo - var photo models.Photo - findErr = imageCollection.FindOne(context.TODO(), bson.M{"name": student.SchoolData.PhotoName}).Decode(&photo) - if findErr != nil { - cancel() - return c.Status(fiber.StatusInternalServerError).JSON(fiber.Map{ - "success": false, - "message": "the student image could not be found", - "error": findErr, - }) - } - - // Save image to local - uniqueId := uuid.New() - filename := strings.Replace(uniqueId.String(), "-", "", -1) - fileExt := strings.Split(file.Filename, ".")[1] - image := fmt.Sprintf("%s.%s", filename, fileExt) - err = c.SaveFile(file, fmt.Sprintf("./database/images/%s", image)) - if err != nil { - cancel() - return c.Status(fiber.StatusInternalServerError).JSON(fiber.Map{ - "success": false, - "message": "the image could not be saved", - "error": err, - }) - } - - // Read the entire file into a byte slice - bytes, err := ioutil.ReadFile(fmt.Sprintf("./database/images/%s", image)) - if err != nil { - cancel() - return c.Status(fiber.StatusInternalServerError).JSON(fiber.Map{ - "success": false, - "message": "the image could not be read", - "error": err, - }) - } - - var base64Encoding string = toBase64(bytes) - - // Update image name and base64 data - update_time, _ := time.Parse(time.RFC3339, time.Now().Format(time.RFC3339)) - update := bson.M{ - "$set": bson.M{ - "base64": base64Encoding, - "updated_at": update_time, - }, - } - _, updateErr := imageCollection.UpdateOne( - ctx, - bson.M{"_id": photo.ID}, - update, - ) - if updateErr != nil { - cancel() - return c.Status(fiber.StatusInternalServerError).JSON(fiber.Map{ - "success": false, - "message": "the image could not be updated", - "error": updateErr, - }) - } - defer cancel() - - // Remove local image - os.Remove(fmt.Sprintf("./database/images/%s", image)) - - return c.Status(fiber.StatusOK).JSON(fiber.Map{ - "success": true, - "message": "successfully updated student photo", - }) -} - -func UpdateStudentEmail(c *fiber.Ctx) error { - var data map[string]string - ctx, cancel := context.WithTimeout(context.Background(), 30*time.Second) - - if err := c.BodyParser(&data); err != nil { - cancel() - return c.Status(fiber.StatusInternalServerError).JSON(fiber.Map{ - "success": false, - "message": "Failed to parse body", - "error": err, - }) - } - - var sid string - var verifiedStudent bool - verifiedAdmin, _ := AuthenticateUser(c, 3) - verifiedStudent, sid = AuthenticateUser(c, 1) - // Ensure Authenticated admin sent request - if !verifiedAdmin && !verifiedStudent { - cancel() - return c.Status(fiber.StatusUnauthorized).JSON(fiber.Map{ - "success": false, - "message": "Unauthorized: only an admin or teacher can perform this action", - }) - } - - if verifiedAdmin && data["sid"] == "" { - cancel() - return c.Status(fiber.StatusBadRequest).JSON(fiber.Map{ - "success": false, - "message": "missing required fields", - }) - } else if verifiedAdmin { - sid = data["sid"] - } - - // Check required fields are included - if data["email"] == "" { - cancel() - return c.Status(fiber.StatusBadRequest).JSON(fiber.Map{ - "success": false, - "message": "missing required fields", - }) - } - - update_time, _ := time.Parse(time.RFC3339, time.Now().Format(time.RFC3339)) - update := bson.M{ - "$set": bson.M{ - "personaldata.email": data["email"], - "updated_at": update_time, - }, - } - - result, updateErr := studentCollection.UpdateOne( - ctx, - bson.M{"schooldata.sid": sid}, - update, - ) - if updateErr != nil { - cancel() - return c.Status(fiber.StatusInternalServerError).JSON(fiber.Map{ - "success": false, - "message": "the student could not be updated", - "error": updateErr, - }) - } - defer cancel() - - return c.Status(fiber.StatusOK).JSON(fiber.Map{ - "success": true, - "message": "successfully updated student", - "result": result, - }) -} - -func RemoveStudentsDisabled(c *fiber.Ctx) error { - var data map[string]string - ctx, cancel := context.WithTimeout(context.Background(), 30*time.Second) - - if err := c.BodyParser(&data); err != nil { - cancel() - return c.Status(fiber.StatusInternalServerError).JSON(fiber.Map{ - "success": false, - "message": "Failed to parse body", - "error": err, - }) - } - - // Ensure Authorized admin sent request - if verified, _ := AuthenticateUser(c, 3); !verified { - cancel() - return c.Status(fiber.StatusUnauthorized).JSON(fiber.Map{ - "success": false, - "message": "Unauthorized: only an admin can perform this action", - }) - } - - // Check required fields are included - if data["sid"] == "" { - cancel() - return c.Status(fiber.StatusBadRequest).JSON(fiber.Map{ - "success": false, - "message": "missing required fields", - }) - } - - update_time, _ := time.Parse(time.RFC3339, time.Now().Format(time.RFC3339)) - update := bson.M{ - "$set": bson.M{ - "accountdata.accountdisabled": false, - "accountdata.alerted": false, - "accountdata.attempts": 0, - "updated_at": update_time, - }, - } - - result, updateErr := studentCollection.UpdateOne( - ctx, - bson.M{"schooldata.sid": data["sid"]}, - update, - ) - if updateErr != nil { - cancel() - return c.Status(fiber.StatusInternalServerError).JSON(fiber.Map{ - "success": false, - "message": "the student account could not be enabled", - "error": updateErr, - }) - } - defer cancel() - - return c.Status(fiber.StatusOK).JSON(fiber.Map{ - "success": true, - "message": "successfully enabled student account", - "result": result, - }) -} - -func RemoveTeachersDisabled(c *fiber.Ctx) error { - var data map[string]string - ctx, cancel := context.WithTimeout(context.Background(), 30*time.Second) - - if err := c.BodyParser(&data); err != nil { - cancel() - return c.Status(fiber.StatusInternalServerError).JSON(fiber.Map{ - "success": false, - "message": "Failed to parse body", - "error": err, - }) - } - - // Ensure Authorized admin sent request - if verified, _ := AuthenticateUser(c, 3); !verified { - cancel() - return c.Status(fiber.StatusUnauthorized).JSON(fiber.Map{ - "success": false, - "message": "Unauthorized: only an admin can perform this action", - }) - } - - // Check required fields are included - if data["tid"] == "" { - cancel() - return c.Status(fiber.StatusBadRequest).JSON(fiber.Map{ - "success": false, - "message": "missing required fields", - }) - } - - update_time, _ := time.Parse(time.RFC3339, time.Now().Format(time.RFC3339)) - update := bson.M{ - "$set": bson.M{ - "accountdata.accountdisabled": false, - "accountdata.attempts": 0, - "updated_at": update_time, - }, - } - - result, updateErr := teacherCollection.UpdateOne( - ctx, - bson.M{"schooldata.tid": data["tid"]}, - update, - ) - if updateErr != nil { - cancel() - return c.Status(fiber.StatusInternalServerError).JSON(fiber.Map{ - "success": false, - "message": "the teacher account could not be enabled", - "error": updateErr, - }) - } - defer cancel() - - return c.Status(fiber.StatusOK).JSON(fiber.Map{ - "success": true, - "message": "successfully enabled teacher account", - "result": result, - }) -} - -/* - Far later on this function is going to be completely automated. - Instead of an admin sending a request to update the homeroom of - a student or teacher, the system will take the room number of - the teacher's or student's Block 2 class from their schedule. - - Though this function would remain for students only, for example - a student requests a course change, if its their block 2 the - admin would have to alter their homeroom to be the new class - number. -*/ -func UpdateTeacherHomeroom(c *fiber.Ctx) error { - var data map[string]string - ctx, cancel := context.WithTimeout(context.Background(), 30*time.Second) - - if err := c.BodyParser(&data); err != nil { - cancel() - return c.Status(fiber.StatusInternalServerError).JSON(fiber.Map{ - "success": false, - "message": "Failed to parse body", - "error": err, - }) - } - - // Ensure Authenticated admin sent request - if verified, _ := AuthenticateUser(c, 3); !verified { - cancel() - return c.Status(fiber.StatusUnauthorized).JSON(fiber.Map{ - "success": false, - "message": "Unauthorized: only an admin can perform this action", - }) - } - - // Check required fields are included - if data["tid"] == "" || data["homeroom"] == "" { - cancel() - return c.Status(fiber.StatusBadRequest).JSON(fiber.Map{ - "success": false, - "message": "missing required fields", - }) - } - - update_time, _ := time.Parse(time.RFC3339, time.Now().Format(time.RFC3339)) - update := bson.M{ - "$set": bson.M{ - "schooldata.homeroom": data["homeroom"], - "updated_at": update_time, - }, - } - - _, updateErr := teacherCollection.UpdateOne( - ctx, - bson.M{"schooldata.tid": data["tid"]}, - update, - ) - if updateErr != nil { - cancel() - return c.Status(fiber.StatusInternalServerError).JSON(fiber.Map{ - "success": false, - "message": "the teacher could not be updated", - "error": updateErr, - }) - } - defer cancel() - - return c.Status(fiber.StatusOK).JSON(fiber.Map{ - "success": true, - "message": "successfully updated teacher", - }) -} - -func UpdateTeacherPassword(c *fiber.Ctx) error { - var data map[string]string - ctx, cancel := context.WithTimeout(context.Background(), 30*time.Second) - - if err := c.BodyParser(&data); err != nil { - cancel() - return c.Status(fiber.StatusInternalServerError).JSON(fiber.Map{ - "success": false, - "message": "Failed to parse body", - "error": err, - }) - } - - cookie := c.Cookies("jwt") - - token, err := jwt.ParseWithClaims(cookie, &jwt.StandardClaims{}, func(token *jwt.Token) (interface{}, error) { - return []byte(SecretKey), nil - }) - if err != nil { - cancel() - return c.Status(fiber.StatusUnauthorized).JSON(fiber.Map{ - "success": false, - "message": "not authorized", - }) - } - - claims := token.Claims.(*jwt.StandardClaims) - - var teacher models.Teacher - findErr := teacherCollection.FindOne(ctx, bson.M{"schooldata.tid": claims.Issuer}).Decode(&teacher) - if findErr != nil { - cancel() - return c.Status(fiber.StatusBadRequest).JSON(fiber.Map{ - "success": false, - "message": "teacher not found", - }) - } - - // Check required fields are included - if data["password"] == "" || data["newpassword1"] == "" || data["newpassword2"] == "" { - cancel() - return c.Status(fiber.StatusBadRequest).JSON(fiber.Map{ - "success": false, - "message": "missing required fields", - }) - } - - if !teacher.ComparePasswords(data["password"]) { - cancel() - return c.Status(fiber.StatusOK).JSON(fiber.Map{ - "success": false, - "message": "Your password is incorrect", - }) - } - - if data["newpassword1"] != data["newpassword2"] { - cancel() - return c.Status(fiber.StatusBadRequest).JSON(fiber.Map{ - "success": false, - "message": "Your new passwords must match", - }) - } - - if teacher.UsedPassword(data["newpassword1"]) { - cancel() - return c.Status(fiber.StatusBadRequest).JSON(fiber.Map{ - "success": false, - "message": "Your new password cannot be the same as a previous password", - }) - } - - update_time, _ := time.Parse(time.RFC3339, time.Now().Format(time.RFC3339)) - update := bson.M{ - "$set": bson.M{ - "accountdata.password": teacher.HashPassword(data["newpassword1"]), - "accountdata.temppassword": false, // If it were a temp password, its not now - "updated_at": update_time, - }, - "$push": bson.M{ - "accountdata.hashhistory": teacher.HashPassword(data["newpassword1"]), - }, - } - - _, updateErr := teacherCollection.UpdateOne( - ctx, - bson.M{"schooldata.tid": claims.Issuer}, - update, - ) - if updateErr != nil { - cancel() - return c.Status(fiber.StatusInternalServerError).JSON(fiber.Map{ - "success": false, - "message": "the teacher password could not be updated", - "error": updateErr, - }) - } - defer cancel() - - subject := "Password Changed" - receiver := teacher.PersonalData.Email - r := NewRequest([]string{receiver}, subject) - - if sent := r.Send("./templates/selfPasswordChanged.html", map[string]string{"username": teacher.PersonalData.FirstName}); !sent { - return c.Status(fiber.StatusInternalServerError).JSON(fiber.Map{ - "success": false, - "message": "Could not send password to teachers email", - }) - } - - return c.Status(fiber.StatusOK).JSON(fiber.Map{ - "success": true, - "message": "successfully updated teacher password", - }) -} - -func ResetTeacherPassword(c *fiber.Ctx) error { - var data map[string]string - ctx, cancel := context.WithTimeout(context.Background(), 30*time.Second) - - if err := c.BodyParser(&data); err != nil { - cancel() - return c.Status(fiber.StatusInternalServerError).JSON(fiber.Map{ - "success": false, - "message": "Failed to parse body", - "error": err, - }) - } - - // Check required fields are included (email must be personal email) - if data["tid"] == "" || data["email"] == "" { - cancel() - return c.Status(fiber.StatusBadRequest).JSON(fiber.Map{ - "success": false, - "message": "missing required fields", - }) - } - - var teacher models.Teacher - findErr := teacherCollection.FindOne(context.TODO(), bson.M{"schooldata.tid": data["tid"]}).Decode(&teacher) - if findErr != nil { - cancel() - return c.Status(fiber.StatusBadRequest).JSON(fiber.Map{ - "success": false, - "message": "teacher not found", - }) - } - - if teacher.PersonalData.Email != data["email"] { - cancel() - return c.Status(fiber.StatusOK).JSON(fiber.Map{ - "success": false, - "message": "Your personal email is incorrect", - }) - } - - tempPass := teacher.GeneratePassword(12, 1, 1, 1) - update_time, _ := time.Parse(time.RFC3339, time.Now().Format(time.RFC3339)) - update := bson.M{ - "$set": bson.M{ - "accountdata.password": teacher.HashPassword(tempPass), - "accountdata.temppassword": true, - "updated_at": update_time, - }, - } - - result, updateErr := studentCollection.UpdateOne( - ctx, - bson.M{"schooldata.tid": data["tid"]}, - update, - ) - if updateErr != nil { - cancel() - return c.Status(fiber.StatusInternalServerError).JSON(fiber.Map{ - "success": false, - "message": "the teacher password could not be updated", - "error": updateErr, - }) - } - defer cancel() - - // Send teacher personal email temp password - subject := "Password Changed" - receiver := teacher.PersonalData.Email - r := NewRequest([]string{receiver}, subject) - - if sent := r.Send("./templates/passwordChanged.html", map[string]string{"username": teacher.PersonalData.FirstName, "password": tempPass}); !sent { - return c.Status(fiber.StatusInternalServerError).JSON(fiber.Map{ - "success": false, - "message": "Could not send password to teachers email", - }) - } - - return c.Status(fiber.StatusOK).JSON(fiber.Map{ - "success": true, - "message": "successfully updated teacher password", - "result": result, - }) -} - -func UpdateTeacherAddress(c *fiber.Ctx) error { - var data map[string]string - ctx, cancel := context.WithTimeout(context.Background(), 30*time.Second) - - if err := c.BodyParser(&data); err != nil { - cancel() - return c.Status(fiber.StatusInternalServerError).JSON(fiber.Map{ - "success": false, - "message": "Failed to parse body", - "error": err, - }) - } - - // Ensure Authenticated admin sent request - if verified, _ := AuthenticateUser(c, 3); !verified { - cancel() - return c.Status(fiber.StatusUnauthorized).JSON(fiber.Map{ - "success": false, - "message": "Unauthorized: only an admin can perform this action", - }) - } - - // Check required fields are included - if data["tid"] == "" || data["address"] == "" || data["city"] == "" || data["province"] == "" || data["postal"] == "" { - cancel() - return c.Status(fiber.StatusBadRequest).JSON(fiber.Map{ - "success": false, - "message": "missing required fields", - }) - } - - update_time, _ := time.Parse(time.RFC3339, time.Now().Format(time.RFC3339)) - update := bson.M{ - "$set": bson.M{ - "personaldata.address": data["address"], - "personaldata.city": data["city"], - "personaldata.province": data["province"], - "personaldata.postal": data["postal"], - "updated_at": update_time, - }, - } - - _, updateErr := teacherCollection.UpdateOne( - ctx, - bson.M{"schooldata.tid": data["tid"]}, - update, - ) - if updateErr != nil { - cancel() - return c.Status(fiber.StatusInternalServerError).JSON(fiber.Map{ - "success": false, - "message": "the teacher could not be updated", - "error": updateErr, - }) - } - defer cancel() - - return c.Status(fiber.StatusOK).JSON(fiber.Map{ - "success": true, - "message": "successfully updated teacher", - }) -} - -func UpdateTeacherPhoto(c *fiber.Ctx) error { - ctx, cancel := context.WithTimeout(context.Background(), 30*time.Second) - - //Ensure Authenticated admin sent request - if verified, _ := AuthenticateUser(c, 3); !verified { - cancel() - return c.Status(fiber.StatusUnauthorized).JSON(fiber.Map{ - "success": false, - "message": "Unauthorized: only an admin can perform this action", - }) - } - - tid := c.FormValue("tid") - if tid == "" { - cancel() - return c.Status(fiber.StatusBadRequest).JSON(fiber.Map{ - "success": false, - "message": "missing required fields", - }) - } - - // Get teacher - var teacher models.Teacher - findErr := teacherCollection.FindOne(context.TODO(), bson.M{"schooldata.tid": tid}).Decode(&teacher) - if findErr != nil { - cancel() - return c.Status(fiber.StatusBadRequest).JSON(fiber.Map{ - "success": false, - "message": "the teacher could not be found", - "error": findErr, - }) - } - - // Collect image - file, err := c.FormFile("image") - if err != nil { - cancel() - return c.Status(fiber.StatusInternalServerError).JSON(fiber.Map{ - "success": false, - "message": "the image could not be retrieved", - "error": err, - }) - } - - // Get student photo - var photo models.Photo - findErr = imageCollection.FindOne(context.TODO(), bson.M{"name": teacher.SchoolData.PhotoName}).Decode(&photo) - if findErr != nil { - cancel() - return c.Status(fiber.StatusInternalServerError).JSON(fiber.Map{ - "success": false, - "message": "the student image could not be found", - "error": findErr, - }) - } - - // Save image to local - uniqueId := uuid.New() - filename := strings.Replace(uniqueId.String(), "-", "", -1) - fileExt := strings.Split(file.Filename, ".")[1] - image := fmt.Sprintf("%s.%s", filename, fileExt) - err = c.SaveFile(file, fmt.Sprintf("./database/images/%s", image)) - if err != nil { - cancel() - return c.Status(fiber.StatusInternalServerError).JSON(fiber.Map{ - "success": false, - "message": "the image could not be saved", - "error": err, - }) - } - - // Read the entire file into a byte slice - bytes, err := ioutil.ReadFile(fmt.Sprintf("./database/images/%s", image)) - if err != nil { - cancel() - return c.Status(fiber.StatusInternalServerError).JSON(fiber.Map{ - "success": false, - "message": "the image could not be read", - "error": err, - }) - } - - var base64Encoding string = toBase64(bytes) - - // Update image name and base64 data - update_time, _ := time.Parse(time.RFC3339, time.Now().Format(time.RFC3339)) - update := bson.M{ - "$set": bson.M{ - "base64": base64Encoding, - "updated_at": update_time, - }, - } - _, updateErr := imageCollection.UpdateOne( - ctx, - bson.M{"_id": photo.ID}, - update, - ) - if updateErr != nil { - cancel() - return c.Status(fiber.StatusInternalServerError).JSON(fiber.Map{ - "success": false, - "message": "the image could not be updated", - "error": updateErr, - }) - } - defer cancel() - - // Remove local image - os.Remove(fmt.Sprintf("./database/images/%s", image)) - - return c.Status(fiber.StatusOK).JSON(fiber.Map{ - "success": true, - "message": "successfully updated teacher photo", - }) -} - -func UpdateTeacherEmail(c *fiber.Ctx) error { - var data map[string]string - ctx, cancel := context.WithTimeout(context.Background(), 30*time.Second) - - if err := c.BodyParser(&data); err != nil { - cancel() - return c.Status(fiber.StatusInternalServerError).JSON(fiber.Map{ - "success": false, - "message": "Failed to parse body", - "error": err, - }) - } - - var tid string - var verifiedTeacher bool - - verifiedAdmin, _ := AuthenticateUser(c, 3) - verifiedTeacher, tid = AuthenticateUser(c, 2) - // Ensure Authenticated admin sent request - if !verifiedAdmin && !verifiedTeacher { - cancel() - return c.Status(fiber.StatusUnauthorized).JSON(fiber.Map{ - "success": false, - "message": "Unauthorized: only an admin or teacher can perform this action", - }) - } - - if verifiedAdmin && data["tid"] == "" { - cancel() - return c.Status(fiber.StatusBadRequest).JSON(fiber.Map{ - "success": false, - "message": "missing required fields", - }) - } else if verifiedAdmin { - tid = data["tid"] - } - - // Check required fields are included - if data["email"] == "" { - cancel() - return c.Status(fiber.StatusBadRequest).JSON(fiber.Map{ - "success": false, - "message": "missing required fields", - }) - } - - update_time, _ := time.Parse(time.RFC3339, time.Now().Format(time.RFC3339)) - update := bson.M{ - "$set": bson.M{ - "personaldata.email": data["email"], - "updated_at": update_time, - }, - } - - _, updateErr := teacherCollection.UpdateOne( - ctx, - bson.M{"schooldata.tid": tid}, - update, - ) - if updateErr != nil { - cancel() - return c.Status(fiber.StatusInternalServerError).JSON(fiber.Map{ - "success": false, - "message": "the teacher could not be updated", - "error": updateErr, - }) - } - defer cancel() - - return c.Status(fiber.StatusOK).JSON(fiber.Map{ - "success": true, - "message": "successfully updated teacher", - }) -} - -func UpdateTeacherName(c *fiber.Ctx) error { - var data map[string]string - ctx, cancel := context.WithTimeout(context.Background(), 30*time.Second) - - if err := c.BodyParser(&data); err != nil { - cancel() - return c.Status(fiber.StatusInternalServerError).JSON(fiber.Map{ - "success": false, - "message": "Failed to parse body", - "error": err, - }) - } - - // Ensure Authenticated admin sent request - if verified, _ := AuthenticateUser(c, 3); !verified { - cancel() - return c.Status(fiber.StatusUnauthorized).JSON(fiber.Map{ - "success": false, - "message": "Unauthorized: only an admin can perform this action", - }) - } - - // Check id and names are included - if data["tid"] == "" || data["firstname"] == "" || data["lastname"] == "" { - cancel() - return c.Status(fiber.StatusBadRequest).JSON(fiber.Map{ - "success": false, - "message": "missing required fields", - }) - } - - // Get teacher - var teacher models.Teacher - findErr := teacherCollection.FindOne(ctx, bson.M{"schooldata.tid": data["tid"]}).Decode(&teacher) - if findErr != nil { - cancel() - return c.Status(fiber.StatusBadRequest).JSON(fiber.Map{ - "success": false, - "message": "the teacher could not be found", - "error": findErr, - }) - } - - var middlename string = "" - - if data["middlename"] != "" { - middlename = data["middlename"] - } - - update_time, _ := time.Parse(time.RFC3339, time.Now().Format(time.RFC3339)) - update := bson.M{ - "$set": bson.M{ - "personaldata.firstname": data["firstname"], - "personaldata.middlename": middlename, - "personaldata.lastname": data["lastname"], - "updated_at": update_time, - }, - } - - _, updateErr := teacherCollection.UpdateOne( - ctx, - bson.M{"schooldata.tid": data["tid"]}, - update, - ) - if updateErr != nil { - cancel() - return c.Status(fiber.StatusInternalServerError).JSON(fiber.Map{ - "success": false, - "message": "the teacher could not be updated", - "error": updateErr, - }) - } - defer cancel() - - return c.Status(fiber.StatusOK).JSON(fiber.Map{ - "success": true, - "message": "successfully updated teacher", - }) -} - -func UpdateContactName(c *fiber.Ctx) error { - var data map[string]string - ctx, cancel := context.WithTimeout(context.Background(), 30*time.Second) - - if err := c.BodyParser(&data); err != nil { - cancel() - return c.Status(fiber.StatusInternalServerError).JSON(fiber.Map{ - "success": false, - "message": "Failed to parse body", - "error": err, - }) - } - - // Ensure Authenticated admin sent request - if verified, _ := AuthenticateUser(c, 3); !verified { - cancel() - return c.Status(fiber.StatusUnauthorized).JSON(fiber.Map{ - "success": false, - "message": "Unauthorized: only an admin can perform this action", - }) - } - - // Check required fields are included - if data["_id"] == "" || data["firstname"] == "" || data["lastname"] == "" { - cancel() - return c.Status(fiber.StatusBadRequest).JSON(fiber.Map{ - "success": false, - "message": "missing required fields", - }) - } - - var middlename string = "" - - if data["middlename"] != "" { - middlename = data["middlename"] - } - - update_time, _ := time.Parse(time.RFC3339, time.Now().Format(time.RFC3339)) - update := bson.M{ - "$set": bson.M{ - "firstname": data["firstname"], - "middlename": middlename, - "lastname": data["lastname"], - "updated_at": update_time, - }, - } - - _, updateErr := contactCollection.UpdateOne( - ctx, - bson.M{"_id": data["_id"]}, - update, - ) - if updateErr != nil { - cancel() - return c.Status(fiber.StatusInternalServerError).JSON(fiber.Map{ - "success": false, - "message": "the contact could not be updated", - "error": updateErr, - }) - } - defer cancel() - - return c.Status(fiber.StatusOK).JSON(fiber.Map{ - "success": true, - "message": "successfully updated contact", - }) -} - -func UpdateContactAddress(c *fiber.Ctx) error { - var data map[string]string - ctx, cancel := context.WithTimeout(context.Background(), 30*time.Second) - - if err := c.BodyParser(&data); err != nil { - cancel() - return c.Status(fiber.StatusInternalServerError).JSON(fiber.Map{ - "success": false, - "message": "Failed to parse body", - "error": err, - }) - } - - // Ensure Authenticated admin sent request - if verified, _ := AuthenticateUser(c, 3); !verified { - cancel() - return c.Status(fiber.StatusUnauthorized).JSON(fiber.Map{ - "success": false, - "message": "Unauthorized: only an admin can perform this action", - }) - } - - // Check required fields are included - if data["_id"] == "" || data["address"] == "" || data["city"] == "" || data["province"] == "" || data["postal"] == "" { - cancel() - return c.Status(fiber.StatusBadRequest).JSON(fiber.Map{ - "success": false, - "message": "missing required fields", - }) - } - - update_time, _ := time.Parse(time.RFC3339, time.Now().Format(time.RFC3339)) - update := bson.M{ - "$set": bson.M{ - "address": data["address"], - "city": data["city"], - "province": data["province"], - "postal": data["postal"], - "updated_at": update_time, - }, - } - - _, updateErr := contactCollection.UpdateOne( - ctx, - bson.M{"_id": data["_id"]}, - update, - ) - if updateErr != nil { - cancel() - return c.Status(fiber.StatusInternalServerError).JSON(fiber.Map{ - "success": false, - "message": "the contact could not be updated", - "error": updateErr, - }) - } - defer cancel() - - return c.Status(fiber.StatusOK).JSON(fiber.Map{ - "success": true, - "message": "successfully updated contact", - }) -} - -func UpdateContactHomePhone(c *fiber.Ctx) error { - var data map[string]string - ctx, cancel := context.WithTimeout(context.Background(), 30*time.Second) - - if err := c.BodyParser(&data); err != nil { - cancel() - return c.Status(fiber.StatusInternalServerError).JSON(fiber.Map{ - "success": false, - "message": "Failed to parse body", - "error": err, - }) - } - - // Ensure Authenticated admin sent request - if verified, _ := AuthenticateUser(c, 3); !verified { - cancel() - return c.Status(fiber.StatusUnauthorized).JSON(fiber.Map{ - "success": false, - "message": "Unauthorized: only an admin can perform this action", - }) - } - - // Check id of contact and new priority number is included - if data["_id"] == "" || data["newnumber"] == "" { - cancel() - return c.Status(fiber.StatusBadRequest).JSON(fiber.Map{ - "success": false, - "message": "missing required fields", - }) - } - - update_time, _ := time.Parse(time.RFC3339, time.Now().Format(time.RFC3339)) - update := bson.M{ - "$set": bson.M{ - "homephone": data["newnumber"], - "updated_at": update_time, - }, - } - - _, updateErr := contactCollection.UpdateOne( - ctx, - bson.M{"_id": data["_id"]}, - update, - ) - if updateErr != nil { - cancel() - return c.Status(fiber.StatusInternalServerError).JSON(fiber.Map{ - "success": false, - "message": "contact could not be updated", - "error": updateErr, - }) - } - defer cancel() - - return c.Status(fiber.StatusOK).JSON(fiber.Map{ - "success": true, - "message": "successfully updated contact", - }) -} - -func UpdateContactWorkPhone(c *fiber.Ctx) error { - var data map[string]string - ctx, cancel := context.WithTimeout(context.Background(), 30*time.Second) - - if err := c.BodyParser(&data); err != nil { - cancel() - return c.Status(fiber.StatusInternalServerError).JSON(fiber.Map{ - "success": false, - "message": "Failed to parse body", - "error": err, - }) - } - - // Ensure Authenticated admin sent request - if verified, _ := AuthenticateUser(c, 3); !verified { - cancel() - return c.Status(fiber.StatusUnauthorized).JSON(fiber.Map{ - "success": false, - "message": "Unauthorized: only an admin can perform this action", - }) - } - - // Check id of contact and new priority number is included - if data["_id"] == "" || data["newnumber"] == "" { - cancel() - return c.Status(fiber.StatusBadRequest).JSON(fiber.Map{ - "success": false, - "message": "missing required fields", - }) - } - - update_time, _ := time.Parse(time.RFC3339, time.Now().Format(time.RFC3339)) - update := bson.M{ - "$set": bson.M{ - "workphone": data["newnumber"], - "updated_at": update_time, - }, - } - - _, updateErr := contactCollection.UpdateOne( - ctx, - bson.M{"_id": data["_id"]}, - update, - ) - if updateErr != nil { - cancel() - return c.Status(fiber.StatusInternalServerError).JSON(fiber.Map{ - "success": false, - "message": "contact could not be updated", - "error": updateErr, - }) - } - defer cancel() - - return c.Status(fiber.StatusOK).JSON(fiber.Map{ - "success": true, - "message": "successfully updated contact", - }) -} - -func UpdateContactEmail(c *fiber.Ctx) error { - var data map[string]string - ctx, cancel := context.WithTimeout(context.Background(), 30*time.Second) - - if err := c.BodyParser(&data); err != nil { - cancel() - return c.Status(fiber.StatusInternalServerError).JSON(fiber.Map{ - "success": false, - "message": "Failed to parse body", - "error": err, - }) - } - - // Ensure Authenticated admin sent request - if verified, _ := AuthenticateUser(c, 3); !verified { - cancel() - return c.Status(fiber.StatusUnauthorized).JSON(fiber.Map{ - "success": false, - "message": "Unauthorized: only an admin can perform this action", - }) - } - - // Check id of contact and new priority number is included - if data["_id"] == "" || data["email"] == "" { - cancel() - return c.Status(fiber.StatusBadRequest).JSON(fiber.Map{ - "success": false, - "message": "missing required fields", - }) - } - - update_time, _ := time.Parse(time.RFC3339, time.Now().Format(time.RFC3339)) - update := bson.M{ - "$set": bson.M{ - "email": data["email"], - "updated_at": update_time, - }, - } - - _, updateErr := contactCollection.UpdateOne( - ctx, - bson.M{"_id": data["_id"]}, - update, - ) - if updateErr != nil { - cancel() - return c.Status(fiber.StatusInternalServerError).JSON(fiber.Map{ - "success": false, - "message": "contact could not be updated", - "error": updateErr, - }) - } - defer cancel() - - return c.Status(fiber.StatusOK).JSON(fiber.Map{ - "success": true, - "message": "successfully updated contact", - }) -} - -func UpdateContactPriority(c *fiber.Ctx) error { - var data map[string]interface{} - ctx, cancel := context.WithTimeout(context.Background(), 30*time.Second) - - if err := c.BodyParser(&data); err != nil { - cancel() - return c.Status(fiber.StatusInternalServerError).JSON(fiber.Map{ - "success": false, - "message": "Failed to parse body", - "error": err, - }) - } - - // Ensure Authenticated admin sent request - if verified, _ := AuthenticateUser(c, 3); !verified { - cancel() - return c.Status(fiber.StatusUnauthorized).JSON(fiber.Map{ - "success": false, - "message": "Unauthorized: only an admin can perform this action", - }) - } - - // Check id of contact and new priority number is included - if data["_id"] == nil || data["priority"] == nil { - cancel() - return c.Status(fiber.StatusBadRequest).JSON(fiber.Map{ - "success": false, - "message": "missing required fields", - }) - } - - var priority int = data["priority"].(int) - - if priority > 10 || priority < 1 { - cancel() - return c.Status(fiber.StatusBadRequest).JSON(fiber.Map{ - "success": false, - "message": "invalid priority", - }) - } - - update_time, _ := time.Parse(time.RFC3339, time.Now().Format(time.RFC3339)) - update := bson.M{ - "$set": bson.M{ - "priority": priority, - "updated_at": update_time, - }, - } - - _, updateErr := contactCollection.UpdateOne( - ctx, - bson.M{"_id": data["_id"]}, - update, - ) - if updateErr != nil { - cancel() - return c.Status(fiber.StatusInternalServerError).JSON(fiber.Map{ - "success": false, - "message": "contact could not be updated", - "error": updateErr, - }) - } - defer cancel() - - return c.Status(fiber.StatusOK).JSON(fiber.Map{ - "success": true, - "message": "successfully updated contact", - }) -} - -func UpdateLockerCombo(c *fiber.Ctx) error { - var data map[string]string - ctx, cancel := context.WithTimeout(context.Background(), 30*time.Second) - - if err := c.BodyParser(&data); err != nil { - cancel() - return c.Status(fiber.StatusInternalServerError).JSON(fiber.Map{ - "success": false, - "message": "Failed to parse body", - "error": err, - }) - } - - // Ensure Authenticated admin sent request - if verified, _ := AuthenticateUser(c, 3); !verified { - cancel() - return c.Status(fiber.StatusUnauthorized).JSON(fiber.Map{ - "success": false, - "message": "Unauthorized: only an admin can perform this action", - }) - } - - // Check locker number is included - if data["lockernumber"] == "" || data["newlockercombo"] == "" { - cancel() - return c.Status(fiber.StatusBadRequest).JSON(fiber.Map{ - "success": false, - "message": "missing required fields", - }) - } - - update_time, _ := time.Parse(time.RFC3339, time.Now().Format(time.RFC3339)) - update := bson.M{ - "$set": bson.M{ - "lockercombo": data["newlockercombo"], - "updated_at": update_time, - }, - } - - _, updateErr := lockerCollection.UpdateOne( - ctx, - bson.M{"lockernumber": data["lockernumber"]}, - update, - ) - if updateErr != nil { - cancel() - return c.Status(fiber.StatusInternalServerError).JSON(fiber.Map{ - "success": false, - "message": "the locker could not be updated", - "error": updateErr, - }) - } - defer cancel() - - return c.Status(fiber.StatusOK).JSON(fiber.Map{ - "success": true, - "message": "successfully updated locker", - }) -} - -func RemoveStudent(c *fiber.Ctx) error { - var data map[string]string - ctx, cancel := context.WithTimeout(context.Background(), 30*time.Second) - - if err := c.BodyParser(&data); err != nil { - cancel() - return c.Status(fiber.StatusInternalServerError).JSON(fiber.Map{ - "success": false, - "message": "Failed to parse body", - "error": err, - }) - } - - // Ensure Authenticated admin sent request - if verified, _ := AuthenticateUser(c, 3); !verified { - cancel() - return c.Status(fiber.StatusUnauthorized).JSON(fiber.Map{ - "success": false, - "message": "Unauthorized: only an admin can perform this action", - }) - } - - // Check student id is included - if data["sid"] == "" { - cancel() - return c.Status(fiber.StatusBadRequest).JSON(fiber.Map{ - "success": false, - "message": "missing required fields", - }) - } - - _, deleteErr := idCollection.DeleteOne(ctx, bson.M{"cid": data["sid"]}) - if deleteErr != nil { - cancel() - return c.Status(fiber.StatusInternalServerError).JSON(fiber.Map{ - "success": false, - "message": "the identification number could not be deleted", - "error": deleteErr, - }) - } - - _, deleteErr = studentCollection.DeleteOne(ctx, bson.M{"schooldata.sid": data["sid"]}) - if deleteErr != nil { - cancel() - return c.Status(fiber.StatusInternalServerError).JSON(fiber.Map{ - "success": false, - "message": "the student could not be deleted", - "error": deleteErr, - }) - } - defer cancel() - - return c.Status(fiber.StatusOK).JSON(fiber.Map{ - "success": true, - "message": "successfully deleted student", - }) -} - -func RemoveTeacher(c *fiber.Ctx) error { - var data map[string]string - ctx, cancel := context.WithTimeout(context.Background(), 30*time.Second) - - if err := c.BodyParser(&data); err != nil { - cancel() - return c.Status(fiber.StatusInternalServerError).JSON(fiber.Map{ - "success": false, - "message": "Failed to parse body", - "error": err, - }) - } - - // Ensure Authenticated admin sent request - if verified, _ := AuthenticateUser(c, 3); !verified { - cancel() - return c.Status(fiber.StatusUnauthorized).JSON(fiber.Map{ - "success": false, - "message": "Unauthorized: only an admin can perform this action", - }) - } - - // Check student id is included - if data["tid"] == "" { - cancel() - return c.Status(fiber.StatusBadRequest).JSON(fiber.Map{ - "success": false, - "message": "missing required fields", - }) - } - - _, deleteErr := idCollection.DeleteOne(ctx, bson.M{"cid": data["tid"]}) - if deleteErr != nil { - cancel() - return c.Status(fiber.StatusInternalServerError).JSON(fiber.Map{ - "success": false, - "message": "the identification number could not be deleted", - "error": deleteErr, - }) - } - - _, deleteErr = teacherCollection.DeleteOne(ctx, bson.M{"schooldata.tid": data["tid"]}) - if deleteErr != nil { - cancel() - return c.Status(fiber.StatusInternalServerError).JSON(fiber.Map{ - "success": false, - "message": "the teacher could not be deleted", - "error": deleteErr, - }) - } - defer cancel() - - return c.Status(fiber.StatusOK).JSON(fiber.Map{ - "success": true, - "message": "successfully deleted teacher", - }) -} - -func RemoveAdmin(c *fiber.Ctx) error { - var data map[string]string - ctx, cancel := context.WithTimeout(context.Background(), 30*time.Second) - - if err := c.BodyParser(&data); err != nil { - cancel() - return c.Status(fiber.StatusInternalServerError).JSON(fiber.Map{ - "success": false, - "message": "Failed to parse body", - "error": err, - }) - } - - // Ensure Authenticated admin sent request - if verified, _ := AuthenticateUser(c, 3); !verified { - cancel() - return c.Status(fiber.StatusUnauthorized).JSON(fiber.Map{ - "success": false, - "message": "Unauthorized: only an admin can perform this action", - }) - } - - // Check student id is included - if data["aid"] == "" { - cancel() - return c.Status(fiber.StatusBadRequest).JSON(fiber.Map{ - "success": false, - "message": "missing required fields", - }) - } - - _, deleteErr := idCollection.DeleteOne(ctx, bson.M{"cid": data["aid"]}) - if deleteErr != nil { - cancel() - return c.Status(fiber.StatusInternalServerError).JSON(fiber.Map{ - "success": false, - "message": "the identification number could not be deleted", - "error": deleteErr, - }) - } - - _, deleteErr = adminCollection.DeleteOne(ctx, bson.M{"aid": data["aid"]}) - if deleteErr != nil { - cancel() - return c.Status(fiber.StatusInternalServerError).JSON(fiber.Map{ - "success": false, - "message": "the admin could not be deleted", - "error": deleteErr, - }) - } - defer cancel() - - return c.Status(fiber.StatusOK).JSON(fiber.Map{ - "success": true, - "message": "successfully deleted admin", - }) -} - -func UpdateAdminName(c *fiber.Ctx) error { - var data map[string]string - ctx, cancel := context.WithTimeout(context.Background(), 30*time.Second) - - if err := c.BodyParser(&data); err != nil { - cancel() - return c.Status(fiber.StatusInternalServerError).JSON(fiber.Map{ - "success": false, - "message": "Failed to parse body", - "error": err, - }) - } - - cookie := c.Cookies("jwt") - - token, err := jwt.ParseWithClaims(cookie, &jwt.StandardClaims{}, func(token *jwt.Token) (interface{}, error) { - return []byte(SecretKey), nil - }) - if err != nil { - cancel() - return c.Status(fiber.StatusUnauthorized).JSON(fiber.Map{ - "success": false, - "message": "not authorized", - }) - } - - claims := token.Claims.(*jwt.StandardClaims) - - var admin models.Admin - findErr := adminCollection.FindOne(ctx, bson.M{"aid": claims.Issuer}).Decode(&admin) - if findErr != nil { - cancel() - return c.Status(fiber.StatusBadRequest).JSON(fiber.Map{ - "success": false, - "message": "admin not found", - }) - } - - // Check required fields are included - if data["firstname"] == "" || data["lastname"] == "" { - cancel() - return c.Status(fiber.StatusBadRequest).JSON(fiber.Map{ - "success": false, - "message": "missing required fields", - }) - } - - update_time, _ := time.Parse(time.RFC3339, time.Now().Format(time.RFC3339)) - update := bson.M{ - "$set": bson.M{ - "firstname": data["firstname"], - "lastname": data["lastname"], - "updated_at": update_time, - }, - } - - _, updateErr := teacherCollection.UpdateOne( - ctx, - bson.M{"aid": claims.Issuer}, - update, - ) - if updateErr != nil { - cancel() - return c.Status(fiber.StatusInternalServerError).JSON(fiber.Map{ - "success": false, - "message": "the admin could not be updated", - "error": updateErr, - }) - } - defer cancel() - - return c.Status(fiber.StatusOK).JSON(fiber.Map{ - "success": true, - "message": "successfully updated admin", - }) -} - -func UpdateAdminEmail(c *fiber.Ctx) error { - var data map[string]string - ctx, cancel := context.WithTimeout(context.Background(), 30*time.Second) - - if err := c.BodyParser(&data); err != nil { - cancel() - return c.Status(fiber.StatusInternalServerError).JSON(fiber.Map{ - "success": false, - "message": "Failed to parse body", - "error": err, - }) - } - - cookie := c.Cookies("jwt") - - token, err := jwt.ParseWithClaims(cookie, &jwt.StandardClaims{}, func(token *jwt.Token) (interface{}, error) { - return []byte(SecretKey), nil - }) - if err != nil { - cancel() - return c.Status(fiber.StatusUnauthorized).JSON(fiber.Map{ - "success": false, - "message": "not authorized", - }) - } - - claims := token.Claims.(*jwt.StandardClaims) - - var admin models.Admin - findErr := adminCollection.FindOne(ctx, bson.M{"aid": claims.Issuer}).Decode(&admin) - if findErr != nil { - cancel() - return c.Status(fiber.StatusBadRequest).JSON(fiber.Map{ - "success": false, - "message": "admin not found", - }) - } - - // Check required fields are included - if data["email"] == "" { - cancel() - return c.Status(fiber.StatusBadRequest).JSON(fiber.Map{ - "success": false, - "message": "missing required fields", - }) - } - - update_time, _ := time.Parse(time.RFC3339, time.Now().Format(time.RFC3339)) - update := bson.M{ - "$set": bson.M{ - "email": data["email"], - "updated_at": update_time, - }, - } - - _, updateErr := teacherCollection.UpdateOne( - ctx, - bson.M{"aid": claims.Issuer}, - update, - ) - if updateErr != nil { - cancel() - return c.Status(fiber.StatusInternalServerError).JSON(fiber.Map{ - "success": false, - "message": "the admin could not be updated", - "error": updateErr, - }) - } - defer cancel() - - return c.Status(fiber.StatusOK).JSON(fiber.Map{ - "success": true, - "message": "successfully updated admin", - }) -} - -func UpdateAdminPassword(c *fiber.Ctx) error { - var data map[string]string - ctx, cancel := context.WithTimeout(context.Background(), 30*time.Second) - - if err := c.BodyParser(&data); err != nil { - cancel() - return c.Status(fiber.StatusInternalServerError).JSON(fiber.Map{ - "success": false, - "message": "Failed to parse body", - "error": err, - }) - } - - cookie := c.Cookies("jwt") - - token, err := jwt.ParseWithClaims(cookie, &jwt.StandardClaims{}, func(token *jwt.Token) (interface{}, error) { - return []byte(SecretKey), nil - }) - if err != nil { - cancel() - return c.Status(fiber.StatusUnauthorized).JSON(fiber.Map{ - "success": false, - "message": "not authorized", - }) - } - - claims := token.Claims.(*jwt.StandardClaims) - - var admin models.Admin - findErr := adminCollection.FindOne(ctx, bson.M{"aid": claims.Issuer}).Decode(&admin) - if findErr != nil { - cancel() - return c.Status(fiber.StatusBadRequest).JSON(fiber.Map{ - "success": false, - "message": "admin not found", - }) - } - - // Check required fields are included - if data["password"] == "" || data["newpassword1"] == "" || data["newpassword2"] == "" { - cancel() - return c.Status(fiber.StatusBadRequest).JSON(fiber.Map{ - "success": false, - "message": "missing required fields", - }) - } - - if !admin.ComparePasswords(data["password"]) { - cancel() - return c.Status(fiber.StatusOK).JSON(fiber.Map{ - "success": false, - "message": "Your password is incorrect", - }) - } - - if data["newpassword1"] != data["newpassword2"] { - cancel() - return c.Status(fiber.StatusBadRequest).JSON(fiber.Map{ - "success": false, - "message": "Your new passwords must match", - }) - } - - update_time, _ := time.Parse(time.RFC3339, time.Now().Format(time.RFC3339)) - update := bson.M{ - "$set": bson.M{ - "password": admin.HashPassword(data["newpassword1"]), - "temppassword": false, // If it were a temp password, its not now - "updated_at": update_time, - }, - } - - _, updateErr := teacherCollection.UpdateOne( - ctx, - bson.M{"aid": claims.Issuer}, - update, - ) - if updateErr != nil { - cancel() - return c.Status(fiber.StatusInternalServerError).JSON(fiber.Map{ - "success": false, - "message": "the admin password could not be updated", - "error": updateErr, - }) - } - defer cancel() - - subject := "Password Changed" - receiver := admin.Email - r := NewRequest([]string{receiver}, subject) - - if sent := r.Send("./templates/selfPasswordChanged.html", map[string]string{"username": admin.FirstName}); !sent { - return c.Status(fiber.StatusInternalServerError).JSON(fiber.Map{ - "success": false, - "message": "Could not send password to admins email", - }) - } - - return c.Status(fiber.StatusOK).JSON(fiber.Map{ - "success": true, - "message": "successfully updated admin password", - }) -} diff --git a/models/studentModel.go b/models/studentModel.go index 73ca761..0a06967 100644 --- a/models/studentModel.go +++ b/models/studentModel.go @@ -18,8 +18,8 @@ import ( "go.mongodb.org/mongo-driver/mongo" ) -var lockerCollection *mongo.Collection = database.OpenCollection(database.Client, "lockers") -var studentCollection *mongo.Collection = database.OpenCollection(database.Client, "students") +var LockerCollection *mongo.Collection = database.OpenCollection(database.Client, "lockers") +var StudentCollection *mongo.Collection = database.OpenCollection(database.Client, "students") type Student struct { ID primitive.ObjectID `bson:"_id"` @@ -75,7 +75,7 @@ func (s *Student) HashPassword(password string) string { func (s *Student) EmailExists(email string) bool { var student Student - findErr := studentCollection.FindOne(context.TODO(), bson.M{"accountdata.schoolemail": email}).Decode(&student) + findErr := StudentCollection.FindOne(context.TODO(), bson.M{"accountdata.schoolemail": email}).Decode(&student) if findErr != nil { return false } diff --git a/models/teacherModel.go b/models/teacherModel.go index b301451..acb256f 100644 --- a/models/teacherModel.go +++ b/models/teacherModel.go @@ -24,7 +24,7 @@ var ( allCharSet = lowerCharSet + upperCharSet + specialCharSet + numberSet ) -var teacherCollection *mongo.Collection = database.OpenCollection(database.Client, "teachers") +var TeacherCollection *mongo.Collection = database.OpenCollection(database.Client, "teachers") type Teacher struct { ID primitive.ObjectID `bson:"_id"` @@ -73,7 +73,7 @@ func (t *Teacher) HashPassword(password string) string { func (t *Teacher) EmailExists(email string) bool { var teacher Teacher - findErr := teacherCollection.FindOne(context.TODO(), bson.M{"accountdata.schoolemail": email}).Decode(&teacher) + findErr := TeacherCollection.FindOne(context.TODO(), bson.M{"accountdata.schoolemail": email}).Decode(&teacher) if findErr != nil { return false } diff --git a/routes/routes.go b/routes/routes.go index fa0244f..d96f6e1 100644 --- a/routes/routes.go +++ b/routes/routes.go @@ -2,6 +2,7 @@ package routes import ( "github.com/SowinskiBraeden/school-management-api/controllers" + "github.com/SowinskiBraeden/school-management-api/controllers/update" "github.com/gofiber/fiber/v2" ) @@ -27,27 +28,27 @@ func Setup(app *fiber.App) { app.Post(routerPrefix+"/student/login", controllers.StudentLogin) // Update Student Handler - app.Post(routerPrefix+"/student/updateName", controllers.UpdateStudentName) - app.Post(routerPrefix+"/student/updateGradeLevel", controllers.UpdateStudentGradeLevel) - app.Post(routerPrefix+"/student/updateHomeroom", controllers.UpdateStudentHomeroom) - app.Post(routerPrefix+"/student/updateLocker", controllers.UpdateStudentLocker) - app.Post(routerPrefix+"/studnet/updateYOG", controllers.UpdateStudentYOG) - app.Post(routerPrefix+"/studnet/addContact", controllers.AddStudentContact) - app.Post(routerPrefix+"/student/removeContact", controllers.RemoveStudentContact) - app.Post(routerPrefix+"/student/updatePassword", controllers.UpdateStudentPassword) - app.Post(routerPrefix+"/student/resetPassword", controllers.ResetStudentPassword) - app.Post(routerPrefix+"/student/updateAddress", controllers.UpdateStudentAddress) - app.Post(routerPrefix+"/student/updatePhoto", controllers.UpdateStudentPhoto) - app.Post(routerPrefix+"/student/updateEmail", controllers.UpdateStudentEmail) + app.Post(routerPrefix+"/student/updateName", update.UpdateStudentName) + app.Post(routerPrefix+"/student/updateGradeLevel", update.UpdateStudentGradeLevel) + app.Post(routerPrefix+"/student/updateHomeroom", update.UpdateStudentHomeroom) + app.Post(routerPrefix+"/student/updateLocker", update.UpdateStudentLocker) + app.Post(routerPrefix+"/studnet/updateYOG", update.UpdateStudentYOG) + app.Post(routerPrefix+"/studnet/addContact", update.AddStudentContact) + app.Post(routerPrefix+"/student/removeContact", update.RemoveStudentContact) + app.Post(routerPrefix+"/student/updatePassword", update.UpdateStudentPassword) + app.Post(routerPrefix+"/student/resetPassword", update.ResetStudentPassword) + app.Post(routerPrefix+"/student/updateAddress", update.UpdateStudentAddress) + app.Post(routerPrefix+"/student/updatePhoto", update.UpdateStudentPhoto) + app.Post(routerPrefix+"/student/updateEmail", update.UpdateStudentEmail) // Student Contact Handler app.Post(routerPrefix+"/contact/createContact", controllers.CreateContact) - app.Post(routerPrefix+"/contact/updateName", controllers.UpdateContactName) - app.Post(routerPrefix+"/contact/updateAddress", controllers.UpdateContactAddress) - app.Post(routerPrefix+"/contact/updateHomePhone", controllers.UpdateContactHomePhone) - app.Post(routerPrefix+"/contact/updateWorkPhone", controllers.UpdateContactWorkPhone) - app.Post(routerPrefix+"/contact/updateEmail", controllers.UpdateContactEmail) - app.Post(routerPrefix+"/contact/updatePriority", controllers.UpdateContactPriority) + app.Post(routerPrefix+"/contact/updateName", update.UpdateContactName) + app.Post(routerPrefix+"/contact/updateAddress", update.UpdateContactAddress) + app.Post(routerPrefix+"/contact/updateHomePhone", update.UpdateContactHomePhone) + app.Post(routerPrefix+"/contact/updateWorkPhone", update.UpdateContactWorkPhone) + app.Post(routerPrefix+"/contact/updateEmail", update.UpdateContactEmail) + app.Post(routerPrefix+"/contact/updatePriority", update.UpdateContactPriority) app.Post(routerPrefix+"/contact/deleteContact", controllers.DeleteContact) // Teacher Authentication Handler @@ -56,13 +57,13 @@ func Setup(app *fiber.App) { app.Post(routerPrefix+"/teacher/login", controllers.TeacherLogin) // Teacher Update Handler - app.Post(routerPrefix+"/teacher/updatePassword", controllers.UpdateTeacherPassword) - app.Post(routerPrefix+"/teacher/updateAddress", controllers.UpdateTeacherAddress) - app.Post(routerPrefix+"/teacher/updatePhoto", controllers.UpdateTeacherPhoto) - app.Post(routerPrefix+"/teacher/updateName", controllers.UpdateTeacherName) - app.Post(routerPrefix+"/teacher/updateHomeroom", controllers.UpdateTeacherHomeroom) - app.Post(routerPrefix+"/teacher/updateEmail", controllers.UpdateTeacherEmail) - app.Post(routerPrefix+"/teacher/resetPassword", controllers.ResetTeacherPassword) + app.Post(routerPrefix+"/teacher/updatePassword", update.UpdateTeacherPassword) + app.Post(routerPrefix+"/teacher/updateAddress", update.UpdateTeacherAddress) + app.Post(routerPrefix+"/teacher/updatePhoto", update.UpdateTeacherPhoto) + app.Post(routerPrefix+"/teacher/updateName", update.UpdateTeacherName) + app.Post(routerPrefix+"/teacher/updateHomeroom", update.UpdateTeacherHomeroom) + app.Post(routerPrefix+"/teacher/updateEmail", update.UpdateTeacherEmail) + app.Post(routerPrefix+"/teacher/resetPassword", update.ResetTeacherPassword) // General Routes app.Post(routerPrefix+"/logout", controllers.Logout) @@ -73,14 +74,14 @@ func Setup(app *fiber.App) { app.Post(routerPrefix+"/admin/login", controllers.AdminLogin) // Admin Update Handler - app.Post(routerPrefix+"/admin/updateName", controllers.UpdateAdminName) - app.Post(routerPrefix+"/admin/updateEmail", controllers.UpdateAdminEmail) - app.Post(routerPrefix+"/admin/updatePassword", controllers.UpdateAdminPassword) + app.Post(routerPrefix+"/admin/updateName", update.UpdateAdminName) + app.Post(routerPrefix+"/admin/updateEmail", update.UpdateAdminEmail) + app.Post(routerPrefix+"/admin/updatePassword", update.UpdateAdminPassword) // General Command Handling - app.Post(routerPrefix+"/admin/updateLockerCombo", controllers.UpdateLockerCombo) - app.Post(routerPrefix+"/admin/enableStudent", controllers.RemoveStudentsDisabled) - app.Post(routerPrefix+"/admin/enableTeacher", controllers.RemoveTeachersDisabled) + app.Post(routerPrefix+"/admin/updateLockerCombo", update.UpdateLockerCombo) + app.Post(routerPrefix+"/admin/enableStudent", update.RemoveStudentsDisabled) + app.Post(routerPrefix+"/admin/enableTeacher", update.RemoveTeachersDisabled) // Delete Handler app.Post(routerPrefix+"/remove/student", controllers.RemoveStudent) diff --git a/staticcheck.conf b/staticcheck.conf new file mode 100644 index 0000000..d0d8950 --- /dev/null +++ b/staticcheck.conf @@ -0,0 +1 @@ +dot_import_whitelist = ["github.com/SowinskiBraeden/school-management-api/controllers"] \ No newline at end of file