(Feat): Initial Commit
Some checks failed
Build & Test / build-test (push) Has been cancelled
Build & Test / swagger-codegen-cli (push) Has been cancelled
CodeQL / Analyze (go) (push) Has been cancelled

This commit is contained in:
2026-07-03 19:41:31 +05:30
commit 7e940c83a7
461 changed files with 45002 additions and 0 deletions

107
internal/push/service.go Normal file
View File

@@ -0,0 +1,107 @@
package push
import (
"context"
"database/sql"
"errors"
"fmt"
"allaboutapps.dev/aw/go-starter/internal/data/dto"
"allaboutapps.dev/aw/go-starter/internal/models"
"allaboutapps.dev/aw/go-starter/internal/util"
)
type ProviderType string
const (
ProviderTypeFCM ProviderType = "fcm"
ProviderTypeAPN ProviderType = "apn"
)
type Service struct {
DB *sql.DB
provider map[ProviderType]Provider
}
type ProviderSendResponse struct {
// token the message was sent to
Token string
// flag to indicate if the token is still valid
// not every error means that the token is invalid
Valid bool
// ogiginal error
Err error
}
type Provider interface {
Send(token string, title string, message string) ProviderSendResponse
SendMulticast(tokens []string, title, message string) []ProviderSendResponse
GetProviderType() ProviderType
}
func New(db *sql.DB) *Service {
return &Service{
DB: db,
provider: make(map[ProviderType]Provider),
}
}
func (s *Service) RegisterProvider(p Provider) {
s.provider[p.GetProviderType()] = p
}
func (s *Service) ResetProviders() {
s.provider = make(map[ProviderType]Provider)
}
func (s *Service) GetProviderCount() int {
return len(s.provider)
}
func (s *Service) SendToUser(ctx context.Context, user *dto.User, title string, message string) error {
if s.GetProviderCount() < 1 {
return errors.New("no provider found")
}
log := util.LogFromContext(ctx)
for providerType, provider := range s.provider {
// get all registered tokens for provider
pushTokens, err := models.PushTokens(
models.PushTokenWhere.Provider.EQ(string(providerType)),
models.PushTokenWhere.UserID.EQ(user.ID),
).All(ctx, s.DB)
if err != nil {
return fmt.Errorf("failed to get push tokens: %w", err)
}
var tokens []string
for _, token := range pushTokens {
tokens = append(tokens, token.Token)
}
responseSlice := provider.SendMulticast(tokens, title, message)
tokenToDelete := make([]string, 0)
for _, res := range responseSlice {
if res.Err != nil && res.Valid {
log.Debug().Err(res.Err).Str("token", res.Token).Str("provider", string(provider.GetProviderType())).Msgf("Error while sending push message to provider with valid token.")
}
if !res.Valid {
tokenToDelete = append(tokenToDelete, res.Token)
}
}
// delete invalid tokens
_, err = models.PushTokens(
models.PushTokenWhere.Token.IN(tokenToDelete),
models.PushTokenWhere.UserID.EQ(user.ID),
).DeleteAll(ctx, s.DB)
if err != nil {
log.Debug().Err(err).Str("provider", string(provider.GetProviderType())).Msg("Could not delete invalid tokens for provider")
return err
}
}
return nil
}