ntfy-server/server/types.go

Ignoring revisions in .git-blame-ignore-revs. Click here to bypass and see the normal blame view.

473 lines
15 KiB
Go
Raw Normal View History

package server
2021-10-29 13:58:14 -04:00
import (
2022-01-15 23:17:46 -05:00
"net/http"
"heckel.io/ntfy/v2/model"
2023-11-16 20:54:58 -05:00
"heckel.io/ntfy/v2/user"
"heckel.io/ntfy/v2/util"
2021-10-29 13:58:14 -04:00
)
// Event constants
const (
openEvent = model.OpenEvent
keepaliveEvent = model.KeepaliveEvent
messageEvent = model.MessageEvent
messageDeleteEvent = model.MessageDeleteEvent
messageClearEvent = model.MessageClearEvent
pollRequestEvent = model.PollRequestEvent
messageIDLength = model.MessageIDLength
2021-10-29 13:58:14 -04:00
)
2026-02-18 21:07:31 -05:00
// SinceMarker aliases
var (
2026-02-18 21:07:31 -05:00
sinceAllMessages = model.SinceAllMessages
sinceNoMessages = model.SinceNoMessages
sinceLatestMessage = model.SinceLatestMessage
)
// Error aliases
var (
errMessageNotFound = model.ErrMessageNotFound
)
// Constructors and helpers
var (
newMessage = model.NewMessage
newDefaultMessage = model.NewDefaultMessage
newOpenMessage = model.NewOpenMessage
newKeepaliveMessage = model.NewKeepaliveMessage
newActionMessage = model.NewActionMessage
newAction = model.NewAction
newSinceTime = model.NewSinceTime
newSinceID = model.NewSinceID
validMessageID = model.ValidMessageID
)
2023-02-03 22:21:50 -05:00
// newPollRequestMessage is a convenience method to create a poll request message
func newPollRequestMessage(topic, pollID string) *model.Message {
m := newMessage(pollRequestEvent, topic, newMessageBody)
m.PollID = pollID
2026-01-08 20:57:55 -05:00
return m
}
2022-03-15 16:00:59 -04:00
// publishMessage is used as input when publishing as JSON
type publishMessage struct {
2026-02-18 20:48:41 -05:00
Topic string `json:"topic"`
SequenceID string `json:"sequence_id"`
Title string `json:"title"`
Message string `json:"message"`
Priority int `json:"priority"`
Tags []string `json:"tags"`
Click string `json:"click"`
Icon string `json:"icon"`
Actions []model.Action `json:"actions"`
2026-02-18 20:48:41 -05:00
Attach string `json:"attach"`
Markdown bool `json:"markdown"`
Filename string `json:"filename"`
Email string `json:"email"`
Call string `json:"call"`
Cache string `json:"cache"` // use string as it defaults to true (or use &bool instead)
Firebase string `json:"firebase"` // use string as it defaults to true (or use &bool instead)
Delay string `json:"delay"`
2022-03-15 16:00:59 -04:00
}
// messageEncoder is a function that knows how to encode a message
type messageEncoder func(msg *model.Message) (string, error)
2022-01-15 23:17:46 -05:00
type queryFilter struct {
2022-05-26 18:52:55 -04:00
ID string
2022-01-15 23:17:46 -05:00
Message string
Title string
Tags []string
Priority []int
}
func parseQueryFilters(r *http.Request) (*queryFilter, error) {
2022-05-26 18:52:55 -04:00
idFilter := readParam(r, "x-id", "id")
2022-01-15 23:17:46 -05:00
messageFilter := readParam(r, "x-message", "message", "m")
titleFilter := readParam(r, "x-title", "title", "t")
tagsFilter := util.SplitNoEmpty(readParam(r, "x-tags", "tags", "tag", "ta"), ",")
priorityFilter := make([]int, 0)
for _, p := range util.SplitNoEmpty(readParam(r, "x-priority", "priority", "prio", "p"), ",") {
priority, err := util.ParsePriority(p)
if err != nil {
2022-07-01 09:28:42 -04:00
return nil, errHTTPBadRequestPriorityInvalid
2022-01-15 23:17:46 -05:00
}
priorityFilter = append(priorityFilter, priority)
}
return &queryFilter{
2022-05-26 18:52:55 -04:00
ID: idFilter,
2022-01-15 23:17:46 -05:00
Message: messageFilter,
Title: titleFilter,
Tags: tagsFilter,
Priority: priorityFilter,
}, nil
}
func (q *queryFilter) Pass(msg *model.Message) bool {
2026-01-13 16:31:13 -05:00
if msg.Event != messageEvent && msg.Event != messageDeleteEvent && msg.Event != messageClearEvent {
2022-01-15 23:17:46 -05:00
return true // filters only apply to messages
2022-05-26 18:52:55 -04:00
} else if q.ID != "" && msg.ID != q.ID {
2022-01-15 23:17:46 -05:00
return false
2022-05-26 18:52:55 -04:00
} else if q.Message != "" && msg.Message != q.Message {
return false
} else if q.Title != "" && msg.Title != q.Title {
2022-01-15 23:17:46 -05:00
return false
}
messagePriority := msg.Priority
if messagePriority == 0 {
messagePriority = 3 // For query filters, default priority (3) is the same as "not set" (0)
}
2022-10-01 15:50:48 -04:00
if len(q.Priority) > 0 && !util.Contains(q.Priority, messagePriority) {
2022-01-15 23:17:46 -05:00
return false
}
2022-10-01 15:50:48 -04:00
if len(q.Tags) > 0 && !util.ContainsAll(msg.Tags, q.Tags) {
2022-01-15 23:17:46 -05:00
return false
}
return true
}
2022-12-14 23:11:22 -05:00
2025-07-27 10:15:48 +02:00
// templateMode represents the mode in which templates are used
//
// It can be
// - empty: templating is disabled
// - a boolean string (yes/1/true/no/0/false): inline-templating mode
// - a filename (e.g. grafana): template mode with a file
2025-07-16 13:49:15 +02:00
type templateMode string
2025-07-27 10:15:48 +02:00
// Enabled returns true if templating is enabled
2025-07-16 13:49:15 +02:00
func (t templateMode) Enabled() bool {
return t != ""
}
2025-07-27 10:15:48 +02:00
// InlineMode returns true if inline-templating mode is enabled
func (t templateMode) InlineMode() bool {
return t.Enabled() && isBoolValue(string(t))
}
// FileMode returns true if file-templating mode is enabled
func (t templateMode) FileMode() bool {
return t.Enabled() && !isBoolValue(string(t))
}
// FileName returns the filename if file-templating mode is enabled, or an empty string otherwise
func (t templateMode) FileName() string {
if t.FileMode() {
return string(t)
2025-07-16 13:49:15 +02:00
}
2025-07-27 10:15:48 +02:00
return ""
2025-07-16 13:49:15 +02:00
}
// templateFile represents a template file with title, message, and priority
2025-07-27 10:15:48 +02:00
// It is used for file-based templates, e.g. grafana, influxdb, etc.
//
// Example YAML:
//
// title: "Alert: {{ .Title }}"
// message: |
// This is a {{ .Type }} alert.
// It can be multiline.
// priority: '{{ if eq .status "Error" }}5{{ else }}3{{ end }}'
2025-07-16 13:49:15 +02:00
type templateFile struct {
Title *string `yaml:"title"`
Message *string `yaml:"message"`
Priority *string `yaml:"priority"`
2025-07-16 13:49:15 +02:00
}
2022-12-24 12:22:54 -05:00
type apiHealthResponse struct {
Healthy bool `json:"healthy"`
}
2022-12-24 12:26:56 -05:00
2026-02-08 14:28:27 -05:00
type apiVersionResponse struct {
Version string `json:"version"`
Commit string `json:"commit"`
Date string `json:"date"`
}
2023-04-20 22:04:11 -04:00
type apiStatsResponse struct {
Messages int64 `json:"messages"`
MessagesRate float64 `json:"messages_rate"` // Average number of messages per second
}
type apiUserAddOrUpdateRequest struct {
2023-05-13 22:07:54 -04:00
Username string `json:"username"`
Password string `json:"password"`
Hash string `json:"hash"`
2023-05-13 22:07:54 -04:00
Tier string `json:"tier"`
// Do not add 'role' here. We don't want to add admins via the API.
}
type apiUserResponse struct {
Username string `json:"username"`
Role string `json:"role"`
Tier string `json:"tier,omitempty"`
Grants []*apiUserGrantResponse `json:"grants,omitempty"`
}
type apiUserGrantResponse struct {
Topic string `json:"topic"` // This may be a pattern
Permission string `json:"permission"`
}
2023-05-13 22:07:54 -04:00
type apiUserDeleteRequest struct {
Username string `json:"username"`
}
2023-05-13 14:39:31 -04:00
type apiAccessAllowRequest struct {
Username string `json:"username"`
Topic string `json:"topic"` // This may be a pattern
2023-05-13 14:39:31 -04:00
Permission string `json:"permission"`
}
type apiAccessResetRequest struct {
Username string `json:"username"`
Topic string `json:"topic"`
}
2022-12-14 23:11:22 -05:00
type apiAccountCreateRequest struct {
Username string `json:"username"`
Password string `json:"password"`
}
2022-12-28 22:16:11 -05:00
type apiAccountPasswordChangeRequest struct {
Password string `json:"password"`
NewPassword string `json:"new_password"`
2022-12-28 22:16:11 -05:00
}
type apiAccountDeleteRequest struct {
Password string `json:"password"`
}
2023-01-27 23:10:59 -05:00
type apiAccountTokenIssueRequest struct {
Label *string `json:"label"`
Expires *int64 `json:"expires"` // Unix timestamp
}
type apiAccountTokenUpdateRequest struct {
Token string `json:"token"`
Label *string `json:"label"`
Expires *int64 `json:"expires"` // Unix timestamp
}
2022-12-14 23:11:22 -05:00
type apiAccountTokenResponse struct {
Token string `json:"token"`
Label string `json:"label,omitempty"`
LastAccess int64 `json:"last_access,omitempty"`
LastOrigin string `json:"last_origin,omitempty"`
Expires int64 `json:"expires,omitempty"` // Unix timestamp
Provisioned bool `json:"provisioned,omitempty"` // True if this token was provisioned by the server config
2022-12-14 23:11:22 -05:00
}
2023-05-16 22:27:48 -04:00
type apiAccountPhoneNumberVerifyRequest struct {
Number string `json:"number"`
Channel string `json:"channel"`
}
type apiAccountPhoneNumberAddRequest struct {
2023-05-11 13:50:10 -04:00
Number string `json:"number"`
2023-05-17 10:39:15 -04:00
Code string `json:"code"` // Only set when adding a phone number
2023-05-11 13:50:10 -04:00
}
type apiAccountTier struct {
2023-01-09 15:40:46 -05:00
Code string `json:"code"`
Name string `json:"name"`
2022-12-17 15:17:52 -05:00
}
2022-12-19 09:59:32 -05:00
type apiAccountLimits struct {
Basis string `json:"basis,omitempty"` // "ip" or "tier"
Messages int64 `json:"messages"`
MessagesExpiryDuration int64 `json:"messages_expiry_duration"`
Emails int64 `json:"emails"`
2023-05-07 11:59:15 -04:00
Calls int64 `json:"calls"`
Reservations int64 `json:"reservations"`
AttachmentTotalSize int64 `json:"attachment_total_size"`
AttachmentFileSize int64 `json:"attachment_file_size"`
AttachmentExpiryDuration int64 `json:"attachment_expiry_duration"`
AttachmentBandwidth int64 `json:"attachment_bandwidth"`
2022-12-19 09:59:32 -05:00
}
type apiAccountStats struct {
2022-12-19 16:22:13 -05:00
Messages int64 `json:"messages"`
MessagesRemaining int64 `json:"messages_remaining"`
Emails int64 `json:"emails"`
EmailsRemaining int64 `json:"emails_remaining"`
2023-05-07 11:59:15 -04:00
Calls int64 `json:"calls"`
CallsRemaining int64 `json:"calls_remaining"`
Reservations int64 `json:"reservations"`
ReservationsRemaining int64 `json:"reservations_remaining"`
2022-12-19 16:22:13 -05:00
AttachmentTotalSize int64 `json:"attachment_total_size"`
AttachmentTotalSizeRemaining int64 `json:"attachment_total_size_remaining"`
2022-12-14 23:11:22 -05:00
}
2023-01-02 20:08:37 -05:00
type apiAccountReservation struct {
Topic string `json:"topic"`
Everyone string `json:"everyone"`
2023-01-01 15:21:43 -05:00
}
2023-01-15 23:29:46 -05:00
type apiAccountBilling struct {
Customer bool `json:"customer"`
Subscription bool `json:"subscription"`
Status string `json:"status,omitempty"`
2023-02-21 22:44:30 -05:00
Interval string `json:"interval,omitempty"`
2023-01-15 23:29:46 -05:00
PaidUntil int64 `json:"paid_until,omitempty"`
2023-01-16 10:35:12 -05:00
CancelAt int64 `json:"cancel_at,omitempty"`
2023-01-15 23:29:46 -05:00
}
2022-12-27 22:14:14 -05:00
type apiAccountResponse struct {
2023-05-12 21:47:41 -04:00
Username string `json:"username"`
Role string `json:"role,omitempty"`
SyncTopic string `json:"sync_topic,omitempty"`
Provisioned bool `json:"provisioned,omitempty"`
2023-05-12 21:47:41 -04:00
Language string `json:"language,omitempty"`
Notification *user.NotificationPrefs `json:"notification,omitempty"`
Subscriptions []*user.Subscription `json:"subscriptions,omitempty"`
Reservations []*apiAccountReservation `json:"reservations,omitempty"`
Tokens []*apiAccountTokenResponse `json:"tokens,omitempty"`
PhoneNumbers []string `json:"phone_numbers,omitempty"`
Tier *apiAccountTier `json:"tier,omitempty"`
Limits *apiAccountLimits `json:"limits,omitempty"`
Stats *apiAccountStats `json:"stats,omitempty"`
Billing *apiAccountBilling `json:"billing,omitempty"`
2022-12-14 23:11:22 -05:00
}
2022-12-30 14:20:48 -05:00
2023-01-12 12:04:18 -05:00
type apiAccountReservationRequest struct {
Topic string `json:"topic"`
Everyone string `json:"everyone"`
2022-12-30 14:20:48 -05:00
}
2023-01-04 20:34:22 -05:00
type apiConfigResponse struct {
2023-01-10 22:51:51 -05:00
BaseURL string `json:"base_url"`
AppRoot string `json:"app_root"`
EnableLogin bool `json:"enable_login"`
RequireLogin bool `json:"require_login"`
2023-01-10 22:51:51 -05:00
EnableSignup bool `json:"enable_signup"`
EnablePayments bool `json:"enable_payments"`
EnableCalls bool `json:"enable_calls"`
2023-05-17 10:58:28 -04:00
EnableEmails bool `json:"enable_emails"`
2023-01-10 22:51:51 -05:00
EnableReservations bool `json:"enable_reservations"`
EnableWebPush bool `json:"enable_web_push"`
2023-02-28 14:38:31 -05:00
BillingContact string `json:"billing_contact"`
WebPushPublicKey string `json:"web_push_public_key"`
2023-01-10 22:51:51 -05:00
DisallowedTopics []string `json:"disallowed_topics"`
2026-01-17 20:36:15 -05:00
ConfigHash string `json:"config_hash"`
2023-01-04 20:34:22 -05:00
}
2023-01-14 06:43:44 -05:00
2023-02-21 22:44:30 -05:00
type apiAccountBillingPrices struct {
Month int64 `json:"month"`
Year int64 `json:"year"`
}
2023-01-17 10:09:37 -05:00
type apiAccountBillingTier struct {
2023-02-21 22:44:30 -05:00
Code string `json:"code,omitempty"`
Name string `json:"name,omitempty"`
Prices *apiAccountBillingPrices `json:"prices,omitempty"`
Limits *apiAccountLimits `json:"limits"`
2023-01-14 06:43:44 -05:00
}
2023-01-17 10:09:37 -05:00
type apiAccountBillingSubscriptionCreateResponse struct {
2023-01-14 06:43:44 -05:00
RedirectURL string `json:"redirect_url"`
}
2023-01-17 10:09:37 -05:00
type apiAccountBillingSubscriptionChangeRequest struct {
2023-02-21 22:44:30 -05:00
Tier string `json:"tier"`
Interval string `json:"interval"`
2023-01-17 10:09:37 -05:00
}
2023-01-14 06:43:44 -05:00
type apiAccountBillingPortalRedirectResponse struct {
RedirectURL string `json:"redirect_url"`
}
type apiAccountSyncTopicResponse struct {
Event string `json:"event"`
}
2023-01-17 10:09:37 -05:00
type apiSuccessResponse struct {
Success bool `json:"success"`
}
func newSuccessResponse() *apiSuccessResponse {
return &apiSuccessResponse{
Success: true,
}
}
2023-01-18 15:50:06 -05:00
type apiStripeSubscriptionUpdatedEvent struct {
ID string `json:"id"`
Customer string `json:"customer"`
Status string `json:"status"`
CurrentPeriodEnd int64 `json:"current_period_end"`
CancelAt int64 `json:"cancel_at"`
Items *struct {
Data []*struct {
Price *struct {
2023-02-21 22:44:30 -05:00
ID string `json:"id"`
Recurring *struct {
Interval string `json:"interval"`
} `json:"recurring"`
2023-01-18 15:50:06 -05:00
} `json:"price"`
} `json:"data"`
} `json:"items"`
}
type apiStripeSubscriptionDeletedEvent struct {
2023-01-22 22:21:30 -05:00
ID string `json:"id"`
2023-01-18 15:50:06 -05:00
Customer string `json:"customer"`
}
2023-06-08 23:09:38 -04:00
type apiWebPushUpdateSubscriptionRequest struct {
Endpoint string `json:"endpoint"`
Auth string `json:"auth"`
P256dh string `json:"p256dh"`
Topics []string `json:"topics"`
}
2023-06-15 22:25:05 -04:00
// List of possible Web Push events (see sw.js)
2023-06-08 12:20:12 -04:00
const (
webPushMessageEvent = "message"
webPushExpiringEvent = "subscription_expiring"
)
type webPushPayload struct {
2026-02-18 20:48:41 -05:00
Event string `json:"event"`
SubscriptionID string `json:"subscription_id"`
Message *model.Message `json:"message"`
}
func newWebPushPayload(subscriptionID string, message *model.Message) *webPushPayload {
2023-06-15 22:25:05 -04:00
return &webPushPayload{
2023-06-08 12:20:12 -04:00
Event: webPushMessageEvent,
2023-06-02 14:45:05 +02:00
SubscriptionID: subscriptionID,
2026-01-08 11:39:32 -05:00
Message: message,
2023-06-02 14:45:05 +02:00
}
}
type webPushControlMessagePayload struct {
Event string `json:"event"`
}
2023-06-15 22:25:05 -04:00
func newWebPushSubscriptionExpiringPayload() *webPushControlMessagePayload {
return &webPushControlMessagePayload{
2023-06-08 12:20:12 -04:00
Event: webPushExpiringEvent,
2023-06-02 14:45:05 +02:00
}
}
2023-06-19 10:50:14 +02:00
// https://developer.mozilla.org/en-US/docs/Web/Manifest
type webManifestResponse struct {
2023-06-20 21:46:09 -04:00
Name string `json:"name"`
Description string `json:"description"`
ShortName string `json:"short_name"`
Scope string `json:"scope"`
StartURL string `json:"start_url"`
Display string `json:"display"`
BackgroundColor string `json:"background_color"`
ThemeColor string `json:"theme_color"`
Icons []*webManifestIcon `json:"icons"`
2023-06-19 10:50:14 +02:00
}
type webManifestIcon struct {
SRC string `json:"src"`
Sizes string `json:"sizes"`
Type string `json:"type"`
}