Refactor SendPulse into shared service, wire app registration to Sojorn Members list (book 568122)
This commit is contained in:
parent
90ff6e223b
commit
2ad148f607
|
|
@ -1,11 +1,7 @@
|
||||||
package main
|
package main
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"bytes"
|
|
||||||
"context"
|
"context"
|
||||||
"encoding/json"
|
|
||||||
"fmt"
|
|
||||||
"io"
|
|
||||||
"net/http"
|
"net/http"
|
||||||
"os"
|
"os"
|
||||||
"os/signal"
|
"os/signal"
|
||||||
|
|
@ -116,6 +112,7 @@ func main() {
|
||||||
notificationService := services.NewNotificationService(notifRepo, pushService, userRepo)
|
notificationService := services.NewNotificationService(notifRepo, pushService, userRepo)
|
||||||
|
|
||||||
emailService := services.NewEmailService(cfg)
|
emailService := services.NewEmailService(cfg)
|
||||||
|
sendPulseService := services.NewSendPulseService(cfg.SendPulseID, cfg.SendPulseSecret)
|
||||||
|
|
||||||
// Load moderation configuration
|
// Load moderation configuration
|
||||||
moderationConfig := config.NewModerationConfig()
|
moderationConfig := config.NewModerationConfig()
|
||||||
|
|
@ -133,7 +130,7 @@ func main() {
|
||||||
userHandler := handlers.NewUserHandler(userRepo, postRepo, notificationService, assetService)
|
userHandler := handlers.NewUserHandler(userRepo, postRepo, notificationService, assetService)
|
||||||
postHandler := handlers.NewPostHandler(postRepo, userRepo, feedService, assetService, notificationService, moderationService, contentFilter)
|
postHandler := handlers.NewPostHandler(postRepo, userRepo, feedService, assetService, notificationService, moderationService, contentFilter)
|
||||||
chatHandler := handlers.NewChatHandler(chatRepo, notificationService, hub)
|
chatHandler := handlers.NewChatHandler(chatRepo, notificationService, hub)
|
||||||
authHandler := handlers.NewAuthHandler(userRepo, cfg, emailService)
|
authHandler := handlers.NewAuthHandler(userRepo, cfg, emailService, sendPulseService)
|
||||||
categoryHandler := handlers.NewCategoryHandler(categoryRepo)
|
categoryHandler := handlers.NewCategoryHandler(categoryRepo)
|
||||||
keyHandler := handlers.NewKeyHandler(userRepo)
|
keyHandler := handlers.NewKeyHandler(userRepo)
|
||||||
backupHandler := handlers.NewBackupHandler(repository.NewBackupRepository(dbPool))
|
backupHandler := handlers.NewBackupHandler(repository.NewBackupRepository(dbPool))
|
||||||
|
|
@ -200,10 +197,8 @@ func main() {
|
||||||
c.JSON(500, gin.H{"error": "Failed to join waitlist"})
|
c.JSON(500, gin.H{"error": "Failed to join waitlist"})
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
// Add to SendPulse in background
|
// Add to SendPulse waitlist in background
|
||||||
go func(email string) {
|
go sendPulseService.AddToWaitlist(req.Email)
|
||||||
addToSendPulse(cfg, email)
|
|
||||||
}(req.Email)
|
|
||||||
c.JSON(200, gin.H{"message": "You're on the list!"})
|
c.JSON(200, gin.H{"message": "You're on the list!"})
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
@ -466,55 +461,3 @@ func main() {
|
||||||
|
|
||||||
log.Info().Msg("Server exiting")
|
log.Info().Msg("Server exiting")
|
||||||
}
|
}
|
||||||
|
|
||||||
func addToSendPulse(cfg *config.Config, email string) {
|
|
||||||
if cfg.SendPulseID == "" || cfg.SendPulseSecret == "" {
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
// 1. Get OAuth token
|
|
||||||
tokenBody, _ := json.Marshal(map[string]string{
|
|
||||||
"grant_type": "client_credentials",
|
|
||||||
"client_id": cfg.SendPulseID,
|
|
||||||
"client_secret": cfg.SendPulseSecret,
|
|
||||||
})
|
|
||||||
tokenResp, err := http.Post("https://api.sendpulse.com/oauth/access_token", "application/json", bytes.NewReader(tokenBody))
|
|
||||||
if err != nil {
|
|
||||||
log.Error().Err(err).Msg("SendPulse: failed to get token")
|
|
||||||
return
|
|
||||||
}
|
|
||||||
defer tokenResp.Body.Close()
|
|
||||||
|
|
||||||
var tokenData struct {
|
|
||||||
AccessToken string `json:"access_token"`
|
|
||||||
}
|
|
||||||
if err := json.NewDecoder(tokenResp.Body).Decode(&tokenData); err != nil || tokenData.AccessToken == "" {
|
|
||||||
log.Error().Err(err).Msg("SendPulse: failed to parse token")
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
// 2. Add subscriber to Sojorn Waitlist (address book 568090)
|
|
||||||
subBody, _ := json.Marshal(map[string]interface{}{
|
|
||||||
"emails": []map[string]string{
|
|
||||||
{"email": email},
|
|
||||||
},
|
|
||||||
})
|
|
||||||
req, _ := http.NewRequest("POST", "https://api.sendpulse.com/addressbooks/568090/emails", bytes.NewReader(subBody))
|
|
||||||
req.Header.Set("Authorization", "Bearer "+tokenData.AccessToken)
|
|
||||||
req.Header.Set("Content-Type", "application/json")
|
|
||||||
|
|
||||||
resp, err := http.DefaultClient.Do(req)
|
|
||||||
if err != nil {
|
|
||||||
log.Error().Err(err).Msg("SendPulse: failed to add subscriber")
|
|
||||||
return
|
|
||||||
}
|
|
||||||
defer resp.Body.Close()
|
|
||||||
|
|
||||||
if resp.StatusCode >= 300 {
|
|
||||||
body, _ := io.ReadAll(resp.Body)
|
|
||||||
log.Error().Str("status", fmt.Sprintf("%d", resp.StatusCode)).Str("body", string(body)).Msg("SendPulse: add subscriber failed")
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
log.Info().Str("email", email).Msg("SendPulse: subscriber added to waitlist")
|
|
||||||
}
|
|
||||||
|
|
|
||||||
|
|
@ -23,13 +23,14 @@ import (
|
||||||
)
|
)
|
||||||
|
|
||||||
type AuthHandler struct {
|
type AuthHandler struct {
|
||||||
repo *repository.UserRepository
|
repo *repository.UserRepository
|
||||||
config *config.Config
|
config *config.Config
|
||||||
emailService *services.EmailService
|
emailService *services.EmailService
|
||||||
|
sendPulseService *services.SendPulseService
|
||||||
}
|
}
|
||||||
|
|
||||||
func NewAuthHandler(repo *repository.UserRepository, cfg *config.Config, emailService *services.EmailService) *AuthHandler {
|
func NewAuthHandler(repo *repository.UserRepository, cfg *config.Config, emailService *services.EmailService, sendPulseService *services.SendPulseService) *AuthHandler {
|
||||||
return &AuthHandler{repo: repo, config: cfg, emailService: emailService}
|
return &AuthHandler{repo: repo, config: cfg, emailService: emailService, sendPulseService: sendPulseService}
|
||||||
}
|
}
|
||||||
|
|
||||||
type RegisterRequest struct {
|
type RegisterRequest struct {
|
||||||
|
|
@ -154,6 +155,12 @@ func (h *AuthHandler) Register(c *gin.Context) {
|
||||||
log.Printf("[Auth] Failed to send email to %s: %v", req.Email, err)
|
log.Printf("[Auth] Failed to send email to %s: %v", req.Email, err)
|
||||||
}
|
}
|
||||||
}()
|
}()
|
||||||
|
|
||||||
|
// Add to SendPulse Members list if user opted into newsletter
|
||||||
|
if req.EmailNewsletter && h.sendPulseService != nil {
|
||||||
|
go h.sendPulseService.AddToMembers(req.Email)
|
||||||
|
}
|
||||||
|
|
||||||
c.JSON(http.StatusCreated, gin.H{
|
c.JSON(http.StatusCreated, gin.H{
|
||||||
"message": "Registration successful. Please verify your email to activate your account.",
|
"message": "Registration successful. Please verify your email to activate your account.",
|
||||||
"state": "verification_pending",
|
"state": "verification_pending",
|
||||||
|
|
|
||||||
96
go-backend/internal/services/sendpulse_service.go
Normal file
96
go-backend/internal/services/sendpulse_service.go
Normal file
|
|
@ -0,0 +1,96 @@
|
||||||
|
package services
|
||||||
|
|
||||||
|
import (
|
||||||
|
"bytes"
|
||||||
|
"encoding/json"
|
||||||
|
"fmt"
|
||||||
|
"io"
|
||||||
|
"net/http"
|
||||||
|
|
||||||
|
"github.com/rs/zerolog/log"
|
||||||
|
)
|
||||||
|
|
||||||
|
const (
|
||||||
|
SendPulseWaitlistBookID = 568090
|
||||||
|
SendPulseMembersBookID = 568122
|
||||||
|
)
|
||||||
|
|
||||||
|
type SendPulseService struct {
|
||||||
|
ClientID string
|
||||||
|
ClientSecret string
|
||||||
|
}
|
||||||
|
|
||||||
|
func NewSendPulseService(clientID, clientSecret string) *SendPulseService {
|
||||||
|
return &SendPulseService{ClientID: clientID, ClientSecret: clientSecret}
|
||||||
|
}
|
||||||
|
|
||||||
|
func (s *SendPulseService) getToken() (string, error) {
|
||||||
|
tokenBody, _ := json.Marshal(map[string]string{
|
||||||
|
"grant_type": "client_credentials",
|
||||||
|
"client_id": s.ClientID,
|
||||||
|
"client_secret": s.ClientSecret,
|
||||||
|
})
|
||||||
|
resp, err := http.Post("https://api.sendpulse.com/oauth/access_token", "application/json", bytes.NewReader(tokenBody))
|
||||||
|
if err != nil {
|
||||||
|
return "", err
|
||||||
|
}
|
||||||
|
defer resp.Body.Close()
|
||||||
|
|
||||||
|
var data struct {
|
||||||
|
AccessToken string `json:"access_token"`
|
||||||
|
}
|
||||||
|
if err := json.NewDecoder(resp.Body).Decode(&data); err != nil {
|
||||||
|
return "", err
|
||||||
|
}
|
||||||
|
if data.AccessToken == "" {
|
||||||
|
return "", fmt.Errorf("empty access token")
|
||||||
|
}
|
||||||
|
return data.AccessToken, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (s *SendPulseService) AddSubscriber(bookID int, email string) {
|
||||||
|
if s.ClientID == "" || s.ClientSecret == "" {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
token, err := s.getToken()
|
||||||
|
if err != nil {
|
||||||
|
log.Error().Err(err).Msg("SendPulse: failed to get token")
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
subBody, _ := json.Marshal(map[string]interface{}{
|
||||||
|
"emails": []map[string]string{
|
||||||
|
{"email": email},
|
||||||
|
},
|
||||||
|
})
|
||||||
|
url := fmt.Sprintf("https://api.sendpulse.com/addressbooks/%d/emails", bookID)
|
||||||
|
req, _ := http.NewRequest("POST", url, bytes.NewReader(subBody))
|
||||||
|
req.Header.Set("Authorization", "Bearer "+token)
|
||||||
|
req.Header.Set("Content-Type", "application/json")
|
||||||
|
|
||||||
|
resp, err := http.DefaultClient.Do(req)
|
||||||
|
if err != nil {
|
||||||
|
log.Error().Err(err).Int("book_id", bookID).Msg("SendPulse: failed to add subscriber")
|
||||||
|
return
|
||||||
|
}
|
||||||
|
defer resp.Body.Close()
|
||||||
|
|
||||||
|
if resp.StatusCode >= 300 {
|
||||||
|
body, _ := io.ReadAll(resp.Body)
|
||||||
|
log.Error().Int("status", resp.StatusCode).Str("body", string(body)).Int("book_id", bookID).Msg("SendPulse: add subscriber failed")
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
log.Info().Str("email", email).Int("book_id", bookID).Msg("SendPulse: subscriber added")
|
||||||
|
}
|
||||||
|
|
||||||
|
// AddToWaitlist adds an email to the Sojorn Waitlist
|
||||||
|
func (s *SendPulseService) AddToWaitlist(email string) {
|
||||||
|
s.AddSubscriber(SendPulseWaitlistBookID, email)
|
||||||
|
}
|
||||||
|
|
||||||
|
// AddToMembers adds an email to the Sojorn Members list
|
||||||
|
func (s *SendPulseService) AddToMembers(email string) {
|
||||||
|
s.AddSubscriber(SendPulseMembersBookID, email)
|
||||||
|
}
|
||||||
Loading…
Reference in a new issue