package postgres import ( "context" "database/sql" "encoding/json" "errors" "time" "github.com/rede5/gohorsejobs/backend/internal/core/domain/entity" ) type EmailRepository struct { db *sql.DB } func NewEmailRepository(db *sql.DB) *EmailRepository { return &EmailRepository{db: db} } // Templates func (r *EmailRepository) ListTemplates(ctx context.Context) ([]*entity.EmailTemplate, error) { query := `SELECT id, slug, subject, body_html, variables, created_at, updated_at FROM email_templates ORDER BY slug` rows, err := r.db.QueryContext(ctx, query) if err != nil { return nil, err } defer rows.Close() var templates []*entity.EmailTemplate for rows.Next() { t := &entity.EmailTemplate{} var varsJSON []byte if err := rows.Scan(&t.ID, &t.Slug, &t.Subject, &t.BodyHTML, &varsJSON, &t.CreatedAt, &t.UpdatedAt); err != nil { return nil, err } if len(varsJSON) > 0 { json.Unmarshal(varsJSON, &t.Variables) } templates = append(templates, t) } return templates, nil } func (r *EmailRepository) GetTemplate(ctx context.Context, slug string) (*entity.EmailTemplate, error) { query := `SELECT id, slug, subject, body_html, variables, created_at, updated_at FROM email_templates WHERE slug = $1` row := r.db.QueryRowContext(ctx, query, slug) t := &entity.EmailTemplate{} var varsJSON []byte err := row.Scan(&t.ID, &t.Slug, &t.Subject, &t.BodyHTML, &varsJSON, &t.CreatedAt, &t.UpdatedAt) if err != nil { if err == sql.ErrNoRows { return nil, nil } return nil, err } if len(varsJSON) > 0 { json.Unmarshal(varsJSON, &t.Variables) } return t, nil } func (r *EmailRepository) CreateTemplate(ctx context.Context, tmpl *entity.EmailTemplate) error { query := `INSERT INTO email_templates (slug, subject, body_html, variables, updated_at) VALUES ($1, $2, $3, $4, $5) RETURNING id, created_at` varsJSON, _ := json.Marshal(tmpl.Variables) if varsJSON == nil { varsJSON = []byte("[]") } return r.db.QueryRowContext(ctx, query, tmpl.Slug, tmpl.Subject, tmpl.BodyHTML, varsJSON, time.Now()).Scan(&tmpl.ID, &tmpl.CreatedAt) } func (r *EmailRepository) UpdateTemplate(ctx context.Context, tmpl *entity.EmailTemplate) error { query := `UPDATE email_templates SET subject=$1, body_html=$2, variables=$3, updated_at=$4 WHERE slug=$5` varsJSON, _ := json.Marshal(tmpl.Variables) if varsJSON == nil { varsJSON = []byte("[]") } res, err := r.db.ExecContext(ctx, query, tmpl.Subject, tmpl.BodyHTML, varsJSON, time.Now(), tmpl.Slug) if err != nil { return err } affected, _ := res.RowsAffected() if affected == 0 { return errors.New("template not found") } return nil } func (r *EmailRepository) DeleteTemplate(ctx context.Context, slug string) error { _, err := r.db.ExecContext(ctx, "DELETE FROM email_templates WHERE slug=$1", slug) return err } // Settings func (r *EmailRepository) GetSettings(ctx context.Context) (*entity.EmailSettings, error) { // We assume there's mostly one active setting, order by updated_at desc query := `SELECT id, provider, smtp_host, smtp_port, smtp_user, smtp_pass, smtp_secure, sender_name, sender_email, amqp_url, is_active, updated_at FROM email_settings WHERE is_active = true ORDER BY updated_at DESC LIMIT 1` row := r.db.QueryRowContext(ctx, query) s := &entity.EmailSettings{} err := row.Scan( &s.ID, &s.Provider, &s.SMTPHost, &s.SMTPPort, &s.SMTPUser, &s.SMTPPass, &s.SMTPSecure, &s.SenderName, &s.SenderEmail, &s.AMQPURL, &s.IsActive, &s.UpdatedAt, ) if err != nil { if err == sql.ErrNoRows { // Return default empty struct or nil return nil, nil } return nil, err } return s, nil } func (r *EmailRepository) UpdateSettings(ctx context.Context, s *entity.EmailSettings) error { // We can either update the existing row or Insert a new one (audit history). // Let's Insert a new one and set others to inactive if we want history, OR just update for simplicity as per requirement. // Migration 027 says "id UUID PRIMARY KEY". Let's try to update if ID exists, else insert. // Actually, GetSettings fetches the latest active. // Let's implement logical "Upsert active settings". // If ID is provided, update. If not, insert. if s.ID != "" { query := `UPDATE email_settings SET provider=$1, smtp_host=$2, smtp_port=$3, smtp_user=$4, smtp_pass=$5, smtp_secure=$6, sender_name=$7, sender_email=$8, amqp_url=$9, is_active=$10, updated_at=$11 WHERE id=$12` _, err := r.db.ExecContext(ctx, query, s.Provider, s.SMTPHost, s.SMTPPort, s.SMTPUser, s.SMTPPass, s.SMTPSecure, s.SenderName, s.SenderEmail, s.AMQPURL, s.IsActive, time.Now(), s.ID, ) return err } // Insert new query := `INSERT INTO email_settings ( provider, smtp_host, smtp_port, smtp_user, smtp_pass, smtp_secure, sender_name, sender_email, amqp_url, is_active) VALUES ($1, $2, $3, $4, $5, $6, $7, $8, $9, $10) RETURNING id` return r.db.QueryRowContext(ctx, query, s.Provider, s.SMTPHost, s.SMTPPort, s.SMTPUser, s.SMTPPass, s.SMTPSecure, s.SenderName, s.SenderEmail, s.AMQPURL, true, ).Scan(&s.ID) }