updates for server files size limits
This commit is contained in:
parent
b65ad60fe8
commit
aeaf3f4596
8 changed files with 175 additions and 32 deletions
|
|
@ -82,6 +82,7 @@ type Service struct {
|
|||
mapboxToken string
|
||||
emailSignupEnable bool
|
||||
emailSignupPhonePrefixes []string
|
||||
maxUploadFileBytes int64
|
||||
|
||||
appConfigOnce sync.Once
|
||||
appConfigCache domain.AppConfig
|
||||
|
|
@ -129,6 +130,42 @@ func WithEmailSignupPhonePrefixes(prefixes []string) Option {
|
|||
}
|
||||
}
|
||||
|
||||
// maxUploadFilePartBytes mirrors internal/app/files.MaxUploadPartBytes (not
|
||||
// imported to avoid pulling the whole files package into help just for one
|
||||
// constant): the wire size of one upload chunk, used to convert
|
||||
// TELESRV_STORAGE_MAX_UPLOAD_FILE_BYTES into the part-count unit
|
||||
// upload_max_fileparts_default/_premium are declared in.
|
||||
const maxUploadFilePartBytes = 524288
|
||||
|
||||
// WithMaxUploadFileBytes overrides the stock upload_max_fileparts_default/
|
||||
// _premium app config keys (already in tdesktopDefaultAppConfigBase, values
|
||||
// 4000/8000 -- 2000/4000 MB at 512KB/part) with TELESRV_STORAGE_MAX_UPLOAD_FILE_BYTES's
|
||||
// part-count equivalent, when configured smaller than the protocol default.
|
||||
// tdesktop's Data::PremiumLimits::uploadMaxDefault/Premium already read
|
||||
// these two keys (data_premium_limits.cpp) and feed both the pre-upload size
|
||||
// check and its warning dialog (localimageloader.cpp's FileSizeLimit/
|
||||
// FileSizePremiumLimit, storage_media_prepare.cpp) -- so overriding the
|
||||
// number here is enough to make an adapted client warn about (and reject)
|
||||
// files above this self-hosted server's real ceiling, instead of only its
|
||||
// own hardcoded ~2/4GB assumption. 0 (unlimited, the default) leaves the
|
||||
// stock 4000/8000 values untouched.
|
||||
func WithMaxUploadFileBytes(bytes int64) Option {
|
||||
return func(s *Service) {
|
||||
s.maxUploadFileBytes = bytes
|
||||
}
|
||||
}
|
||||
|
||||
// maxUploadFileParts converts a configured byte ceiling to the number of
|
||||
// 512KB parts it takes to hold it, rounding up (a partial final part still
|
||||
// needs a whole slot) and never below 1.
|
||||
func maxUploadFileParts(bytes int64) int64 {
|
||||
parts := (bytes + maxUploadFilePartBytes - 1) / maxUploadFilePartBytes
|
||||
if parts < 1 {
|
||||
return 1
|
||||
}
|
||||
return parts
|
||||
}
|
||||
|
||||
// NewService 创建 help 服务。
|
||||
func NewService(appConfigs store.AppConfigStore, countries store.CountryStore, opts ...Option) *Service {
|
||||
s := &Service{
|
||||
|
|
@ -143,12 +180,12 @@ func NewService(appConfigs store.AppConfigStore, countries store.CountryStore, o
|
|||
return s
|
||||
}
|
||||
|
||||
func defaultAppConfig(mapboxToken string, emailSignupEnable bool, emailSignupPhonePrefixes []string) domain.AppConfig {
|
||||
jsonBytes := defaultAppConfigJSON(mapboxToken, emailSignupEnable, emailSignupPhonePrefixes)
|
||||
return domain.AppConfig{Client: tdesktopClient, Hash: defaultAppConfigHashFor(mapboxToken, emailSignupEnable, emailSignupPhonePrefixes), JSON: jsonBytes}
|
||||
func defaultAppConfig(mapboxToken string, emailSignupEnable bool, emailSignupPhonePrefixes []string, maxUploadFileBytes int64) domain.AppConfig {
|
||||
jsonBytes := defaultAppConfigJSON(mapboxToken, emailSignupEnable, emailSignupPhonePrefixes, maxUploadFileBytes)
|
||||
return domain.AppConfig{Client: tdesktopClient, Hash: defaultAppConfigHashFor(mapboxToken, emailSignupEnable, emailSignupPhonePrefixes, maxUploadFileBytes), JSON: jsonBytes}
|
||||
}
|
||||
|
||||
func defaultAppConfigJSON(mapboxToken string, emailSignupEnable bool, emailSignupPhonePrefixes []string) []byte {
|
||||
func defaultAppConfigJSON(mapboxToken string, emailSignupEnable bool, emailSignupPhonePrefixes []string, maxUploadFileBytes int64) []byte {
|
||||
androidInvoiceBilling := `,"premium_playmarket_direct_currency_list":` + compatandroid.DirectInvoiceCurrenciesJSON()
|
||||
base := tdesktopDefaultAppConfigBase + tdesktopNoForwardsAppConfig + androidInvoiceBilling
|
||||
if emailSignupEnable {
|
||||
|
|
@ -159,6 +196,11 @@ func defaultAppConfigJSON(mapboxToken string, emailSignupEnable bool, emailSignu
|
|||
}
|
||||
}
|
||||
}
|
||||
if maxUploadFileBytes > 0 {
|
||||
parts := strconv.FormatInt(maxUploadFileParts(maxUploadFileBytes), 10)
|
||||
base = strings.Replace(base, `"upload_max_fileparts_default":4000`, `"upload_max_fileparts_default":`+parts, 1)
|
||||
base = strings.Replace(base, `"upload_max_fileparts_premium":8000`, `"upload_max_fileparts_premium":`+parts, 1)
|
||||
}
|
||||
if mapboxToken == "" {
|
||||
return []byte(base + `}`)
|
||||
}
|
||||
|
|
@ -170,7 +212,7 @@ func defaultAppConfigJSON(mapboxToken string, emailSignupEnable bool, emailSignu
|
|||
return []byte(base + `,"tdesktop_config_map":{"maps":` + tokenJSON + `,"geo":` + tokenJSON + `,"bmaps":` + tokenJSON + `,"bgeo":` + tokenJSON + `}}`)
|
||||
}
|
||||
|
||||
func defaultAppConfigHashFor(mapboxToken string, emailSignupEnable bool, emailSignupPhonePrefixes []string) int {
|
||||
func defaultAppConfigHashFor(mapboxToken string, emailSignupEnable bool, emailSignupPhonePrefixes []string, maxUploadFileBytes int64) int {
|
||||
h := defaultAppConfigHash
|
||||
if emailSignupEnable {
|
||||
h += 1000003 // large odd offset so toggling the flag always changes the hash
|
||||
|
|
@ -178,6 +220,9 @@ func defaultAppConfigHashFor(mapboxToken string, emailSignupEnable bool, emailSi
|
|||
h += 1 + int(crc32.ChecksumIEEE([]byte(strings.Join(emailSignupPhonePrefixes, ",")))&0x3fffffff)
|
||||
}
|
||||
}
|
||||
if maxUploadFileBytes > 0 {
|
||||
h += 1 + int(crc32.ChecksumIEEE([]byte(strconv.FormatInt(maxUploadFileBytes, 10)))&0x3fffffff)
|
||||
}
|
||||
if mapboxToken == "" {
|
||||
return h
|
||||
}
|
||||
|
|
@ -243,9 +288,9 @@ func (s *Service) accountAppConfig(ctx context.Context, userID int64, base domai
|
|||
|
||||
func (s *Service) loadAppConfig(ctx context.Context) domain.AppConfig {
|
||||
if s == nil {
|
||||
return defaultAppConfig("", false, nil)
|
||||
return defaultAppConfig("", false, nil, 0)
|
||||
}
|
||||
defaultCfg := defaultAppConfig(s.mapboxToken, s.emailSignupEnable, s.emailSignupPhonePrefixes)
|
||||
defaultCfg := defaultAppConfig(s.mapboxToken, s.emailSignupEnable, s.emailSignupPhonePrefixes, s.maxUploadFileBytes)
|
||||
s.appConfigOnce.Do(func() {
|
||||
if s.appConfigs == nil {
|
||||
s.appConfigCache = defaultCfg
|
||||
|
|
|
|||
|
|
@ -119,6 +119,66 @@ func containsJSONCurrency(values []any, want string) bool {
|
|||
return false
|
||||
}
|
||||
|
||||
func TestAppConfigKeepsStockUploadPartsLimitsByDefault(t *testing.T) {
|
||||
cfg, notModified, err := (*Service)(nil).GetAppConfig(context.Background(), 0, 0)
|
||||
if err != nil || notModified {
|
||||
t.Fatalf("GetAppConfig = notModified %v err %v", notModified, err)
|
||||
}
|
||||
var decoded map[string]any
|
||||
if err := json.Unmarshal(cfg.JSON, &decoded); err != nil {
|
||||
t.Fatalf("app config json invalid: %v", err)
|
||||
}
|
||||
if got := decoded["upload_max_fileparts_default"]; got != float64(4000) {
|
||||
t.Fatalf("upload_max_fileparts_default = %v, want 4000 (unconfigured limit)", got)
|
||||
}
|
||||
if got := decoded["upload_max_fileparts_premium"]; got != float64(8000) {
|
||||
t.Fatalf("upload_max_fileparts_premium = %v, want 8000 (unconfigured limit)", got)
|
||||
}
|
||||
}
|
||||
|
||||
func TestAppConfigUsesConfiguredMaxUploadFileBytesAndHash(t *testing.T) {
|
||||
// 500MiB / 512KiB-per-part = 1000 parts exactly.
|
||||
svc := NewService(nil, nil, WithMaxUploadFileBytes(500*1024*1024))
|
||||
cfg, notModified, err := svc.GetAppConfig(context.Background(), 0, 0)
|
||||
if err != nil || notModified {
|
||||
t.Fatalf("GetAppConfig = notModified %v err %v", notModified, err)
|
||||
}
|
||||
if cfg.Hash == defaultAppConfigHash {
|
||||
t.Fatalf("hash = %d, want limit-specific hash", cfg.Hash)
|
||||
}
|
||||
if _, notModified, err := svc.GetAppConfig(context.Background(), 0, cfg.Hash); err != nil || !notModified {
|
||||
t.Fatalf("GetAppConfig(hash) = notModified %v err %v, want notModified", notModified, err)
|
||||
}
|
||||
var decoded map[string]any
|
||||
if err := json.Unmarshal(cfg.JSON, &decoded); err != nil {
|
||||
t.Fatalf("app config json invalid: %v", err)
|
||||
}
|
||||
if got := decoded["upload_max_fileparts_default"]; got != float64(1000) {
|
||||
t.Fatalf("upload_max_fileparts_default = %v, want 1000", got)
|
||||
}
|
||||
if got := decoded["upload_max_fileparts_premium"]; got != float64(1000) {
|
||||
t.Fatalf("upload_max_fileparts_premium = %v, want 1000", got)
|
||||
}
|
||||
}
|
||||
|
||||
func TestMaxUploadFilePartsRoundsUpAndFloorsAtOne(t *testing.T) {
|
||||
cases := []struct {
|
||||
bytes int64
|
||||
want int64
|
||||
}{
|
||||
{0, 1},
|
||||
{1, 1},
|
||||
{maxUploadFilePartBytes, 1},
|
||||
{maxUploadFilePartBytes + 1, 2},
|
||||
{500 * 1024 * 1024, 1000},
|
||||
}
|
||||
for _, c := range cases {
|
||||
if got := maxUploadFileParts(c.bytes); got != c.want {
|
||||
t.Errorf("maxUploadFileParts(%d) = %d, want %d", c.bytes, got, c.want)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func TestAppConfigOmitsMapboxTokenByDefault(t *testing.T) {
|
||||
cfg, notModified, err := (*Service)(nil).GetAppConfig(context.Background(), 0, 0)
|
||||
if err != nil || notModified {
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue