From 66333ea72786b1bf2612b2aae7e507d645aaa9e1 Mon Sep 17 00:00:00 2001 From: Sas Andy Date: Thu, 12 Dec 2024 09:53:29 +0700 Subject: [PATCH] auto logout --- cmd/api/api.go | 8 +- rest-files/auth.http | 19 ++- services/auth/auth.routes.go | 38 ++++- services/auth/auth.store.go | 279 ++++++++++++++++++++++++++++++- services/auth/jwt.go | 133 ++++++++++++++- services/auth/oauth.routes.go | 1 + services/doctor/doctor.routes.go | 8 +- services/staff/staff.routes.go | 61 +++++++ services/staff/staff.stores.go | 34 ++++ types/oauth.types.go | 17 +- types/staff.types.go | 16 ++ types/user.types.go | 3 +- utils/utils.go | 17 +- 13 files changed, 611 insertions(+), 23 deletions(-) create mode 100644 services/staff/staff.routes.go create mode 100644 services/staff/staff.stores.go create mode 100644 types/staff.types.go diff --git a/cmd/api/api.go b/cmd/api/api.go index a9f7724..4013dec 100644 --- a/cmd/api/api.go +++ b/cmd/api/api.go @@ -13,6 +13,7 @@ import ( "sismedika.com/sas/westone/services/error_log" "sismedika.com/sas/westone/services/patient" "sismedika.com/sas/westone/services/person" + "sismedika.com/sas/westone/services/staff" "sismedika.com/sas/westone/services/terminology" "sismedika.com/sas/westone/services/user" ) @@ -63,9 +64,14 @@ func (s *APIServer) Run() error { // md doctor doctorStore := doctor.NewStore(s.db) - doctorHandler := doctor.NewHandler(doctorStore) + doctorHandler := doctor.NewHandler(doctorStore, oauthStore) doctorHandler.RegisterRoutes(subrouter) + // md staff + staffStore := staff.NewStore(s.db) + staffHandler := staff.NewHandler(staffStore, oauthStore) + staffHandler.RegisterRoutes(subrouter) + // userHandler := user.NewHandler(userStore, errorLogStore) // userHandler.RegisterRoutes(subrouter) diff --git a/rest-files/auth.http b/rest-files/auth.http index 9de3893..e6ad3d6 100644 --- a/rest-files/auth.http +++ b/rest-files/auth.http @@ -2,10 +2,27 @@ POST http://localhost:8080/westone/api/v1/auth/login Content-Type: application/json { - "email":"joko@gmail.com", + "email":"joko", "password":"sas321" } +### +POST http://localhost:8080/westone/api/v1/staff/getstaff +Content-Type: application/json +Authorization: "Bearer eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJBZ2VudCI6InZzY29kZS1yZXN0Y2xpZW50IiwiSVAiOiIxMjcuMC4wLjE6NDkyOTciLCJJc19Db3VyaWVyIjoiIiwiTGFzdExvZ2luIjoiMjAyNC0xMi0xMSAxMTo1MDoxMSIsIk1fU3RhZmZOYW1lIjoiSm9rbyIsIk1fVXNlckRlZmF1bHRUU2FtcGxlU3RhdGlvbklEIjoiIiwiTV9Vc2VyRW1haWwiOiIiLCJNX1VzZXJHcm91cERhc2hib2FyZCI6Indlc3RvbmUtdWkvbG9naW4tY29iYS8iLCJNX1VzZXJJRCI6MSwiTV9Vc2VyTmFtZSI6Impva28iLCJUaW1lX0F1dG9sb2dvdXQiOiIxMDAwMDAwMCIsIlR5cGVfQWt1biI6Indlc3RvbmUiLCJWZXJzaW9uIjoidjEifQ.XYGlF8p_Dwopnbd91biQQEl1y0bOlb8j_EYEelgKLbE" + +{ + "staffname":"joko", + "token":"eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJBZ2VudCI6InZzY29kZS1yZXN0Y2xpZW50IiwiSVAiOiIxMjcuMC4wLjE6NDkyOTciLCJJc19Db3VyaWVyIjoiIiwiTGFzdExvZ2luIjoiMjAyNC0xMi0xMSAxMTo1MDoxMSIsIk1fU3RhZmZOYW1lIjoiSm9rbyIsIk1fVXNlckRlZmF1bHRUU2FtcGxlU3RhdGlvbklEIjoiIiwiTV9Vc2VyRW1haWwiOiIiLCJNX1VzZXJHcm91cERhc2hib2FyZCI6Indlc3RvbmUtdWkvbG9naW4tY29iYS8iLCJNX1VzZXJJRCI6MSwiTV9Vc2VyTmFtZSI6Impva28iLCJUaW1lX0F1dG9sb2dvdXQiOiIxMDAwMDAwMCIsIlR5cGVfQWt1biI6Indlc3RvbmUiLCJWZXJzaW9uIjoidjEifQ.XYGlF8p_Dwopnbd91biQQEl1y0bOlb8j_EYEelgKLbE" +} +### +POST http://localhost:8080/westone/api/v1/staff/getstaff +Content-Type: application/json +Authorization: "Bearer eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJBZ2VudCI6InZzY29kZS1yZXN0Y2xpZW50IiwiSVAiOiIxMjcuMC4wLjE6NDkzMzUiLCJJc19Db3VyaWVyIjoiIiwiTGFzdExvZ2luIjoiMjAyNC0xMi0xMSAwODoyODowNyIsIk1fU3RhZmZOYW1lIjoiSm9rbyIsIk1fVXNlckRlZmF1bHRUU2FtcGxlU3RhdGlvbklEIjoiIiwiTV9Vc2VyRW1haWwiOiIiLCJNX1VzZXJHcm91cERhc2hib2FyZCI6Indlc3RvbmUtdWkvbG9naW4tY29iYS8iLCJNX1VzZXJJRCI6MSwiTV9Vc2VyTmFtZSI6Impva28iLCJUaW1lX0F1dG9sb2dvdXQiOiIxMDAwMDAwMCIsIlR5cGVfQWt1biI6Indlc3RvbmUiLCJWZXJzaW9uIjoidjEifQ.pwb3btxL74UrdRb9otnWTi36aL15plT4KKBRMmRPELU" + +{ + "staffname":"joko" +} #### # POST http://localhost:8080/api/v1/auth/google/linking # Content-Type: application/json diff --git a/services/auth/auth.routes.go b/services/auth/auth.routes.go index 4858653..e73cd29 100644 --- a/services/auth/auth.routes.go +++ b/services/auth/auth.routes.go @@ -29,14 +29,16 @@ func (h *Handler) handleLogin(w http.ResponseWriter, r *http.Request) { fmt.Println(r.UserAgent()) if err := utils.ParseJSON(r, &payload); err != nil { utils.WriteError(w, http.StatusBadRequest, err) - h.store.LogSignIn(payload.Email, r.RemoteAddr, "FAILED", "LOGIN", "westone") + // h.store.LogSignIn(payload.Email, r.RemoteAddr, "FAILED", "LOGIN", "westone") + h.store.LogRISLogin(0, r.UserAgent(), payload.Email, err.Error(), "FAILED") return } if err := utils.Validate.Struct(payload); err != nil { errorz := err.(validator.ValidationErrors) utils.WriteError(w, http.StatusBadRequest, fmt.Errorf("invalid payload: %v", errorz)) - h.store.LogSignIn(payload.Email, r.RemoteAddr, "FAILED", "LOGIN", "westone") + // h.store.LogSignIn(payload.Email, r.RemoteAddr, "FAILED", "LOGIN", "westone") + h.store.LogRISLogin(0, r.UserAgent(), payload.Email, err.Error(), "FAILED") return } @@ -48,7 +50,8 @@ func (h *Handler) handleLogin(w http.ResponseWriter, r *http.Request) { h.errorz.CreateErrorLog(*logError) utils.WriteErrorLog(w, http.StatusBadRequest, *logError) } - h.store.LogSignIn(payload.Email, r.RemoteAddr, "FAILED", "LOGIN", "westone") + // h.store.LogSignIn(payload.Email, r.RemoteAddr, "FAILED", "LOGIN", "westone") + h.store.LogRISLogin(0, r.UserAgent(), response.M_StaffName, "FAILED", err.Error()) return } @@ -63,7 +66,34 @@ func (h *Handler) handleLogin(w http.ResponseWriter, r *http.Request) { if err != nil { utils.WriteError(w, http.StatusInternalServerError, err) - h.store.LogSignIn(payload.Email, r.RemoteAddr, "FAILED", "LOGIN", "westone") + // h.store.LogSignIn(payload.Email, r.RemoteAddr, "FAILED", "LOGIN", "westone") + h.store.LogRISLogin(0, r.UserAgent(), payload.Email, err.Error(), "FAILED") + return + } + + err = h.store.UpdateUserToken(response.M_UserID, token) + if err != nil { + fmt.Println("err diluar", err) + tst := h.store.LogRISLogin(0, r.UserAgent(), payload.Email, err.Error(), "FAILED") + if tst != nil { + fmt.Println("err log ris login", tst) + } + var logError *utils.LogError + if errors.As(err, &logError) { + h.errorz.CreateErrorLog(*logError) + utils.WriteErrorLog(w, http.StatusBadRequest, *logError) + } + return + } + + err = h.store.LogRISLogin(response.M_UserID, r.UserAgent(), response.M_StaffName, "SUCCESS", "SUCCESS") + if err != nil { + var logError *utils.LogError + if errors.As(err, &logError) { + h.errorz.CreateErrorLog(*logError) + utils.WriteErrorLog(w, http.StatusBadRequest, *logError) + } + // h.store.LogSignIn(payload.Email, r.RemoteAddr, "FAILED", "LOGIN", "westone") return } diff --git a/services/auth/auth.store.go b/services/auth/auth.store.go index 7919fa9..09a2ae2 100644 --- a/services/auth/auth.store.go +++ b/services/auth/auth.store.go @@ -2,6 +2,8 @@ package auth import ( "context" + "fmt" + "strconv" "time" "sismedika.com/sas/westone/types" @@ -16,7 +18,7 @@ func (s *Store) SignInWestone(email string, password string) (*types.User, error M_UserID, M_UserName, M_UserGroupDashboard, - IFNULL(M_UserFullName, "") as M_StaffName, + M_StaffName, 10000000 as time_autologout FROM m_user JOIN m_usergroup ON M_UserM_UserGroupID = M_UserGroupID @@ -66,11 +68,282 @@ func (s *Store) LogSignIn(email string, ip string, status string, tipe string, p _, err = tx.NamedExec(qry, logval) if err != nil { - return err + return &utils.LogError{ + Code: "151", + TraceID: utils.RandomTraceID(15), + Type: "LOG AUTH", + Params: "email: " + email, + Query: "-", + Message: err.Error(), + TimeStamp: time.Now().Format("2006-01-02 15:04:05"), + } } if err = tx.Commit(); err != nil { - return err + return &utils.LogError{ + Code: "151", + TraceID: utils.RandomTraceID(15), + Type: "LOG AUTH", + Params: "email: " + email, + Query: "-", + Message: err.Error(), + TimeStamp: time.Now().Format("2006-01-02 15:04:05"), + } + } + + return nil +} +func (s *Store) LogRISLogin(userID int, userAgent string, userName string, message string, status string) error { + tx, err := s.db.BeginTxx(context.Background(), nil) + if err != nil { + return err + } + + defer func() { + if err != nil { + tx.Rollback() + } + }() + logval := types.LogRISLogin{ + LogLoginM_UserID: strconv.Itoa(userID), + LogLoginDateTime: time.Now().Format("2006-01-02 15:04:05"), + LogLoginUserAgent: userAgent, + LogLoginUserName: userName, + LogLoginMessage: message, + LogLoginStatus: status, + } + + qry := `INSERT INTO log_login + (Log_LoginM_UserID, Log_LoginDateTime, Log_LoginUserAgent, Log_LoginUserName, Log_LoginMessage, Log_LoginStatus) + VALUES (:Log_LoginM_UserID, :Log_LoginDateTime, :Log_LoginUserAgent, :Log_LoginUserName, :Log_LoginMessage, :Log_LoginStatus)` + + _, err = tx.NamedExec(qry, logval) + if err != nil { + return &utils.LogError{ + Code: "151", + TraceID: utils.RandomTraceID(15), + Type: "LOG AUTH", + Params: "userName: " + userName, + Query: "-", + Message: err.Error(), + TimeStamp: time.Now().Format("2006-01-02 15:04:05"), + } + } + + if err = tx.Commit(); err != nil { + return &utils.LogError{ + Code: "151", + TraceID: utils.RandomTraceID(15), + Type: "LOG AUTH", + Params: "userName: " + userName, + Query: "-", + Message: err.Error(), + TimeStamp: time.Now().Format("2006-01-02 15:04:05"), + } + } + + return nil +} +func (s *Store) UpdateUserToken(userID int, token string) error { + tx, err := s.db.BeginTxx(context.Background(), nil) + if err != nil { + return &utils.LogError{ + Code: "151", + TraceID: utils.RandomTraceID(15), + Type: "LOG AUTH", + Params: "", + Query: "-", + Message: err.Error(), + TimeStamp: time.Now().Format("2006-01-02 15:04:05"), + } + } + + defer func() { + if err != nil { + tx.Rollback() + } + }() + + qrySys := `SELECT Conf_SystemIsAutoLogOut, Conf_SystemAutoLogOutDuration FROM conf_system LIMIT 1` + var isAutoLogOut string + var autoLogOutDuration int + err = s.db.QueryRow(qrySys).Scan(&isAutoLogOut, &autoLogOutDuration) + if err != nil { + return &utils.LogError{ + Code: "151", + TraceID: utils.RandomTraceID(15), + Type: "AUTH", + Params: "", + Query: "-", + Message: err.Error(), + TimeStamp: time.Now().Format("2006-01-02 15:04:05"), + } + } + + expiredDate := time.Now().Add(time.Duration(autoLogOutDuration) * time.Minute) + + qry := `UPDATE m_user SET M_UserToken = :token, M_UserExpiredToken = :expiredDate WHERE M_UserID = :userID` + + _, err = tx.NamedExec(qry, map[string]interface{}{ + "token": token, + "userID": userID, + "expiredDate": expiredDate.Format("2006-01-02 15:04:05"), + }) + // fmt.Println("err", err) + if err != nil { + return &utils.LogError{ + Code: "151", + TraceID: utils.RandomTraceID(15), + Type: "LOG AUTH", + Params: "", + Query: "-", + Message: err.Error(), + TimeStamp: time.Now().Format("2006-01-02 15:04:05"), + } + } + + if err = tx.Commit(); err != nil { + return &utils.LogError{ + Code: "151", + TraceID: utils.RandomTraceID(15), + Type: "LOG AUTH", + Params: "", + Query: "-", + Message: err.Error(), + TimeStamp: time.Now().Format("2006-01-02 15:04:05"), + } + } + + return nil +} +func (s *Store) UpdateExpiredToken(token string) error { + // fmt.Printf("Masuk expired token") + tx, err := s.db.BeginTxx(context.Background(), nil) + if err != nil { + return fmt.Errorf("failed to begin transaction: %v", err) + } + + defer func() { + if err != nil { + tx.Rollback() + } + }() + + qrySys := `SELECT Conf_SystemIsAutoLogOut, Conf_SystemAutoLogOutDuration FROM conf_system LIMIT 1` + var isAutoLogOut string + var autoLogOutDuration int + err = s.db.QueryRow(qrySys).Scan(&isAutoLogOut, &autoLogOutDuration) + if err != nil { + return fmt.Errorf("failed to get system config: %v", err) + } + + if isAutoLogOut == "Y" { + qryEt := `SELECT DATE_FORMAT(M_UserExpiredToken, '%Y-%m-%d %H:%i:%s') as M_UserExpiredToken FROM m_user WHERE M_UserToken = ?` + + var expiredToken string + err = s.db.Get(&expiredToken, qryEt, token) + if err != nil { + return fmt.Errorf("failed to get expired token: %v", err) + } + + timeNow := time.Now().Format("2006-01-02 15:04:05") + + expiredTime, err := time.Parse("2006-01-02 15:04:05", expiredToken) + if err != nil { + return fmt.Errorf("failed to parse expired token: %v", err) + } + + timeNowParsed, err := time.Parse("2006-01-02 15:04:05", timeNow) + if err != nil { + return fmt.Errorf("failed to parse current time: %v", err) + } + + if timeNowParsed.After(expiredTime) { + fmt.Printf("timenow: %v, expiredTime: %v", timeNow, expiredTime) + // fmt.Println("token expired") + return fmt.Errorf("token expired") + } else { + // fmt.Println("token belum expired") + fmt.Printf("timenow: %v, expiredTime: %v", timeNow, expiredTime) + + } + + timeNowAdd := timeNowParsed.Add(time.Duration(autoLogOutDuration) * time.Minute) + qryUpdateExpiredToken := `UPDATE m_user SET M_UserExpiredToken = ? WHERE M_UserToken = ?` + _, err = tx.Exec(qryUpdateExpiredToken, timeNowAdd.Format("2006-01-02 15:04:05"), token) + if err != nil { + return fmt.Errorf("failed to update expired token: %v", err) + } + + if err = tx.Commit(); err != nil { + return fmt.Errorf("failed to commit transaction: %v", err) + } + } + + return nil +} +func (s *Store) UpdateExpiredTokenBu(userID int) error { + fmt.Printf("Masuk expired token") + tx, err := s.db.BeginTxx(context.Background(), nil) + if err != nil { + return fmt.Errorf("failed to begin transaction: %v", err) + } + + defer func() { + if err != nil { + tx.Rollback() + } + }() + + qrySys := `SELECT Conf_SystemIsAutoLogOut, Conf_SystemAutoLogOutDuration FROM conf_system LIMIT 1` + var isAutoLogOut string + var autoLogOutDuration int + err = s.db.QueryRow(qrySys).Scan(&isAutoLogOut, &autoLogOutDuration) + if err != nil { + return fmt.Errorf("failed to get system config: %v", err) + } + + if isAutoLogOut == "Y" { + qryEt := `SELECT DATE_FORMAT(M_UserExpiredToken, '%Y-%m-%d %H:%i:%s') as M_UserExpiredToken FROM m_user WHERE M_UserID = ?` + + var expiredToken string + err = s.db.Get(&expiredToken, qryEt, userID) + if err != nil { + return fmt.Errorf("failed to get expired token: %v", err) + } + + timeNow := time.Now().Format("2006-01-02 15:04:05") + + expiredTime, err := time.Parse("2006-01-02 15:04:05", expiredToken) + if err != nil { + return fmt.Errorf("failed to parse expired token: %v", err) + } + + timeNowParsed, err := time.Parse("2006-01-02 15:04:05", timeNow) + if err != nil { + return fmt.Errorf("failed to parse current time: %v", err) + } + + if timeNowParsed.After(expiredTime) { + fmt.Printf("timenow: %v, expiredTime: %v", timeNow, expiredTime) + fmt.Println("token expired") + return fmt.Errorf("token expired") + } else { + fmt.Println("token belum expired") + fmt.Printf("timenow: %v, expiredTime: %v", timeNow, expiredTime) + + } + + timeNowAdd := timeNowParsed.Add(time.Duration(autoLogOutDuration) * time.Minute) + qryUpdateExpiredToken := `UPDATE m_user SET M_UserExpiredToken = ? WHERE M_UserID = ?` + _, err = tx.Exec(qryUpdateExpiredToken, timeNowAdd.Format("2006-01-02 15:04:05"), userID) + if err != nil { + return fmt.Errorf("failed to update expired token: %v", err) + } + + if err = tx.Commit(); err != nil { + return fmt.Errorf("failed to commit transaction: %v", err) + } } return nil diff --git a/services/auth/jwt.go b/services/auth/jwt.go index 0e5e061..eec7186 100644 --- a/services/auth/jwt.go +++ b/services/auth/jwt.go @@ -5,8 +5,10 @@ import ( "fmt" "log" "net/http" + "time" "github.com/golang-jwt/jwt/v5" + "github.com/jmoiron/sqlx" "sismedika.com/sas/westone/configs" "sismedika.com/sas/westone/types" "sismedika.com/sas/westone/utils" @@ -64,7 +66,7 @@ func CreateJWT(data types.DataJWT) (string, error) { token := jwt.NewWithClaims(jwt.SigningMethodHS256, jwt.MapClaims{ "M_UserID": data.M_UserID, "M_UserEmail": data.M_UserEmail, - "M_UserUsername": data.M_UserUsername, + "M_UserName": data.M_UserName, "M_UserGroupDashboard": data.M_UserGroupDashboard, "M_UserDefaultTSampleStationID": data.M_UserDefaultTSampleStationID, "M_StaffName": data.M_StaffName, @@ -88,7 +90,9 @@ func CreateJWT(data types.DataJWT) (string, error) { func validateJWT(tokenString string) (*jwt.Token, error) { return jwt.Parse(tokenString, func(token *jwt.Token) (interface{}, error) { if _, ok := token.Method.(*jwt.SigningMethodHMAC); !ok { - return nil, fmt.Errorf("unexpected signing method: %v", token.Header["alg"]) + err := fmt.Errorf("unexpected signing method: %v", token.Header["alg"]) + fmt.Printf("Error validating JWT token: %v\n", err) + return nil, err } return []byte(configs.Envs.JWTSecret), nil @@ -98,6 +102,9 @@ func validateJWT(tokenString string) (*jwt.Token, error) { func permissionDenied(w http.ResponseWriter) { utils.WriteError(w, http.StatusForbidden, fmt.Errorf("PERMISSION DENIED")) } +func tokenExpired(w http.ResponseWriter) { + utils.WriteError(w, http.StatusForbidden, fmt.Errorf("TOKEN EXPIRED")) +} func GetUserIDFromContext(ctx context.Context) int { userID, ok := ctx.Value(UserContextKey).(int) @@ -132,3 +139,125 @@ func AuthMiddleware(next http.Handler) http.Handler { next.ServeHTTP(w, r) }) } +func WithAuthStore(store types.OauthStore) func(http.Handler) http.Handler { + return func(next http.Handler) http.Handler { + return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { + ctx := context.WithValue(r.Context(), "auth_store", store) + + //start + tokenstr := utils.GetTokenFromRequest(r) + token, err := validateJWT(tokenstr) + if err != nil { + log.Printf("[ERROR] Failed to validate jwt token: %v", err) + permissionDenied(w) + return + } + + if !token.Valid { + log.Println("[ERROR] Invalid token") + permissionDenied(w) + return + } + + // claims := token.Claims.(jwt.MapClaims) + // userID := int(claims["M_UserID"].(float64)) + + // Ambil store dari context + // fmt.Printf("store: %v", store) + // store, ok := r.Context().Value("auth_store").(types.OauthStore) + // if !ok { + // log.Println("[ERROR] Auth store not found in context") + // permissionDenied(w) + // return + // } + + // Update expired token + if err := store.UpdateExpiredToken(tokenstr); err != nil { + if err.Error() == "token expired" { + tokenExpired(w) + } else { + log.Printf("[ERROR] Failed to update token expiry: %v", err) + permissionDenied(w) + } + return + } + + //end + next.ServeHTTP(w, r.WithContext(ctx)) + }) + } +} + +func UpdateExpiredTokentest(userID int, db *sqlx.DB) error { + tx, err := db.BeginTxx(context.Background(), nil) + if err != nil { + return fmt.Errorf("failed to begin transaction: %v", err) + } + + defer func() { + if err != nil { + tx.Rollback() + } + }() + + qrySys := `SELECT Conf_SystemIsAutoLogOut, Conf_SystemAutoLogOutDuration FROM conf_system LIMIT 1` + var isAutoLogOut string + var autoLogOutDuration int + err = db.QueryRow(qrySys).Scan(&isAutoLogOut, &autoLogOutDuration) + if err != nil { + return fmt.Errorf("failed to get system config: %v", err) + } + + if isAutoLogOut == "Y" { + qryEt := `SELECT DATE_FORMAT(M_UserExpiredToken, '%Y-%m-%d %H:%i:%s') as M_UserExpiredToken FROM m_user WHERE M_UserID = ?` + + var expiredToken string + err = db.Get(&expiredToken, qryEt, userID) + if err != nil { + return fmt.Errorf("failed to get expired token: %v", err) + } + + timeNow := time.Now() + expiredTime, err := time.Parse("2006-01-02 15:04:05", expiredToken) + if err != nil { + return fmt.Errorf("failed to parse expired token: %v", err) + } + + if timeNow.After(expiredTime) { + return fmt.Errorf("token expired") + } + + timeNowAdd := timeNow.Add(time.Duration(autoLogOutDuration) * time.Minute) + qryUpdateExpiredToken := `UPDATE m_user SET M_UserExpiredToken = ? WHERE M_UserID = ?` + _, err = tx.Exec(qryUpdateExpiredToken, timeNowAdd.Format("2006-01-02 15:04:05"), userID) + if err != nil { + return fmt.Errorf("failed to update expired token: %v", err) + } + + qry := `UPDATE m_user SET M_UserExpiredToken = NOW() WHERE M_UserID = :userID` + + _, err = tx.NamedExec(qry, map[string]interface{}{ + "userID": userID, + }) + fmt.Println("err", err) + if err != nil { + return fmt.Errorf("failed to update expired token: %v", err) + } + + if err = tx.Commit(); err != nil { + return fmt.Errorf("failed to commit transaction: %v", err) + } + } + + return nil +} + +// Middleware untuk menyimpan store ke context +func WithStore(store *Store) func(http.Handler) http.Handler { + return func(next http.Handler) http.Handler { + return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { + ctx := context.WithValue(r.Context(), "store", store) + next.ServeHTTP(w, r.WithContext(ctx)) + }) + } +} diff --git a/services/auth/oauth.routes.go b/services/auth/oauth.routes.go index a96d1e2..58267a5 100644 --- a/services/auth/oauth.routes.go +++ b/services/auth/oauth.routes.go @@ -45,6 +45,7 @@ func (h *Handler) RegisterRoutes(router *mux.Router) { authroute.HandleFunc("/{provider}/authcode", h.handlerRedirectCode).Methods(http.MethodGet) protec := authroute.PathPrefix("/redirect").Subrouter() + protec.Use(AuthMiddleware) protec.HandleFunc("/dashboard", h.handleRedirectDash).Methods(http.MethodGet) } diff --git a/services/doctor/doctor.routes.go b/services/doctor/doctor.routes.go index 6d64781..adaeb66 100644 --- a/services/doctor/doctor.routes.go +++ b/services/doctor/doctor.routes.go @@ -12,15 +12,17 @@ import ( ) type Handler struct { - store types.DoctorStore + store types.DoctorStore + authStore types.OauthStore } -func NewHandler(store types.DoctorStore) *Handler { - return &Handler{store: store} +func NewHandler(store types.DoctorStore, authStore types.OauthStore) *Handler { + return &Handler{store: store, authStore: authStore} } func (h *Handler) RegisterRoutes(router *mux.Router) { doctorroutes := router.PathPrefix("/mddoctor").Subrouter() + doctorroutes.Use(auth.WithAuthStore(h.authStore)) doctorroutes.Use(auth.AuthMiddleware) doctorroutes.HandleFunc("/searchdoctor", h.handlerSearchDoctor).Methods(http.MethodPost) diff --git a/services/staff/staff.routes.go b/services/staff/staff.routes.go new file mode 100644 index 0000000..057187b --- /dev/null +++ b/services/staff/staff.routes.go @@ -0,0 +1,61 @@ +package staff + +import ( + "fmt" + "net/http" + + "github.com/go-playground/validator/v10" + "github.com/gorilla/mux" + "sismedika.com/sas/westone/services/auth" + "sismedika.com/sas/westone/types" + "sismedika.com/sas/westone/utils" +) + +type Handler struct { + store types.StaffStore + authStore types.OauthStore +} + +func NewHandler(store types.StaffStore, authStore types.OauthStore) *Handler { + return &Handler{store: store, authStore: authStore} +} + +func (h *Handler) RegisterRoutes(router *mux.Router) { + staffRoute := router.PathPrefix("/staff").Subrouter() + staffRoute.Use(auth.WithAuthStore(h.authStore)) + staffRoute.HandleFunc("/getstaff", h.handelGetStaff).Methods(http.MethodPost) + staffRoute.HandleFunc("/", h.coba).Methods(http.MethodPost) +} + +func (h *Handler) coba(w http.ResponseWriter, r *http.Request) { + var payload types.GetStaffPayload + if err := utils.ParseJSON(r, &payload); err != nil { + utils.WriteError(w, http.StatusBadRequest, err) + return + } + fmt.Printf("payload: %+v\n", payload) + fmt.Printf("COBA") + + utils.WriteJSON(w, http.StatusOK, payload) +} +func (h *Handler) handelGetStaff(w http.ResponseWriter, r *http.Request) { + var payload types.GetStaffPayload + if err := utils.ParseJSON(r, &payload); err != nil { + utils.WriteError(w, http.StatusBadRequest, err) + return + } + + if err := utils.Validate.Struct(payload); err != nil { + erros := err.(validator.ValidationErrors) + utils.WriteError(w, http.StatusBadRequest, fmt.Errorf("invalid payload: %v", erros)) + return + } + + response, err := h.store.GetStaff(payload.StaffName) + if err != nil { + utils.WriteError(w, http.StatusInternalServerError, err) + return + } + + utils.WriteJSON(w, http.StatusOK, response) +} diff --git a/services/staff/staff.stores.go b/services/staff/staff.stores.go new file mode 100644 index 0000000..7da4116 --- /dev/null +++ b/services/staff/staff.stores.go @@ -0,0 +1,34 @@ +package staff + +import ( + "github.com/jmoiron/sqlx" + "sismedika.com/sas/westone/types" +) + +type Store struct { + db *sqlx.DB +} + +func NewStore(db *sqlx.DB) *Store { + return &Store{db: db} +} +func (s *Store) GetStaff(name string) (*types.Staff, error) { + term := new(types.Staff) + + Keyword := "%" + name + "%" + qry := `SELECT + M_StaffID, + M_StaffName, + M_StaffEmail, + M_StaffNakesID, + M_StaffIsActive + FROM m_staff + WHERE (M_StaffName LIKE ? OR M_StaffEmail LIKE ? ) + AND M_StaffIsActive = 'Y' + ` + if err := s.db.Get(term, qry, Keyword, Keyword); err != nil { + return nil, err + } + + return term, nil +} diff --git a/types/oauth.types.go b/types/oauth.types.go index a755868..8d62cc6 100644 --- a/types/oauth.types.go +++ b/types/oauth.types.go @@ -1,6 +1,8 @@ package types -import "github.com/markbates/goth" +import ( + "github.com/markbates/goth" +) type OauthStore interface { AddGoolgeAccount(user UserGoogle) error @@ -9,6 +11,9 @@ type OauthStore interface { CompareAuthCode(authcode string, user goth.User, typez string) (int, error) SignInWestone(email string, password string) (*User, error) LogSignIn(email string, ip string, status string, tipe string, provider string) error + LogRISLogin(userID int, userAgent string, userName string, message string, status string) error + UpdateUserToken(userID int, token string) error + UpdateExpiredToken(token string) error } type UserGoogle struct { @@ -41,7 +46,7 @@ type GenerateAuthCode struct { } type SignInPayload struct { - Email string `json:"email" validate:"required,email"` + Email string `json:"email" validate:"required"` Password string `json:"password" validate:"required,min=6,max=130"` } @@ -55,3 +60,11 @@ type LogLogin struct { LogLoginIsActive string `db:"Log_LoginIsActive" json:"logLoginIsActive"` LogLoginLastUpdated string `db:"Log_LoginLastUpdated" json:"logLoginLastUpdated"` } +type LogRISLogin struct { + LogLoginM_UserID string `db:"Log_LoginM_UserID" json:"LogLoginM_UserID"` + LogLoginDateTime string `db:"Log_LoginDateTime" json:"LogLoginDateTime"` + LogLoginUserAgent string `db:"Log_LoginUserAgent" json:"LogLoginUserAgent"` + LogLoginStatus string `db:"Log_LoginStatus" json:"LogLoginStatus"` + LogLoginUserName string `db:"Log_LoginUserName" json:"LogLoginUserName"` + LogLoginMessage string `db:"Log_LoginMessage" json:"LogLoginMessage"` +} diff --git a/types/staff.types.go b/types/staff.types.go new file mode 100644 index 0000000..17668b5 --- /dev/null +++ b/types/staff.types.go @@ -0,0 +1,16 @@ +package types + +type StaffStore interface { + GetStaff(name string) (*Staff, error) +} + +type Staff struct { + M_StaffID int `json:"M_StaffID" db:"M_StaffID"` + M_StaffName string `json:"M_StaffName" db:"M_StaffName"` + M_StaffEmail string `json:"M_StaffEmail" db:"M_StaffEmail"` + M_StaffNakesID string `json:"M_StaffNakesID" db:"M_StaffNakesID"` + M_StaffIsActive string `json:"M_StaffIsActive" db:"M_StaffIsActive"` +} +type GetStaffPayload struct { + StaffName string `json:"staffname"` +} diff --git a/types/user.types.go b/types/user.types.go index 1412c76..a4560f9 100644 --- a/types/user.types.go +++ b/types/user.types.go @@ -23,7 +23,8 @@ type RegisterUserPayload struct { type User struct { M_UserID int `json:"M_UserID" db:"M_UserID"` M_UserEmail string `json:"M_UserEmail" db:"M_UserEmail"` - M_UserUsername string `json:"M_UserUsername" db:"M_UserUsername"` + M_UserName string `json:"M_UserName" db:"M_UserName"` + M_UserExpiredToken string `json:"M_UserExpiredToken" db:"M_UserExpiredToken"` M_UserGroupDashboard string `json:"M_UserGroupDashboard" db:"M_UserGroupDashboard"` M_UserDefaultTSampleStationID string `json:"M_UserDefaultT_SampleStationID" db:"M_UserDefaultT_SampleStationID"` M_StaffName string `json:"M_StaffName" db:"M_StaffName"` diff --git a/utils/utils.go b/utils/utils.go index 90d4352..bb5a7ab 100644 --- a/utils/utils.go +++ b/utils/utils.go @@ -42,16 +42,21 @@ func ParseJSON(r *http.Request, v any) error { } func GetTokenFromRequest(r *http.Request) string { - tokenAuth := r.Header.Get("Authorization") - if strings.HasPrefix(tokenAuth, "Bearer ") { - return strings.TrimPrefix(tokenAuth, "Bearer ") - } + // fmt.Printf("Request: %v\n", r.) + var tokenAuth string + tokenAuth = r.Header.Get("Authorization") + // fmt.Printf("Token Auth: %v\n", tokenAuth) tokenQuery := r.URL.Query().Get("token") + // fmt.Printf("Token Query: %v\n", tokenQuery) if tokenQuery != "" { - return tokenQuery + tokenAuth = tokenQuery } - return "" + tokenAuth = strings.TrimSpace(tokenAuth) + tokenAuth = strings.ReplaceAll(tokenAuth, "Bearer", "") + tokenAuth = strings.ReplaceAll(tokenAuth, "\"", "") + tokenAuth = strings.TrimSpace(tokenAuth) + return tokenAuth } func MatchStruct(src interface{}, dst interface{}) error {