package queries import ( "database/sql" "encoding/json" "fmt" "time" "github.com/Fimeg/RedFlag/aggregator-server/internal/models" "github.com/google/uuid" "github.com/jmoiron/sqlx" ) type SecuritySettingsQueries struct { db *sqlx.DB } func NewSecuritySettingsQueries(db *sqlx.DB) *SecuritySettingsQueries { return &SecuritySettingsQueries{db: db} } // GetSetting retrieves a specific security setting by category and key func (q *SecuritySettingsQueries) GetSetting(category, key string) (*models.SecuritySetting, error) { query := ` SELECT id, category, key, value, is_encrypted, created_at, updated_at, created_by, updated_by FROM security_settings WHERE category = $1 AND key = $2 ` var setting models.SecuritySetting err := q.db.Get(&setting, query, category, key) if err != nil { if err == sql.ErrNoRows { return nil, nil } return nil, fmt.Errorf("failed to get security setting: %w", err) } return &setting, nil } // GetAllSettings retrieves all security settings func (q *SecuritySettingsQueries) GetAllSettings() ([]models.SecuritySetting, error) { query := ` SELECT id, category, key, value, is_encrypted, created_at, updated_at, created_by, updated_by FROM security_settings ORDER BY category, key ` var settings []models.SecuritySetting err := q.db.Select(&settings, query) if err != nil { return nil, fmt.Errorf("failed to get all security settings: %w", err) } return settings, nil } // GetSettingsByCategory retrieves all settings for a specific category func (q *SecuritySettingsQueries) GetSettingsByCategory(category string) ([]models.SecuritySetting, error) { query := ` SELECT id, category, key, value, is_encrypted, created_at, updated_at, created_by, updated_by FROM security_settings WHERE category = $1 ORDER BY key ` var settings []models.SecuritySetting err := q.db.Select(&settings, query, category) if err != nil { return nil, fmt.Errorf("failed to get security settings by category: %w", err) } return settings, nil } // CreateSetting creates a new security setting func (q *SecuritySettingsQueries) CreateSetting(category, key string, value interface{}, isEncrypted bool, createdBy *uuid.UUID) (*models.SecuritySetting, error) { // Convert value to JSON string valueJSON, err := json.Marshal(value) if err != nil { return nil, fmt.Errorf("failed to marshal setting value: %w", err) } setting := &models.SecuritySetting{ ID: uuid.New(), Category: category, Key: key, Value: string(valueJSON), IsEncrypted: isEncrypted, CreatedAt: time.Now().UTC(), CreatedBy: createdBy, } query := ` INSERT INTO security_settings ( id, category, key, value, is_encrypted, created_at, created_by ) VALUES ( :id, :category, :key, :value, :is_encrypted, :created_at, :created_by ) RETURNING * ` rows, err := q.db.NamedQuery(query, setting) if err != nil { return nil, fmt.Errorf("failed to create security setting: %w", err) } defer rows.Close() if rows.Next() { var createdSetting models.SecuritySetting if err := rows.StructScan(&createdSetting); err != nil { return nil, fmt.Errorf("failed to scan created setting: %w", err) } return &createdSetting, nil } return nil, fmt.Errorf("failed to create security setting: no rows returned") } // UpdateSetting updates an existing security setting func (q *SecuritySettingsQueries) UpdateSetting(category, key string, value interface{}, updatedBy *uuid.UUID) (*models.SecuritySetting, *string, error) { // Get the old value first oldSetting, err := q.GetSetting(category, key) if err != nil { return nil, nil, fmt.Errorf("failed to get old setting: %w", err) } if oldSetting == nil { return nil, nil, fmt.Errorf("setting not found") } var oldValue *string if oldSetting != nil { oldValue = &oldSetting.Value } // Convert new value to JSON string valueJSON, err := json.Marshal(value) if err != nil { return nil, oldValue, fmt.Errorf("failed to marshal setting value: %w", err) } now := time.Now().UTC() query := ` UPDATE security_settings SET value = $1, updated_at = $2, updated_by = $3 WHERE category = $4 AND key = $5 RETURNING id, category, key, value, is_encrypted, created_at, updated_at, created_by, updated_by ` var updatedSetting models.SecuritySetting err = q.db.QueryRow(query, string(valueJSON), now, updatedBy, category, key).Scan( &updatedSetting.ID, &updatedSetting.Category, &updatedSetting.Key, &updatedSetting.Value, &updatedSetting.IsEncrypted, &updatedSetting.CreatedAt, &updatedSetting.UpdatedAt, &updatedSetting.CreatedBy, &updatedSetting.UpdatedBy, ) if err != nil { return nil, oldValue, fmt.Errorf("failed to update security setting: %w", err) } return &updatedSetting, oldValue, nil } // DeleteSetting deletes a security setting func (q *SecuritySettingsQueries) DeleteSetting(category, key string) (*string, error) { // Get the old value first oldSetting, err := q.GetSetting(category, key) if err != nil { return nil, fmt.Errorf("failed to get old setting: %w", err) } if oldSetting == nil { return nil, nil } query := ` DELETE FROM security_settings WHERE category = $1 AND key = $2 RETURNING value ` var oldValue string err = q.db.QueryRow(query, category, key).Scan(&oldValue) if err != nil { if err == sql.ErrNoRows { return nil, nil } return nil, fmt.Errorf("failed to delete security setting: %w", err) } return &oldValue, nil } // CreateAuditLog creates an audit log entry for setting changes func (q *SecuritySettingsQueries) CreateAuditLog(settingID, userID uuid.UUID, action, oldValue, newValue, reason string) error { audit := &models.SecuritySettingAudit{ ID: uuid.New(), SettingID: settingID, UserID: userID, Action: action, OldValue: &oldValue, NewValue: &newValue, Reason: reason, CreatedAt: time.Now().UTC(), } // Handle null values for old/new values if oldValue == "" { audit.OldValue = nil } if newValue == "" { audit.NewValue = nil } query := ` INSERT INTO security_setting_audit ( id, setting_id, user_id, action, old_value, new_value, reason, created_at ) VALUES ( :id, :setting_id, :user_id, :action, :old_value, :new_value, :reason, :created_at ) ` _, err := q.db.NamedExec(query, audit) if err != nil { return fmt.Errorf("failed to create audit log: %w", err) } return nil } // GetAuditLogs retrieves audit logs for a setting func (q *SecuritySettingsQueries) GetAuditLogs(category, key string, limit int) ([]models.SecuritySettingAudit, error) { query := ` SELECT sa.id, sa.setting_id, sa.user_id, sa.action, sa.old_value, sa.new_value, sa.reason, sa.created_at FROM security_setting_audit sa INNER JOIN security_settings s ON sa.setting_id = s.id WHERE s.category = $1 AND s.key = $2 ORDER BY sa.created_at DESC LIMIT $3 ` var audits []models.SecuritySettingAudit err := q.db.Select(&audits, query, category, key, limit) if err != nil { return nil, fmt.Errorf("failed to get audit logs: %w", err) } return audits, nil }