Spaces:
Runtime error
Runtime error
Merge pull request #23 from orion-ai-project/dev
Browse filesfix: options database to key-based data structure
- controllers/auth_controller.go +0 -1
- models/entity/constant.go +1 -1
- models/entity/entity.go +10 -21
- provider/provider.go +1 -2
- repositories/auth_repository.go +30 -13
- repositories/connection_repositories.go +17 -20
- services/auth_service.go +19 -23
controllers/auth_controller.go
CHANGED
|
@@ -55,7 +55,6 @@ func (c *authController) Register(ctx *gin.Context) {
|
|
| 55 |
|
| 56 |
ctx.SetSameSite(http.SameSiteNoneMode)
|
| 57 |
ctx.SetCookie("RefreshToken", resp.RefreshToken, 3600*24*7, "/", "", true, true)
|
| 58 |
-
|
| 59 |
utils.SendResponse[dto.AuthResponse, any](ctx, nil, *resp, nil)
|
| 60 |
}
|
| 61 |
|
|
|
|
| 55 |
|
| 56 |
ctx.SetSameSite(http.SameSiteNoneMode)
|
| 57 |
ctx.SetCookie("RefreshToken", resp.RefreshToken, 3600*24*7, "/", "", true, true)
|
|
|
|
| 58 |
utils.SendResponse[dto.AuthResponse, any](ctx, nil, *resp, nil)
|
| 59 |
}
|
| 60 |
|
models/entity/constant.go
CHANGED
|
@@ -3,7 +3,7 @@ package models
|
|
| 3 |
const (
|
| 4 |
CONNECTION_SUCCESS_MESSAGE = "Connection initialized. Please scan the QR code in your terminal."
|
| 5 |
CONNECTION_INIT_SUCCESS = "Connection process initiated successfully"
|
| 6 |
-
QR_SCAN_INSTRUCTION = "Check
|
| 7 |
POSTGRES_DIALECT = "postgres"
|
| 8 |
WHATSAPP_STATUS_CONNECTED = "connected"
|
| 9 |
WHATSAPP_STATUS_DISCONNECTED = "disconnected"
|
|
|
|
| 3 |
const (
|
| 4 |
CONNECTION_SUCCESS_MESSAGE = "Connection initialized. Please scan the QR code in your terminal."
|
| 5 |
CONNECTION_INIT_SUCCESS = "Connection process initiated successfully"
|
| 6 |
+
QR_SCAN_INSTRUCTION = "Check Screen for QR code scanning"
|
| 7 |
POSTGRES_DIALECT = "postgres"
|
| 8 |
WHATSAPP_STATUS_CONNECTED = "connected"
|
| 9 |
WHATSAPP_STATUS_DISCONNECTED = "disconnected"
|
models/entity/entity.go
CHANGED
|
@@ -7,30 +7,19 @@ import (
|
|
| 7 |
"gorm.io/gorm"
|
| 8 |
)
|
| 9 |
|
| 10 |
-
type
|
| 11 |
ID uuid.UUID `gorm:"primaryKey;type:uuid;default:gen_random_uuid()" json:"id"`
|
| 12 |
-
|
| 13 |
-
|
|
|
|
|
|
|
|
|
|
| 14 |
IsActive bool `gorm:"default:false" json:"is_active"`
|
| 15 |
CreatedAt time.Time `json:"created_at"`
|
| 16 |
UpdatedAt time.Time `json:"updated_at"`
|
| 17 |
DeletedAt gorm.DeletedAt `gorm:"index" json:"deleted_at"`
|
| 18 |
}
|
| 19 |
|
| 20 |
-
func (WhatsAppAccount) TableName() string {
|
| 21 |
-
return "whatsapp_accounts"
|
| 22 |
-
}
|
| 23 |
-
|
| 24 |
-
type User struct {
|
| 25 |
-
ID uuid.UUID `gorm:"primaryKey;type:uuid;default:gen_random_uuid()" json:"id"`
|
| 26 |
-
Username string `gorm:"size:100;uniqueIndex;not null" json:"username"`
|
| 27 |
-
Password string `gorm:"not null" json:"-"`
|
| 28 |
-
Role string `gorm:"default:'user';not null" json:"role"`
|
| 29 |
-
CreatedAt time.Time `json:"created_at"`
|
| 30 |
-
UpdatedAt time.Time `json:"updated_at"`
|
| 31 |
-
DeletedAt gorm.DeletedAt `gorm:"index" json:"deleted_at"`
|
| 32 |
-
}
|
| 33 |
-
|
| 34 |
func (User) TableName() string {
|
| 35 |
return "users"
|
| 36 |
}
|
|
@@ -47,11 +36,11 @@ func (RefreshToken) TableName() string {
|
|
| 47 |
return "refresh_tokens"
|
| 48 |
}
|
| 49 |
|
| 50 |
-
type
|
| 51 |
-
|
| 52 |
-
|
| 53 |
}
|
| 54 |
|
| 55 |
-
func (
|
| 56 |
return "settings"
|
| 57 |
}
|
|
|
|
| 7 |
"gorm.io/gorm"
|
| 8 |
)
|
| 9 |
|
| 10 |
+
type User struct {
|
| 11 |
ID uuid.UUID `gorm:"primaryKey;type:uuid;default:gen_random_uuid()" json:"id"`
|
| 12 |
+
Username string `gorm:"size:100;uniqueIndex;not null" json:"username"`
|
| 13 |
+
Password string `gorm:"not null" json:"-"`
|
| 14 |
+
Role string `gorm:"default:'user';not null" json:"role"`
|
| 15 |
+
AccountName string `gorm:"size:100;default:'Default Device'" json:"account_name"`
|
| 16 |
+
JID string `gorm:"column:jid;size:255" json:"jid"`
|
| 17 |
IsActive bool `gorm:"default:false" json:"is_active"`
|
| 18 |
CreatedAt time.Time `json:"created_at"`
|
| 19 |
UpdatedAt time.Time `json:"updated_at"`
|
| 20 |
DeletedAt gorm.DeletedAt `gorm:"index" json:"deleted_at"`
|
| 21 |
}
|
| 22 |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 23 |
func (User) TableName() string {
|
| 24 |
return "users"
|
| 25 |
}
|
|
|
|
| 36 |
return "refresh_tokens"
|
| 37 |
}
|
| 38 |
|
| 39 |
+
type Setting struct {
|
| 40 |
+
Key string `gorm:"primaryKey;size:255" json:"key"`
|
| 41 |
+
Value string `gorm:"type:text" json:"value"`
|
| 42 |
}
|
| 43 |
|
| 44 |
+
func (Setting) TableName() string {
|
| 45 |
return "settings"
|
| 46 |
}
|
provider/provider.go
CHANGED
|
@@ -33,10 +33,9 @@ func NewAppProvider() AppProvider {
|
|
| 33 |
|
| 34 |
// Auto-Migrate Entities
|
| 35 |
_ = configProvider.ProvideDatabaseConfig().AutoMigrateAll(
|
| 36 |
-
&entity.WhatsAppAccount{},
|
| 37 |
&entity.User{},
|
| 38 |
&entity.RefreshToken{},
|
| 39 |
-
&entity.
|
| 40 |
)
|
| 41 |
|
| 42 |
return &appProvider{
|
|
|
|
| 33 |
|
| 34 |
// Auto-Migrate Entities
|
| 35 |
_ = configProvider.ProvideDatabaseConfig().AutoMigrateAll(
|
|
|
|
| 36 |
&entity.User{},
|
| 37 |
&entity.RefreshToken{},
|
| 38 |
+
&entity.Setting{},
|
| 39 |
)
|
| 40 |
|
| 41 |
return &appProvider{
|
repositories/auth_repository.go
CHANGED
|
@@ -18,7 +18,7 @@ type AuthRepository interface {
|
|
| 18 |
RevokeAllUserRefreshTokens(userID uuid.UUID) error
|
| 19 |
UpdateUser(user *entity.User) error
|
| 20 |
DeleteUser(id uuid.UUID) error
|
| 21 |
-
|
| 22 |
SetRegistrationEnabled(enabled bool) error
|
| 23 |
HasValidRefreshToken(userID uuid.UUID) (bool, error)
|
| 24 |
}
|
|
@@ -85,23 +85,40 @@ func (r *authRepository) DeleteUser(id uuid.UUID) error {
|
|
| 85 |
return r.db.Delete(&entity.User{}, id).Error
|
| 86 |
}
|
| 87 |
|
| 88 |
-
func (r *authRepository)
|
| 89 |
-
var
|
| 90 |
-
if err := r.db.First(&
|
| 91 |
-
|
| 92 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 93 |
}
|
| 94 |
-
return
|
| 95 |
}
|
| 96 |
|
| 97 |
func (r *authRepository) SetRegistrationEnabled(enabled bool) error {
|
| 98 |
-
|
| 99 |
-
if
|
| 100 |
-
|
| 101 |
-
return r.db.Create(&settings).Error
|
| 102 |
}
|
| 103 |
-
|
| 104 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 105 |
}
|
| 106 |
|
| 107 |
func (r *authRepository) HasValidRefreshToken(userID uuid.UUID) (bool, error) {
|
|
|
|
| 18 |
RevokeAllUserRefreshTokens(userID uuid.UUID) error
|
| 19 |
UpdateUser(user *entity.User) error
|
| 20 |
DeleteUser(id uuid.UUID) error
|
| 21 |
+
GetSetting(key string) (string, error)
|
| 22 |
SetRegistrationEnabled(enabled bool) error
|
| 23 |
HasValidRefreshToken(userID uuid.UUID) (bool, error)
|
| 24 |
}
|
|
|
|
| 85 |
return r.db.Delete(&entity.User{}, id).Error
|
| 86 |
}
|
| 87 |
|
| 88 |
+
func (r *authRepository) GetSetting(key string) (string, error) {
|
| 89 |
+
var setting entity.Setting
|
| 90 |
+
if err := r.db.Where("key = ?", key).First(&setting).Error; err != nil {
|
| 91 |
+
if err == gorm.ErrRecordNotFound {
|
| 92 |
+
// Return default "true" for registration_enabled if not found, to match original default behavior?
|
| 93 |
+
// The original default was true in struct tag.
|
| 94 |
+
if key == "registration_enabled" {
|
| 95 |
+
return "true", nil
|
| 96 |
+
}
|
| 97 |
+
return "", nil
|
| 98 |
+
}
|
| 99 |
+
return "", err
|
| 100 |
}
|
| 101 |
+
return setting.Value, nil
|
| 102 |
}
|
| 103 |
|
| 104 |
func (r *authRepository) SetRegistrationEnabled(enabled bool) error {
|
| 105 |
+
value := "true"
|
| 106 |
+
if !enabled {
|
| 107 |
+
value = "false"
|
|
|
|
| 108 |
}
|
| 109 |
+
|
| 110 |
+
setting := entity.Setting{
|
| 111 |
+
Key: "registration_enabled",
|
| 112 |
+
Value: value,
|
| 113 |
+
}
|
| 114 |
+
|
| 115 |
+
// Upsert: On conflict update value
|
| 116 |
+
var count int64
|
| 117 |
+
r.db.Model(&entity.Setting{}).Where("key = ?", "registration_enabled").Count(&count)
|
| 118 |
+
if count > 0 {
|
| 119 |
+
return r.db.Model(&entity.Setting{}).Where("key = ?", "registration_enabled").Update("value", value).Error
|
| 120 |
+
}
|
| 121 |
+
return r.db.Create(&setting).Error
|
| 122 |
}
|
| 123 |
|
| 124 |
func (r *authRepository) HasValidRefreshToken(userID uuid.UUID) (bool, error) {
|
repositories/connection_repositories.go
CHANGED
|
@@ -20,7 +20,7 @@ type ConnectionRepository interface {
|
|
| 20 |
InitializeClient(ctx context.Context, accountID uuid.UUID) (*whatsmeow.Client, <-chan whatsmeow.QRChannelItem, error)
|
| 21 |
UpdateAccountStatus(accountID uuid.UUID, jid string, isActive bool) error
|
| 22 |
DeleteDevice(accountID uuid.UUID) error
|
| 23 |
-
|
| 24 |
IsJIDUsedByOther(jid string, currentAccountID uuid.UUID) bool
|
| 25 |
}
|
| 26 |
|
|
@@ -49,21 +49,18 @@ func NewConnectionRepository(db *gorm.DB) (ConnectionRepository, error) {
|
|
| 49 |
}
|
| 50 |
|
| 51 |
func (r *connectionRepository) InitializeClient(ctx context.Context, accountID uuid.UUID) (*whatsmeow.Client, <-chan whatsmeow.QRChannelItem, error) {
|
| 52 |
-
var
|
| 53 |
|
| 54 |
-
|
| 55 |
-
|
| 56 |
-
|
| 57 |
-
}
|
| 58 |
-
if err := r.db.FirstOrCreate(&acc, entity.WhatsAppAccount{ID: accountID}).Error; err != nil {
|
| 59 |
-
return nil, nil, http_error.ERR_DB_CONNECTION_FAILED
|
| 60 |
}
|
| 61 |
|
| 62 |
var deviceStore *store.Device
|
| 63 |
var err error
|
| 64 |
|
| 65 |
-
if
|
| 66 |
-
jid, parseErr := types.ParseJID(
|
| 67 |
if parseErr != nil {
|
| 68 |
return nil, nil, http_error.ERR_INVALID_JID
|
| 69 |
}
|
|
@@ -88,20 +85,20 @@ func (r *connectionRepository) InitializeClient(ctx context.Context, accountID u
|
|
| 88 |
}
|
| 89 |
|
| 90 |
func (r *connectionRepository) UpdateAccountStatus(accountID uuid.UUID, jid string, isActive bool) error {
|
| 91 |
-
return r.db.Model(&entity.
|
| 92 |
"jid": jid,
|
| 93 |
"is_active": isActive,
|
| 94 |
}).Error
|
| 95 |
}
|
| 96 |
|
| 97 |
func (r *connectionRepository) DeleteDevice(accountID uuid.UUID) error {
|
| 98 |
-
var
|
| 99 |
-
if err := r.db.First(&
|
| 100 |
return err
|
| 101 |
}
|
| 102 |
|
| 103 |
-
if
|
| 104 |
-
jid, _ := types.ParseJID(
|
| 105 |
device, err := r.container.GetDevice(context.Background(), jid)
|
| 106 |
if err == nil && device != nil {
|
| 107 |
_ = device.Delete(context.Background())
|
|
@@ -111,17 +108,17 @@ func (r *connectionRepository) DeleteDevice(accountID uuid.UUID) error {
|
|
| 111 |
return r.UpdateAccountStatus(accountID, "", false)
|
| 112 |
}
|
| 113 |
|
| 114 |
-
func (r *connectionRepository)
|
| 115 |
-
var
|
| 116 |
-
if err := r.db.First(&
|
| 117 |
return nil, err
|
| 118 |
}
|
| 119 |
-
return &
|
| 120 |
}
|
| 121 |
|
| 122 |
func (r *connectionRepository) IsJIDUsedByOther(jid string, currentAccountID uuid.UUID) bool {
|
| 123 |
var count int64
|
| 124 |
-
r.db.Model(&entity.
|
| 125 |
Where("jid = ? AND id != ?", jid, currentAccountID).
|
| 126 |
Count(&count)
|
| 127 |
return count > 0
|
|
|
|
| 20 |
InitializeClient(ctx context.Context, accountID uuid.UUID) (*whatsmeow.Client, <-chan whatsmeow.QRChannelItem, error)
|
| 21 |
UpdateAccountStatus(accountID uuid.UUID, jid string, isActive bool) error
|
| 22 |
DeleteDevice(accountID uuid.UUID) error
|
| 23 |
+
FindUserByID(accountID uuid.UUID) (*entity.User, error)
|
| 24 |
IsJIDUsedByOther(jid string, currentAccountID uuid.UUID) bool
|
| 25 |
}
|
| 26 |
|
|
|
|
| 49 |
}
|
| 50 |
|
| 51 |
func (r *connectionRepository) InitializeClient(ctx context.Context, accountID uuid.UUID) (*whatsmeow.Client, <-chan whatsmeow.QRChannelItem, error) {
|
| 52 |
+
var user entity.User
|
| 53 |
|
| 54 |
+
// Check if user exists. accountID should be userID.
|
| 55 |
+
if err := r.db.First(&user, accountID).Error; err != nil {
|
| 56 |
+
return nil, nil, http_error.ERR_DB_CONNECTION_FAILED // Or user not found
|
|
|
|
|
|
|
|
|
|
| 57 |
}
|
| 58 |
|
| 59 |
var deviceStore *store.Device
|
| 60 |
var err error
|
| 61 |
|
| 62 |
+
if user.JID != "" {
|
| 63 |
+
jid, parseErr := types.ParseJID(user.JID)
|
| 64 |
if parseErr != nil {
|
| 65 |
return nil, nil, http_error.ERR_INVALID_JID
|
| 66 |
}
|
|
|
|
| 85 |
}
|
| 86 |
|
| 87 |
func (r *connectionRepository) UpdateAccountStatus(accountID uuid.UUID, jid string, isActive bool) error {
|
| 88 |
+
return r.db.Model(&entity.User{ID: accountID}).Updates(map[string]interface{}{
|
| 89 |
"jid": jid,
|
| 90 |
"is_active": isActive,
|
| 91 |
}).Error
|
| 92 |
}
|
| 93 |
|
| 94 |
func (r *connectionRepository) DeleteDevice(accountID uuid.UUID) error {
|
| 95 |
+
var user entity.User
|
| 96 |
+
if err := r.db.First(&user, accountID).Error; err != nil {
|
| 97 |
return err
|
| 98 |
}
|
| 99 |
|
| 100 |
+
if user.JID != "" {
|
| 101 |
+
jid, _ := types.ParseJID(user.JID)
|
| 102 |
device, err := r.container.GetDevice(context.Background(), jid)
|
| 103 |
if err == nil && device != nil {
|
| 104 |
_ = device.Delete(context.Background())
|
|
|
|
| 108 |
return r.UpdateAccountStatus(accountID, "", false)
|
| 109 |
}
|
| 110 |
|
| 111 |
+
func (r *connectionRepository) FindUserByID(accountID uuid.UUID) (*entity.User, error) {
|
| 112 |
+
var user entity.User
|
| 113 |
+
if err := r.db.First(&user, accountID).Error; err != nil {
|
| 114 |
return nil, err
|
| 115 |
}
|
| 116 |
+
return &user, nil
|
| 117 |
}
|
| 118 |
|
| 119 |
func (r *connectionRepository) IsJIDUsedByOther(jid string, currentAccountID uuid.UUID) bool {
|
| 120 |
var count int64
|
| 121 |
+
r.db.Model(&entity.User{}).
|
| 122 |
Where("jid = ? AND id != ?", jid, currentAccountID).
|
| 123 |
Count(&count)
|
| 124 |
return count > 0
|
services/auth_service.go
CHANGED
|
@@ -41,8 +41,8 @@ func NewAuthService(authRepo repositories.AuthRepository, connectionRepo reposit
|
|
| 41 |
|
| 42 |
func (s *authService) Register(req dto.RegisterRequest) (*dto.AuthResponse, error) {
|
| 43 |
// Check if registration is enabled
|
| 44 |
-
|
| 45 |
-
if
|
| 46 |
return nil, http_error.ERR_REGISTRATION_DISABLED
|
| 47 |
}
|
| 48 |
|
|
@@ -134,23 +134,19 @@ func (s *authService) GetMe(userID uuid.UUID) (*dto.MeResponse, error) {
|
|
| 134 |
}
|
| 135 |
|
| 136 |
resp := &dto.MeResponse{
|
| 137 |
-
UserID:
|
| 138 |
-
Username:
|
| 139 |
-
Role:
|
| 140 |
-
|
| 141 |
-
|
| 142 |
-
|
| 143 |
-
|
| 144 |
-
|
| 145 |
-
|
| 146 |
-
|
| 147 |
-
|
| 148 |
-
|
| 149 |
-
|
| 150 |
-
if len(parts) > 0 {
|
| 151 |
-
phoneUtils := strings.Split(parts[0], ":")
|
| 152 |
-
resp.Phone = phoneUtils[0]
|
| 153 |
-
}
|
| 154 |
}
|
| 155 |
}
|
| 156 |
|
|
@@ -203,9 +199,9 @@ func (s *authService) AssignRole(userID uuid.UUID, role string) error {
|
|
| 203 |
}
|
| 204 |
|
| 205 |
func (s *authService) IsRegisterEnabled() bool {
|
| 206 |
-
|
| 207 |
-
if
|
| 208 |
-
return true
|
| 209 |
}
|
| 210 |
-
return
|
| 211 |
}
|
|
|
|
| 41 |
|
| 42 |
func (s *authService) Register(req dto.RegisterRequest) (*dto.AuthResponse, error) {
|
| 43 |
// Check if registration is enabled
|
| 44 |
+
val, _ := s.authRepo.GetSetting("registration_enabled")
|
| 45 |
+
if val == "false" {
|
| 46 |
return nil, http_error.ERR_REGISTRATION_DISABLED
|
| 47 |
}
|
| 48 |
|
|
|
|
| 134 |
}
|
| 135 |
|
| 136 |
resp := &dto.MeResponse{
|
| 137 |
+
UserID: user.ID,
|
| 138 |
+
Username: user.Username,
|
| 139 |
+
Role: user.Role,
|
| 140 |
+
JID: user.JID,
|
| 141 |
+
AccountName: user.AccountName,
|
| 142 |
+
IsActive: user.IsActive,
|
| 143 |
+
}
|
| 144 |
+
|
| 145 |
+
if user.JID != "" {
|
| 146 |
+
parts := strings.Split(user.JID, "@")
|
| 147 |
+
if len(parts) > 0 {
|
| 148 |
+
phoneUtils := strings.Split(parts[0], ":")
|
| 149 |
+
resp.Phone = phoneUtils[0]
|
|
|
|
|
|
|
|
|
|
|
|
|
| 150 |
}
|
| 151 |
}
|
| 152 |
|
|
|
|
| 199 |
}
|
| 200 |
|
| 201 |
func (s *authService) IsRegisterEnabled() bool {
|
| 202 |
+
val, err := s.authRepo.GetSetting("registration_enabled")
|
| 203 |
+
if err != nil {
|
| 204 |
+
return true // Default to true if error fetching, or maybe false? Staying with logic of previous implementation which defaulted to true implicitly if not set.
|
| 205 |
}
|
| 206 |
+
return val == "true"
|
| 207 |
}
|