mirror of
https://github.com/binwiederhier/ntfy.git
synced 2026-11-10 14:44:04 +08:00
Merge remote-tracking branch 'origin/main' into cluster-drain
# Conflicts: # cluster/cluster_mesh_test.go # server/server.go # server/server_cluster_test.go
This commit is contained in:
commit
70781daf2d
27 files changed
+1362
-392
No files matched your search
+80
-52
@@ -270,6 +270,40 @@ func New(conf *Config) (*Server, error) {
|
||||
CallFormat: conf.TwilioCallFormat,
|
||||
BuildVersion: conf.BuildVersion,
|
||||
})
|
||||
var banner *ban.Service
|
||||
if conf.BanFile != "" {
|
||||
banner = ban.NewService(&ban.Config{
|
||||
File: conf.BanFile,
|
||||
Window: conf.BanWindow,
|
||||
Threshold: conf.BanThreshold,
|
||||
Weights: conf.BanWeights,
|
||||
PrefixBitsIPv4: conf.VisitorPrefixBitsIPv4,
|
||||
PrefixBitsIPv6: conf.VisitorPrefixBitsIPv6,
|
||||
})
|
||||
}
|
||||
// Peers reach this node on the dedicated cluster listener, never on the public ones
|
||||
advertiseURL := conf.ClusterAdvertiseURL
|
||||
if advertiseURL == "" && conf.ClusterListen != "" {
|
||||
advertiseURL = "http://" + conf.ClusterListen
|
||||
}
|
||||
s := &Server{
|
||||
config: conf,
|
||||
db: pool,
|
||||
messageCache: messageCache,
|
||||
webPush: wp,
|
||||
attachment: attachmentStore,
|
||||
twilio: twilioClient,
|
||||
mailer: sender,
|
||||
ban: banner,
|
||||
topics: topics,
|
||||
messages: messages,
|
||||
messagesHistory: []int64{messages},
|
||||
messagesFlushed: messages, // The loaded total is already persisted; only new publishes are deltas
|
||||
visitors: make(map[string]*visitor),
|
||||
stripe: stripe,
|
||||
streams: newStreamRegistry(),
|
||||
}
|
||||
s.priceCache = util.NewLookupCache(s.fetchStripePrices, conf.StripePriceCacheDuration)
|
||||
var userManager *user.Manager
|
||||
if conf.AuthFile != "" || pool != nil {
|
||||
authConfig := &user.Config{
|
||||
@@ -285,6 +319,8 @@ func New(conf *Config) (*Server, error) {
|
||||
QueueWriterInterval: conf.AuthStatsQueueWriterInterval,
|
||||
AccessCacheEnabled: conf.AuthAccessCacheEnabled,
|
||||
AccessCacheReloadInterval: conf.AuthAccessCacheReloadInterval,
|
||||
AccessCacheChangedFunc: s.reauthorizeSubscribers,
|
||||
AccessWrittenFunc: s.broadcastAccessChanged,
|
||||
}
|
||||
if pool != nil {
|
||||
userManager, err = user.NewPostgresManager(pool, authConfig)
|
||||
@@ -309,57 +345,22 @@ func New(conf *Config) (*Server, error) {
|
||||
}
|
||||
firebaseClient = newFirebaseClient(sender, auther)
|
||||
}
|
||||
var banner *ban.Service
|
||||
if conf.BanFile != "" {
|
||||
banner = ban.NewService(&ban.Config{
|
||||
File: conf.BanFile,
|
||||
Window: conf.BanWindow,
|
||||
Threshold: conf.BanThreshold,
|
||||
Weights: conf.BanWeights,
|
||||
PrefixBitsIPv4: conf.VisitorPrefixBitsIPv4,
|
||||
PrefixBitsIPv6: conf.VisitorPrefixBitsIPv6,
|
||||
})
|
||||
}
|
||||
// Peers reach this node on the dedicated cluster listener, never on the public ones
|
||||
advertiseURL := conf.ClusterAdvertiseURL
|
||||
if advertiseURL == "" && conf.ClusterListen != "" {
|
||||
advertiseURL = "http://" + conf.ClusterListen
|
||||
}
|
||||
s := &Server{
|
||||
config: conf,
|
||||
db: pool,
|
||||
messageCache: messageCache,
|
||||
webPush: wp,
|
||||
attachment: attachmentStore,
|
||||
firebaseClient: firebaseClient,
|
||||
twilio: twilioClient,
|
||||
mailer: sender,
|
||||
ban: banner,
|
||||
topics: topics,
|
||||
userManager: userManager,
|
||||
messages: messages,
|
||||
messagesHistory: []int64{messages},
|
||||
messagesFlushed: messages, // The loaded total is already persisted; only new publishes are deltas
|
||||
visitors: make(map[string]*visitor),
|
||||
stripe: stripe,
|
||||
streams: newStreamRegistry(),
|
||||
}
|
||||
// Everything from here on needs the server itself: the price cache calls a method on it, and
|
||||
// the cluster delivers peer messages through callbacks into it (which is how the cluster
|
||||
// package stays independent of the server)
|
||||
s.priceCache = util.NewLookupCache(s.fetchStripePrices, conf.StripePriceCacheDuration)
|
||||
s.userManager = userManager
|
||||
s.firebaseClient = firebaseClient
|
||||
if pool != nil {
|
||||
metrics.SetDBPoolsFunc(s.dbPoolStats)
|
||||
}
|
||||
s.cluster, err = cluster.New(&cluster.Config{
|
||||
Enabled: conf.ClusterListen != "", // Setting experimental-cluster-listen implicitly enables clustering
|
||||
NodeID: cluster.NodeID(conf.ClusterNodeID),
|
||||
AdvertiseURL: advertiseURL,
|
||||
Role: conf.ClusterRole,
|
||||
Secret: conf.ClusterSecret,
|
||||
BatchLinger: conf.ClusterBatchLinger,
|
||||
IsolatedFunc: s.maybeCloseLocalSubscribers,
|
||||
MaxMessageBytes: int64(conf.MessageSizeLimit)*4 + 1024, // Envelope overhead over the raw message
|
||||
Enabled: conf.ClusterListen != "", // Setting experimental-cluster-listen implicitly enables clustering
|
||||
NodeID: cluster.NodeID(conf.ClusterNodeID),
|
||||
AdvertiseURL: advertiseURL,
|
||||
Role: conf.ClusterRole,
|
||||
Secret: conf.ClusterSecret,
|
||||
BatchLinger: conf.ClusterBatchLinger,
|
||||
UsersDeletedFunc: s.handleUsersDeleted,
|
||||
AccessChangedFunc: s.handlePeerAccessChanged,
|
||||
IsolatedFunc: s.maybeCloseLocalSubscribers,
|
||||
MaxMessageBytes: int64(conf.MessageSizeLimit)*4 + 1024, // Envelope overhead over the raw message
|
||||
}, pool, s.deliverFromBus)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
@@ -408,6 +409,11 @@ func createAttachmentStore(conf *Config, messageCache *message.Cache) (*attachme
|
||||
return nil, nil
|
||||
}
|
||||
|
||||
// testHookBeforeSubscribe, if non-nil, is called by the subscribe handlers after the request was
|
||||
// authorized and before its stream subscribes. Tests use it to land an access change in between;
|
||||
// it is always nil in production.
|
||||
var testHookBeforeSubscribe func()
|
||||
|
||||
// Run executes the main server. It listens on HTTP (+ HTTPS, if configured), and starts
|
||||
// a manager go routine to print stats and prune messages.
|
||||
func (s *Server) Run() error {
|
||||
@@ -1646,17 +1652,23 @@ func (s *Server) handleSubscribeHTTP(w http.ResponseWriter, r *http.Request, v *
|
||||
w.Header().Set("Incremental", "?1") // Ask proxies to forward the stream without buffering (RFC 10036)
|
||||
ctx, cancel := context.WithCancel(context.Background())
|
||||
defer cancel()
|
||||
if testHookBeforeSubscribe != nil {
|
||||
testHookBeforeSubscribe()
|
||||
}
|
||||
streamID := s.streams.Add(cancel)
|
||||
defer s.streams.Remove(streamID)
|
||||
subscriberIDs := make([]int, 0)
|
||||
for _, t := range topics {
|
||||
subscriberIDs = append(subscriberIDs, t.Subscribe(sub, v.MaybeUserID(), cancel))
|
||||
subscriberIDs = append(subscriberIDs, t.Subscribe(sub, v.User(), cancel))
|
||||
}
|
||||
defer func() {
|
||||
for i, subscriberID := range subscriberIDs {
|
||||
topics[i].Unsubscribe(subscriberID) // Order!
|
||||
}
|
||||
}()
|
||||
if err := s.authorizeSubscribed(v, topics); err != nil {
|
||||
return err
|
||||
}
|
||||
if err := sub(v, model.NewOpenMessage(topicsStr)); err != nil { // Send out open message
|
||||
return err
|
||||
}
|
||||
@@ -1799,17 +1811,23 @@ func (s *Server) handleSubscribeWS(w http.ResponseWriter, r *http.Request, v *vi
|
||||
}
|
||||
return s.sendOldMessages(w, topics, since, scheduled, v, sub)
|
||||
}
|
||||
if testHookBeforeSubscribe != nil {
|
||||
testHookBeforeSubscribe()
|
||||
}
|
||||
streamID := s.streams.Add(cancel)
|
||||
defer s.streams.Remove(streamID)
|
||||
subscriberIDs := make([]int, 0)
|
||||
for _, t := range topics {
|
||||
subscriberIDs = append(subscriberIDs, t.Subscribe(sub, v.MaybeUserID(), cancel))
|
||||
subscriberIDs = append(subscriberIDs, t.Subscribe(sub, v.User(), cancel))
|
||||
}
|
||||
defer func() {
|
||||
for i, subscriberID := range subscriberIDs {
|
||||
topics[i].Unsubscribe(subscriberID) // Order!
|
||||
}
|
||||
}()
|
||||
if err := s.authorizeSubscribed(v, topics); err != nil {
|
||||
return err
|
||||
}
|
||||
if err := sub(v, model.NewOpenMessage(topicsStr)); err != nil { // Send out open message
|
||||
return err
|
||||
}
|
||||
@@ -2068,14 +2086,12 @@ func (s *Server) topicFromID(v *visitor, id string) (*topic, error) {
|
||||
|
||||
// topicsFromPattern returns a list of topics matching the given pattern, but it does not create them.
|
||||
func (s *Server) topicsFromPattern(pattern string) ([]*topic, error) {
|
||||
s.mu.RLock()
|
||||
defer s.mu.RUnlock()
|
||||
patternRegexp, err := regexp.Compile("^" + strings.ReplaceAll(pattern, "*", ".*") + "$")
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
topics := make([]*topic, 0)
|
||||
for _, t := range s.topics {
|
||||
for _, t := range s.allTopics() {
|
||||
if patternRegexp.MatchString(t.ID) {
|
||||
topics = append(topics, t)
|
||||
}
|
||||
@@ -2083,6 +2099,18 @@ func (s *Server) topicsFromPattern(pattern string) ([]*topic, error) {
|
||||
return topics, nil
|
||||
}
|
||||
|
||||
// allTopics returns all topics. Callers that scan them (~313k on ntfy.sh) do so without s.mu held,
|
||||
// which every request that creates a visitor or topic needs.
|
||||
func (s *Server) allTopics() []*topic {
|
||||
s.mu.RLock()
|
||||
defer s.mu.RUnlock()
|
||||
topics := make([]*topic, 0, len(s.topics))
|
||||
for _, t := range s.topics {
|
||||
topics = append(topics, t)
|
||||
}
|
||||
return topics
|
||||
}
|
||||
|
||||
func (s *Server) runSMTPServer() error {
|
||||
s.smtpServerBackend = newMailBackend(s.config, s.handle)
|
||||
s.smtpServer = smtp.NewServer(s.smtpServerBackend)
|
||||
|
||||
Reference in new issue
Block a user