Skip to content

Commit

Permalink
Merge pull request #11 from mattermost/add-native
Browse files Browse the repository at this point in the history
Add ability for push proxy to support multiple apps at the same time.
  • Loading branch information
coreyhulen authored Feb 27, 2017
2 parents c049bc1 + b88a26e commit 64af164
Show file tree
Hide file tree
Showing 430 changed files with 46,950 additions and 14,444 deletions.
6 changes: 5 additions & 1 deletion Makefile
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
.PHONY: all dist build-server package test clean run
.PHONY: all dist build-server package test clean run update-dependencies-after-release

GOPATH ?= $(GOPATH:)
GOFLAGS ?= $(GOFLAGS:)
Expand All @@ -21,6 +21,10 @@ all: dist

dist: | build-server test package

update-dependencies-after-release:
@echo Run this to updated the go lang dependencies after a major release
glide up

build-server: | .prebuild
@echo Building proxy push server

Expand Down
22 changes: 16 additions & 6 deletions config/mattermost-push-proxy.json
Original file line number Diff line number Diff line change
@@ -1,12 +1,22 @@

{
"ListenAddress": ":8066",
"ApplePushUseDevelopment": false,
"ApplePushCertPrivate": "",
"ApplePushCertPassword": "",
"ApplePushTopic": "com.mattermost.Mattermost",
"AndroidApiKey": "",
"ThrottlePerSec": 300,
"ThrottleMemoryStoreSize": 50000,
"ThrottleVaryByHeader": "X-Forwarded-For"
"ThrottleVaryByHeader": "X-Forwarded-For",
"ApplePushSettings": [
{
"Type" : "apple",
"ApplePushUseDevelopment": false,
"ApplePushCertPrivate": "",
"ApplePushCertPassword": "",
"ApplePushTopic": "com.mattermost.Mattermost"
}
],
"AndroidPushSettings": [
{
"Type" : "android",
"AndroidApiKey": ""
}
]
}
26 changes: 16 additions & 10 deletions glide.lock

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

12 changes: 9 additions & 3 deletions glide.yaml
Original file line number Diff line number Diff line change
@@ -1,10 +1,8 @@
package: github.com/mattermost/mattermost-push-proxy
import:
- package: github.com/alexjlockwood/gcm
- package: github.com/braintree/manners
version: 0.4.0
- package: github.com/gorilla/mux
version: v1.1
version: v1.3.0
- package: github.com/kyokomi/emoji
version: v1.4
- package: github.com/sideshow/apns2
Expand All @@ -27,3 +25,11 @@ import:
- package: golang.org/x/tools
subpackages:
- go/buildutil
- package: github.com/tylerb/graceful
version: v1.2.15
- package: github.com/gorilla/handlers
version: v1.2
- package: github.com/urfave/negroni
version: v0.2.0
- package: gopkg.in/tylerb/graceful.v1
version: v1.2.15
68 changes: 68 additions & 0 deletions server/android_notification_server.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,68 @@
// Copyright (c) 2015 Mattermost, Inc. All Rights Reserved.
// See License.txt for license information.

package server

import (
"fmt"

"github.com/alexjlockwood/gcm"
"github.com/kyokomi/emoji"
)

type AndroidNotificationServer struct {
AndroidPushSettings AndroidPushSettings
}

func NewAndroideNotificationServer(settings AndroidPushSettings) NotificationServer {
return &AndroidNotificationServer{AndroidPushSettings: settings}
}

func (me *AndroidNotificationServer) Initialize() bool {
LogInfo(fmt.Sprintf("Initializing anroid notificaiton server for type=%v", me.AndroidPushSettings.Type))

if len(me.AndroidPushSettings.AndroidApiKey) == 0 {
LogError("Android push notifications not configured. Mssing AndroidApiKey.")
return false
}

return true
}

func (me *AndroidNotificationServer) SendNotification(msg *PushNotification) PushResponse {
var data map[string]interface{}
if msg.Type == PUSH_TYPE_CLEAR {
data = map[string]interface{}{"type": PUSH_TYPE_CLEAR, "channel_id": msg.ChannelId, "team_id": msg.TeamId}
} else {
data = map[string]interface{}{"type": PUSH_TYPE_MESSAGE, "message": emoji.Sprint(msg.Message), "channel_id": msg.ChannelId, "channel_name": msg.ChannelName, "team_id": msg.TeamId}
}

regIDs := []string{msg.DeviceId}
gcmMsg := gcm.NewMessage(data, regIDs...)

sender := &gcm.Sender{ApiKey: me.AndroidPushSettings.AndroidApiKey}

if len(me.AndroidPushSettings.AndroidApiKey) > 0 {
LogInfo("Sending android push notification")
resp, err := sender.Send(gcmMsg, 2)

if err != nil {
LogError(fmt.Sprintf("Failed to send GCM push sid=%v did=%v err=%v", msg.ServerId, msg.DeviceId, err))
return NewErrorPushResponse("unknown transport error")
}

if resp.Failure > 0 {

LogError(fmt.Sprintf("Android response failure: %v", resp))

if len(resp.Results) > 0 && resp.Results[0].Error == "InvalidRegistration" {
return NewRemovePushResponse()

} else {
return NewErrorPushResponse("unknown send response error")
}
}
}

return NewOkPushResponse()
}
94 changes: 94 additions & 0 deletions server/apple_notification_server.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,94 @@
// Copyright (c) 2015 Mattermost, Inc. All Rights Reserved.
// See License.txt for license information.

