package services import ( "context" "database/sql" "encoding/json" "fmt" "log" amqp "github.com/rabbitmq/amqp091-go" ) type EmailService struct { db *sql.DB credentialsService *CredentialsService } func NewEmailService(db *sql.DB, cs *CredentialsService) *EmailService { return &EmailService{ db: db, credentialsService: cs, } // Ensure return pointer matches } type EmailJob struct { To string `json:"to"` Template string `json:"template"` // slug Variables map[string]interface{} `json:"variables"` } // SendTemplateEmail queues an email via RabbitMQ func (s *EmailService) SendTemplateEmail(ctx context.Context, to, templateSlug string, variables map[string]interface{}) error { // 1. Get AMQP URL from email_settings var amqpURL sql.NullString err := s.db.QueryRowContext(ctx, "SELECT amqp_url FROM email_settings LIMIT 1").Scan(&amqpURL) if err != nil && err != sql.ErrNoRows { log.Printf("[EmailService] Failed to fetch AMQP URL: %v", err) } url := "" if amqpURL.Valid { url = amqpURL.String } if url == "" { // Log but don't error hard if just testing, but for "system" we need IT. // Return error so caller knows. return fmt.Errorf("AMQP URL not configured in email_settings") } // 2. Connect & Publish conn, err := amqp.Dial(url) if err != nil { return fmt.Errorf("failed to connect to RabbitMQ: %w", err) } defer conn.Close() ch, err := conn.Channel() if err != nil { return fmt.Errorf("failed to open channel: %w", err) } defer ch.Close() q, err := ch.QueueDeclare( "mail_queue", // name true, // durable false, // delete when unused false, // exclusive false, // no-wait nil, // arguments ) if err != nil { return fmt.Errorf("failed to declare queue: %w", err) } job := EmailJob{To: to, Template: templateSlug, Variables: variables} body, _ := json.Marshal(job) err = ch.PublishWithContext(ctx, "", // exchange q.Name, // routing key false, // mandatory false, // immediate amqp.Publishing{ ContentType: "application/json", Body: body, }) if err != nil { return fmt.Errorf("failed to publish message: %w", err) } log.Printf("[EmailService] Queued email to %s (Template: %s)", to, templateSlug) return nil }