From 68765a8f21db8a1b732f26f5bc1bf0e1a8e52d8f Mon Sep 17 00:00:00 2001 From: =Corey Hulen Date: Mon, 30 Nov 2015 22:38:38 -0800 Subject: PLT-902 switching to push proxy server --- api/post.go | 16 +++++- config/config.json | 5 +- mattermost.go | 26 ++++++--- model/config.go | 18 ++++-- model/push_notification.go | 45 +++++++++++++++ model/push_notification_test.go | 19 ++++++ utils/apns.go | 37 ------------ utils/config.go | 1 + .../components/admin_console/email_settings.jsx | 67 +++++++++++++++++++++- 9 files changed, 178 insertions(+), 56 deletions(-) create mode 100644 model/push_notification.go create mode 100644 model/push_notification_test.go delete mode 100644 utils/apns.go diff --git a/api/post.go b/api/post.go index 88d0127d3..81cc9a1c6 100644 --- a/api/post.go +++ b/api/post.go @@ -536,7 +536,7 @@ func sendNotificationsAndForget(c *Context, post *model.Post, team *model.Team, l4g.Error("Failed to send mention email successfully email=%v err=%v", profileMap[id].Email, err) } - if len(utils.Cfg.EmailSettings.ApplePushServer) > 0 { + if *utils.Cfg.EmailSettings.SendPushNotifications { sessionChan := Srv.Store.Session().GetSessions(id) if result := <-sessionChan; result.Err != nil { l4g.Error("Failed to retrieve sessions in notifications id=%v, err=%v", id, result.Err) @@ -548,7 +548,19 @@ func sendNotificationsAndForget(c *Context, post *model.Post, team *model.Team, if len(session.DeviceId) > 0 && alreadySeen[session.DeviceId] == "" && strings.HasPrefix(session.DeviceId, "apple:") { alreadySeen[session.DeviceId] = session.DeviceId - utils.SendAppleNotifyAndForget(strings.TrimPrefix(session.DeviceId, "apple:"), subjectPage.Render(), 1) + msg := model.PushNotification{} + msg.Platform = model.PUSH_NOTIFY_APPLE + msg.Message = subjectPage.Render() + msg.Badge = 1 + msg.DeviceId = strings.TrimPrefix(session.DeviceId, "apple:") + msg.ServerId = utils.CfgDiagnosticId + + httpClient := http.Client{} + request, _ := http.NewRequest("POST", *utils.Cfg.EmailSettings.PushNotificationServer+"/api/v1/send_push", strings.NewReader(msg.ToJson())) + + if _, err := httpClient.Do(request); err != nil { + l4g.Error("Failed to send push notificationid=%v, err=%v", id, err) + } } } } diff --git a/config/config.json b/config/config.json index 932bed8a2..999ea8a83 100644 --- a/config/config.json +++ b/config/config.json @@ -68,9 +68,8 @@ "ConnectionSecurity": "", "InviteSalt": "bjlSR4QqkXFBr7TP4oDzlfZmcNuH9YoS", "PasswordResetSalt": "vZ4DcKyVVRlKHHJpexcuXzojkE5PZ5eL", - "ApplePushServer": "", - "ApplePushCertPublic": "", - "ApplePushCertPrivate": "" + "SendPushNotifications": true, + "PushNotificationServer": "https://push.mattermost.com" }, "RateLimitSettings": { "EnableRateLimiter": true, diff --git a/mattermost.go b/mattermost.go index 2d5727400..eaab1de88 100644 --- a/mattermost.go +++ b/mattermost.go @@ -68,6 +68,7 @@ func main() { manualtesting.InitManualTesting() } + setDiagnosticId() runSecurityAndDiagnosticsJobAndForget() // wait for kill signal before attempting to gracefully shutdown @@ -80,6 +81,21 @@ func main() { } } +func setDiagnosticId() { + if result := <-api.Srv.Store.System().Get(); result.Err == nil { + props := result.Data.(model.StringMap) + + id := props[model.SYSTEM_DIAGNOSTIC_ID] + if len(id) == 0 { + id = model.NewId() + systemId := &model.System{Name: model.SYSTEM_DIAGNOSTIC_ID, Value: id} + <-api.Srv.Store.System().Save(systemId) + } + + utils.CfgDiagnosticId = id + } +} + func runSecurityAndDiagnosticsJobAndForget() { go func() { for { @@ -92,15 +108,9 @@ func runSecurityAndDiagnosticsJobAndForget() { if (currentTime - lastSecurityTime) > 1000*60*60*24*1 { l4g.Debug("Checking for security update from Mattermost") - id := props[model.SYSTEM_DIAGNOSTIC_ID] - if len(id) == 0 { - id = model.NewId() - systemId := &model.System{Name: model.SYSTEM_DIAGNOSTIC_ID, Value: id} - <-api.Srv.Store.System().Save(systemId) - } - v := url.Values{} - v.Set(utils.PROP_DIAGNOSTIC_ID, id) + + v.Set(utils.PROP_DIAGNOSTIC_ID, utils.CfgDiagnosticId) v.Set(utils.PROP_DIAGNOSTIC_BUILD, model.CurrentVersion+"."+model.BuildNumber) v.Set(utils.PROP_DIAGNOSTIC_DATABASE, utils.Cfg.SqlSettings.DriverName) v.Set(utils.PROP_DIAGNOSTIC_OS, runtime.GOOS) diff --git a/model/config.go b/model/config.go index 50a8dc133..195cefae8 100644 --- a/model/config.go +++ b/model/config.go @@ -96,11 +96,8 @@ type EmailSettings struct { ConnectionSecurity string InviteSalt string PasswordResetSalt string - - // For Future Use - ApplePushServer string - ApplePushCertPublic string - ApplePushCertPrivate string + SendPushNotifications *bool + PushNotificationServer *string } type RateLimitSettings struct { @@ -181,6 +178,17 @@ func (o *Config) SetDefaults() { o.TeamSettings.EnableTeamListing = new(bool) *o.TeamSettings.EnableTeamListing = false } + + if o.EmailSettings.SendPushNotifications == nil { + o.EmailSettings.SendPushNotifications = new(bool) + *o.EmailSettings.SendPushNotifications = true + } + + if o.EmailSettings.PushNotificationServer == nil { + o.EmailSettings.PushNotificationServer = new(string) + *o.EmailSettings.PushNotificationServer = "https://push.mattermost.com" + } + } func (o *Config) IsValid() *AppError { diff --git a/model/push_notification.go b/model/push_notification.go new file mode 100644 index 000000000..76f5bd125 --- /dev/null +++ b/model/push_notification.go @@ -0,0 +1,45 @@ +// Copyright (c) 2015 Mattermost, Inc. All Rights Reserved. +// See License.txt for license information. + +package model + +import ( + "encoding/json" + "io" +) + +const ( + PUSH_NOTIFY_APPLE = "apple" + PUSH_NOTIFY_ANDROID = "android" +) + +type PushNotification struct { + Platform string `json:"platform"` + ServerId string `json:"server_id"` + DeviceId string `json:"device_id"` + Category string `json:"category"` + Sound string `json:"sound"` + Message string `json:"message"` + Badge int `json:"badge"` + ContentAvailable int `json:"cont_ava"` +} + +func (me *PushNotification) ToJson() string { + b, err := json.Marshal(me) + if err != nil { + return "" + } else { + return string(b) + } +} + +func PushNotificationFromJson(data io.Reader) *PushNotification { + decoder := json.NewDecoder(data) + var me PushNotification + err := decoder.Decode(&me) + if err == nil { + return &me + } else { + return nil + } +} diff --git a/model/push_notification_test.go b/model/push_notification_test.go new file mode 100644 index 000000000..94329f389 --- /dev/null +++ b/model/push_notification_test.go @@ -0,0 +1,19 @@ +// Copyright (c) 2015 Mattermost, Inc. All Rights Reserved. +// See License.txt for license information. + +package model + +import ( + "strings" + "testing" +) + +func TestPushNotification(t *testing.T) { + msg := PushNotification{Platform: "test"} + json := msg.ToJson() + result := PushNotificationFromJson(strings.NewReader(json)) + + if msg.Platform != result.Platform { + t.Fatal("Ids do not match") + } +} diff --git a/utils/apns.go b/utils/apns.go deleted file mode 100644 index 06e8ce6ef..000000000 --- a/utils/apns.go +++ /dev/null @@ -1,37 +0,0 @@ -// Copyright (c) 2015 Mattermost, Inc. All Rights Reserved. -// See License.txt for license information. - -package utils - -import ( - l4g "code.google.com/p/log4go" - "fmt" - "github.com/anachronistic/apns" - "github.com/mattermost/platform/model" -) - -func SendAppleNotifyAndForget(deviceId string, message string, badge int) { - go func() { - if err := SendAppleNotify(deviceId, message, badge); err != nil { - l4g.Error(fmt.Sprintf("%v %v", err.Message, err.DetailedError)) - } - }() -} - -func SendAppleNotify(deviceId string, message string, badge int) *model.AppError { - payload := apns.NewPayload() - payload.Alert = message - payload.Badge = 1 - - pn := apns.NewPushNotification() - pn.DeviceToken = deviceId - pn.AddPayload(payload) - client := apns.BareClient(Cfg.EmailSettings.ApplePushServer, Cfg.EmailSettings.ApplePushCertPublic, Cfg.EmailSettings.ApplePushCertPrivate) - resp := client.Send(pn) - - if resp.Error != nil { - return model.NewAppError("", "Could not send apple push notification", fmt.Sprintf("id=%v err=%v", deviceId, resp.Error)) - } else { - return nil - } -} diff --git a/utils/config.go b/utils/config.go index 2fd799cd1..0b292a2ca 100644 --- a/utils/config.go +++ b/utils/config.go @@ -24,6 +24,7 @@ const ( ) var Cfg *model.Config = &model.Config{} +var CfgDiagnosticId = "" var CfgLastModified int64 = 0 var CfgFileName string = "" var ClientCfg map[string]string = map[string]string{} diff --git a/web/react/components/admin_console/email_settings.jsx b/web/react/components/admin_console/email_settings.jsx index d0565a0e0..238ace3da 100644 --- a/web/react/components/admin_console/email_settings.jsx +++ b/web/react/components/admin_console/email_settings.jsx @@ -18,6 +18,7 @@ export default class EmailSettings extends React.Component { this.state = { sendEmailNotifications: this.props.config.EmailSettings.SendEmailNotifications, + sendPushNotifications: this.props.config.EmailSettings.SendPushNotifications, saveNeeded: false, serverError: null, emailSuccess: null, @@ -36,6 +37,14 @@ export default class EmailSettings extends React.Component { s.sendEmailNotifications = false; } + if (action === 'sendPushNotifications_true') { + s.sendPushNotifications = true; + } + + if (action === 'sendPushNotifications_false') { + s.sendPushNotifications = false; + } + this.setState(s); } @@ -43,11 +52,12 @@ export default class EmailSettings extends React.Component { var config = this.props.config; config.EmailSettings.EnableSignUpWithEmail = ReactDOM.findDOMNode(this.refs.allowSignUpWithEmail).checked; config.EmailSettings.SendEmailNotifications = ReactDOM.findDOMNode(this.refs.sendEmailNotifications).checked; + config.EmailSettings.SendPushlNotifications = ReactDOM.findDOMNode(this.refs.sendPushNotifications).checked; config.EmailSettings.RequireEmailVerification = ReactDOM.findDOMNode(this.refs.requireEmailVerification).checked; - config.EmailSettings.SendEmailNotifications = ReactDOM.findDOMNode(this.refs.sendEmailNotifications).checked; config.EmailSettings.FeedbackName = ReactDOM.findDOMNode(this.refs.feedbackName).value.trim(); config.EmailSettings.FeedbackEmail = ReactDOM.findDOMNode(this.refs.feedbackEmail).value.trim(); config.EmailSettings.SMTPServer = ReactDOM.findDOMNode(this.refs.SMTPServer).value.trim(); + config.EmailSettings.PushNotificationServer = ReactDOM.findDOMNode(this.refs.PushNotificationServer).value.trim(); config.EmailSettings.SMTPPort = ReactDOM.findDOMNode(this.refs.SMTPPort).value.trim(); config.EmailSettings.SMTPUsername = ReactDOM.findDOMNode(this.refs.SMTPUsername).value.trim(); config.EmailSettings.SMTPPassword = ReactDOM.findDOMNode(this.refs.SMTPPassword).value.trim(); @@ -525,6 +535,61 @@ export default class EmailSettings extends React.Component { +
+ +
+ + +

{'Typically set to true in production. When true, Mattermost attempts to send iOS and Android push notifications through the push notification server.'}

+
+
+ +
+ +
+ +

{'Location of the push notification server.'}

+
+
+
{serverError} -- cgit v1.2.3-1-g7c22 From f5907e21cad055b241718d2bd1530bd4c22e77c7 Mon Sep 17 00:00:00 2001 From: =Corey Hulen Date: Mon, 30 Nov 2015 22:43:16 -0800 Subject: fixing other configs --- docker/dev/config_docker.json | 5 ++--- docker/local/config_docker.json | 5 ++--- 2 files changed, 4 insertions(+), 6 deletions(-) diff --git a/docker/dev/config_docker.json b/docker/dev/config_docker.json index c23a72cd1..4c5502ddd 100644 --- a/docker/dev/config_docker.json +++ b/docker/dev/config_docker.json @@ -68,9 +68,8 @@ "ConnectionSecurity": "", "InviteSalt": "bjlSR4QqkXFBr7TP4oDzlfZmcNuH9YoS", "PasswordResetSalt": "vZ4DcKyVVRlKHHJpexcuXzojkE5PZ5eL", - "ApplePushServer": "", - "ApplePushCertPublic": "", - "ApplePushCertPrivate": "" + "SendPushNotifications": true, + "PushNotificationServer": "https://push.mattermost.com" }, "RateLimitSettings": { "EnableRateLimiter": true, diff --git a/docker/local/config_docker.json b/docker/local/config_docker.json index c23a72cd1..4c5502ddd 100644 --- a/docker/local/config_docker.json +++ b/docker/local/config_docker.json @@ -68,9 +68,8 @@ "ConnectionSecurity": "", "InviteSalt": "bjlSR4QqkXFBr7TP4oDzlfZmcNuH9YoS", "PasswordResetSalt": "vZ4DcKyVVRlKHHJpexcuXzojkE5PZ5eL", - "ApplePushServer": "", - "ApplePushCertPublic": "", - "ApplePushCertPrivate": "" + "SendPushNotifications": true, + "PushNotificationServer": "https://push.mattermost.com" }, "RateLimitSettings": { "EnableRateLimiter": true, -- cgit v1.2.3-1-g7c22