Compare commits
9 Commits
v2.14.0
...
debian-str
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
57a51ab2da | ||
|
|
998dbd9054 | ||
|
|
a5a55bd43a | ||
|
|
00409d834b | ||
|
|
d9ab7cc78d | ||
|
|
99a2ca8802 | ||
|
|
ea338ae4fa | ||
|
|
32fa8d43c1 | ||
|
|
5ccc131e73 |
11
cmd/serve.go
11
cmd/serve.go
@@ -16,10 +16,10 @@ import (
|
|||||||
"syscall"
|
"syscall"
|
||||||
"time"
|
"time"
|
||||||
|
|
||||||
"github.com/stripe/stripe-go/v74"
|
|
||||||
"github.com/urfave/cli/v2"
|
"github.com/urfave/cli/v2"
|
||||||
"github.com/urfave/cli/v2/altsrc"
|
"github.com/urfave/cli/v2/altsrc"
|
||||||
"heckel.io/ntfy/v2/log"
|
"heckel.io/ntfy/v2/log"
|
||||||
|
"heckel.io/ntfy/v2/payments"
|
||||||
"heckel.io/ntfy/v2/server"
|
"heckel.io/ntfy/v2/server"
|
||||||
"heckel.io/ntfy/v2/user"
|
"heckel.io/ntfy/v2/user"
|
||||||
"heckel.io/ntfy/v2/util"
|
"heckel.io/ntfy/v2/util"
|
||||||
@@ -279,6 +279,8 @@ func execServe(c *cli.Context) error {
|
|||||||
// Check values
|
// Check values
|
||||||
if firebaseKeyFile != "" && !util.FileExists(firebaseKeyFile) {
|
if firebaseKeyFile != "" && !util.FileExists(firebaseKeyFile) {
|
||||||
return errors.New("if set, FCM key file must exist")
|
return errors.New("if set, FCM key file must exist")
|
||||||
|
} else if firebaseKeyFile != "" && !server.FirebaseAvailable {
|
||||||
|
return errors.New("cannot set firebase-key-file, support for Firebase is not available (nofirebase)")
|
||||||
} else if webPushPublicKey != "" && (webPushPrivateKey == "" || webPushFile == "" || webPushEmailAddress == "" || baseURL == "") {
|
} else if webPushPublicKey != "" && (webPushPrivateKey == "" || webPushFile == "" || webPushEmailAddress == "" || baseURL == "") {
|
||||||
return errors.New("if web push is enabled, web-push-private-key, web-push-public-key, web-push-file, web-push-email-address, and base-url should be set. run 'ntfy webpush keys' to generate keys")
|
return errors.New("if web push is enabled, web-push-private-key, web-push-public-key, web-push-file, web-push-email-address, and base-url should be set. run 'ntfy webpush keys' to generate keys")
|
||||||
} else if keepaliveInterval < 5*time.Second {
|
} else if keepaliveInterval < 5*time.Second {
|
||||||
@@ -320,6 +322,8 @@ func execServe(c *cli.Context) error {
|
|||||||
return errors.New("cannot set enable-signup, enable-login, enable-reserve-topics, or stripe-secret-key if auth-file is not set")
|
return errors.New("cannot set enable-signup, enable-login, enable-reserve-topics, or stripe-secret-key if auth-file is not set")
|
||||||
} else if enableSignup && !enableLogin {
|
} else if enableSignup && !enableLogin {
|
||||||
return errors.New("cannot set enable-signup without also setting enable-login")
|
return errors.New("cannot set enable-signup without also setting enable-login")
|
||||||
|
} else if !payments.Available && (stripeSecretKey != "" || stripeWebhookKey != "") {
|
||||||
|
return errors.New("cannot set stripe-secret-key or stripe-webhook-key, support for payments is not available in this build (nopayments)")
|
||||||
} else if stripeSecretKey != "" && (stripeWebhookKey == "" || baseURL == "") {
|
} else if stripeSecretKey != "" && (stripeWebhookKey == "" || baseURL == "") {
|
||||||
return errors.New("if stripe-secret-key is set, stripe-webhook-key and base-url must also be set")
|
return errors.New("if stripe-secret-key is set, stripe-webhook-key and base-url must also be set")
|
||||||
} else if twilioAccount != "" && (twilioAuthToken == "" || twilioPhoneNumber == "" || twilioVerifyService == "" || baseURL == "" || authFile == "") {
|
} else if twilioAccount != "" && (twilioAuthToken == "" || twilioPhoneNumber == "" || twilioVerifyService == "" || baseURL == "" || authFile == "") {
|
||||||
@@ -329,6 +333,8 @@ func execServe(c *cli.Context) error {
|
|||||||
if messageSizeLimit > 5*1024*1024 {
|
if messageSizeLimit > 5*1024*1024 {
|
||||||
return errors.New("message-size-limit cannot be higher than 5M")
|
return errors.New("message-size-limit cannot be higher than 5M")
|
||||||
}
|
}
|
||||||
|
} else if !server.WebPushAvailable && (webPushPrivateKey != "" || webPushPublicKey != "" || webPushFile != "") {
|
||||||
|
return errors.New("cannot enable WebPush, support is not available in this build (nowebpush)")
|
||||||
} else if webPushExpiryWarningDuration > 0 && webPushExpiryWarningDuration > webPushExpiryDuration {
|
} else if webPushExpiryWarningDuration > 0 && webPushExpiryWarningDuration > webPushExpiryDuration {
|
||||||
return errors.New("web push expiry warning duration cannot be higher than web push expiry duration")
|
return errors.New("web push expiry warning duration cannot be higher than web push expiry duration")
|
||||||
} else if behindProxy && proxyForwardedHeader == "" {
|
} else if behindProxy && proxyForwardedHeader == "" {
|
||||||
@@ -396,8 +402,7 @@ func execServe(c *cli.Context) error {
|
|||||||
|
|
||||||
// Stripe things
|
// Stripe things
|
||||||
if stripeSecretKey != "" {
|
if stripeSecretKey != "" {
|
||||||
stripe.EnableTelemetry = false // Whoa!
|
payments.Setup(stripeSecretKey)
|
||||||
stripe.Key = stripeSecretKey
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// Add default forbidden topics
|
// Add default forbidden topics
|
||||||
|
|||||||
@@ -1,4 +1,4 @@
|
|||||||
//go:build !noserver
|
//go:build !noserver && !nowebpush
|
||||||
|
|
||||||
package cmd
|
package cmd
|
||||||
|
|
||||||
|
|||||||
21
payments/payments.go
Normal file
21
payments/payments.go
Normal file
@@ -0,0 +1,21 @@
|
|||||||
|
//go:build !nopayments
|
||||||
|
|
||||||
|
package payments
|
||||||
|
|
||||||
|
import "github.com/stripe/stripe-go/v74"
|
||||||
|
|
||||||
|
// Available is a constant used to indicate that Stripe support is available.
|
||||||
|
// It can be disabled with the 'nopayments' build tag.
|
||||||
|
const Available = true
|
||||||
|
|
||||||
|
// SubscriptionStatus is an alias for stripe.SubscriptionStatus
|
||||||
|
type SubscriptionStatus stripe.SubscriptionStatus
|
||||||
|
|
||||||
|
// PriceRecurringInterval is an alias for stripe.PriceRecurringInterval
|
||||||
|
type PriceRecurringInterval stripe.PriceRecurringInterval
|
||||||
|
|
||||||
|
// Setup sets the Stripe secret key and disables telemetry
|
||||||
|
func Setup(stripeSecretKey string) {
|
||||||
|
stripe.EnableTelemetry = false // Whoa!
|
||||||
|
stripe.Key = stripeSecretKey
|
||||||
|
}
|
||||||
18
payments/payments_dummy.go
Normal file
18
payments/payments_dummy.go
Normal file
@@ -0,0 +1,18 @@
|
|||||||
|
//go:build nopayments
|
||||||
|
|
||||||
|
package payments
|
||||||
|
|
||||||
|
// Available is a constant used to indicate that Stripe support is available.
|
||||||
|
// It can be disabled with the 'nopayments' build tag.
|
||||||
|
const Available = false
|
||||||
|
|
||||||
|
// SubscriptionStatus is a dummy type
|
||||||
|
type SubscriptionStatus string
|
||||||
|
|
||||||
|
// PriceRecurringInterval is dummy type
|
||||||
|
type PriceRecurringInterval string
|
||||||
|
|
||||||
|
// Setup is a dummy type
|
||||||
|
func Setup(stripeSecretKey string) {
|
||||||
|
// Nothing to see here
|
||||||
|
}
|
||||||
@@ -10,6 +10,7 @@ import (
|
|||||||
"errors"
|
"errors"
|
||||||
"fmt"
|
"fmt"
|
||||||
"gopkg.in/yaml.v2"
|
"gopkg.in/yaml.v2"
|
||||||
|
"heckel.io/ntfy/v2/payments"
|
||||||
"io"
|
"io"
|
||||||
"net"
|
"net"
|
||||||
"net/http"
|
"net/http"
|
||||||
@@ -165,7 +166,7 @@ func New(conf *Config) (*Server, error) {
|
|||||||
mailer = &smtpSender{config: conf}
|
mailer = &smtpSender{config: conf}
|
||||||
}
|
}
|
||||||
var stripe stripeAPI
|
var stripe stripeAPI
|
||||||
if conf.StripeSecretKey != "" {
|
if payments.Available && conf.StripeSecretKey != "" {
|
||||||
stripe = newStripeAPI()
|
stripe = newStripeAPI()
|
||||||
}
|
}
|
||||||
messageCache, err := createMessageCache(conf)
|
messageCache, err := createMessageCache(conf)
|
||||||
|
|||||||
@@ -1,3 +1,5 @@
|
|||||||
|
//go:build !nofirebase
|
||||||
|
|
||||||
package server
|
package server
|
||||||
|
|
||||||
import (
|
import (
|
||||||
@@ -14,6 +16,10 @@ import (
|
|||||||
)
|
)
|
||||||
|
|
||||||
const (
|
const (
|
||||||
|
// FirebaseAvailable is a constant used to indicate that Firebase support is available.
|
||||||
|
// It can be disabled with the 'nofirebase' build tag.
|
||||||
|
FirebaseAvailable = true
|
||||||
|
|
||||||
fcmMessageLimit = 4000
|
fcmMessageLimit = 4000
|
||||||
fcmApnsBodyMessageLimit = 100
|
fcmApnsBodyMessageLimit = 100
|
||||||
)
|
)
|
||||||
@@ -73,7 +79,7 @@ type firebaseSenderImpl struct {
|
|||||||
client *messaging.Client
|
client *messaging.Client
|
||||||
}
|
}
|
||||||
|
|
||||||
func newFirebaseSender(credentialsFile string) (*firebaseSenderImpl, error) {
|
func newFirebaseSender(credentialsFile string) (firebaseSender, 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 {
|
||||||
return nil, err
|
return nil, err
|
||||||
|
|||||||
38
server/server_firebase_dummy.go
Normal file
38
server/server_firebase_dummy.go
Normal file
@@ -0,0 +1,38 @@
|
|||||||
|
//go:build nofirebase
|
||||||
|
|
||||||
|
package server
|
||||||
|
|
||||||
|
import (
|
||||||
|
"errors"
|
||||||
|
"heckel.io/ntfy/v2/user"
|
||||||
|
)
|
||||||
|
|
||||||
|
const (
|
||||||
|
// FirebaseAvailable is a constant used to indicate that Firebase support is available.
|
||||||
|
// It can be disabled with the 'nofirebase' build tag.
|
||||||
|
FirebaseAvailable = false
|
||||||
|
)
|
||||||
|
|
||||||
|
var (
|
||||||
|
errFirebaseNotAvailable = errors.New("Firebase not available")
|
||||||
|
errFirebaseTemporarilyBanned = errors.New("visitor temporarily banned from using Firebase")
|
||||||
|
)
|
||||||
|
|
||||||
|
type firebaseClient struct {
|
||||||
|
}
|
||||||
|
|
||||||
|
func (c *firebaseClient) Send(v *visitor, m *message) error {
|
||||||
|
return errFirebaseNotAvailable
|
||||||
|
}
|
||||||
|
|
||||||
|
type firebaseSender interface {
|
||||||
|
Send(m string) error
|
||||||
|
}
|
||||||
|
|
||||||
|
func newFirebaseClient(sender firebaseSender, auther user.Auther) *firebaseClient {
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func newFirebaseSender(credentialsFile string) (firebaseSender, error) {
|
||||||
|
return nil, errFirebaseNotAvailable
|
||||||
|
}
|
||||||
@@ -1,3 +1,5 @@
|
|||||||
|
//go:build !nofirebase
|
||||||
|
|
||||||
package server
|
package server
|
||||||
|
|
||||||
import (
|
import (
|
||||||
|
|||||||
@@ -1,3 +1,5 @@
|
|||||||
|
//go:build !nopayments
|
||||||
|
|
||||||
package server
|
package server
|
||||||
|
|
||||||
import (
|
import (
|
||||||
@@ -12,6 +14,7 @@ import (
|
|||||||
"github.com/stripe/stripe-go/v74/subscription"
|
"github.com/stripe/stripe-go/v74/subscription"
|
||||||
"github.com/stripe/stripe-go/v74/webhook"
|
"github.com/stripe/stripe-go/v74/webhook"
|
||||||
"heckel.io/ntfy/v2/log"
|
"heckel.io/ntfy/v2/log"
|
||||||
|
"heckel.io/ntfy/v2/payments"
|
||||||
"heckel.io/ntfy/v2/user"
|
"heckel.io/ntfy/v2/user"
|
||||||
"heckel.io/ntfy/v2/util"
|
"heckel.io/ntfy/v2/util"
|
||||||
"io"
|
"io"
|
||||||
@@ -22,7 +25,7 @@ import (
|
|||||||
|
|
||||||
// Payments in ntfy are done via Stripe.
|
// Payments in ntfy are done via Stripe.
|
||||||
//
|
//
|
||||||
// Pretty much all payments related things are in this file. The following processes
|
// Pretty much all payments-related things are in this file. The following processes
|
||||||
// handle payments:
|
// handle payments:
|
||||||
//
|
//
|
||||||
// - Checkout:
|
// - Checkout:
|
||||||
@@ -464,8 +467,8 @@ func (s *Server) updateSubscriptionAndTier(r *http.Request, v *visitor, u *user.
|
|||||||
billing := &user.Billing{
|
billing := &user.Billing{
|
||||||
StripeCustomerID: customerID,
|
StripeCustomerID: customerID,
|
||||||
StripeSubscriptionID: subscriptionID,
|
StripeSubscriptionID: subscriptionID,
|
||||||
StripeSubscriptionStatus: stripe.SubscriptionStatus(status),
|
StripeSubscriptionStatus: payments.SubscriptionStatus(status),
|
||||||
StripeSubscriptionInterval: stripe.PriceRecurringInterval(interval),
|
StripeSubscriptionInterval: payments.PriceRecurringInterval(interval),
|
||||||
StripeSubscriptionPaidUntil: time.Unix(paidUntil, 0),
|
StripeSubscriptionPaidUntil: time.Unix(paidUntil, 0),
|
||||||
StripeSubscriptionCancelAt: time.Unix(cancelAt, 0),
|
StripeSubscriptionCancelAt: time.Unix(cancelAt, 0),
|
||||||
}
|
}
|
||||||
|
|||||||
47
server/server_payments_dummy.go
Normal file
47
server/server_payments_dummy.go
Normal file
@@ -0,0 +1,47 @@
|
|||||||
|
//go:build nopayments
|
||||||
|
|
||||||
|
package server
|
||||||
|
|
||||||
|
import (
|
||||||
|
"net/http"
|
||||||
|
)
|
||||||
|
|
||||||
|
type stripeAPI interface {
|
||||||
|
CancelSubscription(id string) (string, error)
|
||||||
|
}
|
||||||
|
|
||||||
|
func newStripeAPI() stripeAPI {
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (s *Server) fetchStripePrices() (map[string]int64, error) {
|
||||||
|
return nil, errHTTPNotFound
|
||||||
|
}
|
||||||
|
|
||||||
|
func (s *Server) handleBillingTiersGet(w http.ResponseWriter, _ *http.Request, _ *visitor) error {
|
||||||
|
return errHTTPNotFound
|
||||||
|
}
|
||||||
|
|
||||||
|
func (s *Server) handleAccountBillingSubscriptionCreate(w http.ResponseWriter, r *http.Request, v *visitor) error {
|
||||||
|
return errHTTPNotFound
|
||||||
|
}
|
||||||
|
|
||||||
|
func (s *Server) handleAccountBillingSubscriptionCreateSuccess(w http.ResponseWriter, r *http.Request, v *visitor) error {
|
||||||
|
return errHTTPNotFound
|
||||||
|
}
|
||||||
|
|
||||||
|
func (s *Server) handleAccountBillingSubscriptionUpdate(w http.ResponseWriter, r *http.Request, v *visitor) error {
|
||||||
|
return errHTTPNotFound
|
||||||
|
}
|
||||||
|
|
||||||
|
func (s *Server) handleAccountBillingSubscriptionDelete(w http.ResponseWriter, r *http.Request, v *visitor) error {
|
||||||
|
return errHTTPNotFound
|
||||||
|
}
|
||||||
|
|
||||||
|
func (s *Server) handleAccountBillingPortalSessionCreate(w http.ResponseWriter, r *http.Request, v *visitor) error {
|
||||||
|
return errHTTPNotFound
|
||||||
|
}
|
||||||
|
|
||||||
|
func (s *Server) handleAccountBillingWebhook(_ http.ResponseWriter, r *http.Request, v *visitor) error {
|
||||||
|
return errHTTPNotFound
|
||||||
|
}
|
||||||
@@ -1,3 +1,5 @@
|
|||||||
|
//go:build !nopayments
|
||||||
|
|
||||||
package server
|
package server
|
||||||
|
|
||||||
import (
|
import (
|
||||||
@@ -6,6 +8,7 @@ import (
|
|||||||
"github.com/stretchr/testify/require"
|
"github.com/stretchr/testify/require"
|
||||||
"github.com/stripe/stripe-go/v74"
|
"github.com/stripe/stripe-go/v74"
|
||||||
"golang.org/x/time/rate"
|
"golang.org/x/time/rate"
|
||||||
|
"heckel.io/ntfy/v2/payments"
|
||||||
"heckel.io/ntfy/v2/user"
|
"heckel.io/ntfy/v2/user"
|
||||||
"heckel.io/ntfy/v2/util"
|
"heckel.io/ntfy/v2/util"
|
||||||
"io"
|
"io"
|
||||||
@@ -345,8 +348,8 @@ func TestPayments_Checkout_Success_And_Increase_Rate_Limits_Reset_Visitor(t *tes
|
|||||||
require.Nil(t, u.Tier)
|
require.Nil(t, u.Tier)
|
||||||
require.Equal(t, "", u.Billing.StripeCustomerID)
|
require.Equal(t, "", u.Billing.StripeCustomerID)
|
||||||
require.Equal(t, "", u.Billing.StripeSubscriptionID)
|
require.Equal(t, "", u.Billing.StripeSubscriptionID)
|
||||||
require.Equal(t, stripe.SubscriptionStatus(""), u.Billing.StripeSubscriptionStatus)
|
require.Equal(t, payments.SubscriptionStatus(""), u.Billing.StripeSubscriptionStatus)
|
||||||
require.Equal(t, stripe.PriceRecurringInterval(""), u.Billing.StripeSubscriptionInterval)
|
require.Equal(t, payments.PriceRecurringInterval(""), u.Billing.StripeSubscriptionInterval)
|
||||||
require.Equal(t, int64(0), u.Billing.StripeSubscriptionPaidUntil.Unix())
|
require.Equal(t, int64(0), u.Billing.StripeSubscriptionPaidUntil.Unix())
|
||||||
require.Equal(t, int64(0), u.Billing.StripeSubscriptionCancelAt.Unix())
|
require.Equal(t, int64(0), u.Billing.StripeSubscriptionCancelAt.Unix())
|
||||||
require.Equal(t, int64(0), u.Stats.Messages) // Messages and emails are not persisted for no-tier users!
|
require.Equal(t, int64(0), u.Stats.Messages) // Messages and emails are not persisted for no-tier users!
|
||||||
@@ -362,8 +365,8 @@ func TestPayments_Checkout_Success_And_Increase_Rate_Limits_Reset_Visitor(t *tes
|
|||||||
require.Equal(t, "starter", u.Tier.Code) // Not "pro"
|
require.Equal(t, "starter", u.Tier.Code) // Not "pro"
|
||||||
require.Equal(t, "acct_5555", u.Billing.StripeCustomerID)
|
require.Equal(t, "acct_5555", u.Billing.StripeCustomerID)
|
||||||
require.Equal(t, "sub_1234", u.Billing.StripeSubscriptionID)
|
require.Equal(t, "sub_1234", u.Billing.StripeSubscriptionID)
|
||||||
require.Equal(t, stripe.SubscriptionStatusActive, u.Billing.StripeSubscriptionStatus)
|
require.Equal(t, payments.SubscriptionStatus(stripe.SubscriptionStatusActive), u.Billing.StripeSubscriptionStatus)
|
||||||
require.Equal(t, stripe.PriceRecurringIntervalMonth, u.Billing.StripeSubscriptionInterval)
|
require.Equal(t, payments.PriceRecurringInterval(stripe.PriceRecurringIntervalMonth), u.Billing.StripeSubscriptionInterval)
|
||||||
require.Equal(t, int64(123456789), u.Billing.StripeSubscriptionPaidUntil.Unix())
|
require.Equal(t, int64(123456789), u.Billing.StripeSubscriptionPaidUntil.Unix())
|
||||||
require.Equal(t, int64(0), u.Billing.StripeSubscriptionCancelAt.Unix())
|
require.Equal(t, int64(0), u.Billing.StripeSubscriptionCancelAt.Unix())
|
||||||
require.Equal(t, int64(0), u.Stats.Messages)
|
require.Equal(t, int64(0), u.Stats.Messages)
|
||||||
@@ -473,8 +476,8 @@ func TestPayments_Webhook_Subscription_Updated_Downgrade_From_PastDue_To_Active(
|
|||||||
billing := &user.Billing{
|
billing := &user.Billing{
|
||||||
StripeCustomerID: "acct_5555",
|
StripeCustomerID: "acct_5555",
|
||||||
StripeSubscriptionID: "sub_1234",
|
StripeSubscriptionID: "sub_1234",
|
||||||
StripeSubscriptionStatus: stripe.SubscriptionStatusPastDue,
|
StripeSubscriptionStatus: payments.SubscriptionStatus(stripe.SubscriptionStatusPastDue),
|
||||||
StripeSubscriptionInterval: stripe.PriceRecurringIntervalMonth,
|
StripeSubscriptionInterval: payments.PriceRecurringInterval(stripe.PriceRecurringIntervalMonth),
|
||||||
StripeSubscriptionPaidUntil: time.Unix(123, 0),
|
StripeSubscriptionPaidUntil: time.Unix(123, 0),
|
||||||
StripeSubscriptionCancelAt: time.Unix(456, 0),
|
StripeSubscriptionCancelAt: time.Unix(456, 0),
|
||||||
}
|
}
|
||||||
@@ -517,10 +520,10 @@ func TestPayments_Webhook_Subscription_Updated_Downgrade_From_PastDue_To_Active(
|
|||||||
require.Equal(t, "starter", u.Tier.Code) // Not "pro"
|
require.Equal(t, "starter", u.Tier.Code) // Not "pro"
|
||||||
require.Equal(t, "acct_5555", u.Billing.StripeCustomerID)
|
require.Equal(t, "acct_5555", u.Billing.StripeCustomerID)
|
||||||
require.Equal(t, "sub_1234", u.Billing.StripeSubscriptionID)
|
require.Equal(t, "sub_1234", u.Billing.StripeSubscriptionID)
|
||||||
require.Equal(t, stripe.SubscriptionStatusActive, u.Billing.StripeSubscriptionStatus) // Not "past_due"
|
require.Equal(t, payments.SubscriptionStatus(stripe.SubscriptionStatusActive), u.Billing.StripeSubscriptionStatus) // Not "past_due"
|
||||||
require.Equal(t, stripe.PriceRecurringIntervalYear, u.Billing.StripeSubscriptionInterval) // Not "month"
|
require.Equal(t, payments.PriceRecurringInterval(stripe.PriceRecurringIntervalYear), u.Billing.StripeSubscriptionInterval) // Not "month"
|
||||||
require.Equal(t, int64(1674268231), u.Billing.StripeSubscriptionPaidUntil.Unix()) // Updated
|
require.Equal(t, int64(1674268231), u.Billing.StripeSubscriptionPaidUntil.Unix()) // Updated
|
||||||
require.Equal(t, int64(1674299999), u.Billing.StripeSubscriptionCancelAt.Unix()) // Updated
|
require.Equal(t, int64(1674299999), u.Billing.StripeSubscriptionCancelAt.Unix()) // Updated
|
||||||
|
|
||||||
// Verify that reservations were deleted
|
// Verify that reservations were deleted
|
||||||
r, err := s.userManager.Reservations("phil")
|
r, err := s.userManager.Reservations("phil")
|
||||||
@@ -580,8 +583,8 @@ func TestPayments_Webhook_Subscription_Deleted(t *testing.T) {
|
|||||||
require.Nil(t, s.userManager.ChangeBilling(u.Name, &user.Billing{
|
require.Nil(t, s.userManager.ChangeBilling(u.Name, &user.Billing{
|
||||||
StripeCustomerID: "acct_5555",
|
StripeCustomerID: "acct_5555",
|
||||||
StripeSubscriptionID: "sub_1234",
|
StripeSubscriptionID: "sub_1234",
|
||||||
StripeSubscriptionStatus: stripe.SubscriptionStatusPastDue,
|
StripeSubscriptionStatus: payments.SubscriptionStatus(stripe.SubscriptionStatusPastDue),
|
||||||
StripeSubscriptionInterval: stripe.PriceRecurringIntervalMonth,
|
StripeSubscriptionInterval: payments.PriceRecurringInterval(stripe.PriceRecurringIntervalMonth),
|
||||||
StripeSubscriptionPaidUntil: time.Unix(123, 0),
|
StripeSubscriptionPaidUntil: time.Unix(123, 0),
|
||||||
StripeSubscriptionCancelAt: time.Unix(0, 0),
|
StripeSubscriptionCancelAt: time.Unix(0, 0),
|
||||||
}))
|
}))
|
||||||
@@ -598,7 +601,7 @@ func TestPayments_Webhook_Subscription_Deleted(t *testing.T) {
|
|||||||
require.Nil(t, u.Tier)
|
require.Nil(t, u.Tier)
|
||||||
require.Equal(t, "acct_5555", u.Billing.StripeCustomerID)
|
require.Equal(t, "acct_5555", u.Billing.StripeCustomerID)
|
||||||
require.Equal(t, "", u.Billing.StripeSubscriptionID)
|
require.Equal(t, "", u.Billing.StripeSubscriptionID)
|
||||||
require.Equal(t, stripe.SubscriptionStatus(""), u.Billing.StripeSubscriptionStatus)
|
require.Equal(t, payments.SubscriptionStatus(""), u.Billing.StripeSubscriptionStatus)
|
||||||
require.Equal(t, int64(0), u.Billing.StripeSubscriptionPaidUntil.Unix())
|
require.Equal(t, int64(0), u.Billing.StripeSubscriptionPaidUntil.Unix())
|
||||||
require.Equal(t, int64(0), u.Billing.StripeSubscriptionCancelAt.Unix())
|
require.Equal(t, int64(0), u.Billing.StripeSubscriptionCancelAt.Unix())
|
||||||
|
|
||||||
|
|||||||
@@ -23,7 +23,6 @@ import (
|
|||||||
"testing"
|
"testing"
|
||||||
"time"
|
"time"
|
||||||
|
|
||||||
"github.com/SherClockHolmes/webpush-go"
|
|
||||||
"github.com/stretchr/testify/require"
|
"github.com/stretchr/testify/require"
|
||||||
"heckel.io/ntfy/v2/log"
|
"heckel.io/ntfy/v2/log"
|
||||||
"heckel.io/ntfy/v2/util"
|
"heckel.io/ntfy/v2/util"
|
||||||
@@ -281,30 +280,6 @@ func TestServer_WebEnabled(t *testing.T) {
|
|||||||
rr = request(t, s2, "GET", "/app.html", "", nil)
|
rr = request(t, s2, "GET", "/app.html", "", nil)
|
||||||
require.Equal(t, 200, rr.Code)
|
require.Equal(t, 200, rr.Code)
|
||||||
}
|
}
|
||||||
|
|
||||||
func TestServer_WebPushEnabled(t *testing.T) {
|
|
||||||
conf := newTestConfig(t)
|
|
||||||
conf.WebRoot = "" // Disable web app
|
|
||||||
s := newTestServer(t, conf)
|
|
||||||
|
|
||||||
rr := request(t, s, "GET", "/manifest.webmanifest", "", nil)
|
|
||||||
require.Equal(t, 404, rr.Code)
|
|
||||||
|
|
||||||
conf2 := newTestConfig(t)
|
|
||||||
s2 := newTestServer(t, conf2)
|
|
||||||
|
|
||||||
rr = request(t, s2, "GET", "/manifest.webmanifest", "", nil)
|
|
||||||
require.Equal(t, 404, rr.Code)
|
|
||||||
|
|
||||||
conf3 := newTestConfigWithWebPush(t)
|
|
||||||
s3 := newTestServer(t, conf3)
|
|
||||||
|
|
||||||
rr = request(t, s3, "GET", "/manifest.webmanifest", "", nil)
|
|
||||||
require.Equal(t, 200, rr.Code)
|
|
||||||
require.Equal(t, "application/manifest+json", rr.Header().Get("Content-Type"))
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
func TestServer_PublishLargeMessage(t *testing.T) {
|
func TestServer_PublishLargeMessage(t *testing.T) {
|
||||||
c := newTestConfig(t)
|
c := newTestConfig(t)
|
||||||
c.AttachmentCacheDir = "" // Disable attachments
|
c.AttachmentCacheDir = "" // Disable attachments
|
||||||
@@ -3257,17 +3232,6 @@ func newTestConfigWithAuthFile(t *testing.T) *Config {
|
|||||||
return conf
|
return conf
|
||||||
}
|
}
|
||||||
|
|
||||||
func newTestConfigWithWebPush(t *testing.T) *Config {
|
|
||||||
conf := newTestConfig(t)
|
|
||||||
privateKey, publicKey, err := webpush.GenerateVAPIDKeys()
|
|
||||||
require.Nil(t, err)
|
|
||||||
conf.WebPushFile = filepath.Join(t.TempDir(), "webpush.db")
|
|
||||||
conf.WebPushEmailAddress = "testing@example.com"
|
|
||||||
conf.WebPushPrivateKey = privateKey
|
|
||||||
conf.WebPushPublicKey = publicKey
|
|
||||||
return conf
|
|
||||||
}
|
|
||||||
|
|
||||||
func newTestServer(t *testing.T, config *Config) *Server {
|
func newTestServer(t *testing.T, config *Config) *Server {
|
||||||
server, err := New(config)
|
server, err := New(config)
|
||||||
require.Nil(t, err)
|
require.Nil(t, err)
|
||||||
|
|||||||
@@ -1,3 +1,5 @@
|
|||||||
|
//go:build !nowebpush
|
||||||
|
|
||||||
package server
|
package server
|
||||||
|
|
||||||
import (
|
import (
|
||||||
@@ -13,6 +15,10 @@ import (
|
|||||||
)
|
)
|
||||||
|
|
||||||
const (
|
const (
|
||||||
|
// WebPushAvailable is a constant used to indicate that WebPush support is available.
|
||||||
|
// It can be disabled with the 'nowebpush' build tag.
|
||||||
|
WebPushAvailable = true
|
||||||
|
|
||||||
webPushTopicSubscribeLimit = 50
|
webPushTopicSubscribeLimit = 50
|
||||||
)
|
)
|
||||||
|
|
||||||
|
|||||||
29
server/server_webpush_dummy.go
Normal file
29
server/server_webpush_dummy.go
Normal file
@@ -0,0 +1,29 @@
|
|||||||
|
//go:build nowebpush
|
||||||
|
|
||||||
|
package server
|
||||||
|
|
||||||
|
import (
|
||||||
|
"net/http"
|
||||||
|
)
|
||||||
|
|
||||||
|
const (
|
||||||
|
// WebPushAvailable is a constant used to indicate that WebPush support is available.
|
||||||
|
// It can be disabled with the 'nowebpush' build tag.
|
||||||
|
WebPushAvailable = false
|
||||||
|
)
|
||||||
|
|
||||||
|
func (s *Server) handleWebPushUpdate(w http.ResponseWriter, r *http.Request, v *visitor) error {
|
||||||
|
return errHTTPNotFound
|
||||||
|
}
|
||||||
|
|
||||||
|
func (s *Server) handleWebPushDelete(w http.ResponseWriter, r *http.Request, _ *visitor) error {
|
||||||
|
return errHTTPNotFound
|
||||||
|
}
|
||||||
|
|
||||||
|
func (s *Server) publishToWebPushEndpoints(v *visitor, m *message) {
|
||||||
|
// Nothing to see here
|
||||||
|
}
|
||||||
|
|
||||||
|
func (s *Server) pruneAndNotifyWebPushSubscriptions() {
|
||||||
|
// Nothing to see here
|
||||||
|
}
|
||||||
@@ -1,8 +1,11 @@
|
|||||||
|
//go:build !nowebpush
|
||||||
|
|
||||||
package server
|
package server
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"encoding/json"
|
"encoding/json"
|
||||||
"fmt"
|
"fmt"
|
||||||
|
"github.com/SherClockHolmes/webpush-go"
|
||||||
"github.com/stretchr/testify/require"
|
"github.com/stretchr/testify/require"
|
||||||
"heckel.io/ntfy/v2/user"
|
"heckel.io/ntfy/v2/user"
|
||||||
"heckel.io/ntfy/v2/util"
|
"heckel.io/ntfy/v2/util"
|
||||||
@@ -10,6 +13,7 @@ import (
|
|||||||
"net/http"
|
"net/http"
|
||||||
"net/http/httptest"
|
"net/http/httptest"
|
||||||
"net/netip"
|
"net/netip"
|
||||||
|
"path/filepath"
|
||||||
"strings"
|
"strings"
|
||||||
"sync/atomic"
|
"sync/atomic"
|
||||||
"testing"
|
"testing"
|
||||||
@@ -20,6 +24,28 @@ const (
|
|||||||
testWebPushEndpoint = "https://updates.push.services.mozilla.com/wpush/v1/AAABBCCCDDEEEFFF"
|
testWebPushEndpoint = "https://updates.push.services.mozilla.com/wpush/v1/AAABBCCCDDEEEFFF"
|
||||||
)
|
)
|
||||||
|
|
||||||
|
func TestServer_WebPush_Enabled(t *testing.T) {
|
||||||
|
conf := newTestConfig(t)
|
||||||
|
conf.WebRoot = "" // Disable web app
|
||||||
|
s := newTestServer(t, conf)
|
||||||
|
|
||||||
|
rr := request(t, s, "GET", "/manifest.webmanifest", "", nil)
|
||||||
|
require.Equal(t, 404, rr.Code)
|
||||||
|
|
||||||
|
conf2 := newTestConfig(t)
|
||||||
|
s2 := newTestServer(t, conf2)
|
||||||
|
|
||||||
|
rr = request(t, s2, "GET", "/manifest.webmanifest", "", nil)
|
||||||
|
require.Equal(t, 404, rr.Code)
|
||||||
|
|
||||||
|
conf3 := newTestConfigWithWebPush(t)
|
||||||
|
s3 := newTestServer(t, conf3)
|
||||||
|
|
||||||
|
rr = request(t, s3, "GET", "/manifest.webmanifest", "", nil)
|
||||||
|
require.Equal(t, 200, rr.Code)
|
||||||
|
require.Equal(t, "application/manifest+json", rr.Header().Get("Content-Type"))
|
||||||
|
|
||||||
|
}
|
||||||
func TestServer_WebPush_Disabled(t *testing.T) {
|
func TestServer_WebPush_Disabled(t *testing.T) {
|
||||||
s := newTestServer(t, newTestConfig(t))
|
s := newTestServer(t, newTestConfig(t))
|
||||||
|
|
||||||
@@ -254,3 +280,14 @@ func requireSubscriptionCount(t *testing.T, s *Server, topic string, expectedLen
|
|||||||
require.Nil(t, err)
|
require.Nil(t, err)
|
||||||
require.Len(t, subs, expectedLength)
|
require.Len(t, subs, expectedLength)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func newTestConfigWithWebPush(t *testing.T) *Config {
|
||||||
|
conf := newTestConfig(t)
|
||||||
|
privateKey, publicKey, err := webpush.GenerateVAPIDKeys()
|
||||||
|
require.Nil(t, err)
|
||||||
|
conf.WebPushFile = filepath.Join(t.TempDir(), "webpush.db")
|
||||||
|
conf.WebPushEmailAddress = "testing@example.com"
|
||||||
|
conf.WebPushPrivateKey = privateKey
|
||||||
|
conf.WebPushPublicKey = publicKey
|
||||||
|
return conf
|
||||||
|
}
|
||||||
|
|||||||
@@ -7,9 +7,9 @@ import (
|
|||||||
"errors"
|
"errors"
|
||||||
"fmt"
|
"fmt"
|
||||||
"github.com/mattn/go-sqlite3"
|
"github.com/mattn/go-sqlite3"
|
||||||
"github.com/stripe/stripe-go/v74"
|
|
||||||
"golang.org/x/crypto/bcrypt"
|
"golang.org/x/crypto/bcrypt"
|
||||||
"heckel.io/ntfy/v2/log"
|
"heckel.io/ntfy/v2/log"
|
||||||
|
"heckel.io/ntfy/v2/payments"
|
||||||
"heckel.io/ntfy/v2/util"
|
"heckel.io/ntfy/v2/util"
|
||||||
"net/netip"
|
"net/netip"
|
||||||
"path/filepath"
|
"path/filepath"
|
||||||
@@ -1242,12 +1242,12 @@ func (a *Manager) readUser(rows *sql.Rows) (*User, error) {
|
|||||||
Calls: calls,
|
Calls: calls,
|
||||||
},
|
},
|
||||||
Billing: &Billing{
|
Billing: &Billing{
|
||||||
StripeCustomerID: stripeCustomerID.String, // May be empty
|
StripeCustomerID: stripeCustomerID.String, // May be empty
|
||||||
StripeSubscriptionID: stripeSubscriptionID.String, // May be empty
|
StripeSubscriptionID: stripeSubscriptionID.String, // May be empty
|
||||||
StripeSubscriptionStatus: stripe.SubscriptionStatus(stripeSubscriptionStatus.String), // May be empty
|
StripeSubscriptionStatus: payments.SubscriptionStatus(stripeSubscriptionStatus.String), // May be empty
|
||||||
StripeSubscriptionInterval: stripe.PriceRecurringInterval(stripeSubscriptionInterval.String), // May be empty
|
StripeSubscriptionInterval: payments.PriceRecurringInterval(stripeSubscriptionInterval.String), // May be empty
|
||||||
StripeSubscriptionPaidUntil: time.Unix(stripeSubscriptionPaidUntil.Int64, 0), // May be zero
|
StripeSubscriptionPaidUntil: time.Unix(stripeSubscriptionPaidUntil.Int64, 0), // May be zero
|
||||||
StripeSubscriptionCancelAt: time.Unix(stripeSubscriptionCancelAt.Int64, 0), // May be zero
|
StripeSubscriptionCancelAt: time.Unix(stripeSubscriptionCancelAt.Int64, 0), // May be zero
|
||||||
},
|
},
|
||||||
Deleted: deleted.Valid,
|
Deleted: deleted.Valid,
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -4,7 +4,6 @@ import (
|
|||||||
"database/sql"
|
"database/sql"
|
||||||
"fmt"
|
"fmt"
|
||||||
"github.com/stretchr/testify/require"
|
"github.com/stretchr/testify/require"
|
||||||
"github.com/stripe/stripe-go/v74"
|
|
||||||
"golang.org/x/crypto/bcrypt"
|
"golang.org/x/crypto/bcrypt"
|
||||||
"heckel.io/ntfy/v2/util"
|
"heckel.io/ntfy/v2/util"
|
||||||
"net/netip"
|
"net/netip"
|
||||||
@@ -164,8 +163,8 @@ func TestManager_AddUser_And_Query(t *testing.T) {
|
|||||||
require.Nil(t, a.ChangeBilling("user", &Billing{
|
require.Nil(t, a.ChangeBilling("user", &Billing{
|
||||||
StripeCustomerID: "acct_123",
|
StripeCustomerID: "acct_123",
|
||||||
StripeSubscriptionID: "sub_123",
|
StripeSubscriptionID: "sub_123",
|
||||||
StripeSubscriptionStatus: stripe.SubscriptionStatusActive,
|
StripeSubscriptionStatus: "active",
|
||||||
StripeSubscriptionInterval: stripe.PriceRecurringIntervalMonth,
|
StripeSubscriptionInterval: "month",
|
||||||
StripeSubscriptionPaidUntil: time.Now().Add(time.Hour),
|
StripeSubscriptionPaidUntil: time.Now().Add(time.Hour),
|
||||||
StripeSubscriptionCancelAt: time.Unix(0, 0),
|
StripeSubscriptionCancelAt: time.Unix(0, 0),
|
||||||
}))
|
}))
|
||||||
|
|||||||
@@ -2,8 +2,8 @@ package user
|
|||||||
|
|
||||||
import (
|
import (
|
||||||
"errors"
|
"errors"
|
||||||
"github.com/stripe/stripe-go/v74"
|
|
||||||
"heckel.io/ntfy/v2/log"
|
"heckel.io/ntfy/v2/log"
|
||||||
|
"heckel.io/ntfy/v2/payments"
|
||||||
"net/netip"
|
"net/netip"
|
||||||
"strings"
|
"strings"
|
||||||
"time"
|
"time"
|
||||||
@@ -140,8 +140,8 @@ type Stats struct {
|
|||||||
type Billing struct {
|
type Billing struct {
|
||||||
StripeCustomerID string
|
StripeCustomerID string
|
||||||
StripeSubscriptionID string
|
StripeSubscriptionID string
|
||||||
StripeSubscriptionStatus stripe.SubscriptionStatus
|
StripeSubscriptionStatus payments.SubscriptionStatus
|
||||||
StripeSubscriptionInterval stripe.PriceRecurringInterval
|
StripeSubscriptionInterval payments.PriceRecurringInterval
|
||||||
StripeSubscriptionPaidUntil time.Time
|
StripeSubscriptionPaidUntil time.Time
|
||||||
StripeSubscriptionCancelAt time.Time
|
StripeSubscriptionCancelAt time.Time
|
||||||
}
|
}
|
||||||
|
|||||||
Reference in New Issue
Block a user