@@ -13,14 +13,22 @@ import (
13
13
"os"
14
14
"path"
15
15
"strings"
16
+ "sync"
17
+
18
+ "github.com/gomodule/redigo/redis"
19
+ "github.com/kitabisa/smooch/storage"
16
20
)
17
21
18
22
var (
19
23
ErrUserIDEmpty = errors .New ("user id is empty" )
24
+ ErrKeyIDEmpty = errors .New ("key id is empty" )
25
+ ErrSecretEmpty = errors .New ("secret is empty" )
26
+ ErrRedisNil = errors .New ("redis pool is nil" )
20
27
ErrMessageNil = errors .New ("message is nil" )
21
28
ErrMessageRoleEmpty = errors .New ("message.Role is empty" )
22
29
ErrMessageTypeEmpty = errors .New ("message.Type is empty" )
23
30
ErrVerifySecretEmpty = errors .New ("verify secret is empty" )
31
+ ErrDecodeToken = errors .New ("error decode token" )
24
32
)
25
33
26
34
const (
@@ -46,12 +54,15 @@ type Options struct {
46
54
Logger Logger
47
55
Region string
48
56
HttpClient * http.Client
57
+ RedisPool * redis.Pool
49
58
}
50
59
51
60
type WebhookEventHandler func (payload * Payload )
52
61
53
62
type Client interface {
54
63
Handler () http.Handler
64
+ IsJWTExpired () (bool , error )
65
+ RenewToken () (string , error )
55
66
AddWebhookEventHandler (handler WebhookEventHandler )
56
67
Send (userID string , message * Message ) (* ResponsePayload , error )
57
68
VerifyRequest (r * http.Request ) bool
@@ -63,19 +74,34 @@ type Client interface {
63
74
type smoochClient struct {
64
75
mux * http.ServeMux
65
76
appID string
66
- jwtToken string
77
+ keyID string
78
+ secret string
67
79
verifySecret string
68
80
logger Logger
69
81
region string
70
82
webhookEventHandlers []WebhookEventHandler
71
83
httpClient * http.Client
84
+ mtx sync.Mutex
85
+ RedisStorage * storage.RedisStorage
72
86
}
73
87
74
88
func New (o Options ) (* smoochClient , error ) {
89
+ if o .KeyID == "" {
90
+ return nil , ErrKeyIDEmpty
91
+ }
92
+
93
+ if o .Secret == "" {
94
+ return nil , ErrSecretEmpty
95
+ }
96
+
75
97
if o .VerifySecret == "" {
76
98
return nil , ErrVerifySecretEmpty
77
99
}
78
100
101
+ if o .RedisPool == nil {
102
+ return nil , ErrRedisNil
103
+ }
104
+
79
105
if o .Mux == nil {
80
106
o .Mux = http .NewServeMux ()
81
107
}
@@ -101,19 +127,24 @@ func New(o Options) (*smoochClient, error) {
101
127
region = RegionEU
102
128
}
103
129
104
- jwtToken , err := GenerateJWT ("app" , o .KeyID , o .Secret )
105
- if err != nil {
106
- return nil , err
107
- }
108
-
109
130
sc := & smoochClient {
110
131
mux : o .Mux ,
111
132
appID : o .AppID ,
133
+ keyID : o .KeyID ,
134
+ secret : o .Secret ,
112
135
verifySecret : o .VerifySecret ,
113
136
logger : o .Logger ,
114
137
region : region ,
115
138
httpClient : o .HttpClient ,
116
- jwtToken : jwtToken ,
139
+ RedisStorage : storage .NewRedisStorage (o .RedisPool ),
140
+ }
141
+
142
+ _ , err := sc .RedisStorage .GetTokenFromRedis ()
143
+ if err != nil {
144
+ _ , err := sc .RenewToken ()
145
+ if err != nil {
146
+ return nil , err
147
+ }
117
148
}
118
149
119
150
sc .mux .HandleFunc (o .WebhookURL , sc .handle )
@@ -124,6 +155,36 @@ func (sc *smoochClient) Handler() http.Handler {
124
155
return sc .mux
125
156
}
126
157
158
+ // IsJWTExpired will check whether Smooch JWT is expired or not.
159
+ func (sc * smoochClient ) IsJWTExpired () (bool , error ) {
160
+ jwtToken , err := sc .RedisStorage .GetTokenFromRedis ()
161
+ if err != nil {
162
+ if err == redis .ErrNil {
163
+ return true , nil
164
+ }
165
+ return false , err
166
+ }
167
+ return isJWTExpired (jwtToken , sc .secret )
168
+ }
169
+
170
+ // RenewToken will generate new Smooch JWT token.
171
+ func (sc * smoochClient ) RenewToken () (string , error ) {
172
+ sc .mtx .Lock ()
173
+ defer sc .mtx .Unlock ()
174
+
175
+ jwtToken , err := GenerateJWT ("app" , sc .keyID , sc .secret )
176
+ if err != nil {
177
+ return "" , err
178
+ }
179
+
180
+ err = sc .RedisStorage .SaveTokenToRedis (jwtToken , JWTExpiration )
181
+ if err != nil {
182
+ return "" , err
183
+ }
184
+
185
+ return jwtToken , nil
186
+ }
187
+
127
188
func (sc * smoochClient ) AddWebhookEventHandler (handler WebhookEventHandler ) {
128
189
sc .webhookEventHandlers = append (sc .webhookEventHandlers , handler )
129
190
}
@@ -326,17 +387,37 @@ func (sc *smoochClient) createRequest(
326
387
buf * bytes.Buffer ,
327
388
header http.Header ) (* http.Request , error ) {
328
389
390
+ var req * http.Request
391
+ var err error
392
+ var jwtToken string
393
+
329
394
if header == nil {
330
395
header = http.Header {}
331
396
}
332
397
333
398
if header .Get (contentTypeHeaderKey ) == "" {
334
399
header .Set (contentTypeHeaderKey , contentTypeJSON )
335
400
}
336
- header .Set (authorizationHeaderKey , fmt .Sprintf ("Bearer %s" , sc .jwtToken ))
337
401
338
- var req * http.Request
339
- var err error
402
+ isExpired , err := sc .IsJWTExpired ()
403
+ if err != nil {
404
+ return nil , err
405
+ }
406
+
407
+ if isExpired {
408
+ jwtToken , err = sc .RenewToken ()
409
+ if err != nil {
410
+ return nil , err
411
+ }
412
+ } else {
413
+ jwtToken , err = sc .RedisStorage .GetTokenFromRedis ()
414
+ if err != nil {
415
+ return nil , err
416
+ }
417
+ }
418
+
419
+ header .Set (authorizationHeaderKey , fmt .Sprintf ("Bearer %s" , jwtToken ))
420
+
340
421
if buf == nil {
341
422
req , err = http .NewRequest (method , url , nil )
342
423
} else {
0 commit comments