Initial support for iOS app
This commit is contained in:
parent
2eff8d6b47
commit
1b5be4fc6b
5 changed files with 61 additions and 1 deletions
12
Makefile
12
Makefile
|
@ -25,6 +25,7 @@ help:
|
||||||
@echo " make build - Build"
|
@echo " make build - Build"
|
||||||
@echo " make build-snapshot - Build snapshot"
|
@echo " make build-snapshot - Build snapshot"
|
||||||
@echo " make build-simple - Build (using go build, without goreleaser)"
|
@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 " make clean - Clean build folder"
|
||||||
@echo
|
@echo
|
||||||
@echo "Releasing (requires goreleaser):"
|
@echo "Releasing (requires goreleaser):"
|
||||||
|
@ -114,6 +115,17 @@ build-simple: clean
|
||||||
-ldflags \
|
-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)"
|
"-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
|
clean: .PHONY
|
||||||
rm -rf dist build server/docs
|
rm -rf dist build server/docs
|
||||||
|
|
||||||
|
|
|
@ -20,6 +20,7 @@ Build:
|
||||||
make build - Build
|
make build - Build
|
||||||
make build-snapshot - Build snapshot
|
make build-snapshot - Build snapshot
|
||||||
make build-simple - Build (using go build, without goreleaser)
|
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
|
make clean - Clean build folder
|
||||||
|
|
||||||
Releasing (requires goreleaser):
|
Releasing (requires goreleaser):
|
||||||
|
|
|
@ -13,6 +13,7 @@ import (
|
||||||
|
|
||||||
const (
|
const (
|
||||||
fcmMessageLimit = 4000
|
fcmMessageLimit = 4000
|
||||||
|
fcmApnsBodyMessageLimit = 100
|
||||||
)
|
)
|
||||||
|
|
||||||
// maybeTruncateFCMMessage performs best-effort truncation of FCM messages.
|
// maybeTruncateFCMMessage performs best-effort truncation of FCM messages.
|
||||||
|
@ -34,6 +35,14 @@ func maybeTruncateFCMMessage(m *messaging.Message) *messaging.Message {
|
||||||
return m
|
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) {
|
func createFirebaseSubscriber(credentialsFile string, auther auth.Auther) (subscriber, error) {
|
||||||
fb, err := firebase.NewApp(context.Background(), nil, option.WithCredentialsFile(credentialsFile))
|
fb, err := firebase.NewApp(context.Background(), nil, option.WithCredentialsFile(credentialsFile))
|
||||||
if err != nil {
|
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) {
|
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 data map[string]string // Mostly matches https://ntfy.sh/docs/subscribe/api/#json-message-format
|
||||||
|
var apnsConfig *messaging.APNSConfig
|
||||||
switch m.Event {
|
switch m.Event {
|
||||||
case keepaliveEvent, openEvent:
|
case keepaliveEvent, openEvent:
|
||||||
data = map[string]string{
|
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_expires"] = fmt.Sprintf("%d", m.Attachment.Expires)
|
||||||
data["attachment_url"] = m.Attachment.URL
|
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 {
|
} else {
|
||||||
// If anonymous read for a topic is not allowed, we cannot send the message along
|
// 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.
|
// 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,
|
Topic: m.Topic,
|
||||||
Data: data,
|
Data: data,
|
||||||
Android: androidConfig,
|
Android: androidConfig,
|
||||||
|
APNS: apnsConfig,
|
||||||
}), nil
|
}), nil
|
||||||
}
|
}
|
||||||
|
|
|
@ -32,6 +32,7 @@ func TestToFirebaseMessage_Keepalive(t *testing.T) {
|
||||||
require.Nil(t, err)
|
require.Nil(t, err)
|
||||||
require.Equal(t, "mytopic", fbm.Topic)
|
require.Equal(t, "mytopic", fbm.Topic)
|
||||||
require.Nil(t, fbm.Android)
|
require.Nil(t, fbm.Android)
|
||||||
|
require.Nil(t, fbm.APNS)
|
||||||
require.Equal(t, map[string]string{
|
require.Equal(t, map[string]string{
|
||||||
"id": m.ID,
|
"id": m.ID,
|
||||||
"time": fmt.Sprintf("%d", m.Time),
|
"time": fmt.Sprintf("%d", m.Time),
|
||||||
|
@ -46,6 +47,7 @@ func TestToFirebaseMessage_Open(t *testing.T) {
|
||||||
require.Nil(t, err)
|
require.Nil(t, err)
|
||||||
require.Equal(t, "mytopic", fbm.Topic)
|
require.Equal(t, "mytopic", fbm.Topic)
|
||||||
require.Nil(t, fbm.Android)
|
require.Nil(t, fbm.Android)
|
||||||
|
require.Nil(t, fbm.APNS)
|
||||||
require.Equal(t, map[string]string{
|
require.Equal(t, map[string]string{
|
||||||
"id": m.ID,
|
"id": m.ID,
|
||||||
"time": fmt.Sprintf("%d", m.Time),
|
"time": fmt.Sprintf("%d", m.Time),
|
||||||
|
@ -74,6 +76,17 @@ func TestToFirebaseMessage_Message_Normal_Allowed(t *testing.T) {
|
||||||
require.Equal(t, &messaging.AndroidConfig{
|
require.Equal(t, &messaging.AndroidConfig{
|
||||||
Priority: "high",
|
Priority: "high",
|
||||||
}, fbm.Android)
|
}, 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{
|
require.Equal(t, map[string]string{
|
||||||
"id": m.ID,
|
"id": m.ID,
|
||||||
"time": fmt.Sprintf("%d", m.Time),
|
"time": fmt.Sprintf("%d", m.Time),
|
||||||
|
@ -102,6 +115,7 @@ func TestToFirebaseMessage_Message_Normal_Not_Allowed(t *testing.T) {
|
||||||
require.Equal(t, &messaging.AndroidConfig{
|
require.Equal(t, &messaging.AndroidConfig{
|
||||||
Priority: "high",
|
Priority: "high",
|
||||||
}, fbm.Android)
|
}, fbm.Android)
|
||||||
|
require.Nil(t, fbm.APNS)
|
||||||
require.Equal(t, "", fbm.Data["message"])
|
require.Equal(t, "", fbm.Data["message"])
|
||||||
require.Equal(t, "", fbm.Data["priority"])
|
require.Equal(t, "", fbm.Data["priority"])
|
||||||
require.Equal(t, map[string]string{
|
require.Equal(t, map[string]string{
|
||||||
|
@ -129,6 +143,17 @@ func TestMaybeTruncateFCMMessage(t *testing.T) {
|
||||||
Android: &messaging.AndroidConfig{
|
Android: &messaging.AndroidConfig{
|
||||||
Priority: "high",
|
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"])
|
origMessageLength := len(origFCMMessage.Data["message"])
|
||||||
serializedOrigFCMMessage, _ := json.Marshal(origFCMMessage)
|
serializedOrigFCMMessage, _ := json.Marshal(origFCMMessage)
|
||||||
|
|
|
@ -156,7 +156,7 @@ func TestServer_StaticSites(t *testing.T) {
|
||||||
require.Equal(t, 301, rr.Code)
|
require.Equal(t, 301, rr.Code)
|
||||||
|
|
||||||
rr = request(t, s, "GET", "/docs/", "", nil)
|
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(), `Made with ❤️ by Philipp C. Heckel`)
|
||||||
require.Contains(t, rr.Body.String(), `<script src=static/js/extra.js></script>`)
|
require.Contains(t, rr.Body.String(), `<script src=static/js/extra.js></script>`)
|
||||||
|
|
||||||
|
|
Loading…
Add table
Add a link
Reference in a new issue