diff --git a/go.sum b/go.sum index 73cd9d8f..bfaf339d 100644 --- a/go.sum +++ b/go.sum @@ -2,8 +2,6 @@ cloud.google.com/go v0.26.0/go.mod h1:aQUYkXzVsufM+DwF1aE+0xfcU+56JwCaLick0ClmMT cloud.google.com/go v0.34.0/go.mod h1:aQUYkXzVsufM+DwF1aE+0xfcU+56JwCaLick0ClmMTw= cloud.google.com/go v0.110.2 h1:sdFPBr6xG9/wkBbfhmUz/JmZC7X6LavQgcrVINrKiVA= cloud.google.com/go v0.110.2/go.mod h1:k04UEeEtb6ZBRTv3dZz4CeJC3jKGxyhl0sAiVVquxiw= -cloud.google.com/go/compute v1.19.2 h1:GbJtPo8OKVHbVep8jvM57KidbYHxeE68LOVqouNLrDY= -cloud.google.com/go/compute v1.19.2/go.mod h1:5f5a+iC1IriXYauaQ0EyQmEAEq9CGRnV5xJSQSlTV08= cloud.google.com/go/compute v1.19.3 h1:DcTwsFgGev/wV5+q8o2fzgcHOaac+DKGC91ZlvpsQds= cloud.google.com/go/compute v1.19.3/go.mod h1:qxvISKp/gYnXkSAD1ppcSOveRAmzxicEv/JlizULFrI= cloud.google.com/go/compute/metadata v0.2.3 h1:mg4jlk7mCAj6xXp9UJ4fjI9VUI5rubuGBW5aJ7UnBMY= diff --git a/server/server.go b/server/server.go index 20b8ce03..7e8ea251 100644 --- a/server/server.go +++ b/server/server.go @@ -550,6 +550,7 @@ func (s *Server) handleWebConfig(w http.ResponseWriter, _ *http.Request, _ *visi EnableSignup: s.config.EnableSignup, EnablePayments: s.config.StripeSecretKey != "", EnableCalls: s.config.TwilioAccount != "", + EnableEmails: s.config.SMTPSenderFrom != "", EnableReservations: s.config.EnableReservations, BillingContact: s.config.BillingContact, DisallowedTopics: s.config.DisallowedTopics, @@ -911,7 +912,7 @@ func (s *Server) parsePublishParams(r *http.Request, m *message) (cache bool, fi return false, false, "", "", false, errHTTPBadRequestEmailDisabled } call = readParam(r, "x-call", "call") - if call != "" && s.config.TwilioAccount == "" && s.userManager == nil { + if call != "" && (s.config.TwilioAccount == "" || s.userManager == nil) { return false, false, "", "", false, errHTTPBadRequestPhoneCallsDisabled } else if call != "" && !isBoolValue(call) && !phoneNumberRegex.MatchString(call) { return false, false, "", "", false, errHTTPBadRequestPhoneNumberInvalid diff --git a/server/server.yml b/server/server.yml index 6728d6a4..74841137 100644 --- a/server/server.yml +++ b/server/server.yml @@ -146,6 +146,11 @@ # If enabled, ntfy can perform voice calls via Twilio via the "X-Call" header. # +# - twilio-account is the Twilio account SID, e.g. AC12345beefbeef67890beefbeef122586 +# - twilio-auth-token is the Twilio auth token, e.g. affebeef258625862586258625862586 +# - twilio-from-number is the outgoing phone number you purchased, e.g. +18775132586 +# - twilio-verify-service is the Twilio Verify service SID, e.g. VA12345beefbeef67890beefbeef122586 +# # twilio-account: # twilio-auth-token: # twilio-from-number: diff --git a/server/server_account.go b/server/server_account.go index a0bbaeaf..6e6a6864 100644 --- a/server/server_account.go +++ b/server/server_account.go @@ -108,17 +108,19 @@ func (s *Server) handleAccountGet(w http.ResponseWriter, r *http.Request, v *vis CancelAt: u.Billing.StripeSubscriptionCancelAt.Unix(), } } - reservations, err := s.userManager.Reservations(u.Name) - if err != nil { - return err - } - if len(reservations) > 0 { - response.Reservations = make([]*apiAccountReservation, 0) - for _, r := range reservations { - response.Reservations = append(response.Reservations, &apiAccountReservation{ - Topic: r.Topic, - Everyone: r.Everyone.String(), - }) + if s.config.EnableReservations { + reservations, err := s.userManager.Reservations(u.Name) + if err != nil { + return err + } + if len(reservations) > 0 { + response.Reservations = make([]*apiAccountReservation, 0) + for _, r := range reservations { + response.Reservations = append(response.Reservations, &apiAccountReservation{ + Topic: r.Topic, + Everyone: r.Everyone.String(), + }) + } } } tokens, err := s.userManager.Tokens(u.ID) @@ -141,12 +143,14 @@ func (s *Server) handleAccountGet(w http.ResponseWriter, r *http.Request, v *vis }) } } - phoneNumbers, err := s.userManager.PhoneNumbers(u.ID) - if err != nil { - return err - } - if len(phoneNumbers) > 0 { - response.PhoneNumbers = phoneNumbers + if s.config.TwilioAccount != "" { + phoneNumbers, err := s.userManager.PhoneNumbers(u.ID) + if err != nil { + return err + } + if len(phoneNumbers) > 0 { + response.PhoneNumbers = phoneNumbers + } } } else { response.Username = user.Everyone diff --git a/server/server_account_test.go b/server/server_account_test.go index 465e4be1..119efb16 100644 --- a/server/server_account_test.go +++ b/server/server_account_test.go @@ -151,6 +151,8 @@ func TestAccount_Get_Anonymous(t *testing.T) { require.Equal(t, int64(1004), account.Stats.MessagesRemaining) require.Equal(t, int64(0), account.Stats.Emails) require.Equal(t, int64(24), account.Stats.EmailsRemaining) + require.Equal(t, int64(0), account.Stats.Calls) + require.Equal(t, int64(0), account.Stats.CallsRemaining) rr = request(t, s, "POST", "/mytopic", "", nil) require.Equal(t, 200, rr.Code) @@ -498,6 +500,8 @@ func TestAccount_Reservation_AddAdminSuccess(t *testing.T) { func TestAccount_Reservation_AddRemoveUserWithTierSuccess(t *testing.T) { conf := newTestConfigWithAuthFile(t) conf.EnableSignup = true + conf.EnableReservations = true + conf.TwilioAccount = "dummy" s := newTestServer(t, conf) // Create user @@ -510,6 +514,7 @@ func TestAccount_Reservation_AddRemoveUserWithTierSuccess(t *testing.T) { MessageLimit: 123, MessageExpiryDuration: 86400 * time.Second, EmailLimit: 32, + CallLimit: 10, ReservationLimit: 2, AttachmentFileSizeLimit: 1231231, AttachmentTotalSizeLimit: 123123, @@ -551,6 +556,7 @@ func TestAccount_Reservation_AddRemoveUserWithTierSuccess(t *testing.T) { require.Equal(t, int64(123), account.Limits.Messages) require.Equal(t, int64(86400), account.Limits.MessagesExpiryDuration) require.Equal(t, int64(32), account.Limits.Emails) + require.Equal(t, int64(10), account.Limits.Calls) require.Equal(t, int64(2), account.Limits.Reservations) require.Equal(t, int64(1231231), account.Limits.AttachmentFileSize) require.Equal(t, int64(123123), account.Limits.AttachmentTotalSize) diff --git a/server/server_test.go b/server/server_test.go index e231ab73..57251413 100644 --- a/server/server_test.go +++ b/server/server_test.go @@ -1194,7 +1194,7 @@ func TestServer_PublishDelayedEmail_Fail(t *testing.T) { } func TestServer_PublishDelayedCall_Fail(t *testing.T) { - c := newTestConfig(t) + c := newTestConfigWithAuthFile(t) c.TwilioAccount = "AC1234567890" c.TwilioAuthToken = "AAEAA1234567890" c.TwilioFromNumber = "+1234567890" diff --git a/server/server_twilio_test.go b/server/server_twilio_test.go index 642ad756..1b710130 100644 --- a/server/server_twilio_test.go +++ b/server/server_twilio_test.go @@ -228,7 +228,7 @@ func TestServer_Twilio_Call_UnverifiedNumber(t *testing.T) { } func TestServer_Twilio_Call_InvalidNumber(t *testing.T) { - c := newTestConfig(t) + c := newTestConfigWithAuthFile(t) c.TwilioCallsBaseURL = "https://127.0.0.1" c.TwilioAccount = "AC1234567890" c.TwilioAuthToken = "AAEAA1234567890" @@ -242,7 +242,7 @@ func TestServer_Twilio_Call_InvalidNumber(t *testing.T) { } func TestServer_Twilio_Call_Anonymous(t *testing.T) { - c := newTestConfig(t) + c := newTestConfigWithAuthFile(t) c.TwilioCallsBaseURL = "https://127.0.0.1" c.TwilioAccount = "AC1234567890" c.TwilioAuthToken = "AAEAA1234567890" diff --git a/server/types.go b/server/types.go index 1e9457be..4280f6c9 100644 --- a/server/types.go +++ b/server/types.go @@ -394,6 +394,7 @@ type apiConfigResponse struct { EnableSignup bool `json:"enable_signup"` EnablePayments bool `json:"enable_payments"` EnableCalls bool `json:"enable_calls"` + EnableEmails bool `json:"enable_emails"` EnableReservations bool `json:"enable_reservations"` BillingContact string `json:"billing_contact"` DisallowedTopics []string `json:"disallowed_topics"` diff --git a/web/package-lock.json b/web/package-lock.json index 5457f17d..0d2670ff 100644 --- a/web/package-lock.json +++ b/web/package-lock.json @@ -16262,16 +16262,16 @@ } }, "node_modules/typescript": { - "version": "5.0.4", - "resolved": "https://registry.npmjs.org/typescript/-/typescript-5.0.4.tgz", - "integrity": "sha512-cW9T5W9xY37cc+jfEnaUvX91foxtHkza3Nw3wkoF4sSlKn0MONdkdEndig/qPBWXNkmplh3NzayQzCiHM4/hqw==", + "version": "4.9.5", + "resolved": "https://registry.npmjs.org/typescript/-/typescript-4.9.5.tgz", + "integrity": "sha512-1FXk9E2Hm+QzZQ7z+McJiHL4NW1F2EzMu9Nq9i3zAaGqibafqYwCVU6WyWAuyQRRzOlxou8xZSyXLEN8oKj24g==", "peer": true, "bin": { "tsc": "bin/tsc", "tsserver": "bin/tsserver" }, "engines": { - "node": ">=12.20" + "node": ">=4.2.0" } }, "node_modules/unbox-primitive": { diff --git a/web/public/config.js b/web/public/config.js index b49e440b..89bbed9f 100644 --- a/web/public/config.js +++ b/web/public/config.js @@ -6,12 +6,13 @@ // During web development, you may change values here for rapid testing. var config = { - base_url: "http://127.0.0.1:2586",// FIXME window.location.origin, // Change to test against a different server + base_url: window.location.origin, // Change to test against a different server app_root: "/app", enable_login: true, enable_signup: true, enable_payments: true, enable_reservations: true, + enable_emails: true, enable_calls: true, billing_contact: "", disallowed_topics: ["docs", "static", "file", "app", "account", "settings", "signup", "login", "v1"] diff --git a/web/src/components/Account.js b/web/src/components/Account.js index 83ef0b7d..710510d2 100644 --- a/web/src/components/Account.js +++ b/web/src/components/Account.js @@ -571,22 +571,24 @@ const Stats = () => { value={account.role === Role.USER ? normalize(account.stats.messages, account.limits.messages) : 100} /> - - {t("account_usage_emails_title")} - - - }> -
- {account.stats.emails.toLocaleString()} - {account.role === Role.USER ? t("account_usage_of_limit", { limit: account.limits.emails.toLocaleString() }) : t("account_usage_unlimited")} -
- -
- {(account.role === Role.ADMIN || account.limits.calls > 0) && + {config.enable_emails && + + {t("account_usage_emails_title")} + + + }> +
+ {account.stats.emails.toLocaleString()} + {account.role === Role.USER ? t("account_usage_of_limit", { limit: account.limits.emails.toLocaleString() }) : t("account_usage_unlimited")} +
+ +
+ } + {config.enable_calls && (account.role === Role.ADMIN || account.limits.calls > 0) && {t("account_usage_calls_title")}