dukcapil/internal/processor/novu_processor.go
2025-09-08 12:24:37 +07:00

280 lines
6.8 KiB
Go

package processor
import (
"context"
"fmt"
"net/url"
"eslogad-be/internal/config"
"eslogad-be/internal/contract"
"eslogad-be/internal/entities"
novu "github.com/novuhq/go-novu/lib"
"github.com/google/uuid"
)
type NovuProcessor interface {
CreateSubscriber(ctx context.Context, user *entities.User) error
UpdateSubscriber(ctx context.Context, user *entities.User) error
DeleteSubscriber(ctx context.Context, userID uuid.UUID) error
CreateSubscriberFromContract(ctx context.Context, user *contract.UserResponse) error
BulkCreateSubscribers(ctx context.Context, users []*entities.User) error
SendLetterNotification(ctx context.Context, letterID uuid.UUID, recipientUserID uuid.UUID, subject string, body string) error
}
type NovuProcessorImpl struct {
client *novu.APIClient
config *config.NovuConfig
}
func NewNovuProcessor(cfg *config.NovuConfig) *NovuProcessorImpl {
if cfg.APIKey == "" {
return &NovuProcessorImpl{
client: nil,
config: cfg,
}
}
// Create Novu config with backend URL
novuConfig := &novu.Config{}
if cfg.BaseURL != "" {
backendURL, err := url.Parse(cfg.BaseURL)
if err == nil {
novuConfig.BackendURL = backendURL
}
}
client := novu.NewAPIClient(cfg.APIKey, novuConfig)
return &NovuProcessorImpl{
client: client,
config: cfg,
}
}
func (p *NovuProcessorImpl) CreateSubscriber(ctx context.Context, user *entities.User) error {
if p.client == nil {
return fmt.Errorf("novu client not initialized")
}
subscriberID := user.ID.String()
data := map[string]interface{}{
"userId": user.ID.String(),
"email": user.Email,
"isActive": user.IsActive,
"createdAt": user.CreatedAt,
}
if user.Departments != nil && len(user.Departments) > 0 {
depts := make([]map[string]interface{}, len(user.Departments))
for i, dept := range user.Departments {
depts[i] = map[string]interface{}{
"id": dept.ID.String(),
"name": dept.Name,
"code": dept.Code,
}
}
data["departments"] = depts
}
subscriber := novu.SubscriberPayload{
Email: user.Email,
FirstName: user.Name,
LastName: "",
Phone: "",
Avatar: "",
Data: data,
}
_, err := p.client.SubscriberApi.Identify(ctx, subscriberID, subscriber)
if err != nil {
return fmt.Errorf("failed to create subscriber: %w", err)
}
return nil
}
func (p *NovuProcessorImpl) UpdateSubscriber(ctx context.Context, user *entities.User) error {
if p.client == nil {
return fmt.Errorf("novu client not initialized")
}
subscriberID := user.ID.String()
data := map[string]interface{}{
"userId": user.ID.String(),
"email": user.Email,
"isActive": user.IsActive,
"updatedAt": user.UpdatedAt,
}
if user.Departments != nil && len(user.Departments) > 0 {
depts := make([]map[string]interface{}, len(user.Departments))
for i, dept := range user.Departments {
depts[i] = map[string]interface{}{
"id": dept.ID.String(),
"name": dept.Name,
"code": dept.Code,
}
}
data["departments"] = depts
}
updateData := novu.SubscriberPayload{
Email: user.Email,
FirstName: user.Name,
LastName: "",
Phone: "",
Avatar: "",
Data: data,
}
_, err := p.client.SubscriberApi.Update(ctx, subscriberID, updateData)
if err != nil {
return fmt.Errorf("failed to update subscriber: %w", err)
}
return nil
}
func (p *NovuProcessorImpl) DeleteSubscriber(ctx context.Context, userID uuid.UUID) error {
if p.client == nil {
return fmt.Errorf("novu client not initialized")
}
subscriberID := userID.String()
_, err := p.client.SubscriberApi.Delete(ctx, subscriberID)
if err != nil {
return fmt.Errorf("failed to delete subscriber: %w", err)
}
return nil
}
func (p *NovuProcessorImpl) CreateSubscriberFromContract(ctx context.Context, user *contract.UserResponse) error {
if p.client == nil {
return fmt.Errorf("novu client not initialized")
}
subscriberID := user.ID.String()
data := map[string]interface{}{
"userId": user.ID.String(),
"email": user.Email,
"isActive": user.IsActive,
"createdAt": user.CreatedAt,
}
if user.Roles != nil && len(user.Roles) > 0 {
roles := make([]map[string]interface{}, len(user.Roles))
for i, role := range user.Roles {
roles[i] = map[string]interface{}{
"id": role.ID.String(),
"name": role.Name,
"code": role.Code,
}
}
data["roles"] = roles
}
if user.DepartmentResponse != nil && len(user.DepartmentResponse) > 0 {
depts := make([]map[string]interface{}, len(user.DepartmentResponse))
for i, dept := range user.DepartmentResponse {
depts[i] = map[string]interface{}{
"id": dept.ID.String(),
"name": dept.Name,
"code": dept.Code,
}
}
data["departments"] = depts
}
subscriber := novu.SubscriberPayload{
Email: user.Email,
FirstName: user.Name,
LastName: "",
Phone: "",
Avatar: "",
Data: data,
}
_, err := p.client.SubscriberApi.Identify(ctx, subscriberID, subscriber)
if err != nil {
return fmt.Errorf("failed to create subscriber from contract: %w", err)
}
return nil
}
func (p *NovuProcessorImpl) BulkCreateSubscribers(ctx context.Context, users []*entities.User) error {
if p.client == nil {
return fmt.Errorf("novu client not initialized")
}
var lastErr error
successCount := 0
for _, user := range users {
err := p.CreateSubscriber(ctx, user)
if err != nil {
lastErr = err
continue
}
successCount++
}
if lastErr != nil && successCount == 0 {
return fmt.Errorf("failed to create any subscribers, last error: %w", lastErr)
}
if lastErr != nil {
return fmt.Errorf("created %d out of %d subscribers, last error: %w", successCount, len(users), lastErr)
}
return nil
}
func (p *NovuProcessorImpl) SendLetterNotification(ctx context.Context, letterID uuid.UUID, recipientUserID uuid.UUID, subject string, body string) error {
if p.client == nil {
return fmt.Errorf("novu client not initialized")
}
subscriberID := recipientUserID.String()
// Check if subscriber exists, create if not
_, err := p.client.SubscriberApi.Get(ctx, subscriberID)
if err != nil {
// Subscriber doesn't exist, create a basic one
subscriber := novu.SubscriberPayload{
Email: fmt.Sprintf("%s@placeholder.com", subscriberID),
}
_, err = p.client.SubscriberApi.Identify(ctx, subscriberID, subscriber)
if err != nil {
return fmt.Errorf("failed to ensure subscriber exists: %w", err)
}
}
// Prepare notification payload
url := fmt.Sprintf("en/apps/surat-menyurat/masuk-detail/%s", letterID.String())
payload := map[string]interface{}{
"subject": subject,
"body": body,
"url": url,
}
// Trigger the notification
triggerPayload := novu.ITriggerPayloadOptions{
To: subscriberID,
Payload: payload,
}
_, err = p.client.EventApi.Trigger(ctx, "notification-dashboard", triggerPayload)
if err != nil {
return fmt.Errorf("failed to send letter notification: %w", err)
}
return nil
}