diff --git a/controllers/usersController.go b/controllers/usersController.go index 02f6a06..389f30c 100644 --- a/controllers/usersController.go +++ b/controllers/usersController.go @@ -1,8 +1,11 @@ package controllers import ( + "fmt" "net/http" "os" + "strconv" + "strings" "time" "go-crud/initializers" @@ -11,32 +14,53 @@ import ( "github.com/gin-gonic/gin" "github.com/golang-jwt/jwt/v4" "golang.org/x/crypto/bcrypt" + "gorm.io/gorm" ) func SignUp(c *gin.Context) { - // Get the email/pass off req body var body struct { - Email string - Password string + Email string + Password string + Nik string + Name string + Photo string + Birthdate time.Time + JobTitle string + CreatedBy string + UpdatedBy string + DeletedBy string } - if err := c.ShouldBind(&body); err != nil { + + if err := c.ShouldBindJSON(&body); err != nil { c.JSON(http.StatusBadRequest, gin.H{ "error": "Failed to read body", }) return } - // Hash the password hash, err := bcrypt.GenerateFromPassword([]byte(body.Password), 10) if err != nil { c.JSON(http.StatusBadRequest, gin.H{ - "error": "Failed to generate pass hash", + "error": "Failed to generate password hash", }) return } - // Create the user - user := models.User{Email: body.Email, Password: string(hash)} + user := models.User{ + Email: body.Email, + Password: string(hash), + Nik: body.Nik, + Name: body.Name, + Photo: body.Photo, + Birthdate: body.Birthdate, + JobTitle: body.JobTitle, + LastLoginAt: time.Now(), + CreatedBy: body.Nik, + UpdatedBy: body.Nik, + DeletedBy: body.Nik, + Token: "", + } + result := initializers.DB.Create(&user) if result.Error != nil { @@ -46,52 +70,51 @@ func SignUp(c *gin.Context) { return } - // Respond + updateLoginRecord(c, user.ID, time.Now()) + c.JSON(http.StatusOK, gin.H{}) } func LogIn(c *gin.Context) { - // Get the email/pass off req body var body struct { Email string Password string } - if err := c.ShouldBind(&body); err != nil { + if err := c.ShouldBindJSON(&body); err != nil { c.JSON(http.StatusBadRequest, gin.H{ "error": "Failed to read body", }) return } - // Look up requested user var user models.User initializers.DB.First(&user, "email = ?", body.Email) if user.ID == 0 { c.JSON(http.StatusBadRequest, gin.H{ - "error": "Invalid email or pass", + "error": "Invalid email or password", }) return } - // Compare sent in pass with saved user pass hash err := bcrypt.CompareHashAndPassword([]byte(user.Password), []byte(body.Password)) - if err != nil { c.JSON(http.StatusBadRequest, gin.H{ - "error": "Invalid email or pass", + "error": "Invalid email or password", }) return } - // Generate a jwt token + user.LastLoginAt = time.Now() + initializers.DB.Save(&user) + + updateLoginRecord(c, user.ID, time.Now()) + token := jwt.NewWithClaims(jwt.SigningMethodHS256, jwt.MapClaims{ "sub": user.ID, "exp": time.Now().Add(time.Hour * 24 * 30).Unix(), }) - // Sign and get the complete encoded token as a string using the secret token tokenString, err := token.SignedString([]byte(os.Getenv("SECRET"))) - if err != nil { c.JSON(http.StatusBadRequest, gin.H{ "error": "Failed to create token", @@ -99,7 +122,6 @@ func LogIn(c *gin.Context) { return } - // Send it back c.SetSameSite(http.SameSiteLaxMode) c.SetCookie("Authorization", tokenString, 3600*24*30, "", "", false, true) c.JSON(http.StatusOK, gin.H{ @@ -107,15 +129,12 @@ func LogIn(c *gin.Context) { }) } -func LogOut (c *gin.Context){ - // Hapus informasi autentikasi atau sesi dari pengguna (misalnya token, cookie, atau data sesi) - // Contoh: Hapus cookie autentikasi - c.SetCookie("Authorization", "", -1, "/", "", false, true) +func LogOut(c *gin.Context) { + c.SetCookie("Authorization", "", -1, "/", "", false, true) - // Berikan respons berhasil logout - c.JSON(http.StatusOK, gin.H{ - "message": "Logout successful", - }) + c.JSON(http.StatusOK, gin.H{ + "message": "Logout successful", + }) } func Validate(c *gin.Context) { @@ -125,3 +144,97 @@ func Validate(c *gin.Context) { "message": user, }) } + +func updateLoginRecord(c *gin.Context, userID uint, date time.Time) { + // Cari data rekap login berdasarkan tanggal + var loginRecord models.LoginRecord + result := initializers.DB.Where("login_date = ?", date.Format("2006-01-02")).First(&loginRecord) + + if result.Error != nil && result.Error != gorm.ErrRecordNotFound { + return + } + + if result.Error == gorm.ErrRecordNotFound { + // Jika belum ada data rekap untuk hari ini, buat data baru + loginRecord = models.LoginRecord{ + LoginDate: date, + TotalUsers: 1, + UserIDs: fmt.Sprintf("%d", userID), + } + initializers.DB.Create(&loginRecord) + } else { + // Jika sudah ada data rekap, cek apakah user sudah login pada hari ini + if !containsInt(loginRecord.UserIDs, userID) { + // Jika user belum login hari ini, tambahkan ID pengguna ke dalam array user_ids + loginRecord.TotalUsers++ + loginRecord.UserIDs = fmt.Sprintf("%s,%d", loginRecord.UserIDs, userID) + initializers.DB.Save(&loginRecord) + } + } + + // Ambil data pengguna yang login pada hari ini + var users []models.User + initializers.DB.Find(&users, "id IN (?)", parseUserIDs(loginRecord.UserIDs)) + + // Update login record dengan pengguna yang login pada hari ini + loginRecord.Users = users + + // Update jumlah pengguna (TotalUsers) saat pengguna baru melakukan login + loginRecord.TotalUsers = len(users) + + initializers.DB.Save(&loginRecord) +} + + +// Helper function to check if an int is present in a slice +func containsInt(slice string, val uint) bool { + ids := parseUserIDs(slice) + for _, item := range ids { + if item == val { + return true + } + } + return false +} + +// Helper function to parse user IDs from a string +func parseUserIDs(idsStr string) []uint { + ids := strings.Split(idsStr, ",") + var userIDs []uint + for _, idStr := range ids { + id, err := strconv.ParseUint(idStr, 10, 32) + if err != nil { + continue + } + userIDs = append(userIDs, uint(id)) + } + return userIDs +} + +func GetLoginRecords(c *gin.Context) { + var loginRecords []models.LoginRecord + result := initializers.DB.Find(&loginRecords) + + if result.Error != nil { + c.JSON(http.StatusInternalServerError, gin.H{ + "error": "Failed to fetch login records", + }) + return + } + + // Retrieve user details for each login record + for i := range loginRecords { + var users []models.User + initializers.DB.Find(&users, "id IN (?)", parseUserIDs(loginRecords[i].UserIDs)) + + loginRecords[i].Users = users + } + + // Create JSON response + var response struct { + Records []models.LoginRecord `json:"records"` + } + response.Records = loginRecords + + c.JSON(http.StatusOK, response) +} diff --git a/go-crud.exe b/go-crud.exe index a2221a1..c39b2ed 100644 Binary files a/go-crud.exe and b/go-crud.exe differ diff --git a/go-crud.exe~ b/go-crud.exe~ index 3730b2a..31f4b0f 100644 Binary files a/go-crud.exe~ and b/go-crud.exe~ differ diff --git a/initializers/syncDatabase.go b/initializers/syncDatabase.go index ac44c85..9e4b94b 100644 --- a/initializers/syncDatabase.go +++ b/initializers/syncDatabase.go @@ -6,4 +6,5 @@ func SyncDatabase() { DB.AutoMigrate(&models.Post{}) DB.AutoMigrate(&models.Event{}) DB.AutoMigrate(&models.User{}) + DB.AutoMigrate(&models.LoginRecord{}) } \ No newline at end of file diff --git a/main.go b/main.go index c6d563b..2647c4c 100644 --- a/main.go +++ b/main.go @@ -18,6 +18,8 @@ func main() { r.POST("/signup", controllers.SignUp) r.POST("/login", controllers.LogIn) + r.GET("/login-records", controllers.GetLoginRecords) + r.GET("/validate", middleware.RequireAuth, controllers.Validate) r.Use(middleware.RequireAuth) diff --git a/models/loginRecord.go b/models/loginRecord.go new file mode 100644 index 0000000..21691df --- /dev/null +++ b/models/loginRecord.go @@ -0,0 +1,15 @@ +package models + +import ( + "time" + + "gorm.io/gorm" +) + +type LoginRecord struct { + gorm.Model + LoginDate time.Time + TotalUsers int + UserIDs string `gorm:"type:text"` + Users []User `json:"users" gorm:"-"` +} diff --git a/models/userModel.go b/models/userModel.go index b4260da..e4aa86e 100644 --- a/models/userModel.go +++ b/models/userModel.go @@ -1,9 +1,29 @@ package models -import "gorm.io/gorm" +import ( + "time" + + "gorm.io/gorm" +) type User struct { gorm.Model - Email string `gorm:"unique"` - Password string + Nik string + Name string + Photo string + Birthdate time.Time + JobTitle string + Email string `gorm:"unique"` + LastLoginAt time.Time + CreatedBy string + UpdatedBy string + DeletedBy string + Token string + Username string + RoleID int + Password string } + + + +