-
Notifications
You must be signed in to change notification settings - Fork 0
/
Copy pathapple.go
132 lines (118 loc) · 4.68 KB
/
apple.go
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
package push
import (
"context"
"errors"
"fmt"
"github.com/shitamachi/push-service/config"
"github.com/shitamachi/push-service/log"
"github.com/shitamachi/push-service/models"
"github.com/sideshow/apns2"
"github.com/sideshow/apns2/token"
"go.uber.org/zap"
"net/http"
"reflect"
"sync"
)
type ApplePushClient struct {
clients sync.Map
}
func NewApplePushClient() *ApplePushClient {
return &ApplePushClient{}
}
func InitApplePush(ctx context.Context, appConfig *config.AppConfig) {
for bundleID := range appConfig.ApplePushConfig.Items {
pushClientItem, err := NewApplePushClientItem(ctx, appConfig, bundleID)
if err != nil {
log.WithCtx(ctx).Error("InitApplePush: can not create apple push client", zap.String("bundle_id", bundleID))
continue
}
if GlobalApplePushClient == nil {
GlobalApplePushClient = NewApplePushClient()
}
log.WithCtx(ctx).Info("InitApplePush: create apple push client successfully", zap.String("bundle_id", bundleID))
GlobalApplePushClient.clients.Store(bundleID, pushClientItem)
}
}
func NewApplePushClientItem(ctx context.Context, appConfig *config.AppConfig, bundleID string) (*apns2.Client, error) {
pushConfigItem, ok := appConfig.ApplePushConfig.Items[bundleID]
if !ok {
return nil, NewWrappedError(fmt.Sprintf("init apple %s push client failed", bundleID), CanNotGetClientFromConfig)
}
authKey, err := token.AuthKeyFromBytes([]byte(pushConfigItem.AuthKey))
if err != nil {
log.WithCtx(ctx).Fatal("NewApplePushClient: get auth key from config failed", zap.Error(err))
return nil, err
}
appleToken := &token.Token{
AuthKey: authKey,
// KeyID from developer account (Certificates, Identifiers & Profiles -> Keys)
KeyID: pushConfigItem.KeyID,
// TeamID from developer account (View Account -> Membership)
TeamID: pushConfigItem.TeamID,
}
var client *apns2.Client
switch appConfig.Mode {
case "debug", "test":
client = apns2.NewTokenClient(appleToken).Development()
log.WithCtx(ctx).Info("NewApplePushClient: init development apple push client successfully", zap.String("bundle_id", pushConfigItem.BundleID))
case "release":
client = apns2.NewTokenClient(appleToken).Production()
log.WithCtx(ctx).Info("NewApplePushClient: init production apple push client successfully", zap.String("bundle_id", pushConfigItem.BundleID))
}
return client, nil
}
func (a *ApplePushClient) GetClientByAppID(ctx context.Context, appID string) (interface{}, bool) {
value, ok := a.clients.Load(appID)
if !ok {
log.WithCtx(ctx).Error("GetClientByAppID: can not get apple push client from global", zap.String("bundle_id", appID))
return nil, false
}
return value, true
}
func (a *ApplePushClient) Push(ctx context.Context, message *models.PushMessage) (interface{}, error) {
v, ok := a.GetClientByAppID(ctx, message.GetAppId())
if !ok || v == nil {
log.WithCtx(ctx).Error("ApplePush: can not get push client, value is nil or get operation not ok")
return nil, CanNotGetPushClient
}
client, ok := v.(*apns2.Client)
if !ok {
log.WithCtx(ctx).Error("ApplePush: got client value from global instance, but convert to *apns2.Client failed",
zap.String("type", reflect.TypeOf(client).String()))
return nil, NewWrappedError("convert to *apns2.Client failed", ConvertToSpecificPlatformClientFailed)
}
notification, ok := message.Build(ctx).(*apns2.Notification)
if !ok {
log.WithCtx(ctx).Error("ApplePush: got message ok, but convert to *apns2.Notification failed",
zap.String("type", reflect.TypeOf(notification).String()))
return nil, NewWrappedError("convert to *apns2.Notification failed", ConvertToSpecificPlatformMessageFailed)
}
rep, err := client.PushWithContext(ctx, notification)
if err != nil {
log.WithCtx(ctx).Error("ApplePush: push notification failed", zap.Error(err))
return nil, err
}
switch {
case rep.StatusCode == http.StatusOK:
log.WithCtx(ctx).Debug("ApplePush: send push message response ok", zap.Any("message", message))
return rep, nil
case rep.StatusCode != http.StatusOK:
log.WithCtx(ctx).Error("ApplePush: request send ok but apple response not ok",
zap.Int("code", rep.StatusCode),
zap.String("reason", rep.Reason),
)
return rep, NewWrappedError("apple push: request send ok but apple service response not ok", SendMessageResponseNotOk)
case errors.Is(err, context.DeadlineExceeded):
log.WithCtx(ctx).Warn("ApplePush: send message to apns timeout")
return nil, err
case err != nil:
log.WithCtx(ctx).Error("ApplePush: push notification failed", zap.Error(err))
return nil, err
default:
log.WithCtx(ctx).Warn("ApplePush: unknown push notification status",
zap.Any("apple_resp", rep),
zap.Any("message", message),
zap.Error(err))
return rep, fmt.Errorf("ApplePush: unknown push notification status")
}
}