package server

import (
"fmt"

"github.com/kyokomi/emoji"
apns "github.com/sideshow/apns2"
"github.com/sideshow/apns2/certificate"
"github.com/sideshow/apns2/payload"
)

type AppleNotificationServer struct {
ApplePushSettings ApplePushSettings
AppleClient *apns.Client
}

func NewAppleNotificationServer(settings ApplePushSettings) NotificationServer {
return &AppleNotificationServer{ApplePushSettings: settings}
}

func (me *AppleNotificationServer) Initialize() bool {
LogInfo(fmt.Sprintf("Initializing apple notificaiton server for type=%v", me.ApplePushSettings.Type))

if len(me.ApplePushSettings.ApplePushCertPrivate) > 0 {
appleCert, appleCertErr := certificate.FromPemFile(me.ApplePushSettings.ApplePushCertPrivate, me.ApplePushSettings.ApplePushCertPassword)
if appleCertErr != nil {
LogCritical(fmt.Sprintf("Failed to load the apple pem cert err=%v for type=%v", appleCertErr, me.ApplePushSettings.Type))
return false
}

if me.ApplePushSettings.ApplePushUseDevelopment {
me.AppleClient = apns.NewClient(appleCert).Development()
} else {
me.AppleClient = apns.NewClient(appleCert).Production()
}

return true
} else {
LogError(fmt.Sprintf("Apple push notifications not configured. Mssing ApplePushCertPrivate. for type=%v", me.ApplePushSettings.Type))
return false
}
}

func (me *AppleNotificationServer) SendNotification(msg *PushNotification) PushResponse {
notification := &apns.Notification{}
notification.DeviceToken = msg.DeviceId
payload := payload.NewPayload()
notification.Payload = payload
notification.Topic = me.ApplePushSettings.ApplePushTopic
payload.Badge(msg.Badge)

if msg.Type != PUSH_TYPE_CLEAR {
payload.Alert(emoji.Sprint(msg.Message))
payload.Category(msg.Category)
payload.Sound("default")
}

if len(msg.ChannelId) > 0 {
payload.Custom("channel_id", msg.ChannelId)
}

if len(msg.TeamId) > 0 {
payload.Custom("team_id", msg.TeamId)
}

if len(msg.ChannelName) > 0 {
payload.Custom("channel_name", msg.ChannelName)
}

if me.AppleClient != nil {
LogInfo("Sending apple push notification")
res, err := me.AppleClient.Push(notification)
if err != nil {
LogError(fmt.Sprintf("Failed to send apple push sid=%v did=%v err=%v", msg.ServerId, msg.DeviceId, err))
return NewErrorPushResponse("unknown transport error")
}

if !res.Sent() {
LogError(fmt.Sprintf("Failed to send apple push with res ApnsID=%v reason=%v code=%v", res.ApnsID, res.Reason, res.StatusCode))

if res.Reason == "BadDeviceToken" {
return NewRemovePushResponse()

} else {
return NewErrorPushResponse("unknown send response error")
}
}
}

return NewOkPushResponse()
}
18 changes: 14 additions & 4 deletions server/config_push_proxy.go
Original file line number Diff line number Diff line change
Expand Up @@ -11,14 +11,24 @@ import (

type ConfigPushProxy struct {
ListenAddress string
ThrottlePerSec int
ThrottleMemoryStoreSize int
ThrottleVaryByHeader string
ApplePushSettings []ApplePushSettings
AndroidPushSettings []AndroidPushSettings
}

type ApplePushSettings struct {
Type string
ApplePushUseDevelopment bool
ApplePushCertPrivate string
ApplePushCertPassword string
ApplePushTopic string
AndroidApiKey string
ThrottlePerSec int
ThrottleMemoryStoreSize int
ThrottleVaryByHeader string
}

type AndroidPushSettings struct {
Type string
AndroidApiKey string
}

var CfgPP *ConfigPushProxy = &ConfigPushProxy{}
Expand Down
57 changes: 57 additions & 0 deletions server/push_response.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,57 @@
// Copyright (c) 2015 Mattermost, Inc. All Rights Reserved.
// See License.txt for license information.

package server

import (
"encoding/json"
"io"
)

const (
PUSH_STATUS = "status"
PUSH_STATUS_OK = "OK"
PUSH_STATUS_FAIL = "FAIL"
PUSH_STATUS_REMOVE = "REMOVE"
PUSH_STATUS_ERROR_MSG = "error"
)

type PushResponse map[string]string

func NewOkPushResponse() PushResponse {
m := make(map[string]string)
m[PUSH_STATUS] = PUSH_STATUS_OK
return m
}

func NewRemovePushResponse() PushResponse {
m := make(map[string]string)
m[PUSH_STATUS] = PUSH_STATUS_REMOVE
return m
}

func NewErrorPushResponse(message string) PushResponse {
m := make(map[string]string)
m[PUSH_STATUS] = PUSH_STATUS_FAIL
m[PUSH_STATUS_ERROR_MSG] = message
return m
}

func (me *PushResponse) ToJson() string {
if b, err := json.Marshal(me); err != nil {
return ""
} else {
return string(b)
}
}

func PushResponseFromJson(data io.Reader) PushResponse {
decoder := json.NewDecoder(data)

var objmap PushResponse
if err := decoder.Decode(&objmap); err != nil {
return make(map[string]string)
} else {
return objmap
}
}
Loading

0 comments on commit 64af164

Please sign in to comment.