Attachment size limit
This commit is contained in:
		
							parent
							
								
									24eb27d41c
								
							
						
					
					
						commit
						5eca20469f
					
				
					 5 changed files with 76 additions and 7 deletions
				
			
		
							
								
								
									
										13
									
								
								cmd/serve.go
									
										
									
									
									
								
							
							
						
						
									
										13
									
								
								cmd/serve.go
									
										
									
									
									
								
							|  | @ -21,6 +21,7 @@ var flagsServe = []cli.Flag{ | ||||||
| 	altsrc.NewStringFlag(&cli.StringFlag{Name: "cache-file", Aliases: []string{"C"}, EnvVars: []string{"NTFY_CACHE_FILE"}, Usage: "cache file used for message caching"}), | 	altsrc.NewStringFlag(&cli.StringFlag{Name: "cache-file", Aliases: []string{"C"}, EnvVars: []string{"NTFY_CACHE_FILE"}, Usage: "cache file used for message caching"}), | ||||||
| 	altsrc.NewDurationFlag(&cli.DurationFlag{Name: "cache-duration", Aliases: []string{"b"}, EnvVars: []string{"NTFY_CACHE_DURATION"}, Value: server.DefaultCacheDuration, Usage: "buffer messages for this time to allow `since` requests"}), | 	altsrc.NewDurationFlag(&cli.DurationFlag{Name: "cache-duration", Aliases: []string{"b"}, EnvVars: []string{"NTFY_CACHE_DURATION"}, Value: server.DefaultCacheDuration, Usage: "buffer messages for this time to allow `since` requests"}), | ||||||
| 	altsrc.NewStringFlag(&cli.StringFlag{Name: "attachment-cache-dir", EnvVars: []string{"NTFY_ATTACHMENT_CACHE_DIR"}, Usage: "cache directory for attached files"}), | 	altsrc.NewStringFlag(&cli.StringFlag{Name: "attachment-cache-dir", EnvVars: []string{"NTFY_ATTACHMENT_CACHE_DIR"}, Usage: "cache directory for attached files"}), | ||||||
|  | 	altsrc.NewStringFlag(&cli.StringFlag{Name: "attachment-size-limit", Aliases: []string{"A"}, EnvVars: []string{"NTFY_ATTACHMENT_SIZE_LIMIT"}, DefaultText: "15M", Usage: "attachment size limit (e.g. 10k, 2M)"}), | ||||||
| 	altsrc.NewDurationFlag(&cli.DurationFlag{Name: "keepalive-interval", Aliases: []string{"k"}, EnvVars: []string{"NTFY_KEEPALIVE_INTERVAL"}, Value: server.DefaultKeepaliveInterval, Usage: "interval of keepalive messages"}), | 	altsrc.NewDurationFlag(&cli.DurationFlag{Name: "keepalive-interval", Aliases: []string{"k"}, EnvVars: []string{"NTFY_KEEPALIVE_INTERVAL"}, Value: server.DefaultKeepaliveInterval, Usage: "interval of keepalive messages"}), | ||||||
| 	altsrc.NewDurationFlag(&cli.DurationFlag{Name: "manager-interval", Aliases: []string{"m"}, EnvVars: []string{"NTFY_MANAGER_INTERVAL"}, Value: server.DefaultManagerInterval, Usage: "interval of for message pruning and stats printing"}), | 	altsrc.NewDurationFlag(&cli.DurationFlag{Name: "manager-interval", Aliases: []string{"m"}, EnvVars: []string{"NTFY_MANAGER_INTERVAL"}, Value: server.DefaultManagerInterval, Usage: "interval of for message pruning and stats printing"}), | ||||||
| 	altsrc.NewStringFlag(&cli.StringFlag{Name: "smtp-sender-addr", EnvVars: []string{"NTFY_SMTP_SENDER_ADDR"}, Usage: "SMTP server address (host:port) for outgoing emails"}), | 	altsrc.NewStringFlag(&cli.StringFlag{Name: "smtp-sender-addr", EnvVars: []string{"NTFY_SMTP_SENDER_ADDR"}, Usage: "SMTP server address (host:port) for outgoing emails"}), | ||||||
|  | @ -71,6 +72,7 @@ func execServe(c *cli.Context) error { | ||||||
| 	cacheFile := c.String("cache-file") | 	cacheFile := c.String("cache-file") | ||||||
| 	cacheDuration := c.Duration("cache-duration") | 	cacheDuration := c.Duration("cache-duration") | ||||||
| 	attachmentCacheDir := c.String("attachment-cache-dir") | 	attachmentCacheDir := c.String("attachment-cache-dir") | ||||||
|  | 	attachmentSizeLimitStr := c.String("attachment-size-limit") | ||||||
| 	keepaliveInterval := c.Duration("keepalive-interval") | 	keepaliveInterval := c.Duration("keepalive-interval") | ||||||
| 	managerInterval := c.Duration("manager-interval") | 	managerInterval := c.Duration("manager-interval") | ||||||
| 	smtpSenderAddr := c.String("smtp-sender-addr") | 	smtpSenderAddr := c.String("smtp-sender-addr") | ||||||
|  | @ -109,6 +111,16 @@ func execServe(c *cli.Context) error { | ||||||
| 		return errors.New("if smtp-server-listen is set, smtp-server-domain must also be set") | 		return errors.New("if smtp-server-listen is set, smtp-server-domain must also be set") | ||||||
| 	} | 	} | ||||||
| 
 | 
 | ||||||
|  | 	// Convert | ||||||
|  | 	attachmentSizeLimit := server.DefaultAttachmentSizeLimit | ||||||
|  | 	if attachmentSizeLimitStr != "" { | ||||||
|  | 		var err error | ||||||
|  | 		attachmentSizeLimit, err = util.ParseSize(attachmentSizeLimitStr) | ||||||
|  | 		if err != nil { | ||||||
|  | 			return err | ||||||
|  | 		} | ||||||
|  | 	} | ||||||
|  | 
 | ||||||
| 	// Run server | 	// Run server | ||||||
| 	conf := server.NewConfig() | 	conf := server.NewConfig() | ||||||
| 	conf.BaseURL = baseURL | 	conf.BaseURL = baseURL | ||||||
|  | @ -120,6 +132,7 @@ func execServe(c *cli.Context) error { | ||||||
| 	conf.CacheFile = cacheFile | 	conf.CacheFile = cacheFile | ||||||
| 	conf.CacheDuration = cacheDuration | 	conf.CacheDuration = cacheDuration | ||||||
| 	conf.AttachmentCacheDir = attachmentCacheDir | 	conf.AttachmentCacheDir = attachmentCacheDir | ||||||
|  | 	conf.AttachmentSizeLimit = attachmentSizeLimit | ||||||
| 	conf.KeepaliveInterval = keepaliveInterval | 	conf.KeepaliveInterval = keepaliveInterval | ||||||
| 	conf.ManagerInterval = managerInterval | 	conf.ManagerInterval = managerInterval | ||||||
| 	conf.SMTPSenderAddr = smtpSenderAddr | 	conf.SMTPSenderAddr = smtpSenderAddr | ||||||
|  |  | ||||||
|  | @ -14,7 +14,7 @@ const ( | ||||||
| 	DefaultMinDelay                  = 10 * time.Second | 	DefaultMinDelay                  = 10 * time.Second | ||||||
| 	DefaultMaxDelay                  = 3 * 24 * time.Hour | 	DefaultMaxDelay                  = 3 * 24 * time.Hour | ||||||
| 	DefaultMessageLimit              = 4096 // Bytes | 	DefaultMessageLimit              = 4096 // Bytes | ||||||
| 	DefaultAttachmentSizeLimit       = 15 * 1024 * 1024 | 	DefaultAttachmentSizeLimit       = int64(15 * 1024 * 1024) | ||||||
| 	DefaultAttachmentSizePreviewMax  = 20 * 1024 * 1024 // Bytes | 	DefaultAttachmentSizePreviewMax  = 20 * 1024 * 1024 // Bytes | ||||||
| 	DefaultAttachmentExpiryDuration  = 3 * time.Hour | 	DefaultAttachmentExpiryDuration  = 3 * time.Hour | ||||||
| 	DefaultFirebaseKeepaliveInterval = 3 * time.Hour // Not too frequently to save battery | 	DefaultFirebaseKeepaliveInterval = 3 * time.Hour // Not too frequently to save battery | ||||||
|  |  | ||||||
|  | @ -32,10 +32,10 @@ type message struct { | ||||||
| 
 | 
 | ||||||
| type attachment struct { | type attachment struct { | ||||||
| 	Name       string `json:"name"` | 	Name       string `json:"name"` | ||||||
| 	Type       string `json:"type"` | 	Type       string `json:"type,omitempty"` | ||||||
| 	Size       int64  `json:"size"` | 	Size       int64  `json:"size,omitempty"` | ||||||
| 	Expires    int64  `json:"expires"` | 	Expires    int64  `json:"expires,omitempty"` | ||||||
| 	PreviewURL string `json:"preview_url"` | 	PreviewURL string `json:"preview_url,omitempty"` | ||||||
| 	URL        string `json:"url"` | 	URL        string `json:"url"` | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
|  |  | ||||||
							
								
								
									
										29
									
								
								util/util.go
									
										
									
									
									
								
							
							
						
						
									
										29
									
								
								util/util.go
									
										
									
									
									
								
							|  | @ -6,6 +6,8 @@ import ( | ||||||
| 	"math/rand" | 	"math/rand" | ||||||
| 	"mime" | 	"mime" | ||||||
| 	"os" | 	"os" | ||||||
|  | 	"regexp" | ||||||
|  | 	"strconv" | ||||||
| 	"strings" | 	"strings" | ||||||
| 	"sync" | 	"sync" | ||||||
| 	"time" | 	"time" | ||||||
|  | @ -16,8 +18,9 @@ const ( | ||||||
| ) | ) | ||||||
| 
 | 
 | ||||||
| var ( | var ( | ||||||
| 	random      = rand.New(rand.NewSource(time.Now().UnixNano())) | 	random       = rand.New(rand.NewSource(time.Now().UnixNano())) | ||||||
| 	randomMutex = sync.Mutex{} | 	randomMutex  = sync.Mutex{} | ||||||
|  | 	sizeStrRegex = regexp.MustCompile(`(?i)^(\d+)([gmkb])?$`) | ||||||
| 
 | 
 | ||||||
| 	errInvalidPriority = errors.New("invalid priority") | 	errInvalidPriority = errors.New("invalid priority") | ||||||
| ) | ) | ||||||
|  | @ -178,3 +181,25 @@ func ExtensionByType(contentType string) string { | ||||||
| 		return ".bin" | 		return ".bin" | ||||||
| 	} | 	} | ||||||
| } | } | ||||||
|  | 
 | ||||||
|  | // ParseSize parses a size string like 2K or 2M into bytes. If no unit is found, e.g. 123, bytes is assumed. | ||||||
|  | func ParseSize(s string) (int64, error) { | ||||||
|  | 	matches := sizeStrRegex.FindStringSubmatch(s) | ||||||
|  | 	if matches == nil { | ||||||
|  | 		return -1, fmt.Errorf("invalid size %s", s) | ||||||
|  | 	} | ||||||
|  | 	value, err := strconv.Atoi(matches[1]) | ||||||
|  | 	if err != nil { | ||||||
|  | 		return -1, fmt.Errorf("cannot convert number %s", matches[1]) | ||||||
|  | 	} | ||||||
|  | 	switch strings.ToUpper(matches[2]) { | ||||||
|  | 	case "G": | ||||||
|  | 		return int64(value) * 1024 * 1024 * 1024, nil | ||||||
|  | 	case "M": | ||||||
|  | 		return int64(value) * 1024 * 1024, nil | ||||||
|  | 	case "K": | ||||||
|  | 		return int64(value) * 1024, nil | ||||||
|  | 	default: | ||||||
|  | 		return int64(value), nil | ||||||
|  | 	} | ||||||
|  | } | ||||||
|  |  | ||||||
|  | @ -121,3 +121,34 @@ func TestShortTopicURL(t *testing.T) { | ||||||
| 	require.Equal(t, "ntfy.sh/mytopic", ShortTopicURL("http://ntfy.sh/mytopic")) | 	require.Equal(t, "ntfy.sh/mytopic", ShortTopicURL("http://ntfy.sh/mytopic")) | ||||||
| 	require.Equal(t, "lalala", ShortTopicURL("lalala")) | 	require.Equal(t, "lalala", ShortTopicURL("lalala")) | ||||||
| } | } | ||||||
|  | 
 | ||||||
|  | func TestParseSize_10GSuccess(t *testing.T) { | ||||||
|  | 	s, err := ParseSize("10G") | ||||||
|  | 	if err != nil { | ||||||
|  | 		t.Fatal(err) | ||||||
|  | 	} | ||||||
|  | 	require.Equal(t, 10*1024*1024*1024, s) | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | func TestParseSize_10MUpperCaseSuccess(t *testing.T) { | ||||||
|  | 	s, err := ParseSize("10M") | ||||||
|  | 	if err != nil { | ||||||
|  | 		t.Fatal(err) | ||||||
|  | 	} | ||||||
|  | 	require.Equal(t, 10*1024*1024, s) | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | func TestParseSize_10kLowerCaseSuccess(t *testing.T) { | ||||||
|  | 	s, err := ParseSize("10k") | ||||||
|  | 	if err != nil { | ||||||
|  | 		t.Fatal(err) | ||||||
|  | 	} | ||||||
|  | 	require.Equal(t, 10*1024, s) | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | func TestParseSize_FailureInvalid(t *testing.T) { | ||||||
|  | 	_, err := ParseSize("not a size") | ||||||
|  | 	if err == nil { | ||||||
|  | 		t.Fatalf("expected error, but got none") | ||||||
|  | 	} | ||||||
|  | } | ||||||
|  |  | ||||||
		Loading…
	
	Add table
		Add a link
		
	
		Reference in a new issue