From 1b5be4fc6b72f34981d685b4c1547af2d747d4e6 Mon Sep 17 00:00:00 2001 From: Andrew Cope Date: Wed, 23 Feb 2022 22:15:22 -0500 Subject: [PATCH] Initial support for iOS app --- Makefile | 12 ++++++++++++ docs/develop.md | 1 + server/server_firebase.go | 22 ++++++++++++++++++++++ server/server_firebase_test.go | 25 +++++++++++++++++++++++++ server/server_test.go | 2 +- 5 files changed, 61 insertions(+), 1 deletion(-) diff --git a/Makefile b/Makefile index a9a9a201..538ba1d6 100644 --- a/Makefile +++ b/Makefile @@ -25,6 +25,7 @@ help: @echo " make build - Build" @echo " make build-snapshot - Build snapshot" @echo " make build-simple - Build (using go build, without goreleaser)" + @echo " make build-simple-darwin - Build (using go build, without goreleaser) on macOS systems" @echo " make clean - Clean build folder" @echo @echo "Releasing (requires goreleaser):" @@ -114,6 +115,17 @@ build-simple: clean -ldflags \ "-linkmode=external -extldflags=-static -s -w -X main.version=$(VERSION) -X main.commit=$(shell git rev-parse --short HEAD) -X main.date=$(shell date +%s)" +build-simple-darwin: clean + mkdir -p dist/ntfy_linux_amd64 server/docs + touch server/docs/dummy + export CGO_ENABLED=1 + go build \ + -o dist/ntfy_linux_amd64/ntfy \ + -tags sqlite_omit_load_extension,osusergo,netgo \ + -ldflags \ + "-linkmode=external -s -w -X main.version=$(VERSION) -X main.commit=$(shell git rev-parse --short HEAD) -X main.date=$(shell date +%s)" + + clean: .PHONY rm -rf dist build server/docs diff --git a/docs/develop.md b/docs/develop.md index 99ba6880..7dc63f59 100644 --- a/docs/develop.md +++ b/docs/develop.md @@ -20,6 +20,7 @@ Build: make build - Build make build-snapshot - Build snapshot make build-simple - Build (using go build, without goreleaser) + make build-simple-darwin - Build (using go build, without goreleaser) on macOS systems make clean - Clean build folder Releasing (requires goreleaser): diff --git a/server/server_firebase.go b/server/server_firebase.go index 827fec45..66046c7d 100644 --- a/server/server_firebase.go +++ b/server/server_firebase.go @@ -13,6 +13,7 @@ import ( const ( fcmMessageLimit = 4000 + fcmApnsBodyMessageLimit = 100 ) // maybeTruncateFCMMessage performs best-effort truncation of FCM messages. @@ -34,6 +35,14 @@ func maybeTruncateFCMMessage(m *messaging.Message) *messaging.Message { return m } +func maybeTruncateAPNSBodyMessage(s string) string { + if len(s) >= fcmApnsBodyMessageLimit { + over := len(s) - fcmApnsBodyMessageLimit + 3 // len("...") + return s[:len(s)-over] + "..." + } + return s +} + func createFirebaseSubscriber(credentialsFile string, auther auth.Auther) (subscriber, error) { fb, err := firebase.NewApp(context.Background(), nil, option.WithCredentialsFile(credentialsFile)) if err != nil { @@ -55,6 +64,7 @@ func createFirebaseSubscriber(credentialsFile string, auther auth.Auther) (subsc func toFirebaseMessage(m *message, auther auth.Auther) (*messaging.Message, error) { var data map[string]string // Mostly matches https://ntfy.sh/docs/subscribe/api/#json-message-format + var apnsConfig *messaging.APNSConfig switch m.Event { case keepaliveEvent, openEvent: data = map[string]string{ @@ -88,6 +98,17 @@ func toFirebaseMessage(m *message, auther auth.Auther) (*messaging.Message, erro data["attachment_expires"] = fmt.Sprintf("%d", m.Attachment.Expires) data["attachment_url"] = m.Attachment.URL } + apnsConfig = &messaging.APNSConfig{ + Payload: &messaging.APNSPayload{ + Aps: &messaging.Aps{ + MutableContent: true, + Alert: &messaging.ApsAlert{ + Title: m.Title, + Body: maybeTruncateAPNSBodyMessage(m.Message), + }, + }, + }, + } } else { // If anonymous read for a topic is not allowed, we cannot send the message along // via Firebase. Instead, we send a "poll_request" message, asking the client to poll. @@ -109,5 +130,6 @@ func toFirebaseMessage(m *message, auther auth.Auther) (*messaging.Message, erro Topic: m.Topic, Data: data, Android: androidConfig, + APNS: apnsConfig, }), nil } diff --git a/server/server_firebase_test.go b/server/server_firebase_test.go index 1fdd8a6e..12fdbcc2 100644 --- a/server/server_firebase_test.go +++ b/server/server_firebase_test.go @@ -32,6 +32,7 @@ func TestToFirebaseMessage_Keepalive(t *testing.T) { require.Nil(t, err) require.Equal(t, "mytopic", fbm.Topic) require.Nil(t, fbm.Android) + require.Nil(t, fbm.APNS) require.Equal(t, map[string]string{ "id": m.ID, "time": fmt.Sprintf("%d", m.Time), @@ -46,6 +47,7 @@ func TestToFirebaseMessage_Open(t *testing.T) { require.Nil(t, err) require.Equal(t, "mytopic", fbm.Topic) require.Nil(t, fbm.Android) + require.Nil(t, fbm.APNS) require.Equal(t, map[string]string{ "id": m.ID, "time": fmt.Sprintf("%d", m.Time), @@ -74,6 +76,17 @@ func TestToFirebaseMessage_Message_Normal_Allowed(t *testing.T) { require.Equal(t, &messaging.AndroidConfig{ Priority: "high", }, fbm.Android) + require.Equal(t, &messaging.APNSConfig{ + Payload: &messaging.APNSPayload{ + Aps: &messaging.Aps{ + MutableContent: true, + Alert: &messaging.ApsAlert{ + Title: "some title", + Body: "this is a message", + }, + }, + }, + }, fbm.APNS) require.Equal(t, map[string]string{ "id": m.ID, "time": fmt.Sprintf("%d", m.Time), @@ -102,6 +115,7 @@ func TestToFirebaseMessage_Message_Normal_Not_Allowed(t *testing.T) { require.Equal(t, &messaging.AndroidConfig{ Priority: "high", }, fbm.Android) + require.Nil(t, fbm.APNS) require.Equal(t, "", fbm.Data["message"]) require.Equal(t, "", fbm.Data["priority"]) require.Equal(t, map[string]string{ @@ -129,6 +143,17 @@ func TestMaybeTruncateFCMMessage(t *testing.T) { Android: &messaging.AndroidConfig{ Priority: "high", }, + APNS: &messaging.APNSConfig{ + Payload: &messaging.APNSPayload{ + Aps: &messaging.Aps{ + MutableContent: true, + Alert: &messaging.ApsAlert{ + Title: "some title", + Body: maybeTruncateAPNSBodyMessage(origMessage), + }, + }, + }, + }, } origMessageLength := len(origFCMMessage.Data["message"]) serializedOrigFCMMessage, _ := json.Marshal(origFCMMessage) diff --git a/server/server_test.go b/server/server_test.go index 614cd5c9..a11ce565 100644 --- a/server/server_test.go +++ b/server/server_test.go @@ -156,7 +156,7 @@ func TestServer_StaticSites(t *testing.T) { require.Equal(t, 301, rr.Code) rr = request(t, s, "GET", "/docs/", "", nil) - require.Equal(t, 200, rr.Code) + require.Equal(t, 200, rr.Code) // Test will fail if docs have not yet been built require.Contains(t, rr.Body.String(), `Made with ❤️ by Philipp C. Heckel`) require.Contains(t, rr.Body.String(), ``)