lifedebugger commited on
Commit
34fb971
·
1 Parent(s): 164cc3b

Deploy files from GitHub repository

Browse files
This view is limited to 50 files because it contains too many changes.   See raw diff
Files changed (50) hide show
  1. controller/auth/auth_change_password_controller.go +21 -0
  2. controller/auth/auth_login_controller.go +20 -0
  3. controller/auth/auth_profile_controller.go +21 -0
  4. controller/auth/auth_register_controller.go +20 -0
  5. controller/auth/auth_update_profile_controller.go +24 -0
  6. go.mod +0 -1
  7. go.sum +0 -2
  8. logserror_log.txt +1 -0
  9. middleware/authentication_middleware.go +9 -39
  10. models/authentication_payload_model.go +1 -1
  11. models/custom_claim.go +8 -0
  12. models/response_model.go +1 -0
  13. repositories/account_repository.go +11 -5
  14. repositories/email_verification_repository.go +2 -2
  15. repositories/repository.go +3 -0
  16. router/auth_route.go +18 -0
  17. router/router.go +1 -1
  18. services/authentication_service.go +1 -1
  19. services/email_verification_service.go +1 -1
  20. services/jwt_service.go +46 -11
  21. services/user_profile_service.go +1 -1
  22. space/controller/user/user_change_password_controller.go +21 -0
  23. space/controller/user/user_login_controller.go +1 -1
  24. space/controller/user/user_profile_controller.go +2 -5
  25. space/controller/user/user_update_profile_controller.go +8 -8
  26. space/logserror_log.txt +2 -0
  27. space/models/request_model.go +4 -0
  28. space/repositories/account_repository.go +38 -4
  29. space/repositories/repository.go +1 -0
  30. space/router/user_route.go +2 -0
  31. space/services/authentication_service.go +71 -0
  32. space/services/register_service.go +7 -0
  33. space/services/user_profile_service.go +13 -8
  34. space/space/.github/workflows/main.yml +13 -37
  35. space/space/.github/workflows/main_huggingface.yml +52 -0
  36. space/space/Dockerfile +37 -30
  37. space/space/README.md +19 -3
  38. space/space/docker-compose.yml +31 -0
  39. space/space/space/.gitignore +2 -1
  40. space/space/space/config/config.go +3 -0
  41. space/space/space/config/database_connection_config.go +10 -0
  42. space/space/space/controller/controller.go +7 -0
  43. space/space/space/controller/email/email_create_verification.go +22 -0
  44. space/space/space/controller/email/email_delete_verification.go +22 -0
  45. space/space/space/controller/email/email_verify.go +22 -0
  46. space/space/space/controller/user/user_login_controller.go +20 -0
  47. space/space/space/controller/user/user_profile_controller.go +24 -0
  48. space/space/space/controller/user/user_register_controller.go +20 -0
  49. space/space/space/controller/user/user_update_profile_controller.go +24 -0
  50. space/space/space/docker-compose.yaml +10 -0
controller/auth/auth_change_password_controller.go ADDED
@@ -0,0 +1,21 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ package user
2
+
3
+ import (
4
+ "api.qobiltu.id/controller"
5
+ "api.qobiltu.id/models"
6
+ "api.qobiltu.id/services"
7
+ "github.com/gin-gonic/gin"
8
+ )
9
+
10
+ func ChangePassword(c *gin.Context) {
11
+ authentication := services.AuthenticationService{}
12
+ changePasswordController := controller.Controller[models.ChangePasswordRequest, models.Account, models.AuthenticatedUser]{
13
+ Service: &authentication.Service,
14
+ }
15
+ changePasswordController.HeaderParse(c, func() {
16
+ changePasswordController.Service.Constructor.Id = uint(changePasswordController.AccountData.UserID)
17
+ })
18
+ changePasswordController.RequestJSON(c, func() {
19
+ authentication.Update(changePasswordController.Request.OldPassword, changePasswordController.Request.NewPassword)
20
+ })
21
+ }
controller/auth/auth_login_controller.go ADDED
@@ -0,0 +1,20 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ package user
2
+
3
+ import (
4
+ "api.qobiltu.id/controller"
5
+ "api.qobiltu.id/models"
6
+ "api.qobiltu.id/services"
7
+ "github.com/gin-gonic/gin"
8
+ )
9
+
10
+ func Login(c *gin.Context) {
11
+ authentication := services.AuthenticationService{}
12
+ loginController := controller.Controller[models.LoginRequest, models.Account, models.AuthenticatedUser]{
13
+ Service: &authentication.Service,
14
+ }
15
+ loginController.RequestJSON(c, func() {
16
+ loginController.Service.Constructor.Email = loginController.Request.Email
17
+ loginController.Service.Constructor.Password = loginController.Request.Password
18
+ authentication.Authenticate()
19
+ })
20
+ }
controller/auth/auth_profile_controller.go ADDED
@@ -0,0 +1,21 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ package user
2
+
3
+ import (
4
+ "api.qobiltu.id/controller"
5
+ "api.qobiltu.id/models"
6
+ "api.qobiltu.id/services"
7
+ "github.com/gin-gonic/gin"
8
+ )
9
+
10
+ func Profile(c *gin.Context) {
11
+ userProfile := services.UserProfileService{}
12
+ userProfileController := controller.Controller[any, models.AccountDetails, models.AccountDetails]{
13
+ Service: &userProfile.Service,
14
+ }
15
+ userProfileController.HeaderParse(c, func() {
16
+ userProfileController.Service.Constructor.AccountID = uint(userProfileController.AccountData.UserID)
17
+ userProfile.Retrieve()
18
+ userProfileController.Response(c)
19
+ },
20
+ )
21
+ }
controller/auth/auth_register_controller.go ADDED
@@ -0,0 +1,20 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ package user
2
+
3
+ import (
4
+ "api.qobiltu.id/controller"
5
+ "api.qobiltu.id/models"
6
+ "api.qobiltu.id/services"
7
+ "github.com/gin-gonic/gin"
8
+ )
9
+
10
+ func Register(c *gin.Context) {
11
+ register := services.RegisterService{}
12
+ registerController := controller.Controller[models.RegisterRequest, models.Account, models.Account]{
13
+ Service: &register.Service,
14
+ }
15
+ registerController.RequestJSON(c, func() {
16
+ registerController.Service.Constructor.Password = registerController.Request.Password
17
+ registerController.Service.Constructor.Email = registerController.Request.Email
18
+ register.Create()
19
+ })
20
+ }
controller/auth/auth_update_profile_controller.go ADDED
@@ -0,0 +1,24 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ package user
2
+
3
+ import (
4
+ "api.qobiltu.id/controller"
5
+ "api.qobiltu.id/models"
6
+ "api.qobiltu.id/services"
7
+ "github.com/gin-gonic/gin"
8
+ )
9
+
10
+ func UpdateProfile(c *gin.Context) {
11
+ userProfile := services.UserProfileService{}
12
+ userUpdateProfileController := controller.Controller[models.AccountDetails, models.AccountDetails, models.AccountDetails]{
13
+ Service: &userProfile.Service,
14
+ }
15
+
16
+ userUpdateProfileController.RequestJSON(c, func() {
17
+ userUpdateProfileController.Service.Constructor = userUpdateProfileController.Request
18
+ userUpdateProfileController.HeaderParse(c, func() {
19
+ userUpdateProfileController.Service.Constructor.AccountID = uint(userUpdateProfileController.AccountData.UserID)
20
+ })
21
+ userProfile.Update()
22
+ },
23
+ )
24
+ }
go.mod CHANGED
@@ -3,7 +3,6 @@ module api.qobiltu.id
3
  go 1.24.0
4
 
