91 lines
3.5 KiB
Go
91 lines
3.5 KiB
Go
|
|
package db
|
||
|
|
|
||
|
|
import (
|
||
|
|
"context"
|
||
|
|
"encoding/json"
|
||
|
|
|
||
|
|
"github.com/flatrender/notification-svc/internal/models"
|
||
|
|
"github.com/google/uuid"
|
||
|
|
"github.com/jackc/pgx/v5"
|
||
|
|
)
|
||
|
|
|
||
|
|
// ── Channel provider config ──────────────────────────────────────────────────
|
||
|
|
|
||
|
|
func (s *Store) GetChannelConfigs(ctx context.Context, tenantID uuid.UUID) ([]*models.ChannelConfig, error) {
|
||
|
|
rows, err := s.pool.Query(ctx,
|
||
|
|
`SELECT tenant_id, channel, settings, enabled, updated_at
|
||
|
|
FROM notification.channel_config WHERE tenant_id = $1 ORDER BY channel`, tenantID)
|
||
|
|
if err != nil {
|
||
|
|
return nil, err
|
||
|
|
}
|
||
|
|
defer rows.Close()
|
||
|
|
|
||
|
|
var out []*models.ChannelConfig
|
||
|
|
for rows.Next() {
|
||
|
|
c := &models.ChannelConfig{}
|
||
|
|
var raw []byte
|
||
|
|
if err := rows.Scan(&c.TenantID, &c.Channel, &raw, &c.Enabled, &c.UpdatedAt); err != nil {
|
||
|
|
return nil, err
|
||
|
|
}
|
||
|
|
_ = json.Unmarshal(raw, &c.Settings)
|
||
|
|
out = append(out, c)
|
||
|
|
}
|
||
|
|
return out, rows.Err()
|
||
|
|
}
|
||
|
|
|
||
|
|
func (s *Store) GetChannelConfig(ctx context.Context, tenantID uuid.UUID, channel string) (*models.ChannelConfig, error) {
|
||
|
|
c := &models.ChannelConfig{}
|
||
|
|
var raw []byte
|
||
|
|
err := s.pool.QueryRow(ctx,
|
||
|
|
`SELECT tenant_id, channel, settings, enabled, updated_at
|
||
|
|
FROM notification.channel_config WHERE tenant_id = $1 AND channel = $2`, tenantID, channel).
|
||
|
|
Scan(&c.TenantID, &c.Channel, &raw, &c.Enabled, &c.UpdatedAt)
|
||
|
|
if err == pgx.ErrNoRows {
|
||
|
|
return nil, nil
|
||
|
|
}
|
||
|
|
if err != nil {
|
||
|
|
return nil, err
|
||
|
|
}
|
||
|
|
_ = json.Unmarshal(raw, &c.Settings)
|
||
|
|
return c, nil
|
||
|
|
}
|
||
|
|
|
||
|
|
func (s *Store) UpsertChannelConfig(ctx context.Context, tenantID uuid.UUID, channel string, settings map[string]any, enabled bool) error {
|
||
|
|
raw, _ := json.Marshal(settings)
|
||
|
|
_, err := s.pool.Exec(ctx,
|
||
|
|
`INSERT INTO notification.channel_config (tenant_id, channel, settings, enabled, updated_at)
|
||
|
|
VALUES ($1, $2, $3, $4, NOW())
|
||
|
|
ON CONFLICT (tenant_id, channel)
|
||
|
|
DO UPDATE SET settings = EXCLUDED.settings, enabled = EXCLUDED.enabled, updated_at = NOW()`,
|
||
|
|
tenantID, channel, raw, enabled)
|
||
|
|
return err
|
||
|
|
}
|
||
|
|
|
||
|
|
// ── Email template lookup ────────────────────────────────────────────────────
|
||
|
|
|
||
|
|
func (s *Store) GetEmailTemplate(ctx context.Context, code, locale string) (*models.NotificationTemplate, error) {
|
||
|
|
t := &models.NotificationTemplate{}
|
||
|
|
err := s.pool.QueryRow(ctx,
|
||
|
|
`SELECT id, code, channel, locale, subject, body_text, body_html, is_active
|
||
|
|
FROM notification.notification_templates
|
||
|
|
WHERE code = $1 AND channel = 'Email' AND locale = $2 AND is_active = TRUE
|
||
|
|
LIMIT 1`, code, locale).
|
||
|
|
Scan(&t.ID, &t.Code, &t.Channel, &t.Locale, &t.Subject, &t.BodyText, &t.BodyHTML, &t.IsActive)
|
||
|
|
if err == pgx.ErrNoRows {
|
||
|
|
return nil, nil
|
||
|
|
}
|
||
|
|
return t, err
|
||
|
|
}
|
||
|
|
|
||
|
|
// ── Delivery log ─────────────────────────────────────────────────────────────
|
||
|
|
|
||
|
|
func (s *Store) LogDelivery(ctx context.Context, tenantID, userID uuid.UUID, channel, recipient string,
|
||
|
|
subject, provider, providerMsgID, status, errMsg *string) error {
|
||
|
|
_, err := s.pool.Exec(ctx,
|
||
|
|
`INSERT INTO notification.notification_deliveries
|
||
|
|
(tenant_id, user_id, channel, recipient, subject, provider, provider_message_id, status, error_message, sent_at)
|
||
|
|
VALUES ($1, $2, $3, $4, $5, $6, $7, $8, $9, NOW())`,
|
||
|
|
tenantID, userID, channel, recipient, subject, provider, providerMsgID, status, errMsg)
|
||
|
|
return err
|
||
|
|
}
|