2025-09-28 01:53:09 +00:00
package service
import (
"encoding/base64"
"encoding/json"
"fmt"
2025-09-30 13:34:56 +00:00
"netidhub-saas-be/app/database/entity"
"netidhub-saas-be/app/database/entity/users"
userLevelsRepository "netidhub-saas-be/app/module/user_levels/repository"
"netidhub-saas-be/app/module/users/mapper"
"netidhub-saas-be/app/module/users/repository"
"netidhub-saas-be/app/module/users/request"
"netidhub-saas-be/app/module/users/response"
"netidhub-saas-be/config/config"
"netidhub-saas-be/utils/paginator"
utilSvc "netidhub-saas-be/utils/service"
2025-09-28 01:53:09 +00:00
"strings"
"time"
paseto "aidanwoods.dev/go-paseto"
"github.com/Nerzal/gocloak/v13"
"github.com/google/uuid"
"github.com/rs/zerolog"
)
// UsersService
type usersService struct {
Repo repository . UsersRepository
UserLevelsRepo userLevelsRepository . UserLevelsRepository
Log zerolog . Logger
Keycloak * config . KeycloakConfig
Smtp * config . SmtpConfig
}
// UsersService define interface of IUsersService
type UsersService interface {
2025-10-01 03:10:18 +00:00
All ( authToken string , req request . UsersQueryRequest ) ( users [ ] * response . UsersResponse , paging paginator . Pagination , err error )
Show ( authToken string , id uint ) ( users * response . UsersResponse , err error )
ShowByUsername ( authToken string , username string ) ( users * response . UsersResponse , err error )
ShowUserInfo ( authToken string ) ( users * response . UsersResponse , err error )
Save ( authToken string , req request . UsersCreateRequest ) ( userReturn * users . Users , err error )
2025-09-28 01:53:09 +00:00
Login ( req request . UserLogin ) ( res * gocloak . JWT , err error )
ParetoLogin ( req request . UserLogin ) ( res * response . ParetoLoginResponse , err error )
2025-10-01 03:10:18 +00:00
Update ( authToken string , id uint , req request . UsersUpdateRequest ) ( err error )
Delete ( authToken string , id uint ) error
SavePassword ( authToken string , req request . UserSavePassword ) ( err error )
2025-09-28 01:53:09 +00:00
ResetPassword ( req request . UserResetPassword ) ( err error )
2025-10-01 03:10:18 +00:00
ForgotPassword ( authToken string , req request . UserForgotPassword ) ( err error )
EmailValidationPreLogin ( authToken string , req request . UserEmailValidationRequest ) ( msgResponse * string , err error )
SetupEmail ( authToken string , req request . UserEmailValidationRequest ) ( msgResponse * string , err error )
2025-09-28 01:53:09 +00:00
OtpRequest ( req request . UserOtpRequest ) ( err error )
OtpValidation ( req request . UserOtpValidation ) ( err error )
SendLoginOtp ( name string , email string , otp string ) error
SendRegistrationOtp ( name string , email string , otp string ) error
}
// NewUsersService init UsersService
func NewUsersService ( repo repository . UsersRepository , userLevelsRepo userLevelsRepository . UserLevelsRepository , log zerolog . Logger , keycloak * config . KeycloakConfig , smtp * config . SmtpConfig ) UsersService {
return & usersService {
Repo : repo ,
UserLevelsRepo : userLevelsRepo ,
Log : log ,
Keycloak : keycloak ,
Smtp : smtp ,
}
}
// All implement interface of UsersService
2025-10-01 03:10:18 +00:00
func ( _i * usersService ) All ( authToken string , req request . UsersQueryRequest ) ( users [ ] * response . UsersResponse , paging paginator . Pagination , err error ) {
// Extract clientId from authToken
var clientId * uuid . UUID
if authToken != "" {
user := utilSvc . GetUserInfo ( _i . Log , _i . Repo , authToken )
if user != nil && user . ClientId != nil {
clientId = user . ClientId
_i . Log . Info ( ) . Interface ( "clientId" , clientId ) . Msg ( "Extracted clientId from auth token" )
}
}
2025-09-28 01:53:09 +00:00
results , paging , err := _i . Repo . GetAll ( clientId , req )
if err != nil {
return
}
for _ , result := range results {
users = append ( users , mapper . UsersResponseMapper ( result , _i . UserLevelsRepo , clientId ) )
}
return
}
2025-10-01 03:10:18 +00:00
func ( _i * usersService ) Show ( authToken string , id uint ) ( users * response . UsersResponse , err error ) {
// Extract clientId from authToken
var clientId * uuid . UUID
if authToken != "" {
user := utilSvc . GetUserInfo ( _i . Log , _i . Repo , authToken )
if user != nil && user . ClientId != nil {
clientId = user . ClientId
_i . Log . Info ( ) . Interface ( "clientId" , clientId ) . Msg ( "Extracted clientId from auth token" )
}
}
2025-09-28 01:53:09 +00:00
result , err := _i . Repo . FindOne ( clientId , id )
if err != nil {
return nil , err
}
return mapper . UsersResponseMapper ( result , _i . UserLevelsRepo , clientId ) , nil
}
2025-10-01 03:10:18 +00:00
func ( _i * usersService ) ShowByUsername ( authToken string , username string ) ( users * response . UsersResponse , err error ) {
// Extract clientId from authToken
var clientId * uuid . UUID
if authToken != "" {
user := utilSvc . GetUserInfo ( _i . Log , _i . Repo , authToken )
if user != nil && user . ClientId != nil {
clientId = user . ClientId
_i . Log . Info ( ) . Interface ( "clientId" , clientId ) . Msg ( "Extracted clientId from auth token" )
}
}
2025-09-28 01:53:09 +00:00
result , err := _i . Repo . FindByUsername ( clientId , username )
if err != nil {
return nil , err
}
return mapper . UsersResponseMapper ( result , _i . UserLevelsRepo , clientId ) , nil
}
2025-10-01 03:10:18 +00:00
func ( _i * usersService ) ShowUserInfo ( authToken string ) ( users * response . UsersResponse , err error ) {
// Extract clientId from authToken
var clientId * uuid . UUID
if authToken != "" {
user := utilSvc . GetUserInfo ( _i . Log , _i . Repo , authToken )
if user != nil && user . ClientId != nil {
clientId = user . ClientId
_i . Log . Info ( ) . Interface ( "clientId" , clientId ) . Msg ( "Extracted clientId from auth token" )
}
}
2025-09-28 01:53:09 +00:00
userInfo := utilSvc . GetUserInfo ( _i . Log , _i . Repo , authToken )
return mapper . UsersResponseMapper ( userInfo , _i . UserLevelsRepo , clientId ) , nil
}
2025-10-01 03:10:18 +00:00
func ( _i * usersService ) Save ( authToken string , req request . UsersCreateRequest ) ( userReturn * users . Users , err error ) {
// Extract clientId from authToken
var clientId * uuid . UUID
if authToken != "" {
user := utilSvc . GetUserInfo ( _i . Log , _i . Repo , authToken )
if user != nil && user . ClientId != nil {
clientId = user . ClientId
_i . Log . Info ( ) . Interface ( "clientId" , clientId ) . Msg ( "Extracted clientId from auth token" )
}
}
2025-09-28 01:53:09 +00:00
_i . Log . Info ( ) . Interface ( "data" , req ) . Msg ( "" )
newReq := req . ToEntity ( )
_i . Log . Info ( ) . Interface ( "AUTH TOKEN" , authToken ) . Msg ( "" )
if authToken != "" {
createdBy := utilSvc . GetUserInfo ( _i . Log , _i . Repo , authToken )
newReq . CreatedById = & createdBy . ID
}
keycloakId , err := _i . Keycloak . CreateUser ( req . Fullname , req . Email , req . Username , req . Password )
if err != nil {
return nil , err
}
newReq . KeycloakId = & keycloakId
newReq . TempPassword = & req . Password
// Set ClientId on entity
newReq . ClientId = clientId
return _i . Repo . Create ( newReq )
}
func ( _i * usersService ) Login ( req request . UserLogin ) ( res * gocloak . JWT , err error ) {
_i . Log . Info ( ) . Interface ( "data" , req ) . Msg ( "" )
var loginResponse * gocloak . JWT
if req . RefreshToken == nil {
loginResponse , err = _i . Keycloak . Login ( * req . Username , * req . Password )
} else {
loginResponse , err = _i . Keycloak . RefreshToken ( * req . RefreshToken )
}
if err != nil {
return nil , err
}
return loginResponse , nil
}
func ( _i * usersService ) ParetoLogin ( req request . UserLogin ) ( res * response . ParetoLoginResponse , err error ) {
_i . Log . Info ( ) . Interface ( "data" , req ) . Msg ( "" )
var loginResponse * gocloak . JWT
token := paseto . NewToken ( )
secretKeyHex := "bdc42b1a0ba2bac3e27ba84241f9de06dee71b70f838af8d1beb0417f03d1d00"
secretKey , _ := paseto . V4SymmetricKeyFromHex ( secretKeyHex )
// secretKey := paseto.NewV4SymmetricKey() // to change the secretKey periodically
if req . RefreshToken == nil {
loginResponse , err = _i . Keycloak . Login ( * req . Username , * req . Password )
} else {
// Retrieve Refresh Token
parser := paseto . NewParser ( )
verifiedToken , err := parser . ParseV4Local ( secretKey , * req . RefreshToken , nil )
if err != nil {
panic ( err )
}
refreshToken , _ := verifiedToken . GetString ( "refresh_token" )
_i . Log . Info ( ) . Interface ( "Pareto parse refresh token" , refreshToken ) . Msg ( "" )
loginResponse , err = _i . Keycloak . RefreshToken ( refreshToken )
}
_i . Log . Info ( ) . Interface ( "loginResponse" , loginResponse ) . Msg ( "" )
if err != nil {
return nil , err
}
parseToken , err := ParseJWTToken ( loginResponse . AccessToken )
if err != nil {
return nil , err
}
issuedAt := parseToken [ "iat" ] . ( float64 )
expirationTime := parseToken [ "exp" ] . ( float64 )
issuer := parseToken [ "iss" ] . ( string )
jti := parseToken [ "jti" ] . ( string )
subject := parseToken [ "sub" ] . ( string )
token . SetIssuedAt ( time . Unix ( int64 ( issuedAt ) , 0 ) )
token . SetNotBefore ( time . Unix ( int64 ( issuedAt ) , 0 ) )
token . SetExpiration ( time . Unix ( int64 ( expirationTime ) , 0 ) )
token . SetIssuer ( issuer )
token . SetJti ( jti )
token . SetSubject ( subject )
token . SetString ( "access_token" , loginResponse . AccessToken )
token . SetString ( "refresh_token" , loginResponse . RefreshToken )
_i . Log . Info ( ) . Interface ( "Pareto Generated Key" , secretKey . ExportHex ( ) ) . Msg ( "" )
tokenEncrypted := token . V4Encrypt ( secretKey , nil )
parser := paseto . NewParser ( )
verifiedToken , err := parser . ParseV4Local ( secretKey , tokenEncrypted , nil )
if err != nil {
panic ( err )
}
tokenParsing , _ := verifiedToken . GetString ( "access_token" )
_i . Log . Info ( ) . Interface ( "Pareto parse token" , tokenParsing ) . Msg ( "" )
resLogin := & response . ParetoLoginResponse {
AccessToken : tokenEncrypted ,
}
if err != nil {
return nil , err
}
return resLogin , nil
}
2025-10-01 03:10:18 +00:00
func ( _i * usersService ) Update ( authToken string , id uint , req request . UsersUpdateRequest ) ( err error ) {
// Extract clientId from authToken
var clientId * uuid . UUID
if authToken != "" {
user := utilSvc . GetUserInfo ( _i . Log , _i . Repo , authToken )
if user != nil && user . ClientId != nil {
clientId = user . ClientId
_i . Log . Info ( ) . Interface ( "clientId" , clientId ) . Msg ( "Extracted clientId from auth token" )
}
}
2025-09-28 01:53:09 +00:00
_i . Log . Info ( ) . Interface ( "data" , req ) . Msg ( "" )
newReq := req . ToEntity ( )
findUser , err := _i . Repo . FindOne ( clientId , id )
if err != nil {
return err
}
err = _i . Keycloak . UpdateUser ( findUser . KeycloakId , req . Fullname , req . Email )
if err != nil {
return err
}
// Set ClientId on entity
newReq . ClientId = clientId
return _i . Repo . Update ( clientId , id , newReq )
}
2025-10-01 03:10:18 +00:00
func ( _i * usersService ) Delete ( authToken string , id uint ) error {
// Extract clientId from authToken
var clientId * uuid . UUID
if authToken != "" {
user := utilSvc . GetUserInfo ( _i . Log , _i . Repo , authToken )
if user != nil && user . ClientId != nil {
clientId = user . ClientId
_i . Log . Info ( ) . Interface ( "clientId" , clientId ) . Msg ( "Extracted clientId from auth token" )
}
}
2025-09-28 01:53:09 +00:00
result , err := _i . Repo . FindOne ( clientId , id )
if err != nil {
return err
}
isActive := false
result . IsActive = & isActive
return _i . Repo . Update ( clientId , id , result )
}
2025-10-01 03:10:18 +00:00
func ( _i * usersService ) SavePassword ( authToken string , req request . UserSavePassword ) ( err error ) {
// Extract clientId from authToken
var clientId * uuid . UUID
if authToken != "" {
user := utilSvc . GetUserInfo ( _i . Log , _i . Repo , authToken )
if user != nil && user . ClientId != nil {
clientId = user . ClientId
_i . Log . Info ( ) . Interface ( "clientId" , clientId ) . Msg ( "Extracted clientId from auth token" )
}
}
2025-09-28 01:53:09 +00:00
_i . Log . Info ( ) . Interface ( "data" , req ) . Msg ( "" )
_i . Log . Info ( ) . Interface ( "AUTH TOKEN" , authToken ) . Msg ( "" )
if authToken != "" {
createdBy := utilSvc . GetUserInfo ( _i . Log , _i . Repo , authToken )
tokenString := strings . TrimPrefix ( authToken , "Bearer " )
err := _i . Keycloak . SetPassword ( tokenString , * createdBy . KeycloakId , req . Password )
if err != nil {
return err
}
return nil
} else {
return fmt . Errorf ( "Invalid token" )
}
}
func ( _i * usersService ) ResetPassword ( req request . UserResetPassword ) ( err error ) {
_i . Log . Info ( ) . Interface ( "data" , req ) . Msg ( "" )
if req . Password != req . ConfirmPassword {
return fmt . Errorf ( "Invalid Password" )
}
user , err := _i . Repo . FindByKeycloakId ( req . UserId )
if err != nil {
return fmt . Errorf ( "User Id Not Found" )
}
forgotPassword , err := _i . Repo . FindForgotPassword ( req . UserId , req . CodeRequest )
if err != nil {
return fmt . Errorf ( "Invalid Request" )
}
_i . Log . Info ( ) . Interface ( "data" , forgotPassword ) . Msg ( "" )
_i . Log . Info ( ) . Interface ( "dataForgotPassword" , forgotPassword ) . Msg ( "" )
if user != nil {
err := _i . Keycloak . SetPasswordWithoutToken ( req . UserId , req . Password )
if err != nil {
return err
}
forgotPassword . IsActive = false
forgotPassword . UpdatedAt = time . Now ( )
err = _i . Repo . UpdateForgotPassword ( forgotPassword . ID , forgotPassword )
if err != nil {
return err
}
return nil
} else {
return fmt . Errorf ( "User not found" )
}
}
2025-10-01 03:10:18 +00:00
func ( _i * usersService ) ForgotPassword ( authToken string , req request . UserForgotPassword ) ( err error ) {
// Extract clientId from authToken
var clientId * uuid . UUID
if authToken != "" {
user := utilSvc . GetUserInfo ( _i . Log , _i . Repo , authToken )
if user != nil && user . ClientId != nil {
clientId = user . ClientId
_i . Log . Info ( ) . Interface ( "clientId" , clientId ) . Msg ( "Extracted clientId from auth token" )
}
}
2025-09-28 01:53:09 +00:00
_i . Log . Info ( ) . Interface ( "data" , req ) . Msg ( "" )
user , err := _i . Repo . FindByUsername ( clientId , req . Username )
if err != nil {
return err
}
if user != nil {
codeRequest , err := utilSvc . GenerateNumericCode ( 8 )
if err != nil {
return nil
}
forgotPasswordReq := entity . ForgotPasswords {
KeycloakID : * user . KeycloakId ,
CodeRequest : codeRequest ,
IsActive : true ,
}
err = _i . Repo . CreateForgotPassword ( & forgotPasswordReq )
if err != nil {
return err
}
// send email forgot password
url := fmt . Sprintf ( "https://kontenhumas.com/setup-password?userId=%s&code=%s" , * user . KeycloakId , codeRequest )
subject := "[HUMAS POLRI] Forgot Password"
htmlBody := "<p>Anda telah mengirimkan permintaan untuk melakukan reset password.</p>"
htmlBody += "<p style='padding-bottom: 10px;'>Silahkan buat password akun anda dengan menekan tombol di bawah ini, untuk membuat password baru</p>"
htmlBody += fmt . Sprintf ( "<a style='padding:8px 50px;border-radius:28px;background-color:#37c2b6;text-decoration:none;color:#f4f5f5;' href='%s'>Reset Password</a>" , url )
htmlBody += "<p style='padding-top: 10px;'>Terimakasih.</p>"
err = _i . Smtp . SendEmail ( subject , user . Email , user . Fullname , htmlBody )
return nil
} else {
return fmt . Errorf ( "User not found" )
}
}
func ( _i * usersService ) OtpRequest ( req request . UserOtpRequest ) ( err error ) {
_i . Log . Info ( ) . Interface ( "data" , req ) . Msg ( "" )
codeRequest , err := utilSvc . GenerateNumericCode ( 6 )
if req . Name == nil {
req . Name = & req . Email
}
if err != nil {
return err
}
otpReq := entity . OneTimePasswords {
Email : req . Email ,
Name : req . Name ,
OtpCode : codeRequest ,
IsActive : true ,
}
err = _i . Repo . CreateOtp ( & otpReq )
if err != nil {
return err
}
err = _i . SendRegistrationOtp ( * req . Name , req . Email , codeRequest )
if err != nil {
return err
}
return nil
}
func ( _i * usersService ) OtpValidation ( req request . UserOtpValidation ) ( err error ) {
_i . Log . Info ( ) . Interface ( "data" , req ) . Msg ( "" )
var otp * entity . OneTimePasswords
if req . Email == nil {
otp , err = _i . Repo . FindOtpByIdentity ( * req . Username , req . OtpCode )
if err != nil {
return fmt . Errorf ( "OTP is not valid" )
}
} else {
otp , err = _i . Repo . FindOtpByEmail ( * req . Email , req . OtpCode )
if err != nil {
return fmt . Errorf ( "OTP is not valid" )
}
}
if otp != nil {
if otp . ValidUntil . Before ( time . Now ( ) ) {
return fmt . Errorf ( "OTP has expired" )
}
return nil
} else {
return fmt . Errorf ( "OTP is not valid" )
}
}
2025-10-01 03:10:18 +00:00
func ( _i * usersService ) EmailValidationPreLogin ( authToken string , req request . UserEmailValidationRequest ) ( msgResponse * string , err error ) {
// Extract clientId from authToken
var clientId * uuid . UUID
if authToken != "" {
user := utilSvc . GetUserInfo ( _i . Log , _i . Repo , authToken )
if user != nil && user . ClientId != nil {
clientId = user . ClientId
_i . Log . Info ( ) . Interface ( "clientId" , clientId ) . Msg ( "Extracted clientId from auth token" )
}
}
2025-09-28 01:53:09 +00:00
_i . Log . Info ( ) . Interface ( "data" , req ) . Msg ( "" )
var loginResponse * gocloak . JWT
loginResponse , err = _i . Keycloak . Login ( * req . Username , * req . Password )
if loginResponse == nil || err != nil {
return nil , fmt . Errorf ( "username / password incorrect" )
}
findUser , err := _i . Repo . FindByUsername ( clientId , * req . Username )
if findUser == nil || err != nil {
return nil , fmt . Errorf ( "username / password incorrect" )
}
_i . Log . Info ( ) . Interface ( "data user" , findUser ) . Msg ( "" )
if * findUser . IsEmailUpdated != true {
message := "Continue to setup email"
msgResponse = & message
} else {
codeRequest , err := utilSvc . GenerateNumericCode ( 6 )
if err != nil {
return nil , err
}
otpReq := entity . OneTimePasswords {
Email : findUser . Email ,
Identity : & findUser . Username ,
OtpCode : codeRequest ,
IsActive : true ,
}
err = _i . Repo . CreateOtp ( & otpReq )
if err != nil {
return nil , err
}
err = _i . SendLoginOtp ( findUser . Fullname , findUser . Email , codeRequest )
if err != nil {
return nil , err
} else {
msg := "Email is valid and OTP has been sent"
msgResponse = & msg
}
}
return msgResponse , nil
}
2025-10-01 03:10:18 +00:00
func ( _i * usersService ) SetupEmail ( authToken string , req request . UserEmailValidationRequest ) ( msgResponse * string , err error ) {
// Extract clientId from authToken
var clientId * uuid . UUID
if authToken != "" {
user := utilSvc . GetUserInfo ( _i . Log , _i . Repo , authToken )
if user != nil && user . ClientId != nil {
clientId = user . ClientId
_i . Log . Info ( ) . Interface ( "clientId" , clientId ) . Msg ( "Extracted clientId from auth token" )
}
}
2025-09-28 01:53:09 +00:00
_i . Log . Info ( ) . Interface ( "data" , req ) . Msg ( "" )
var loginResponse * gocloak . JWT
loginResponse , err = _i . Keycloak . Login ( * req . Username , * req . Password )
if loginResponse == nil || err != nil {
return nil , fmt . Errorf ( "username / password incorrect" )
}
_i . Log . Info ( ) . Interface ( "findUser" , "" ) . Msg ( "" )
findUser , err := _i . Repo . FindByUsername ( clientId , * req . Username )
_i . Log . Info ( ) . Interface ( "findUser" , findUser ) . Msg ( "" )
if findUser == nil || err != nil {
return nil , fmt . Errorf ( "username / password incorrect" )
}
isTrue := true
if findUser . Email == * req . OldEmail {
findUser . Email = * req . NewEmail
findUser . IsEmailUpdated = & isTrue
_i . Log . Info ( ) . Interface ( "Update" , "" ) . Msg ( "" )
err = _i . Repo . Update ( clientId , findUser . ID , findUser )
_i . Log . Info ( ) . Interface ( "Update" , err ) . Msg ( "" )
if err != nil {
return nil , err
}
codeRequest , err := utilSvc . GenerateNumericCode ( 6 )
if err != nil {
return nil , err
}
otpReq := entity . OneTimePasswords {
Email : findUser . Email ,
Identity : & findUser . Username ,
OtpCode : codeRequest ,
IsActive : true ,
}
err = _i . Repo . CreateOtp ( & otpReq )
if err != nil {
return nil , err
}
err = _i . SendLoginOtp ( findUser . Fullname , findUser . Email , codeRequest )
if err != nil {
return nil , err
} else {
msg := "Email is valid and OTP has been sent"
msgResponse = & msg
}
} else {
return nil , fmt . Errorf ( "the old email is not same" )
}
return msgResponse , nil
}
func ParseJWTToken ( token string ) ( map [ string ] interface { } , error ) {
// Pisahkan JWT menjadi 3 bagian: header, payload, dan signature
parts := strings . Split ( token , "." )
if len ( parts ) != 3 {
return nil , fmt . Errorf ( "Invalid JWT token" )
}
// Decode bagian payload (index ke-1)
payloadData , err := base64 . RawURLEncoding . DecodeString ( parts [ 1 ] )
if err != nil {
return nil , fmt . Errorf ( "Failed to decode payload: %v" , err )
}
// Ubah payload menjadi map[string]interface{}
var payload map [ string ] interface { }
if err := json . Unmarshal ( payloadData , & payload ) ; err != nil {
return nil , fmt . Errorf ( "Failed to parse payload JSON: %v" , err )
}
return payload , nil
}
func ( _i * usersService ) SendLoginOtp ( name string , email string , otp string ) error {
subject := "[HUMAS POLRI] Permintaan OTP"
htmlBody := fmt . Sprintf ( "<p>Hai %s !</p><p>Berikut kode OTP yang digunakan untuk Login.</p>" , name )
htmlBody += fmt . Sprintf ( "<p style='padding: 10px 50px; background: #eef2f6; border-radius: 8px; max-width: 300px; text-align: center'><b>%s</b></p>" , otp )
htmlBody += "<p style='padding-top: 10px;'>Kode diatas hanya berlaku selama 10 menit. Harap segera masukkan kode tersebut pada aplikasi HUMAS POLRI.</p>"
htmlBody += "<p style='padding-top: 10px; padding-bottom: 10px'>Demi menjaga kerahasiaan data kamu, mohon jangan membagikan kode OTP ke siapapun.</p>"
err := _i . Smtp . SendEmail ( subject , email , name , htmlBody )
return err
}
func ( _i * usersService ) SendRegistrationOtp ( name string , email string , otp string ) error {
subject := "[HUMAS POLRI] Permintaan OTP"
htmlBody := fmt . Sprintf ( "<p>Hai %s !</p><p>Berikut kode OTP yang digunakan untuk Verifikasi Registrasi.</p>" , name )
htmlBody += fmt . Sprintf ( "<p style='padding: 10px 50px; background: #eef2f6; border-radius: 8px; max-width: 300px; text-align: center'><b>%s</b></p>" , otp )
htmlBody += "<p style='padding-top: 10px;'>Kode diatas hanya berlaku selama 10 menit. Harap segera masukkan kode tersebut pada aplikasi HUMAS POLRI.</p>"
htmlBody += "<p style='padding-top: 10px; padding-bottom: 10px'>Demi menjaga kerahasiaan data kamu, mohon jangan membagikan kode OTP ke siapapun.</p>"
err := _i . Smtp . SendEmail ( subject , email , name , htmlBody )
return err
}