5
  require (
6
- github.com/dgrijalva/jwt-go v3.2.0+incompatible
7
  github.com/gin-gonic/gin v1.10.0
8
  github.com/golang-jwt/jwt/v5 v5.2.1
9
  github.com/joho/godotenv v1.5.1
 
3
  go 1.24.0
4
 
5
  require (
 
6
  github.com/gin-gonic/gin v1.10.0
7
  github.com/golang-jwt/jwt/v5 v5.2.1
8
  github.com/joho/godotenv v1.5.1
go.sum CHANGED
@@ -10,8 +10,6 @@ github.com/creack/pty v1.1.9/go.mod h1:oKZEueFk5CKHvIhNR5MUki03XCEU+Q6VDXinZuGJ3
10
  github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
11
  github.com/davecgh/go-spew v1.1.1 h1:vj9j/u1bqnvCEfJOwUhtlOARqs3+rkHYY13jYWTU97c=
12
  github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
13
- github.com/dgrijalva/jwt-go v3.2.0+incompatible h1:7qlOGliEKZXTDg6OTjfoBKDXWrumCAMpl/TFQ4/5kLM=
14
- github.com/dgrijalva/jwt-go v3.2.0+incompatible/go.mod h1:E3ru+11k8xSBh+hMPgOLZmtrrCbhqsmaPHjLKYnJCaQ=
15
  github.com/gabriel-vasile/mimetype v1.4.8 h1:FfZ3gj38NjllZIeJAmMhr+qKL8Wu+nOoI3GqacKw1NM=
16
  github.com/gabriel-vasile/mimetype v1.4.8/go.mod h1:ByKUIKGjh1ODkGM1asKUbQZOLGrPjydw3hYPU2YU9t8=
17
  github.com/gin-contrib/sse v1.0.0 h1:y3bT1mUWUxDpW4JLQg/HnTqV4rozuW4tC9eFKTxYI9E=
 
10
  github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
11
  github.com/davecgh/go-spew v1.1.1 h1:vj9j/u1bqnvCEfJOwUhtlOARqs3+rkHYY13jYWTU97c=
12
  github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
 
 
13
  github.com/gabriel-vasile/mimetype v1.4.8 h1:FfZ3gj38NjllZIeJAmMhr+qKL8Wu+nOoI3GqacKw1NM=
14
  github.com/gabriel-vasile/mimetype v1.4.8/go.mod h1:ByKUIKGjh1ODkGM1asKUbQZOLGrPjydw3hYPU2YU9t8=
15
  github.com/gin-contrib/sse v1.0.0 h1:y3bT1mUWUxDpW4JLQg/HnTqV4rozuW4tC9eFKTxYI9E=
logserror_log.txt CHANGED
@@ -1,2 +1,3 @@
1
  2025/03/18 21:08:07 Error Log : ERROR: relasi « email_verifications » tidak ada (SQLSTATE 42P01)
2
  2025/03/22 14:00:34 Error Log : duplicated key not allowed
 
 
1
  2025/03/18 21:08:07 Error Log : ERROR: relasi « email_verifications » tidak ada (SQLSTATE 42P01)
2
  2025/03/22 14:00:34 Error Log : duplicated key not allowed
3
+ 2025/03/22 16:57:13 Error Log : ERROR: there is no unique or exclusion constraint matching the ON CONFLICT specification (SQLSTATE 42P10)
middleware/authentication_middleware.go CHANGED
@@ -3,52 +3,22 @@
3
  package middleware
4
 
5
  import (
6
- "time"
7
-
8
- "api.qobiltu.id/config"
9
  "api.qobiltu.id/models"
 
10
  "api.qobiltu.id/utils"
11
  "github.com/gin-gonic/gin"
12
- "github.com/golang-jwt/jwt/v5"
13
  )
14
 
15
- var salt = config.Salt
16
- var secretKey = []byte(salt)
17
-
18
- // VerifyPassword verifies if the provided password matches the hashed password
19
-
20
- type CustomClaims struct {
21
- jwt.RegisteredClaims
22
- UserID int `json:"id"`
23
- }
24
-
25
- func VerifyToken(bearer_token string) (int, string, error) {
26
- // fmt.Println(bearer_token)
27
- token, err := jwt.ParseWithClaims(bearer_token, &CustomClaims{}, func(token *jwt.Token) (interface{}, error) {
28
- return secretKey, nil
29
- })
30
- if err != nil {
31
- return 0, "invalid-token", err
32
- }
33
-
34
- // Extract the claims
35
- claims, ok := token.Claims.(*CustomClaims)
36
- if !ok || !token.Valid {
37
- return 0, "invalid-token", err
38
- }
39
- if claims.ExpiresAt != nil && claims.ExpiresAt.Time.Before(time.Now()) {
40
- return 0, "expired", err
41
- }
42
-
43
- return claims.UserID, "valid", err
44
- }
45
-
46
  func AuthUser(c *gin.Context) {
47
  var currAccData models.AccountData
48
- if c.Request.Header["Auth-Bearer-Token"] != nil {
49
- token := c.Request.Header["Auth-Bearer-Token"]
50
- currAccData.UserID, currAccData.VerifyStatus, currAccData.ErrVerif = VerifyToken(token[0])
51
- // fmt.Println("Verify Status :", currAccData.verifyStatus)
 
 
 
 
52
  if currAccData.VerifyStatus == "invalid-token" || currAccData.VerifyStatus == "expired" {
53
  currAccData.UserID = 0
54
  utils.ResponseFAIL(c, 401, models.Exception{Unauthorized: true, Message: "Your session is expired, Please re-Login!"})
 
3
  package middleware
4
 
5
  import (
 
 
 
6
  "api.qobiltu.id/models"
7
+ "api.qobiltu.id/services"
8
  "api.qobiltu.id/utils"
9
  "github.com/gin-gonic/gin"
 
10
  )
11
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
12
  func AuthUser(c *gin.Context) {
13
  var currAccData models.AccountData
14
+ if c.Request.Header["Authorization"] != nil {
15
+ token := c.Request.Header["Authorization"]
16
+ // fmt.Println("Authorization :", token)
17
+
18
+ currAccData.UserID, currAccData.VerifyStatus, currAccData.ErrVerif = services.VerifyToken(token[0])
19
+ // fmt.Println("Verify Status :", currAccData.VerifyStatus)
20
+ // fmt.Println("Verify UserID :", currAccData.UserID)
21
+
22
  if currAccData.VerifyStatus == "invalid-token" || currAccData.VerifyStatus == "expired" {
23
  currAccData.UserID = 0
24
  utils.ResponseFAIL(c, 401, models.Exception{Unauthorized: true, Message: "Your session is expired, Please re-Login!"})
models/authentication_payload_model.go CHANGED
@@ -1,7 +1,7 @@
1
  package models
2
 
3
  type AccountData struct {
4
- UserID int
5
  VerifyStatus string
6
  ErrVerif error
7
  }
 
1
  package models
2
 
3
  type AccountData struct {
4
+ UserID uint
5
  VerifyStatus string
6
  ErrVerif error
7
  }
models/custom_claim.go ADDED
@@ -0,0 +1,8 @@
 
 
 
 
 
 
 
 
 
1
+ package models
2
+
3
+ import "github.com/golang-jwt/jwt/v5"
4
+
5
+ type CustomClaims struct {
6
+ jwt.RegisteredClaims
7
+ UserID uint `json:"id"`
8
+ }
models/response_model.go CHANGED
@@ -13,6 +13,7 @@ type ErrorResponse struct {
13
  Errors Exception `json:"errors"`
14
  MetaData any `json:"meta_data"`
15
  }
 
16
  type AuthenticatedUser struct {
17
  Account Account `json:"account"`
18
  Token string `json:"token"`
 
13
  Errors Exception `json:"errors"`
14
  MetaData any `json:"meta_data"`
15
  }
16
+
17
  type AuthenticatedUser struct {
18
  Account Account `json:"account"`
19
  Token string `json:"token"`
repositories/account_repository.go CHANGED
@@ -17,9 +17,9 @@ func GetAccountbyEmail(email string) Repository[models.Account, models.Account]
17
  return *repo
18
  }
19
 
20
- func GetAccountbyId(account_id uint) Repository[models.Account, models.Account] {
21
  repo := Construct[models.Account, models.Account](
22
- models.Account{Id: account_id},
23
  )
24
  repo.Transactions(
25
  WhereGivenConstructor[models.Account, models.Account],
@@ -27,6 +27,7 @@ func GetAccountbyId(account_id uint) Repository[models.Account, models.Account]
27
  )
28
  return *repo
29
  }
 
30
  func UpdateAccount(account models.Account) Repository[models.Account, models.Account] {
31
  repo := Construct[models.Account, models.Account](
32
  account,
@@ -34,17 +35,20 @@ func UpdateAccount(account models.Account) Repository[models.Account, models.Acc
34
  Update(repo)
35
  return *repo
36
  }
37
- func GetAccountDetailsbyId(account_id uint) Repository[models.AccountDetails, models.AccountDetails] {
 
38
  repo := Construct[models.AccountDetails, models.AccountDetails](
39
- models.AccountDetails{AccountID: account_id},
40
  )
41
- fmt.Println("Account ID:", repo.Constructor.AccountID)
 
42
  repo.Transactions(
43
  WhereGivenConstructor[models.AccountDetails, models.AccountDetails],
44
  Find[models.AccountDetails, models.AccountDetails],
45
  )
46
  return *repo
47
  }
 
48
  func CreateAccount(account models.Account) Repository[models.Account, models.Account] {
49
  repo := Construct[models.Account, models.Account](
50
  account,
@@ -52,6 +56,7 @@ func CreateAccount(account models.Account) Repository[models.Account, models.Acc
52
  Create(repo)
53
  return *repo
54
  }
 
55
  func CreateAccountDetails(accountDetails models.AccountDetails) Repository[models.AccountDetails, models.AccountDetails] {
56
  repo := Construct[models.AccountDetails, models.AccountDetails](
57
  accountDetails,
@@ -61,6 +66,7 @@ func CreateAccountDetails(accountDetails models.AccountDetails) Repository[model
61
  Create(repo)
62
  return *repo
63
  }
 
64
  func UpdateAccountDetails(accountDetails models.AccountDetails) Repository[models.AccountDetails, models.AccountDetails] {
65
  repo := Construct[models.AccountDetails, models.AccountDetails](
66
  accountDetails,
 
17
  return *repo
18
  }
19
 
20
+ func GetAccountById(accountId uint) Repository[models.Account, models.Account] {
21
  repo := Construct[models.Account, models.Account](
22
+ models.Account{Id: accountId},
23
  )
24
  repo.Transactions(
25
  WhereGivenConstructor[models.Account, models.Account],
 
27
  )
28
  return *repo
29
  }
30
+
31
  func UpdateAccount(account models.Account) Repository[models.Account, models.Account] {
32
  repo := Construct[models.Account, models.Account](
33
  account,
 
35
  Update(repo)
36
  return *repo
37
  }
38
+
39
+ func GetDetailAccountById(accountId uint) Repository[models.AccountDetails, models.AccountDetails] {
40
  repo := Construct[models.AccountDetails, models.AccountDetails](
41
+ models.AccountDetails{AccountID: accountId},
42
  )
43
+
44
+ // fmt.Println("Account ID:", repo.Constructor.AccountID)
45
  repo.Transactions(
46
  WhereGivenConstructor[models.AccountDetails, models.AccountDetails],
47
  Find[models.AccountDetails, models.AccountDetails],
48
  )
49
  return *repo
50
  }
51
+
52
  func CreateAccount(account models.Account) Repository[models.Account, models.Account] {
53
  repo := Construct[models.Account, models.Account](
54
  account,
 
56
  Create(repo)
57
  return *repo
58
  }
59
+
60
  func CreateAccountDetails(accountDetails models.AccountDetails) Repository[models.AccountDetails, models.AccountDetails] {
61
  repo := Construct[models.AccountDetails, models.AccountDetails](
62
  accountDetails,
 
66
  Create(repo)
67
  return *repo
68
  }
69
+
70
  func UpdateAccountDetails(accountDetails models.AccountDetails) Repository[models.AccountDetails, models.AccountDetails] {
71
  repo := Construct[models.AccountDetails, models.AccountDetails](
72
  accountDetails,
repositories/email_verification_repository.go CHANGED
@@ -19,10 +19,10 @@ func CreateEmailVerification(accountId uint, dueTime time.Time, token uint) Repo
19
  return *repo
20
  }
21
 
22
- func GetEmailVerification(account_id uint, token uint) Repository[models.EmailVerification, models.EmailVerification] {
23
  repo := Construct[models.EmailVerification, models.EmailVerification](
24
  models.EmailVerification{
25
- AccountID: account_id,
26
  IsExpired: false,
27
  Token: token,
28
  },
 
19
  return *repo
20
  }
21
 
22
+ func GetEmailVerification(accountId uint, token uint) Repository[models.EmailVerification, models.EmailVerification] {
23
  repo := Construct[models.EmailVerification, models.EmailVerification](
24
  models.EmailVerification{
25
+ AccountID: accountId,
26
  IsExpired: false,
27
  Token: token,
28
  },
repositories/repository.go CHANGED
@@ -48,6 +48,7 @@ func Construct[TConstructor any, TResult any](constructor ...TConstructor) *Repo
48
  Transaction: config.DB.Begin(),
49
  }
50
  }
 
51
  func (repo *Repository[T1, T2]) Transactions(transactions ...func(*Repository[T1, T2]) *gorm.DB) {
52
  for _, tx := range transactions {
53
  repo.Transaction = tx(repo)
@@ -56,6 +57,7 @@ func (repo *Repository[T1, T2]) Transactions(transactions ...func(*Repository[T1
56
  }
57
  }
58
  }
 
59
  func WhereGivenConstructor[T1 any, T2 any](repo *Repository[T1, T2]) *gorm.DB {
60
  tx := repo.Transaction.Where(&repo.Constructor)
61
  repo.RowsCount = int(tx.RowsAffected)
@@ -63,6 +65,7 @@ func WhereGivenConstructor[T1 any, T2 any](repo *Repository[T1, T2]) *gorm.DB {
63
  repo.RowsError = tx.Error
64
  return tx
65
  }
 
66
  func Find[T1 any, T2 any](repo *Repository[T1, T2]) *gorm.DB {
67
  tx := repo.Transaction.Find(&repo.Result)
68
  repo.RowsCount = int(tx.RowsAffected)
 
48
  Transaction: config.DB.Begin(),
49
  }
50
  }
51
+
52
  func (repo *Repository[T1, T2]) Transactions(transactions ...func(*Repository[T1, T2]) *gorm.DB) {
53
  for _, tx := range transactions {
54
  repo.Transaction = tx(repo)
 
57
  }
58
  }
59
  }
60
+
61
  func WhereGivenConstructor[T1 any, T2 any](repo *Repository[T1, T2]) *gorm.DB {
62
  tx := repo.Transaction.Where(&repo.Constructor)
63
  repo.RowsCount = int(tx.RowsAffected)
 
65
  repo.RowsError = tx.Error
66
  return tx
67
  }
68
+
69
  func Find[T1 any, T2 any](repo *Repository[T1, T2]) *gorm.DB {
70
  tx := repo.Transaction.Find(&repo.Result)
71
  repo.RowsCount = int(tx.RowsAffected)
router/auth_route.go ADDED
@@ -0,0 +1,18 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ package router
2
+
3
+ import (
4
+ AuthController "api.qobiltu.id/controller/auth"
5
+ "api.qobiltu.id/middleware"
6
+ "github.com/gin-gonic/gin"
7
+ )
8
+
9
+ func AuthRoute(router *gin.Engine) {
10
+ routerGroup := router.Group("/api/v1/auth")
11
+ {
12
+ routerGroup.POST("/login", AuthController.Login)
13
+ routerGroup.POST("/register", AuthController.Register)
14
+ routerGroup.GET("/me", middleware.AuthUser, AuthController.Profile)
15
+ routerGroup.PUT("/me", middleware.AuthUser, AuthController.UpdateProfile)
16
+ routerGroup.PUT("/change-password", middleware.AuthUser, AuthController.ChangePassword)
17
+ }
18
+ }
router/router.go CHANGED
@@ -9,7 +9,7 @@ import (
9
  func StartService() {
10
  router := gin.Default()
11
  router.GET("/", controller.HomeController)
12
- UserRoute(router)
13
  EmailRoute(router)
14
  router.Run(config.TCP_ADDRESS)
15
  }
 
9
  func StartService() {
10
  router := gin.Default()
11
  router.GET("/", controller.HomeController)
12
+ AuthRoute(router)
13
  EmailRoute(router)
14
  router.Run(config.TCP_ADDRESS)
15
  }
services/authentication_service.go CHANGED
@@ -45,7 +45,7 @@ func (s *AuthenticationService) Update(oldPassword string, newPassword string) {
45
  s.Exception.Message = "Password must have at least 8 characters!"
46
  return
47
  }
48
- accountData := repositories.GetAccountbyId(s.Constructor.Id)
49
 
50
  if accountData.NoRecord {
51
  s.Exception.DataNotFound = true
 
45
  s.Exception.Message = "Password must have at least 8 characters!"
46
  return
47
  }
48
+ accountData := repositories.GetAccountById(s.Constructor.Id)
49
 
50
  if accountData.NoRecord {
51
  s.Exception.DataNotFound = true
services/email_verification_service.go CHANGED
@@ -14,7 +14,7 @@ type EmailVerificationService struct {
14
  }
15
 
16
  func (s *EmailVerificationService) Create() {
17
- accountRepo := repositories.GetAccountbyId(s.Constructor.AccountID)
18
  if accountRepo.NoRecord {
19
  s.Error = accountRepo.RowsError
20
  s.Exception.DataNotFound = true
 
14
  }
15
 
16
  func (s *EmailVerificationService) Create() {
17
+ accountRepo := repositories.GetAccountById(s.Constructor.AccountID)
18
  if accountRepo.NoRecord {
19
  s.Error = accountRepo.RowsError
20
  s.Exception.DataNotFound = true
services/jwt_service.go CHANGED
@@ -2,11 +2,12 @@ package services
2
 
3
  import (
4
  "errors"
 
5
  "time"
6
 
7
  "api.qobiltu.id/config"
8
  "api.qobiltu.id/models"
9
- "github.com/dgrijalva/jwt-go"
10
  "golang.org/x/crypto/bcrypt"
11
  )
12
 
@@ -14,22 +15,56 @@ var salt = config.Salt
14
  var secretKey = []byte(salt)
15
 
16
  func GenerateToken(user *models.Account) (string, error) {
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
17
 
18
- // Create a new token
19
- token := jwt.New(jwt.SigningMethodHS256)
20
 
21
- // Set claims
22
- claims := token.Claims.(jwt.MapClaims)
23
- claims["id"] = user.Id
24
- claims["exp"] = time.Now().Add(time.Hour * 24).Unix() // Token expires in 24 hours
 
 
 
 
 
 
25
 
26
- // Sign the token with the secret key
27
- tokenString, err := token.SignedString(secretKey)
28
  if err != nil {
29
- return "", err
 
 
 
 
 
 
 
 
 
30
  }
31
 
32
- return tokenString, nil
33
  }
34
 
35
  func VerifyPassword(hashedPassword, password string) error {
 
2
 
3
  import (
4
  "errors"
5
+ "strings"
6
  "time"
7
 
8
  "api.qobiltu.id/config"
9
  "api.qobiltu.id/models"
10
+ "github.com/golang-jwt/jwt/v5"
11
  "golang.org/x/crypto/bcrypt"
12
  )
13
 
 
15
  var secretKey = []byte(salt)
16
 
17
  func GenerateToken(user *models.Account) (string, error) {
18
+ claims := models.CustomClaims{
19
+ UserID: user.Id,
20
+ RegisteredClaims: jwt.RegisteredClaims{
21
+ ExpiresAt: jwt.NewNumericDate(time.Now().Add(time.Hour * 24)), // Token berlaku 24 jam
22
+ IssuedAt: jwt.NewNumericDate(time.Now()),
23
+ Issuer: "qobiltu.id",
24
+ },
25
+ }
26
+
27
+ // Buat token dengan metode signing
28
+ token := jwt.NewWithClaims(jwt.SigningMethodHS256, claims)
29
+ return token.SignedString(secretKey)
30
+ }
31
+
32
+ func ExtractBearerToken(authHeader string) (string, error) {
33
+ parts := strings.Split(authHeader, " ")
34
+ if len(parts) != 2 || parts[0] != "Bearer" {
35
+ return "", errors.New("invalid authorization header format")
36
+ }
37
+ return parts[1], nil
38
+ }
39
 
40
+ func VerifyToken(bearerToken string) (uint, string, error) {
41
+ // fmt.Println("bearerToken :", bearerToken)
42
 
43
+ tokenData, err := ExtractBearerToken(bearerToken)
44
+ if err != nil {
45
+ return 0, "invalid-token", err
46
+ } else {
47
+ // fmt.Println("Extracted Token:", tokenData)
48
+ }
49
+
50
+ token, err := jwt.ParseWithClaims(tokenData, &models.CustomClaims{}, func(token *jwt.Token) (interface{}, error) {
51
+ return secretKey, nil
52
+ })
53
 
 
 
54
  if err != nil {
55
+ return 0, "invalid-token", err
56
+ }
57
+
58
+ // Extract the claims
59
+ claims, ok := token.Claims.(*models.CustomClaims)
60
+ if !ok || !token.Valid {
61
+ return 0, "invalid-token", err
62
+ }
63
+ if claims.ExpiresAt != nil && claims.ExpiresAt.Time.Before(time.Now()) {
64
+ return 0, "expired", err
65
  }
66
 
67
+ return claims.UserID, "valid", err
68
  }
69
 
70
  func VerifyPassword(hashedPassword, password string) error {
services/user_profile_service.go CHANGED
@@ -20,7 +20,7 @@ func (s *UserProfileService) Create() {
20
  s.Result = userProfile.Result
21
  }
22
  func (s *UserProfileService) Retrieve() {
23
- userProfile := repositories.GetAccountDetailsbyId(s.Constructor.AccountID)
24
  s.Error = userProfile.RowsError
25
  if userProfile.NoRecord {
26
  s.Exception.DataNotFound = true
 
20
  s.Result = userProfile.Result
21
  }
22
  func (s *UserProfileService) Retrieve() {
23
+ userProfile := repositories.GetDetailAccountById(s.Constructor.AccountID)
24
  s.Error = userProfile.RowsError
25
  if userProfile.NoRecord {
26
  s.Exception.DataNotFound = true
space/controller/user/user_change_password_controller.go ADDED
@@ -0,0 +1,21 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ package user
2
+
3
+ import (
4
+ "api.qobiltu.id/controller"
5
+ "api.qobiltu.id/models"
6
+ "api.qobiltu.id/services"
7
+ "github.com/gin-gonic/gin"
8
+ )
9
+
10
+ func ChangePassword(c *gin.Context) {
11
+ authentication := services.AuthenticationService{}
12
+ changePasswordController := controller.Controller[models.ChangePasswordRequest, models.Account, models.AuthenticatedUser]{
13
+ Service: &authentication.Service,
14
+ }
15
+ changePasswordController.HeaderParse(c, func() {
16
+ changePasswordController.Service.Constructor.Id = uint(changePasswordController.AccountData.UserID)
17
+ })
18
+ changePasswordController.RequestJSON(c, func() {
19
+ authentication.Update(changePasswordController.Request.OldPassword, changePasswordController.Request.NewPassword)
20
+ })
21
+ }
space/controller/user/user_login_controller.go CHANGED
@@ -9,7 +9,7 @@ import (
9
 
10
  func Login(c *gin.Context) {
11
  authentication := services.AuthenticationService{}
12
- loginController := controller.Controller[models.LoginRequest, services.LoginConstructor, models.AuthenticatedUser]{
13
  Service: &authentication.Service,
14
  }
15
  loginController.RequestJSON(c, func() {
 
9
 
10
  func Login(c *gin.Context) {
11
  authentication := services.AuthenticationService{}
12
+ loginController := controller.Controller[models.LoginRequest, models.Account, models.AuthenticatedUser]{
13
  Service: &authentication.Service,
14
  }
15
  loginController.RequestJSON(c, func() {
space/controller/user/user_profile_controller.go CHANGED
@@ -1,8 +1,6 @@
1
  package user
2
 
3
  import (
4
- "fmt"
5
-
6
  "api.qobiltu.id/controller"
7
  "api.qobiltu.id/models"
8
  "api.qobiltu.id/services"
@@ -11,12 +9,11 @@ import (
11
 
12
  func Profile(c *gin.Context) {
13
  userProfile := services.UserProfileService{}
14
- userProfileController := controller.Controller[any, services.UserProfileConstructor, models.Account]{
15
  Service: &userProfile.Service,
16
  }
17
- fmt.Println(userProfileController.AccountData)
18
  userProfileController.HeaderParse(c, func() {
19
- userProfileController.Service.Constructor.AccountId = userProfileController.AccountData.UserID
20
  userProfile.Retrieve()
21
  userProfileController.Response(c)
22
  },
 
1
  package user
2
 
3
  import (
 
 
4
  "api.qobiltu.id/controller"
5
  "api.qobiltu.id/models"
6
  "api.qobiltu.id/services"
 
9
 
10
  func Profile(c *gin.Context) {
11
  userProfile := services.UserProfileService{}
12
+ userProfileController := controller.Controller[any, models.AccountDetails, models.AccountDetails]{
13
  Service: &userProfile.Service,
14
  }
 
15
  userProfileController.HeaderParse(c, func() {
16
+ userProfileController.Service.Constructor.AccountID = uint(userProfileController.AccountData.UserID)
17
  userProfile.Retrieve()
18
  userProfileController.Response(c)
19
  },
space/controller/user/user_update_profile_controller.go CHANGED
@@ -1,8 +1,6 @@
1
  package user
2
 
3
  import (
4
- "fmt"
5
-
6
  "api.qobiltu.id/controller"
7
  "api.qobiltu.id/models"
8
  "api.qobiltu.id/services"
@@ -11,14 +9,16 @@ import (
11
 
12
  func UpdateProfile(c *gin.Context) {
13
  userProfile := services.UserProfileService{}
14
- userUpdateProfileController := controller.Controller[any, services.UserProfileConstructor, models.Account]{
15
  Service: &userProfile.Service,
16
  }
17
- fmt.Println(userUpdateProfileController.AccountData)
18
- userUpdateProfileController.HeaderParse(c, func() {
19
- userUpdateProfileController.Service.Constructor.AccountId = userUpdateProfileController.AccountData.UserID
20
- userProfile.Retrieve()
21
- userUpdateProfileController.Response(c)
 
 
22
  },
23
  )
24
  }
 
1
  package user
2
 
3
  import (
 
 
4
  "api.qobiltu.id/controller"
5
  "api.qobiltu.id/models"
6
  "api.qobiltu.id/services"
 
9
 
10
  func UpdateProfile(c *gin.Context) {
11
  userProfile := services.UserProfileService{}
12
+ userUpdateProfileController := controller.Controller[models.AccountDetails, models.AccountDetails, models.AccountDetails]{
13
  Service: &userProfile.Service,
14
  }
15
+
16
+ userUpdateProfileController.RequestJSON(c, func() {
17
+ userUpdateProfileController.Service.Constructor = userUpdateProfileController.Request
18
+ userUpdateProfileController.HeaderParse(c, func() {
19
+ userUpdateProfileController.Service.Constructor.AccountID = uint(userUpdateProfileController.AccountData.UserID)
20
+ })
21
+ userProfile.Update()
22
  },
23
  )
24
  }
space/logserror_log.txt CHANGED
@@ -1 +1,3 @@
1
  2025/03/18 21:08:07 Error Log : ERROR: relasi « email_verifications » tidak ada (SQLSTATE 42P01)
 
 
 
1
  2025/03/18 21:08:07 Error Log : ERROR: relasi « email_verifications » tidak ada (SQLSTATE 42P01)
2
+ 2025/03/22 14:00:34 Error Log : duplicated key not allowed
3
+ 2025/03/22 16:57:13 Error Log : ERROR: there is no unique or exclusion constraint matching the ON CONFLICT specification (SQLSTATE 42P10)
space/models/request_model.go CHANGED
@@ -14,5 +14,9 @@ type RegisterRequest struct {
14
 
15
  type CreateEmailVerificationRequest struct {
16
  AccountID int `json:"account_id" binding:"required"`
 
17
 
 
 
 
18
  }
 
14
 
15
  type CreateEmailVerificationRequest struct {
16
  AccountID int `json:"account_id" binding:"required"`
17
+ }
18
 
19
+ type ChangePasswordRequest struct {
20
+ OldPassword string `json:"old_password" binding:"required" `
21
+ NewPassword string `json:"new_password" binding:"required" `
22
  }
space/repositories/account_repository.go CHANGED
@@ -1,6 +1,8 @@
1
  package repositories
2
 
3
  import (
 
 
4
  "api.qobiltu.id/models"
5
  )
6
 
@@ -25,6 +27,24 @@ func GetAccountbyId(account_id uint) Repository[models.Account, models.Account]
25
  )
26
  return *repo
27
  }
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
28
  func CreateAccount(account models.Account) Repository[models.Account, models.Account] {
29
  repo := Construct[models.Account, models.Account](
30
  account,
@@ -32,7 +52,21 @@ func CreateAccount(account models.Account) Repository[models.Account, models.Acc
32
  Create(repo)
33
  return *repo
34
  }
35
-
36
- // func UpdateAccount(account models.Account) Repository[models.Account, models.Account] {
37
- // repo := Construct
38
- // }
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
  package repositories
2
 
3
  import (
4
+ "fmt"
5
+
6
  "api.qobiltu.id/models"
7
  )
8
 
 
27
  )
28
  return *repo
29
  }
30
+ func UpdateAccount(account models.Account) Repository[models.Account, models.Account] {
31
+ repo := Construct[models.Account, models.Account](
32
+ account,
33
+ )
34
+ Update(repo)
35
+ return *repo
36
+ }
37
+ func GetAccountDetailsbyId(account_id uint) Repository[models.AccountDetails, models.AccountDetails] {
38
+ repo := Construct[models.AccountDetails, models.AccountDetails](
39
+ models.AccountDetails{AccountID: account_id},
40
+ )
41
+ fmt.Println("Account ID:", repo.Constructor.AccountID)
42
+ repo.Transactions(
43
+ WhereGivenConstructor[models.AccountDetails, models.AccountDetails],
44
+ Find[models.AccountDetails, models.AccountDetails],
45
+ )
46
+ return *repo
47
+ }
48
  func CreateAccount(account models.Account) Repository[models.Account, models.Account] {
49
  repo := Construct[models.Account, models.Account](
50
  account,
 
52
  Create(repo)
53
  return *repo
54
  }
55
+ func CreateAccountDetails(accountDetails models.AccountDetails) Repository[models.AccountDetails, models.AccountDetails] {
56
+ repo := Construct[models.AccountDetails, models.AccountDetails](
57
+ accountDetails,
58
+ )
59
+ fmt.Println(accountDetails)
60
+ fmt.Println("Account ID : ", accountDetails.AccountID)
61
+ Create(repo)
62
+ return *repo
63
+ }
64
+ func UpdateAccountDetails(accountDetails models.AccountDetails) Repository[models.AccountDetails, models.AccountDetails] {
65
+ repo := Construct[models.AccountDetails, models.AccountDetails](
66
+ accountDetails,
67
+ )
68
+ fmt.Println(accountDetails)
69
+ fmt.Println("Account ID : ", accountDetails.AccountID)
70
+ Update(repo)
71
+ return *repo
72
+ }
space/repositories/repository.go CHANGED
@@ -93,6 +93,7 @@ func Update[T1 any](repo *Repository[T1, T1]) *gorm.DB {
93
  repo.RowsCount = int(tx.RowsAffected)
94
  repo.NoRecord = repo.RowsCount == 0
95
  repo.RowsError = tx.Error
 
96
  return tx
97
  }
98
 
 
93
  repo.RowsCount = int(tx.RowsAffected)
94
  repo.NoRecord = repo.RowsCount == 0
95
  repo.RowsError = tx.Error
96
+ repo.Result = repo.Constructor
97
  return tx
98
  }
99
 
space/router/user_route.go CHANGED
@@ -12,5 +12,7 @@ func UserRoute(router *gin.Engine) {
12
  routerGroup.POST("/login", UserController.Login)
13
  routerGroup.POST("/register", UserController.Register)
14
  routerGroup.GET("/me", middleware.AuthUser, UserController.Profile)
 
 
15
  }
16
  }
 
12
  routerGroup.POST("/login", UserController.Login)
13
  routerGroup.POST("/register", UserController.Register)
14
  routerGroup.GET("/me", middleware.AuthUser, UserController.Profile)
15
+ routerGroup.PUT("/me", middleware.AuthUser, UserController.UpdateProfile)
16
+ routerGroup.PUT("/change-password", middleware.AuthUser, UserController.ChangePassword)
17
  }
18
  }
space/services/authentication_service.go ADDED
@@ -0,0 +1,71 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ package services
2
+
3
+ import (
4
+ "errors"
5
+ "fmt"
6
+
7
+ "api.qobiltu.id/models"
8
+ "api.qobiltu.id/repositories"
9
+ )
10
+
11
+ type AuthenticationService struct {
12
+ Service[models.Account, models.AuthenticatedUser]
13
+ }
14
+
15
+ func (s *AuthenticationService) Authenticate() {
16
+ accountData := repositories.GetAccountbyEmail(s.Constructor.Email)
17
+ if accountData.NoRecord {
18
+ s.Exception.DataNotFound = true
19
+ s.Exception.Message = "there is no account with given credentials!"
20
+ return
21
+ }
22
+ if VerifyPassword(accountData.Result.Password, s.Constructor.Password) != nil {
23
+ s.Exception.Unauthorized = true
24
+ s.Exception.Message = "incorrect password!"
25
+ return
26
+ }
27
+
28
+ token, err_tok := GenerateToken(&accountData.Result)
29
+
30
+ if err_tok != nil {
31
+ s.Error = errors.Join(s.Error, err_tok)
32
+ }
33
+
34
+ accountData.Result.Password = "SECRET"
35
+ s.Result = models.AuthenticatedUser{
36
+ Account: accountData.Result,
37
+ Token: token,
38
+ }
39
+ s.Error = accountData.RowsError
40
+ }
41
+
42
+ func (s *AuthenticationService) Update(oldPassword string, newPassword string) {
43
+ if len(newPassword) < 8 {
44
+ s.Exception.InvalidPasswordLength = true
45
+ s.Exception.Message = "Password must have at least 8 characters!"
46
+ return
47
+ }
48
+ accountData := repositories.GetAccountbyId(s.Constructor.Id)
49
+
50
+ if accountData.NoRecord {
51
+ s.Exception.DataNotFound = true
52
+ s.Exception.Message = "there is no account with given credentials!"
53
+ return
54
+ }
55
+ fmt.Println("Result Password", accountData.Result.Password)
56
+ fmt.Println("old Password given", oldPassword)
57
+ if VerifyPassword(accountData.Result.Password, oldPassword) != nil {
58
+ s.Exception.Unauthorized = true
59
+ s.Exception.Message = "incorrect old password!"
60
+ return
61
+ }
62
+ accountData.Result.Password = newPassword
63
+ changePassword := repositories.UpdateAccount(accountData.Result)
64
+ changePassword.Result.Password = "SECRET"
65
+ s.Result = models.AuthenticatedUser{
66
+ Account: changePassword.Result,
67
+ }
68
+ s.Error = changePassword.RowsError
69
+ }
70
+
71
+ // LoginHandler handles user login
space/services/register_service.go CHANGED
@@ -33,6 +33,13 @@ func (s *RegisterService) Create() {
33
  s.Exception.Message = "Bad request!"
34
  return
35
  }
 
 
 
 
 
 
 
36
  s.Error = accountCreated.RowsError
37
  s.Result = accountCreated.Result
38
  s.Result.Password = "SECRET"
 
33
  s.Exception.Message = "Bad request!"
34
  return
35
  }
36
+ userProfile := UserProfileService{}
37
+ userProfile.Constructor.AccountID = accountCreated.Result.Id
38
+ userProfile.Create()
39
+ if userProfile.Error != nil {
40
+ s.Error = userProfile.Error
41
+ return
42
+ }
43
  s.Error = accountCreated.RowsError
44
  s.Result = accountCreated.Result
45
  s.Result.Password = "SECRET"
space/services/user_profile_service.go CHANGED
@@ -5,33 +5,38 @@ import (
5
  "api.qobiltu.id/repositories"
6
  )
7
 
8
- type UserProfileConstructor struct {
9
- AccountId int
10
- }
11
  type UserProfileService struct {
12
- Service[UserProfileConstructor, models.Account]
13
  }
14
 
 
 
 
 
 
 
 
 
 
 
15
  func (s *UserProfileService) Retrieve() {
16
- userProfile := repositories.GetAccountbyId(uint(s.Constructor.AccountId))
17
  s.Error = userProfile.RowsError
18
  if userProfile.NoRecord {
19
  s.Exception.DataNotFound = true
20
  s.Exception.Message = "There is no account with given credentials!"
21
  return
22
  }
23
- s.Result.Password = "SECRET"
24
  s.Result = userProfile.Result
25
  }
26
 
27
  func (s *UserProfileService) Update() {
28
- userProfile := repositories.GetAccountbyId(uint(s.Constructor.AccountId))
29
  s.Error = userProfile.RowsError
30
  if userProfile.NoRecord {
31
  s.Exception.DataNotFound = true
32
  s.Exception.Message = "There is no account with given credentials!"
33
  return
34
  }
35
- s.Result.Password = "SECRET"
36
  s.Result = userProfile.Result
37
  }
 
5
  "api.qobiltu.id/repositories"
6
  )
7
 
 
 
 
8
  type UserProfileService struct {
9
+ Service[models.AccountDetails, models.AccountDetails]
10
  }
11
 
12
+ func (s *UserProfileService) Create() {
13
+ userProfile := repositories.CreateAccountDetails(s.Constructor)
14
+ s.Error = userProfile.RowsError
15
+ if userProfile.NoRecord {
16
+ s.Exception.DataNotFound = true
17
+ s.Exception.Message = "There is no account with given credentials!"
18
+ return
19
+ }
20
+ s.Result = userProfile.Result
21
+ }
22
  func (s *UserProfileService) Retrieve() {
23
+ userProfile := repositories.GetAccountDetailsbyId(s.Constructor.AccountID)
24
  s.Error = userProfile.RowsError
25
  if userProfile.NoRecord {
26
  s.Exception.DataNotFound = true
27
  s.Exception.Message = "There is no account with given credentials!"
28
  return
29
  }
 
30
  s.Result = userProfile.Result
31
  }
32
 
33
  func (s *UserProfileService) Update() {
34
+ userProfile := repositories.UpdateAccountDetails(s.Constructor)
35
  s.Error = userProfile.RowsError
36
  if userProfile.NoRecord {
37
  s.Exception.DataNotFound = true
38
  s.Exception.Message = "There is no account with given credentials!"
39
  return
40
  }
 
41
  s.Result = userProfile.Result
42
  }
space/space/.github/workflows/main.yml CHANGED
@@ -1,4 +1,4 @@
1
- name: Deploy to Development via Huggingface
2
 
3
  on:
4
  push:
@@ -6,47 +6,23 @@ on:
6
  - main
7
 
8
  jobs:
9
- deploy-to-huggingface:
10
  runs-on: ubuntu-latest
11
 
12
  steps:
13
- # Checkout repository
14
  - name: Checkout Repository
15
  uses: actions/checkout@v3
16
 
17
- # Setup Git
18
- - name: Setup Git for Huggingface
19
- run: |
20
- git config --global user.email "abdan.hafidz@gmail.com"
21
- git config --global user.name "abdanhafidz"
22
-
23
- # Clone Huggingface Space Repository
24
- - name: Clone Huggingface Space
25
- env:
26
- HF_TOKEN: ${{ secrets.HF_TOKEN }}
27
- run: |
28
- git clone https://huggingface.co/spaces/lifedebugger/api-qobiltu-dev space
29
-
30
- # Update Git Remote URL and Pull Latest Changes
31
- - name: Update Remote and Pull Changes
32
- env:
33
- HF_TOKEN: ${{ secrets.HF_TOKEN }}
34
- run: |
35
- cd space
36
- git remote set-url origin https://lifedebugger:$HF_TOKEN@huggingface.co/spaces/lifedebugger/api-qobiltu-dev
37
- git pull origin main || echo "No changes to pull"
38
-
39
- # Copy Files to Huggingface Space
40
- - name: Copy Files to Space
41
- run: |
42
- rsync -av --exclude='.git' ./ space/
43
 
44
- # Commit and Push to Huggingface Space
45
- - name: Commit and Push to Huggingface
46
- env:
47
- HF_TOKEN: ${{ secrets.HF_TOKEN }}
48
  run: |
49
- cd space
50
- git add .
51
- git commit -m "Deploy files from GitHub repository" || echo "No changes to commit"
52
- git push origin main || echo "No changes to push"
 
 
 
1
+ name: Deploy Golang App
2
 
3
  on:
4
  push:
 
6
  - main
7
 
8
  jobs:
9
+ deploy:
10
  runs-on: ubuntu-latest
11
 
12
  steps:
 
13
  - name: Checkout Repository
14
  uses: actions/checkout@v3
15
 
16
+ - name: Set up SSH key
17
+ uses: webfactory/ssh-agent@v0.5.4
18
+ with:
19
+ ssh-private-key: ${{ secrets.SSH_PRIVATE_KEY }}
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
20
 
21
+ - name: Deploy to VPS
 
 
 
22
  run: |
23
+ ssh -o StrictHostKeyChecking=no qobiltu@103.23.199.136 << 'EOF'
24
+ cd /home/qobiltu/api-qobiltu
25
+ git pull origin main
26
+ docker-compose down -v
27
+ docker-compose up --build -d
28
+ EOF
space/space/.github/workflows/main_huggingface.yml ADDED
@@ -0,0 +1,52 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ name: Deploy to Development via Huggingface
2
+
3
+ on:
4
+ push:
5
+ branches:
6
+ - main
7
+
8
+ jobs:
9
+ deploy-to-huggingface:
10
+ runs-on: ubuntu-latest
11
+
12
+ steps:
13
+ # Checkout repository
14
+ - name: Checkout Repository
15
+ uses: actions/checkout@v3
16
+
17
+ # Setup Git
18
+ - name: Setup Git for Huggingface
19
+ run: |
20
+ git config --global user.email "abdan.hafidz@gmail.com"
21
+ git config --global user.name "abdanhafidz"
22
+
23
+ # Clone Huggingface Space Repository
24
+ - name: Clone Huggingface Space
25
+ env:
26
+ HF_TOKEN: ${{ secrets.HF_TOKEN }}
27
+ run: |
28
+ git clone https://huggingface.co/spaces/lifedebugger/api-qobiltu-dev space
29
+
30
+ # Update Git Remote URL and Pull Latest Changes
31
+ - name: Update Remote and Pull Changes
32
+ env:
33
+ HF_TOKEN: ${{ secrets.HF_TOKEN }}
34
+ run: |
35
+ cd space
36
+ git remote set-url origin https://lifedebugger:$HF_TOKEN@huggingface.co/spaces/lifedebugger/api-qobiltu-dev
37
+ git pull origin main || echo "No changes to pull"
38
+
39
+ # Copy Files to Huggingface Space
40
+ - name: Copy Files to Space
41
+ run: |
42
+ rsync -av --exclude='.git' ./ space/
43
+
44
+ # Commit and Push to Huggingface Space
45
+ - name: Commit and Push to Huggingface
46
+ env:
47
+ HF_TOKEN: ${{ secrets.HF_TOKEN }}
48
+ run: |
49
+ cd space
50
+ git add .
51
+ git commit -m "Deploy files from GitHub repository" || echo "No changes to commit"
52
+ git push origin main || echo "No changes to push"
space/space/Dockerfile CHANGED
@@ -1,30 +1,37 @@
1
- # Gunakan image dasar Golang versi 1.21.6
2
- FROM golang:1.21.6
3
-
4
- # Set working directory
5
- WORKDIR /app
6
-
7
- # Copy go.mod dan go.sum
8
- COPY go.mod go.sum ./
9
-
10
- # Download dependencies
11
- RUN go mod download
12
-
13
- # Copy seluruh kode
14
- COPY . .
15
-
16
- # Buat file .env dengan variabel environment yang dibutuhkan
17
- RUN echo "DB_HOST=aws-0-ap-southeast-1.pooler.supabase.com" >> .env && \
18
- echo "DB_USER=postgres.rdscploxoikqsevhduii" >> .env && \
19
- echo "DB_PASSWORD=Qobiltu12233334444" >> .env && \
20
- echo "DB_PORT=5432" >> .env && \
21
- echo "DB_NAME=postgres" >> .env && \
22
- echo "HOST_ADDRESS = 0.0.0.0" >> .env && \
23
- echo "HOST_PORT = 7860" >> .env && \
24
- echo "SALT=NZNZtY7dNPz8l0dWINJZLKafWaJrql1s" >> .env && \
25
- echo "LOG_PATH = logs" >> .env
26
- # Build aplikasi
27
- RUN go build -o main .
28
-
29
- # Jalankan aplikasi
30
- CMD ["./main"]
 
 
 
 
 
 
 
 
1
+ # Gunakan image dasar Golang versi 1.24.1
2
+ FROM golang:1.24.1 AS builder
3
+
4
+ # Set working directory
5
+ WORKDIR /app
6
+
7
+ # Copy go.mod dan go.sum
8
+ COPY go.mod go.sum ./
9
+
10
+ # Download dependencies
11
+ RUN go mod download
12
+
13
+ # Copy seluruh kode
14
+ COPY . .
15
+
16
+ # Build aplikasi
17
+ # RUN go build -o /app/main .
18
+
19
+ # Build aplikasi untuk Linux
20
+ RUN CGO_ENABLED=0 GOOS=linux GOARCH=amd64 go build -o /app/main .
21
+
22
+ # Stage 2: Gunakan image runtime yang lebih kecil
23
+ FROM alpine:latest
24
+
25
+ # Set working directory
26
+ WORKDIR /app
27
+
28
+ # Copy hasil build dari builder ke image runtime
29
+ COPY --from=builder /app/main .
30
+
31
+ # Copy file .env untuk konfigurasi environment
32
+ COPY .env .env
33
+
34
+ RUN chmod +x /app/main
35
+
36
+ # Jalankan aplikasi
37
+ CMD ["./main"]
space/space/README.md CHANGED
@@ -62,10 +62,26 @@ The API uses the `errors` package to handle errors. You can set the `ERROR_PATH`
62
 
63
  # Command Documentation
64
 
65
- ## Update Golang Project version
 
66
  > go mod edit -go 1.24
67
  > go mod tidy
68
 
69
- ## Update all package
70
  > go get -u ./...
71
- > go mod tidy
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
62
 
63
  # Command Documentation
64
 
65
+ ## GO Command
66
+ ### Update Golang Project version
67
  > go mod edit -go 1.24
68
  > go mod tidy
69
 
70
+ ### Update all package
71
  > go get -u ./...
72
+ > go mod tidy
73
+
74
+ ## Docker Command
75
+ ### Enter docker container
76
+ > docker exec -it api-qobiltu sh
77
+
78
+ ### Docker Running Process
79
+ > docker ps
80
+
81
+ ### Rebuild Docker Image
82
+ > docker-compose down -v
83
+ > docker-compose up --build -d
84
+
85
+ ### View Log container
86
+ > docker logs --tail=50 -f api-qobiltu
87
+ > docker logs postgres-db
space/space/docker-compose.yml ADDED
@@ -0,0 +1,31 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ version: '3.8'
2
+
3
+ services:
4
+ app:
5
+ container_name: api-qobiltu
6
+ build: .
7
+ depends_on:
8
+ - db
9
+ env_file: .env
10
+ ports:
11
+ - "8080:8080"
12
+ # volumes:
13
+ # - ./logs:/app/logs
14
+ # - /home/qobiltu/api-qobiltu:/app
15
+ restart: unless-stopped
16
+
17
+ db:
18
+ image: postgres:15
19
+ container_name: postgres-db
20
+ environment:
21
+ POSTGRES_USER: ${DB_USER}
22
+ POSTGRES_PASSWORD: ${DB_PASSWORD}
23
+ POSTGRES_DB: ${DB_NAME}
24
+ ports:
25
+ - "5432:5432"
26
+ volumes:
27
+ - db-data:/var/lib/postgresql/data
28
+ restart: always
29
+
30
+ volumes:
31
+ db-data:
space/space/space/.gitignore CHANGED
@@ -1,4 +1,5 @@
1
  .env
2
  vendor/
3
  quzuu-be.exe
4
- README.md
 
 
1
  .env
2
  vendor/
3
  quzuu-be.exe
4
+ README.md
5
+ .qodo
space/space/space/config/config.go CHANGED
@@ -2,6 +2,7 @@ package config
2
 
3
  import (
4
  "os"
 
5
 
6
  "github.com/joho/godotenv"
7
  )
@@ -10,6 +11,7 @@ var TCP_ADDRESS string
10
  var LOG_PATH string
11
  var HOST_ADDRESS string
12
  var HOST_PORT string
 
13
 
14
  func init() {
15
  godotenv.Load()
@@ -17,5 +19,6 @@ func init() {
17
  HOST_PORT = os.Getenv("HOST_PORT")
18
  TCP_ADDRESS = HOST_ADDRESS + ":" + HOST_PORT
19
  LOG_PATH = os.Getenv("LOG_PATH")
 
20
  // Menampilkan nilai variabel lingkungan
21
  }
 
2
 
3
  import (
4
  "os"
5
+ "strconv"
6
 
7
  "github.com/joho/godotenv"
8
  )
 
11
  var LOG_PATH string
12
  var HOST_ADDRESS string
13
  var HOST_PORT string
14
+ var EMAIL_VERIFICATION_DURATION int
15
 
16
  func init() {
17
  godotenv.Load()
 
19
  HOST_PORT = os.Getenv("HOST_PORT")
20
  TCP_ADDRESS = HOST_ADDRESS + ":" + HOST_PORT
21
  LOG_PATH = os.Getenv("LOG_PATH")
22
+ EMAIL_VERIFICATION_DURATION, _ = strconv.Atoi(os.Getenv("EMAIL_VERIFICATION_DURATION"))
23
  // Menampilkan nilai variabel lingkungan
24
  }
space/space/space/config/database_connection_config.go CHANGED
@@ -51,7 +51,17 @@ func AutoMigrateAll(db *gorm.DB) {
51
  err := db.AutoMigrate(
52
  &models.Account{},
53
  &models.AccountDetails{},
 
 
 
 
 
 
 
 
 
54
  )
 
55
  if err != nil {
56
  log.Fatal(err)
57
  }
 
51
  err := db.AutoMigrate(
52
  &models.Account{},
53
  &models.AccountDetails{},
54
+ &models.EmailVerification{},
55
+ &models.ExternalAuth{},
56
+ &models.FCM{},
57
+ &models.ForgotPassword{},
58
+ &models.Academy{},
59
+ &models.AcademyMaterial{},
60
+ &models.AcademyContent{},
61
+ &models.AcademyMaterialProgress{},
62
+ &models.AcademyContentProgress{},
63
  )
64
+
65
  if err != nil {
66
  log.Fatal(err)
67
  }
space/space/space/controller/controller.go CHANGED
@@ -19,6 +19,13 @@ type (
19
  }
20
  )
21
 
 
 
 
 
 
 
 
22
  func (controller *Controller[T1, T2, T3]) RequestJSON(c *gin.Context, act func()) {
23
  cParam, _ := c.Get("accountData")
24
  if cParam != nil {
 
19
  }
20
  )
21
 
22
+ func (controller *Controller[T1, T2, T3]) HeaderParse(c *gin.Context, act func()) {
23
+ cParam, _ := c.Get("accountData")
24
+ if cParam != nil {
25
+ controller.AccountData = cParam.(models.AccountData)
26
+ }
27
+ act()
28
+ }
29
  func (controller *Controller[T1, T2, T3]) RequestJSON(c *gin.Context, act func()) {
30
  cParam, _ := c.Get("accountData")
31
  if cParam != nil {
space/space/space/controller/email/email_create_verification.go ADDED
@@ -0,0 +1,22 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ package controller
2
+
3
+ import (
4
+ "strconv"
5
+
6
+ "api.qobiltu.id/controller"
7
+ "api.qobiltu.id/models"
8
+ "api.qobiltu.id/services"
9
+ "github.com/gin-gonic/gin"
10
+ )
11
+
12
+ func CreateVerification(c *gin.Context) {
13
+ emailVerification := services.EmailVerificationService{}
14
+ emailVerificationController := controller.Controller[any, models.EmailVerification, models.EmailVerification]{
15
+ Service: &emailVerification.Service,
16
+ }
17
+ query, _ := c.GetQuery("account_id")
18
+ accountId, _ := strconv.Atoi(query)
19
+ emailVerificationController.Service.Constructor.AccountID = uint(accountId)
20
+ emailVerification.Create()
21
+ emailVerificationController.Response(c)
22
+ }
space/space/space/controller/email/email_delete_verification.go ADDED
@@ -0,0 +1,22 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ package controller
2
+
3
+ import (
4
+ "strconv"
5
+
6
+ "api.qobiltu.id/controller"
7
+ "api.qobiltu.id/models"
8
+ "api.qobiltu.id/services"
9
+ "github.com/gin-gonic/gin"
10
+ )
11
+
12
+ func DeleteVerification(c *gin.Context) {
13
+ emailVerification := services.EmailVerificationService{}
14
+ emailVerificationController := controller.Controller[any, models.EmailVerification, models.EmailVerification]{
15
+ Service: &emailVerification.Service,
16
+ }
17
+ query, _ := c.GetQuery("account_id")
18
+ accountId, _ := strconv.Atoi(query)
19
+ emailVerificationController.Service.Constructor.AccountID = uint(accountId)
20
+ emailVerification.Delete()
21
+ emailVerificationController.Response(c)
22
+ }
space/space/space/controller/email/email_verify.go ADDED
@@ -0,0 +1,22 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ package controller
2
+
3
+ import (
4
+ "strconv"
5
+
6
+ "api.qobiltu.id/controller"
7
+ "api.qobiltu.id/models"
8
+ "api.qobiltu.id/services"
9
+ "github.com/gin-gonic/gin"
10
+ )
11
+
12
+ func Verify(c *gin.Context) {
13
+ emailVerification := services.EmailVerificationService{}
14
+ emailVerificationController := controller.Controller[any, models.EmailVerification, models.EmailVerification]{
15
+ Service: &emailVerification.Service,
16
+ }
17
+ query, _ := c.GetQuery("account_id")
18
+ accountId, _ := strconv.Atoi(query)
19
+ emailVerificationController.Service.Constructor.AccountID = uint(accountId)
20
+ emailVerification.Validate()
21
+ emailVerificationController.Response(c)
22
+ }
space/space/space/controller/user/user_login_controller.go ADDED
@@ -0,0 +1,20 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ package user
2
+
3
+ import (
4
+ "api.qobiltu.id/controller"
5
+ "api.qobiltu.id/models"
6
+ "api.qobiltu.id/services"
7
+ "github.com/gin-gonic/gin"
8
+ )
9
+
10
+ func Login(c *gin.Context) {
11
+ authentication := services.AuthenticationService{}
12
+ loginController := controller.Controller[models.LoginRequest, services.LoginConstructor, models.AuthenticatedUser]{
13
+ Service: &authentication.Service,
14
+ }
15
+ loginController.RequestJSON(c, func() {
16
+ loginController.Service.Constructor.Email = loginController.Request.Email
17
+ loginController.Service.Constructor.Password = loginController.Request.Password
18
+ authentication.Authenticate()
19
+ })
20
+ }
space/space/space/controller/user/user_profile_controller.go ADDED
@@ -0,0 +1,24 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ package user
2
+
3
+ import (
4
+ "fmt"
5
+
6
+ "api.qobiltu.id/controller"
7
+ "api.qobiltu.id/models"
8
+ "api.qobiltu.id/services"
9
+ "github.com/gin-gonic/gin"
10
+ )
11
+
12
+ func Profile(c *gin.Context) {
13
+ userProfile := services.UserProfileService{}
14
+ userProfileController := controller.Controller[any, services.UserProfileConstructor, models.Account]{
15
+ Service: &userProfile.Service,
16
+ }
17
+ fmt.Println(userProfileController.AccountData)
18
+ userProfileController.HeaderParse(c, func() {
19
+ userProfileController.Service.Constructor.AccountId = userProfileController.AccountData.UserID
20
+ userProfile.Retrieve()
21
+ userProfileController.Response(c)
22
+ },
23
+ )
24
+ }
space/space/space/controller/user/user_register_controller.go ADDED
@@ -0,0 +1,20 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ package user
2
+
3
+ import (
4
+ "api.qobiltu.id/controller"
5
+ "api.qobiltu.id/models"
6
+ "api.qobiltu.id/services"
7
+ "github.com/gin-gonic/gin"
8
+ )
9
+
10
+ func Register(c *gin.Context) {
11
+ register := services.RegisterService{}
12
+ registerController := controller.Controller[models.RegisterRequest, models.Account, models.Account]{
13
+ Service: &register.Service,
14
+ }
15
+ registerController.RequestJSON(c, func() {
16
+ registerController.Service.Constructor.Password = registerController.Request.Password
17
+ registerController.Service.Constructor.Email = registerController.Request.Email
18
+ register.Create()
19
+ })
20
+ }
space/space/space/controller/user/user_update_profile_controller.go ADDED
@@ -0,0 +1,24 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ package user
2
+
3
+ import (
4
+ "fmt"
5
+
6
+ "api.qobiltu.id/controller"
7
+ "api.qobiltu.id/models"
8
+ "api.qobiltu.id/services"
9
+ "github.com/gin-gonic/gin"
10
+ )
11
+
12
+ func UpdateProfile(c *gin.Context) {
13
+ userProfile := services.UserProfileService{}
14
+ userUpdateProfileController := controller.Controller[any, services.UserProfileConstructor, models.Account]{
15
+ Service: &userProfile.Service,
16
+ }
17
+ fmt.Println(userUpdateProfileController.AccountData)
18
+ userUpdateProfileController.HeaderParse(c, func() {
19
+ userUpdateProfileController.Service.Constructor.AccountId = userUpdateProfileController.AccountData.UserID
20
+ userProfile.Retrieve()
21
+ userUpdateProfileController.Response(c)
22
+ },
23
+ )
24
+ }
space/space/space/docker-compose.yaml ADDED
@@ -0,0 +1,10 @@
 
 
 
 
 
 
 
 
 
 
 
1
+ services:
2
+ postgres:
3
+ container_name: postgres
4
+ image: postgres
5
+ environment:
6
+ - POSTGRES_USER=postgres
7
+ - POSTGRES_PASSWORD=password
8
+ - POSTGRES_DB=qobiltu
9
+ ports:
10
+ - 5432:5432