Files

85 lines
2.9 KiB
Go
Raw Permalink Normal View History

package repository
import (
"context"
"fmt"
"time"
"github.com/google/uuid"
"gorm.io/gorm"
)
// CustomerDevice is one install of the customer app that can receive push
// notifications.
type CustomerDevice struct {
CustomerID uuid.UUID
DeviceID string
Platform *string
FCMToken string
AppVersion *string
}
// CustomerDeviceRepository keeps the FCM tokens of the customer app.
type CustomerDeviceRepository interface {
// Register saves the device's current token. A token is only ever one customer's:
// registering it takes it away from whoever had it before on that phone, so they
// stop getting this customer's notifications.
Register(ctx context.Context, device CustomerDevice, now time.Time) error
// Unregister forgets a device, for instance on logout. An unknown device is not an error.
Unregister(ctx context.Context, customerID uuid.UUID, deviceID string) error
ListTokens(ctx context.Context, customerID uuid.UUID) ([]string, error)
}
type customerDeviceRepository struct {
db *gorm.DB
}
func NewCustomerDeviceRepository(db *gorm.DB) CustomerDeviceRepository {
return &customerDeviceRepository{db: db}
}
func (r *customerDeviceRepository) Register(ctx context.Context, d CustomerDevice, now time.Time) error {
return DBFromContext(ctx, r.db).WithContext(ctx).Transaction(func(tx *gorm.DB) error {
err := tx.Exec(`DELETE FROM customer_devices WHERE fcm_token = ? AND NOT (customer_id = ? AND device_id = ?)`,
d.FCMToken, d.CustomerID, d.DeviceID).Error
if err != nil {
return fmt.Errorf("failed to release FCM token: %w", err)
}
err = tx.Exec(`INSERT INTO customer_devices (customer_id, device_id, platform, fcm_token, app_version, last_active_at)
VALUES (?, ?, ?, ?, ?, ?)
ON CONFLICT (customer_id, device_id) DO UPDATE SET
platform = EXCLUDED.platform,
fcm_token = EXCLUDED.fcm_token,
app_version = EXCLUDED.app_version,
last_active_at = EXCLUDED.last_active_at,
updated_at = NOW()`,
d.CustomerID, d.DeviceID, d.Platform, d.FCMToken, d.AppVersion, now).Error
if err != nil {
return fmt.Errorf("failed to register customer device: %w", err)
}
return nil
})
}
func (r *customerDeviceRepository) Unregister(ctx context.Context, customerID uuid.UUID, deviceID string) error {
err := DBFromContext(ctx, r.db).WithContext(ctx).
Exec(`DELETE FROM customer_devices WHERE customer_id = ? AND device_id = ?`, customerID, deviceID).Error
if err != nil {
return fmt.Errorf("failed to unregister customer device: %w", err)
}
return nil
}
func (r *customerDeviceRepository) ListTokens(ctx context.Context, customerID uuid.UUID) ([]string, error) {
var tokens []string
err := DBFromContext(ctx, r.db).WithContext(ctx).
Table("customer_devices").
Where("customer_id = ? AND fcm_token <> ''", customerID).
Order("last_active_at DESC NULLS LAST").
Pluck("fcm_token", &tokens).Error
if err != nil {
return nil, fmt.Errorf("failed to list customer FCM tokens: %w", err)
}
return tokens, nil
}