[feature] update config types to use bytesize.Size (#828)

* update config size types to use bytesize.Size

* submit unchecked-out file ... 🤦

* fix bytesize config var decoding

* bump bytesize version

* update kim's libraries in readme

* update envparse.sh to output more useful errors

* improve envparse.sh

* remove reliance on jq

* instead, use uint64 for bytesize flag types

* remove redundant type

* fix viper unmarshaling

* Update envparsing.sh

* fix envparsing test

Signed-off-by: kim <grufwub@gmail.com>
Co-authored-by: tobi <31960611+tsmethurst@users.noreply.github.com>
This commit is contained in:
kim 2022-09-29 21:50:43 +01:00 committed by GitHub
parent f0bf69d4d0
commit 1d999712e6
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
30 changed files with 223 additions and 169 deletions

View file

@ -219,6 +219,8 @@ The following libraries and frameworks are used by GoToSocial, with gratitude
- [go-playground/validator](https://github.com/go-playground/validator); struct validation. [MIT License](https://spdx.org/licenses/MIT.html). - [go-playground/validator](https://github.com/go-playground/validator); struct validation. [MIT License](https://spdx.org/licenses/MIT.html).
- [gorilla/websocket](https://github.com/gorilla/websocket); Websocket connectivity. [BSD-2-Clause License](https://spdx.org/licenses/BSD-2-Clause.html). - [gorilla/websocket](https://github.com/gorilla/websocket); Websocket connectivity. [BSD-2-Clause License](https://spdx.org/licenses/BSD-2-Clause.html).
- [gruf/go-debug](https://codeberg.org/gruf/go-debug); profiling support in debug builds. [MIT License](https://spdx.org/licenses/MIT.html). - [gruf/go-debug](https://codeberg.org/gruf/go-debug); profiling support in debug builds. [MIT License](https://spdx.org/licenses/MIT.html).
- [gruf/go-bytesize](https://codeberg.org/gruf/go-bytesize); byte size parsing / formatting. [MIT License](https://spdx.org/licenses/MIT.html).
- [gruf/go-cache](https://codeberg.org/gruf/go-cache); object caching. [MIT License](https://spdx.org/licenses/MIT.html).
- [gruf/go-kv](https://codeberg.org/gruf/go-kv); key-value field formatting. [MIT License](https://spdx.org/licenses/MIT.html). - [gruf/go-kv](https://codeberg.org/gruf/go-kv); key-value field formatting. [MIT License](https://spdx.org/licenses/MIT.html).
- [gruf/go-mutexes](https://codeberg.org/gruf/go-mutexes); mutex map. [MIT License](https://spdx.org/licenses/MIT.html). - [gruf/go-mutexes](https://codeberg.org/gruf/go-mutexes); mutex map. [MIT License](https://spdx.org/licenses/MIT.html).
- [gruf/go-runners](https://codeberg.org/gruf/go-runners); worker pool library. [MIT License](https://spdx.org/licenses/MIT.html). - [gruf/go-runners](https://codeberg.org/gruf/go-runners); worker pool library. [MIT License](https://spdx.org/licenses/MIT.html).

2
go.mod
View file

@ -4,7 +4,7 @@ go 1.19
require ( require (
codeberg.org/gruf/go-atomics v1.1.0 codeberg.org/gruf/go-atomics v1.1.0
codeberg.org/gruf/go-bytesize v0.2.1 codeberg.org/gruf/go-bytesize v1.0.0
codeberg.org/gruf/go-byteutil v1.0.2 codeberg.org/gruf/go-byteutil v1.0.2
codeberg.org/gruf/go-cache/v2 v2.1.4 codeberg.org/gruf/go-cache/v2 v2.1.4
codeberg.org/gruf/go-debug v1.2.0 codeberg.org/gruf/go-debug v1.2.0

4
go.sum
View file

@ -64,8 +64,8 @@ codeberg.org/gruf/go-bitutil v1.0.1/go.mod h1:3ezHnADoiRJs9jgn65AEZ3HY7dsabAYLmm
codeberg.org/gruf/go-bytes v1.0.0/go.mod h1:1v/ibfaosfXSZtRdW2rWaVrDXMc9E3bsi/M9Ekx39cg= codeberg.org/gruf/go-bytes v1.0.0/go.mod h1:1v/ibfaosfXSZtRdW2rWaVrDXMc9E3bsi/M9Ekx39cg=
codeberg.org/gruf/go-bytes v1.0.2 h1:malqE42Ni+h1nnYWBUAJaDDtEzF4aeN4uPN8DfMNNvo= codeberg.org/gruf/go-bytes v1.0.2 h1:malqE42Ni+h1nnYWBUAJaDDtEzF4aeN4uPN8DfMNNvo=
codeberg.org/gruf/go-bytes v1.0.2/go.mod h1:1v/ibfaosfXSZtRdW2rWaVrDXMc9E3bsi/M9Ekx39cg= codeberg.org/gruf/go-bytes v1.0.2/go.mod h1:1v/ibfaosfXSZtRdW2rWaVrDXMc9E3bsi/M9Ekx39cg=
codeberg.org/gruf/go-bytesize v0.2.1 h1:nbAta3FCYe3Q18osqg8Ylk/naOopdqEKiKMpo6KTpAA= codeberg.org/gruf/go-bytesize v1.0.0 h1:/Mcv4prniJLkPEqZ+LZ5/D/e27rNrZZEMmty9jpIvlc=
codeberg.org/gruf/go-bytesize v0.2.1/go.mod h1:n/GU8HzL9f3UNp/mUKyr1qVmTlj7+xacpp0OHfkvLPs= codeberg.org/gruf/go-bytesize v1.0.0/go.mod h1:n/GU8HzL9f3UNp/mUKyr1qVmTlj7+xacpp0OHfkvLPs=
codeberg.org/gruf/go-byteutil v1.0.0/go.mod h1:cWM3tgMCroSzqoBXUXMhvxTxYJp+TbCr6ioISRY5vSU= codeberg.org/gruf/go-byteutil v1.0.0/go.mod h1:cWM3tgMCroSzqoBXUXMhvxTxYJp+TbCr6ioISRY5vSU=
codeberg.org/gruf/go-byteutil v1.0.2 h1:OesVyK5VKWeWdeDR00zRJ+Oy8hjXx1pBhn7WVvcZWVE= codeberg.org/gruf/go-byteutil v1.0.2 h1:OesVyK5VKWeWdeDR00zRJ+Oy8hjXx1pBhn7WVvcZWVE=
codeberg.org/gruf/go-byteutil v1.0.2/go.mod h1:cWM3tgMCroSzqoBXUXMhvxTxYJp+TbCr6ioISRY5vSU= codeberg.org/gruf/go-byteutil v1.0.2/go.mod h1:cWM3tgMCroSzqoBXUXMhvxTxYJp+TbCr6ioISRY5vSU=

View file

@ -21,6 +21,7 @@
import ( import (
"reflect" "reflect"
"codeberg.org/gruf/go-bytesize"
"github.com/mitchellh/mapstructure" "github.com/mitchellh/mapstructure"
) )
@ -76,13 +77,13 @@ type Configuration struct {
AccountsReasonRequired bool `name:"accounts-reason-required" usage:"Do new account signups require a reason to be submitted on registration?"` AccountsReasonRequired bool `name:"accounts-reason-required" usage:"Do new account signups require a reason to be submitted on registration?"`
AccountsAllowCustomCSS bool `name:"accounts-allow-custom-css" usage:"Allow accounts to enable custom CSS for their profile pages and statuses."` AccountsAllowCustomCSS bool `name:"accounts-allow-custom-css" usage:"Allow accounts to enable custom CSS for their profile pages and statuses."`
MediaImageMaxSize int `name:"media-image-max-size" usage:"Max size of accepted images in bytes"` MediaImageMaxSize bytesize.Size `name:"media-image-max-size" usage:"Max size of accepted images in bytes"`
MediaVideoMaxSize int `name:"media-video-max-size" usage:"Max size of accepted videos in bytes"` MediaVideoMaxSize bytesize.Size `name:"media-video-max-size" usage:"Max size of accepted videos in bytes"`
MediaDescriptionMinChars int `name:"media-description-min-chars" usage:"Min required chars for an image description"` MediaDescriptionMinChars int `name:"media-description-min-chars" usage:"Min required chars for an image description"`
MediaDescriptionMaxChars int `name:"media-description-max-chars" usage:"Max permitted chars for an image description"` MediaDescriptionMaxChars int `name:"media-description-max-chars" usage:"Max permitted chars for an image description"`
MediaRemoteCacheDays int `name:"media-remote-cache-days" usage:"Number of days to locally cache media from remote instances. If set to 0, remote media will be kept indefinitely."` MediaRemoteCacheDays int `name:"media-remote-cache-days" usage:"Number of days to locally cache media from remote instances. If set to 0, remote media will be kept indefinitely."`
MediaEmojiLocalMaxSize int `name:"media-emoji-local-max-size" usage:"Max size in bytes of emojis uploaded to this instance via the admin API."` MediaEmojiLocalMaxSize bytesize.Size `name:"media-emoji-local-max-size" usage:"Max size in bytes of emojis uploaded to this instance via the admin API."`
MediaEmojiRemoteMaxSize int `name:"media-emoji-remote-max-size" usage:"Max size in bytes of emojis to download from other instances."` MediaEmojiRemoteMaxSize bytesize.Size `name:"media-emoji-remote-max-size" usage:"Max size in bytes of emojis to download from other instances."`
StorageBackend string `name:"storage-backend" usage:"Storage backend to use for media attachments"` StorageBackend string `name:"storage-backend" usage:"Storage backend to use for media attachments"`
StorageLocalBasePath string `name:"storage-local-base-path" usage:"Full path to an already-created directory where gts should store/retrieve media files. Subfolders will be created within this dir."` StorageLocalBasePath string `name:"storage-local-base-path" usage:"Full path to an already-created directory where gts should store/retrieve media files. Subfolders will be created within this dir."`

View file

@ -72,13 +72,13 @@ func AddServerFlags(cmd *cobra.Command) {
cmd.Flags().Bool(AccountsAllowCustomCSSFlag(), cfg.AccountsAllowCustomCSS, fieldtag("AccountsAllowCustomCSS", "usage")) cmd.Flags().Bool(AccountsAllowCustomCSSFlag(), cfg.AccountsAllowCustomCSS, fieldtag("AccountsAllowCustomCSS", "usage"))
// Media // Media
cmd.Flags().Int(MediaImageMaxSizeFlag(), cfg.MediaImageMaxSize, fieldtag("MediaImageMaxSize", "usage")) cmd.Flags().Uint64(MediaImageMaxSizeFlag(), uint64(cfg.MediaImageMaxSize), fieldtag("MediaImageMaxSize", "usage"))
cmd.Flags().Int(MediaVideoMaxSizeFlag(), cfg.MediaVideoMaxSize, fieldtag("MediaVideoMaxSize", "usage")) cmd.Flags().Uint64(MediaVideoMaxSizeFlag(), uint64(cfg.MediaVideoMaxSize), fieldtag("MediaVideoMaxSize", "usage"))
cmd.Flags().Int(MediaDescriptionMinCharsFlag(), cfg.MediaDescriptionMinChars, fieldtag("MediaDescriptionMinChars", "usage")) cmd.Flags().Int(MediaDescriptionMinCharsFlag(), cfg.MediaDescriptionMinChars, fieldtag("MediaDescriptionMinChars", "usage"))
cmd.Flags().Int(MediaDescriptionMaxCharsFlag(), cfg.MediaDescriptionMaxChars, fieldtag("MediaDescriptionMaxChars", "usage")) cmd.Flags().Int(MediaDescriptionMaxCharsFlag(), cfg.MediaDescriptionMaxChars, fieldtag("MediaDescriptionMaxChars", "usage"))
cmd.Flags().Int(MediaRemoteCacheDaysFlag(), cfg.MediaRemoteCacheDays, fieldtag("MediaRemoteCacheDays", "usage")) cmd.Flags().Int(MediaRemoteCacheDaysFlag(), cfg.MediaRemoteCacheDays, fieldtag("MediaRemoteCacheDays", "usage"))
cmd.Flags().Int(MediaEmojiLocalMaxSizeFlag(), cfg.MediaEmojiLocalMaxSize, fieldtag("MediaEmojiLocalMaxSize", "usage")) cmd.Flags().Uint64(MediaEmojiLocalMaxSizeFlag(), uint64(cfg.MediaEmojiLocalMaxSize), fieldtag("MediaEmojiLocalMaxSize", "usage"))
cmd.Flags().Int(MediaEmojiRemoteMaxSizeFlag(), cfg.MediaEmojiRemoteMaxSize, fieldtag("MediaEmojiRemoteMaxSize", "usage")) cmd.Flags().Uint64(MediaEmojiRemoteMaxSizeFlag(), uint64(cfg.MediaEmojiRemoteMaxSize), fieldtag("MediaEmojiRemoteMaxSize", "usage"))
// Storage // Storage
cmd.Flags().String(StorageBackendFlag(), cfg.StorageBackend, fieldtag("StorageBackend", "usage")) cmd.Flags().String(StorageBackendFlag(), cfg.StorageBackend, fieldtag("StorageBackend", "usage"))

View file

@ -100,7 +100,7 @@ func main() {
fmt.Fprintf(output, "func Set%[1]s(v %[2]s) { global.Set%[1]s(v) }\n\n", field.Name, field.Type.String()) fmt.Fprintf(output, "func Set%[1]s(v %[2]s) { global.Set%[1]s(v) }\n\n", field.Name, field.Type.String())
} }
_ = output.Close() _ = output.Close()
_ = exec.Command("gofmt", "-w", out).Run() _ = exec.Command("gofumports", "-w", out).Run()
// The plain here is that eventually we might be able // The plain here is that eventually we might be able
// to generate an example configuration from struct tags // to generate an example configuration from struct tags

View file

@ -18,7 +18,9 @@
package config package config
import "github.com/spf13/cobra" import (
"github.com/spf13/cobra"
)
var global *ConfigState var global *ConfigState

View file

@ -18,6 +18,8 @@
*/ */
package config package config
import "codeberg.org/gruf/go-bytesize"
// GetLogLevel safely fetches the Configuration value for state's 'LogLevel' field // GetLogLevel safely fetches the Configuration value for state's 'LogLevel' field
func (st *ConfigState) GetLogLevel() (v string) { func (st *ConfigState) GetLogLevel() (v string) {
st.mutex.Lock() st.mutex.Lock()
@ -719,7 +721,7 @@ func GetAccountsAllowCustomCSS() bool { return global.GetAccountsAllowCustomCSS(
func SetAccountsAllowCustomCSS(v bool) { global.SetAccountsAllowCustomCSS(v) } func SetAccountsAllowCustomCSS(v bool) { global.SetAccountsAllowCustomCSS(v) }
// GetMediaImageMaxSize safely fetches the Configuration value for state's 'MediaImageMaxSize' field // GetMediaImageMaxSize safely fetches the Configuration value for state's 'MediaImageMaxSize' field
func (st *ConfigState) GetMediaImageMaxSize() (v int) { func (st *ConfigState) GetMediaImageMaxSize() (v bytesize.Size) {
st.mutex.Lock() st.mutex.Lock()
v = st.config.MediaImageMaxSize v = st.config.MediaImageMaxSize
st.mutex.Unlock() st.mutex.Unlock()
@ -727,7 +729,7 @@ func (st *ConfigState) GetMediaImageMaxSize() (v int) {
} }
// SetMediaImageMaxSize safely sets the Configuration value for state's 'MediaImageMaxSize' field // SetMediaImageMaxSize safely sets the Configuration value for state's 'MediaImageMaxSize' field
func (st *ConfigState) SetMediaImageMaxSize(v int) { func (st *ConfigState) SetMediaImageMaxSize(v bytesize.Size) {
st.mutex.Lock() st.mutex.Lock()
defer st.mutex.Unlock() defer st.mutex.Unlock()
st.config.MediaImageMaxSize = v st.config.MediaImageMaxSize = v
@ -738,13 +740,13 @@ func (st *ConfigState) SetMediaImageMaxSize(v int) {
func MediaImageMaxSizeFlag() string { return "media-image-max-size" } func MediaImageMaxSizeFlag() string { return "media-image-max-size" }
// GetMediaImageMaxSize safely fetches the value for global configuration 'MediaImageMaxSize' field // GetMediaImageMaxSize safely fetches the value for global configuration 'MediaImageMaxSize' field
func GetMediaImageMaxSize() int { return global.GetMediaImageMaxSize() } func GetMediaImageMaxSize() bytesize.Size { return global.GetMediaImageMaxSize() }
// SetMediaImageMaxSize safely sets the value for global configuration 'MediaImageMaxSize' field // SetMediaImageMaxSize safely sets the value for global configuration 'MediaImageMaxSize' field
func SetMediaImageMaxSize(v int) { global.SetMediaImageMaxSize(v) } func SetMediaImageMaxSize(v bytesize.Size) { global.SetMediaImageMaxSize(v) }
// GetMediaVideoMaxSize safely fetches the Configuration value for state's 'MediaVideoMaxSize' field // GetMediaVideoMaxSize safely fetches the Configuration value for state's 'MediaVideoMaxSize' field
func (st *ConfigState) GetMediaVideoMaxSize() (v int) { func (st *ConfigState) GetMediaVideoMaxSize() (v bytesize.Size) {
st.mutex.Lock() st.mutex.Lock()
v = st.config.MediaVideoMaxSize v = st.config.MediaVideoMaxSize
st.mutex.Unlock() st.mutex.Unlock()
@ -752,7 +754,7 @@ func (st *ConfigState) GetMediaVideoMaxSize() (v int) {
} }
// SetMediaVideoMaxSize safely sets the Configuration value for state's 'MediaVideoMaxSize' field // SetMediaVideoMaxSize safely sets the Configuration value for state's 'MediaVideoMaxSize' field
func (st *ConfigState) SetMediaVideoMaxSize(v int) { func (st *ConfigState) SetMediaVideoMaxSize(v bytesize.Size) {
st.mutex.Lock() st.mutex.Lock()
defer st.mutex.Unlock() defer st.mutex.Unlock()
st.config.MediaVideoMaxSize = v st.config.MediaVideoMaxSize = v
@ -763,10 +765,10 @@ func (st *ConfigState) SetMediaVideoMaxSize(v int) {
func MediaVideoMaxSizeFlag() string { return "media-video-max-size" } func MediaVideoMaxSizeFlag() string { return "media-video-max-size" }
// GetMediaVideoMaxSize safely fetches the value for global configuration 'MediaVideoMaxSize' field // GetMediaVideoMaxSize safely fetches the value for global configuration 'MediaVideoMaxSize' field
func GetMediaVideoMaxSize() int { return global.GetMediaVideoMaxSize() } func GetMediaVideoMaxSize() bytesize.Size { return global.GetMediaVideoMaxSize() }
// SetMediaVideoMaxSize safely sets the value for global configuration 'MediaVideoMaxSize' field // SetMediaVideoMaxSize safely sets the value for global configuration 'MediaVideoMaxSize' field
func SetMediaVideoMaxSize(v int) { global.SetMediaVideoMaxSize(v) } func SetMediaVideoMaxSize(v bytesize.Size) { global.SetMediaVideoMaxSize(v) }
// GetMediaDescriptionMinChars safely fetches the Configuration value for state's 'MediaDescriptionMinChars' field // GetMediaDescriptionMinChars safely fetches the Configuration value for state's 'MediaDescriptionMinChars' field
func (st *ConfigState) GetMediaDescriptionMinChars() (v int) { func (st *ConfigState) GetMediaDescriptionMinChars() (v int) {
@ -844,7 +846,7 @@ func GetMediaRemoteCacheDays() int { return global.GetMediaRemoteCacheDays() }
func SetMediaRemoteCacheDays(v int) { global.SetMediaRemoteCacheDays(v) } func SetMediaRemoteCacheDays(v int) { global.SetMediaRemoteCacheDays(v) }
// GetMediaEmojiLocalMaxSize safely fetches the Configuration value for state's 'MediaEmojiLocalMaxSize' field // GetMediaEmojiLocalMaxSize safely fetches the Configuration value for state's 'MediaEmojiLocalMaxSize' field
func (st *ConfigState) GetMediaEmojiLocalMaxSize() (v int) { func (st *ConfigState) GetMediaEmojiLocalMaxSize() (v bytesize.Size) {
st.mutex.Lock() st.mutex.Lock()
v = st.config.MediaEmojiLocalMaxSize v = st.config.MediaEmojiLocalMaxSize
st.mutex.Unlock() st.mutex.Unlock()
@ -852,7 +854,7 @@ func (st *ConfigState) GetMediaEmojiLocalMaxSize() (v int) {
} }
// SetMediaEmojiLocalMaxSize safely sets the Configuration value for state's 'MediaEmojiLocalMaxSize' field // SetMediaEmojiLocalMaxSize safely sets the Configuration value for state's 'MediaEmojiLocalMaxSize' field
func (st *ConfigState) SetMediaEmojiLocalMaxSize(v int) { func (st *ConfigState) SetMediaEmojiLocalMaxSize(v bytesize.Size) {
st.mutex.Lock() st.mutex.Lock()
defer st.mutex.Unlock() defer st.mutex.Unlock()
st.config.MediaEmojiLocalMaxSize = v st.config.MediaEmojiLocalMaxSize = v
@ -863,13 +865,13 @@ func (st *ConfigState) SetMediaEmojiLocalMaxSize(v int) {
func MediaEmojiLocalMaxSizeFlag() string { return "media-emoji-local-max-size" } func MediaEmojiLocalMaxSizeFlag() string { return "media-emoji-local-max-size" }
// GetMediaEmojiLocalMaxSize safely fetches the value for global configuration 'MediaEmojiLocalMaxSize' field // GetMediaEmojiLocalMaxSize safely fetches the value for global configuration 'MediaEmojiLocalMaxSize' field
func GetMediaEmojiLocalMaxSize() int { return global.GetMediaEmojiLocalMaxSize() } func GetMediaEmojiLocalMaxSize() bytesize.Size { return global.GetMediaEmojiLocalMaxSize() }
// SetMediaEmojiLocalMaxSize safely sets the value for global configuration 'MediaEmojiLocalMaxSize' field // SetMediaEmojiLocalMaxSize safely sets the value for global configuration 'MediaEmojiLocalMaxSize' field
func SetMediaEmojiLocalMaxSize(v int) { global.SetMediaEmojiLocalMaxSize(v) } func SetMediaEmojiLocalMaxSize(v bytesize.Size) { global.SetMediaEmojiLocalMaxSize(v) }
// GetMediaEmojiRemoteMaxSize safely fetches the Configuration value for state's 'MediaEmojiRemoteMaxSize' field // GetMediaEmojiRemoteMaxSize safely fetches the Configuration value for state's 'MediaEmojiRemoteMaxSize' field
func (st *ConfigState) GetMediaEmojiRemoteMaxSize() (v int) { func (st *ConfigState) GetMediaEmojiRemoteMaxSize() (v bytesize.Size) {
st.mutex.Lock() st.mutex.Lock()
v = st.config.MediaEmojiRemoteMaxSize v = st.config.MediaEmojiRemoteMaxSize
st.mutex.Unlock() st.mutex.Unlock()
@ -877,7 +879,7 @@ func (st *ConfigState) GetMediaEmojiRemoteMaxSize() (v int) {
} }
// SetMediaEmojiRemoteMaxSize safely sets the Configuration value for state's 'MediaEmojiRemoteMaxSize' field // SetMediaEmojiRemoteMaxSize safely sets the Configuration value for state's 'MediaEmojiRemoteMaxSize' field
func (st *ConfigState) SetMediaEmojiRemoteMaxSize(v int) { func (st *ConfigState) SetMediaEmojiRemoteMaxSize(v bytesize.Size) {
st.mutex.Lock() st.mutex.Lock()
defer st.mutex.Unlock() defer st.mutex.Unlock()
st.config.MediaEmojiRemoteMaxSize = v st.config.MediaEmojiRemoteMaxSize = v
@ -888,10 +890,10 @@ func (st *ConfigState) SetMediaEmojiRemoteMaxSize(v int) {
func MediaEmojiRemoteMaxSizeFlag() string { return "media-emoji-remote-max-size" } func MediaEmojiRemoteMaxSizeFlag() string { return "media-emoji-remote-max-size" }
// GetMediaEmojiRemoteMaxSize safely fetches the value for global configuration 'MediaEmojiRemoteMaxSize' field // GetMediaEmojiRemoteMaxSize safely fetches the value for global configuration 'MediaEmojiRemoteMaxSize' field
func GetMediaEmojiRemoteMaxSize() int { return global.GetMediaEmojiRemoteMaxSize() } func GetMediaEmojiRemoteMaxSize() bytesize.Size { return global.GetMediaEmojiRemoteMaxSize() }
// SetMediaEmojiRemoteMaxSize safely sets the value for global configuration 'MediaEmojiRemoteMaxSize' field // SetMediaEmojiRemoteMaxSize safely sets the value for global configuration 'MediaEmojiRemoteMaxSize' field
func SetMediaEmojiRemoteMaxSize(v int) { global.SetMediaEmojiRemoteMaxSize(v) } func SetMediaEmojiRemoteMaxSize(v bytesize.Size) { global.SetMediaEmojiRemoteMaxSize(v) }
// GetStorageBackend safely fetches the Configuration value for state's 'StorageBackend' field // GetStorageBackend safely fetches the Configuration value for state's 'StorageBackend' field
func (st *ConfigState) GetStorageBackend() (v string) { func (st *ConfigState) GetStorageBackend() (v string) {

View file

@ -133,6 +133,12 @@ func (st *ConfigState) reloadFromViper() {
if err := st.viper.Unmarshal(&st.config, func(c *mapstructure.DecoderConfig) { if err := st.viper.Unmarshal(&st.config, func(c *mapstructure.DecoderConfig) {
c.TagName = "name" c.TagName = "name"
c.ZeroFields = true // empty the config struct before we marshal values into it c.ZeroFields = true // empty the config struct before we marshal values into it
oldhook := c.DecodeHook
c.DecodeHook = mapstructure.ComposeDecodeHookFunc(
mapstructure.TextUnmarshallerHookFunc(),
oldhook,
)
}); err != nil { }); err != nil {
panic(err) panic(err)
} }

View file

@ -67,14 +67,6 @@ func Validate() error {
errs = append(errs, fmt.Errorf("%s must be set", WebAssetBaseDirFlag())) errs = append(errs, fmt.Errorf("%s must be set", WebAssetBaseDirFlag()))
} }
if m := GetMediaEmojiLocalMaxSize(); m < 0 {
errs = append(errs, fmt.Errorf("%s must not be less than 0", MediaEmojiLocalMaxSizeFlag()))
}
if m := GetMediaEmojiRemoteMaxSize(); m < 0 {
errs = append(errs, fmt.Errorf("%s must not be less than 0", MediaEmojiRemoteMaxSizeFlag()))
}
if len(errs) > 0 { if len(errs) > 0 {
errStrings := []string{} errStrings := []string{}
for _, err := range errs { for _, err := range errs {

View file

@ -141,16 +141,6 @@ func (suite *ConfigValidateTestSuite) TestValidateConfigBadProtocolNoHost() {
suite.EqualError(err, "host must be set; protocol must be set to either http or https, provided value was foo") suite.EqualError(err, "host must be set; protocol must be set to either http or https, provided value was foo")
} }
func (suite *ConfigValidateTestSuite) TestValidateConfigBadEmojiSizes() {
testrig.InitTestConfig()
config.SetMediaEmojiLocalMaxSize(-10)
config.SetMediaEmojiRemoteMaxSize(-50)
err := config.Validate()
suite.EqualError(err, "media-emoji-local-max-size must not be less than 0; media-emoji-remote-max-size must not be less than 0")
}
func TestConfigValidateTestSuite(t *testing.T) { func TestConfigValidateTestSuite(t *testing.T) {
suite.Run(t, &ConfigValidateTestSuite{}) suite.Run(t, &ConfigValidateTestSuite{})
} }

View file

@ -496,7 +496,7 @@ func (d *deref) fetchRemoteAccountMedia(ctx context.Context, targetAccount *gtsm
} }
} }
data := func(innerCtx context.Context) (io.Reader, int, error) { data := func(innerCtx context.Context) (io.Reader, int64, error) {
return t.DereferenceMedia(innerCtx, avatarIRI) return t.DereferenceMedia(innerCtx, avatarIRI)
} }
@ -562,7 +562,7 @@ func (d *deref) fetchRemoteAccountMedia(ctx context.Context, targetAccount *gtsm
} }
} }
data := func(innerCtx context.Context) (io.Reader, int, error) { data := func(innerCtx context.Context) (io.Reader, int64, error) {
return t.DereferenceMedia(innerCtx, headerIRI) return t.DereferenceMedia(innerCtx, headerIRI)
} }

View file

@ -42,7 +42,7 @@ func (d *deref) GetRemoteEmoji(ctx context.Context, requestingUsername string, r
return nil, fmt.Errorf("GetRemoteEmoji: error parsing url: %s", err) return nil, fmt.Errorf("GetRemoteEmoji: error parsing url: %s", err)
} }
dataFunc := func(innerCtx context.Context) (io.Reader, int, error) { dataFunc := func(innerCtx context.Context) (io.Reader, int64, error) {
return t.DereferenceMedia(innerCtx, derefURI) return t.DereferenceMedia(innerCtx, derefURI)
} }

View file

@ -42,7 +42,7 @@ func (d *deref) GetRemoteMedia(ctx context.Context, requestingUsername string, a
return nil, fmt.Errorf("GetRemoteMedia: error parsing url: %s", err) return nil, fmt.Errorf("GetRemoteMedia: error parsing url: %s", err)
} }
dataFunc := func(innerCtx context.Context) (io.Reader, int, error) { dataFunc := func(innerCtx context.Context) (io.Reader, int64, error) {
return t.DereferenceMedia(innerCtx, derefURI) return t.DereferenceMedia(innerCtx, derefURI)
} }

View file

@ -43,13 +43,13 @@ type ManagerTestSuite struct {
func (suite *ManagerTestSuite) TestEmojiProcessBlocking() { func (suite *ManagerTestSuite) TestEmojiProcessBlocking() {
ctx := context.Background() ctx := context.Background()
data := func(_ context.Context) (io.Reader, int, error) { data := func(_ context.Context) (io.Reader, int64, error) {
// load bytes from a test image // load bytes from a test image
b, err := os.ReadFile("./test/rainbow-original.png") b, err := os.ReadFile("./test/rainbow-original.png")
if err != nil { if err != nil {
panic(err) panic(err)
} }
return bytes.NewBuffer(b), len(b), nil return bytes.NewBuffer(b), int64(len(b)), nil
} }
emojiID := "01GDQ9G782X42BAMFASKP64343" emojiID := "01GDQ9G782X42BAMFASKP64343"
@ -104,13 +104,13 @@ func (suite *ManagerTestSuite) TestEmojiProcessBlocking() {
func (suite *ManagerTestSuite) TestEmojiProcessBlockingTooLarge() { func (suite *ManagerTestSuite) TestEmojiProcessBlockingTooLarge() {
ctx := context.Background() ctx := context.Background()
data := func(_ context.Context) (io.Reader, int, error) { data := func(_ context.Context) (io.Reader, int64, error) {
// load bytes from a test image // load bytes from a test image
b, err := os.ReadFile("./test/big-panda.gif") b, err := os.ReadFile("./test/big-panda.gif")
if err != nil { if err != nil {
panic(err) panic(err)
} }
return bytes.NewBuffer(b), len(b), nil return bytes.NewBuffer(b), int64(len(b)), nil
} }
emojiID := "01GDQ9G782X42BAMFASKP64343" emojiID := "01GDQ9G782X42BAMFASKP64343"
@ -128,13 +128,13 @@ func (suite *ManagerTestSuite) TestEmojiProcessBlockingTooLarge() {
func (suite *ManagerTestSuite) TestEmojiProcessBlockingTooLargeNoSizeGiven() { func (suite *ManagerTestSuite) TestEmojiProcessBlockingTooLargeNoSizeGiven() {
ctx := context.Background() ctx := context.Background()
data := func(_ context.Context) (io.Reader, int, error) { data := func(_ context.Context) (io.Reader, int64, error) {
// load bytes from a test image // load bytes from a test image
b, err := os.ReadFile("./test/big-panda.gif") b, err := os.ReadFile("./test/big-panda.gif")
if err != nil { if err != nil {
panic(err) panic(err)
} }
return bytes.NewBuffer(b), len(b), nil return bytes.NewBuffer(b), int64(len(b)), nil
} }
emojiID := "01GDQ9G782X42BAMFASKP64343" emojiID := "01GDQ9G782X42BAMFASKP64343"
@ -152,7 +152,7 @@ func (suite *ManagerTestSuite) TestEmojiProcessBlockingTooLargeNoSizeGiven() {
func (suite *ManagerTestSuite) TestEmojiProcessBlockingNoFileSizeGiven() { func (suite *ManagerTestSuite) TestEmojiProcessBlockingNoFileSizeGiven() {
ctx := context.Background() ctx := context.Background()
data := func(_ context.Context) (io.Reader, int, error) { data := func(_ context.Context) (io.Reader, int64, error) {
// load bytes from a test image // load bytes from a test image
b, err := os.ReadFile("./test/rainbow-original.png") b, err := os.ReadFile("./test/rainbow-original.png")
if err != nil { if err != nil {
@ -214,13 +214,13 @@ func (suite *ManagerTestSuite) TestEmojiProcessBlockingNoFileSizeGiven() {
func (suite *ManagerTestSuite) TestSimpleJpegProcessBlocking() { func (suite *ManagerTestSuite) TestSimpleJpegProcessBlocking() {
ctx := context.Background() ctx := context.Background()
data := func(_ context.Context) (io.Reader, int, error) { data := func(_ context.Context) (io.Reader, int64, error) {
// load bytes from a test image // load bytes from a test image
b, err := os.ReadFile("./test/test-jpeg.jpg") b, err := os.ReadFile("./test/test-jpeg.jpg")
if err != nil { if err != nil {
panic(err) panic(err)
} }
return bytes.NewBuffer(b), len(b), nil return bytes.NewBuffer(b), int64(len(b)), nil
} }
accountID := "01FS1X72SK9ZPW0J1QQ68BD264" accountID := "01FS1X72SK9ZPW0J1QQ68BD264"
@ -286,7 +286,7 @@ func (suite *ManagerTestSuite) TestSimpleJpegProcessBlocking() {
func (suite *ManagerTestSuite) TestSimpleJpegProcessBlockingNoContentLengthGiven() { func (suite *ManagerTestSuite) TestSimpleJpegProcessBlockingNoContentLengthGiven() {
ctx := context.Background() ctx := context.Background()
data := func(_ context.Context) (io.Reader, int, error) { data := func(_ context.Context) (io.Reader, int64, error) {
// load bytes from a test image // load bytes from a test image
b, err := os.ReadFile("./test/test-jpeg.jpg") b, err := os.ReadFile("./test/test-jpeg.jpg")
if err != nil { if err != nil {
@ -359,7 +359,7 @@ func (suite *ManagerTestSuite) TestSimpleJpegProcessBlockingNoContentLengthGiven
func (suite *ManagerTestSuite) TestSimpleJpegProcessBlockingReadCloser() { func (suite *ManagerTestSuite) TestSimpleJpegProcessBlockingReadCloser() {
ctx := context.Background() ctx := context.Background()
data := func(_ context.Context) (io.Reader, int, error) { data := func(_ context.Context) (io.Reader, int64, error) {
// open test image as a file // open test image as a file
f, err := os.Open("./test/test-jpeg.jpg") f, err := os.Open("./test/test-jpeg.jpg")
if err != nil { if err != nil {
@ -432,13 +432,13 @@ func (suite *ManagerTestSuite) TestSimpleJpegProcessBlockingReadCloser() {
func (suite *ManagerTestSuite) TestPngNoAlphaChannelProcessBlocking() { func (suite *ManagerTestSuite) TestPngNoAlphaChannelProcessBlocking() {
ctx := context.Background() ctx := context.Background()
data := func(_ context.Context) (io.Reader, int, error) { data := func(_ context.Context) (io.Reader, int64, error) {
// load bytes from a test image // load bytes from a test image
b, err := os.ReadFile("./test/test-png-noalphachannel.png") b, err := os.ReadFile("./test/test-png-noalphachannel.png")
if err != nil { if err != nil {
panic(err) panic(err)
} }
return bytes.NewBuffer(b), len(b), nil return bytes.NewBuffer(b), int64(len(b)), nil
} }
accountID := "01FS1X72SK9ZPW0J1QQ68BD264" accountID := "01FS1X72SK9ZPW0J1QQ68BD264"
@ -504,13 +504,13 @@ func (suite *ManagerTestSuite) TestPngNoAlphaChannelProcessBlocking() {
func (suite *ManagerTestSuite) TestPngAlphaChannelProcessBlocking() { func (suite *ManagerTestSuite) TestPngAlphaChannelProcessBlocking() {
ctx := context.Background() ctx := context.Background()
data := func(_ context.Context) (io.Reader, int, error) { data := func(_ context.Context) (io.Reader, int64, error) {
// load bytes from a test image // load bytes from a test image
b, err := os.ReadFile("./test/test-png-alphachannel.png") b, err := os.ReadFile("./test/test-png-alphachannel.png")
if err != nil { if err != nil {
panic(err) panic(err)
} }
return bytes.NewBuffer(b), len(b), nil return bytes.NewBuffer(b), int64(len(b)), nil
} }
accountID := "01FS1X72SK9ZPW0J1QQ68BD264" accountID := "01FS1X72SK9ZPW0J1QQ68BD264"
@ -576,13 +576,13 @@ func (suite *ManagerTestSuite) TestPngAlphaChannelProcessBlocking() {
func (suite *ManagerTestSuite) TestSimpleJpegProcessBlockingWithCallback() { func (suite *ManagerTestSuite) TestSimpleJpegProcessBlockingWithCallback() {
ctx := context.Background() ctx := context.Background()
data := func(_ context.Context) (io.Reader, int, error) { data := func(_ context.Context) (io.Reader, int64, error) {
// load bytes from a test image // load bytes from a test image
b, err := os.ReadFile("./test/test-jpeg.jpg") b, err := os.ReadFile("./test/test-jpeg.jpg")
if err != nil { if err != nil {
panic(err) panic(err)
} }
return bytes.NewBuffer(b), len(b), nil return bytes.NewBuffer(b), int64(len(b)), nil
} }
// test the callback function by setting a simple boolean // test the callback function by setting a simple boolean
@ -659,13 +659,13 @@ func (suite *ManagerTestSuite) TestSimpleJpegProcessBlockingWithCallback() {
func (suite *ManagerTestSuite) TestSimpleJpegProcessAsync() { func (suite *ManagerTestSuite) TestSimpleJpegProcessAsync() {
ctx := context.Background() ctx := context.Background()
data := func(_ context.Context) (io.Reader, int, error) { data := func(_ context.Context) (io.Reader, int64, error) {
// load bytes from a test image // load bytes from a test image
b, err := os.ReadFile("./test/test-jpeg.jpg") b, err := os.ReadFile("./test/test-jpeg.jpg")
if err != nil { if err != nil {
panic(err) panic(err)
} }
return bytes.NewBuffer(b), len(b), nil return bytes.NewBuffer(b), int64(len(b)), nil
} }
accountID := "01FS1X72SK9ZPW0J1QQ68BD264" accountID := "01FS1X72SK9ZPW0J1QQ68BD264"
@ -744,9 +744,9 @@ func (suite *ManagerTestSuite) TestSimpleJpegQueueSpamming() {
panic(err) panic(err)
} }
data := func(_ context.Context) (io.Reader, int, error) { data := func(_ context.Context) (io.Reader, int64, error) {
// load bytes from a test image // load bytes from a test image
return bytes.NewReader(b), len(b), nil return bytes.NewReader(b), int64(len(b)), nil
} }
accountID := "01FS1X72SK9ZPW0J1QQ68BD264" accountID := "01FS1X72SK9ZPW0J1QQ68BD264"
@ -820,13 +820,13 @@ func (suite *ManagerTestSuite) TestSimpleJpegQueueSpamming() {
func (suite *ManagerTestSuite) TestSimpleJpegProcessBlockingWithDiskStorage() { func (suite *ManagerTestSuite) TestSimpleJpegProcessBlockingWithDiskStorage() {
ctx := context.Background() ctx := context.Background()
data := func(_ context.Context) (io.Reader, int, error) { data := func(_ context.Context) (io.Reader, int64, error) {
// load bytes from a test image // load bytes from a test image
b, err := os.ReadFile("./test/test-jpeg.jpg") b, err := os.ReadFile("./test/test-jpeg.jpg")
if err != nil { if err != nil {
panic(err) panic(err)
} }
return bytes.NewBuffer(b), len(b), nil return bytes.NewBuffer(b), int64(len(b)), nil
} }
accountID := "01FS1X72SK9ZPW0J1QQ68BD264" accountID := "01FS1X72SK9ZPW0J1QQ68BD264"

View file

@ -210,11 +210,11 @@ func (p *ProcessingEmoji) store(ctx context.Context) error {
// concatenate the first bytes with the existing bytes still in the reader (thanks Mara) // concatenate the first bytes with the existing bytes still in the reader (thanks Mara)
readerToStore := io.MultiReader(bytes.NewBuffer(firstBytes), reader) readerToStore := io.MultiReader(bytes.NewBuffer(firstBytes), reader)
var maxEmojiSize int var maxEmojiSize int64
if p.emoji.Domain == "" { if p.emoji.Domain == "" {
maxEmojiSize = config.GetMediaEmojiLocalMaxSize() maxEmojiSize = int64(config.GetMediaEmojiLocalMaxSize())
} else { } else {
maxEmojiSize = config.GetMediaEmojiRemoteMaxSize() maxEmojiSize = int64(config.GetMediaEmojiRemoteMaxSize())
} }
// if we know the fileSize already, make sure it's not bigger than our limit // if we know the fileSize already, make sure it's not bigger than our limit
@ -241,7 +241,7 @@ func (p *ProcessingEmoji) store(ctx context.Context) error {
return fmt.Errorf("store: discovered emoji fileSize (%db) is larger than allowed emojiRemoteMaxSize (%db)", fileSize, maxEmojiSize) return fmt.Errorf("store: discovered emoji fileSize (%db) is larger than allowed emojiRemoteMaxSize (%db)", fileSize, maxEmojiSize)
} }
p.emoji.ImageFileSize = fileSize p.emoji.ImageFileSize = int(fileSize)
p.read = true p.read = true
if p.postData != nil { if p.postData != nil {

View file

@ -315,7 +315,7 @@ func (p *ProcessingMedia) store(ctx context.Context) error {
p.attachment.Type = gtsmodel.FileTypeImage p.attachment.Type = gtsmodel.FileTypeImage
if fileSize > 0 { if fileSize > 0 {
var err error var err error
readerToStore, err = terminator.Terminate(readerToStore, fileSize, extension) readerToStore, err = terminator.Terminate(readerToStore, int(fileSize), extension)
if err != nil { if err != nil {
return fmt.Errorf("store: exif error: %s", err) return fmt.Errorf("store: exif error: %s", err)
} }
@ -344,7 +344,7 @@ func (p *ProcessingMedia) store(ctx context.Context) error {
cached := true cached := true
p.attachment.Cached = &cached p.attachment.Cached = &cached
p.attachment.File.FileSize = fileSize p.attachment.File.FileSize = int(fileSize)
p.read = true p.read = true
if p.postData != nil { if p.postData != nil {

View file

@ -74,13 +74,13 @@ func (suite *PruneRemoteTestSuite) TestPruneAndRecache() {
suite.ErrorIs(err, storage.ErrNotFound) suite.ErrorIs(err, storage.ErrNotFound)
// now recache the image.... // now recache the image....
data := func(_ context.Context) (io.Reader, int, error) { data := func(_ context.Context) (io.Reader, int64, error) {
// load bytes from a test image // load bytes from a test image
b, err := os.ReadFile("../../testrig/media/thoughtsofdog-original.jpeg") b, err := os.ReadFile("../../testrig/media/thoughtsofdog-original.jpeg")
if err != nil { if err != nil {
panic(err) panic(err)
} }
return bytes.NewBuffer(b), len(b), nil return bytes.NewBuffer(b), int64(len(b)), nil
} }
processingRecache, err := suite.manager.RecacheMedia(ctx, data, nil, testAttachment.ID) processingRecache, err := suite.manager.RecacheMedia(ctx, data, nil, testAttachment.ID)
suite.NoError(err) suite.NoError(err)

View file

@ -118,7 +118,7 @@ type AdditionalEmojiInfo struct {
} }
// DataFunc represents a function used to retrieve the raw bytes of a piece of media. // DataFunc represents a function used to retrieve the raw bytes of a piece of media.
type DataFunc func(ctx context.Context) (reader io.Reader, fileSize int, err error) type DataFunc func(ctx context.Context) (reader io.Reader, fileSize int64, err error)
// PostDataCallbackFunc represents a function executed after the DataFunc has been executed, // PostDataCallbackFunc represents a function executed after the DataFunc has been executed,
// and the returned reader has been read. It can be used to clean up any remaining resources. // and the returned reader has been read. It can be used to clean up any remaining resources.

View file

@ -151,19 +151,19 @@ func parseOlderThan(olderThanDays int) (time.Time, error) {
// lengthReader wraps a reader and reads the length of total bytes written as it goes. // lengthReader wraps a reader and reads the length of total bytes written as it goes.
type lengthReader struct { type lengthReader struct {
source io.Reader source io.Reader
length int length int64
} }
func (r *lengthReader) Read(b []byte) (int, error) { func (r *lengthReader) Read(b []byte) (int, error) {
n, err := r.source.Read(b) n, err := r.source.Read(b)
r.length += n r.length += int64(n)
return n, err return n, err
} }
// putStream either puts a file with a known fileSize into storage directly, and returns the // putStream either puts a file with a known fileSize into storage directly, and returns the
// fileSize unchanged, or it wraps the reader with a lengthReader and returns the discovered // fileSize unchanged, or it wraps the reader with a lengthReader and returns the discovered
// fileSize. // fileSize.
func putStream(ctx context.Context, storage storage.Driver, key string, r io.Reader, fileSize int) (int, error) { func putStream(ctx context.Context, storage storage.Driver, key string, r io.Reader, fileSize int64) (int64, error) {
if fileSize > 0 { if fileSize > 0 {
return fileSize, storage.PutStream(ctx, key, r) return fileSize, storage.PutStream(ctx, key, r)
} }

View file

@ -184,13 +184,13 @@ func (p *processor) Update(ctx context.Context, account *gtsmodel.Account, form
// the account's new avatar image. // the account's new avatar image.
func (p *processor) UpdateAvatar(ctx context.Context, avatar *multipart.FileHeader, accountID string) (*gtsmodel.MediaAttachment, error) { func (p *processor) UpdateAvatar(ctx context.Context, avatar *multipart.FileHeader, accountID string) (*gtsmodel.MediaAttachment, error) {
maxImageSize := config.GetMediaImageMaxSize() maxImageSize := config.GetMediaImageMaxSize()
if int(avatar.Size) > maxImageSize { if avatar.Size > int64(maxImageSize) {
return nil, fmt.Errorf("UpdateAvatar: avatar with size %d exceeded max image size of %d bytes", avatar.Size, maxImageSize) return nil, fmt.Errorf("UpdateAvatar: avatar with size %d exceeded max image size of %d bytes", avatar.Size, maxImageSize)
} }
dataFunc := func(innerCtx context.Context) (io.Reader, int, error) { dataFunc := func(innerCtx context.Context) (io.Reader, int64, error) {
f, err := avatar.Open() f, err := avatar.Open()
return f, int(avatar.Size), err return f, avatar.Size, err
} }
isAvatar := true isAvatar := true
@ -211,13 +211,13 @@ func (p *processor) UpdateAvatar(ctx context.Context, avatar *multipart.FileHead
// the account's new header image. // the account's new header image.
func (p *processor) UpdateHeader(ctx context.Context, header *multipart.FileHeader, accountID string) (*gtsmodel.MediaAttachment, error) { func (p *processor) UpdateHeader(ctx context.Context, header *multipart.FileHeader, accountID string) (*gtsmodel.MediaAttachment, error) {
maxImageSize := config.GetMediaImageMaxSize() maxImageSize := config.GetMediaImageMaxSize()
if int(header.Size) > maxImageSize { if header.Size > int64(maxImageSize) {
return nil, fmt.Errorf("UpdateHeader: header with size %d exceeded max image size of %d bytes", header.Size, maxImageSize) return nil, fmt.Errorf("UpdateHeader: header with size %d exceeded max image size of %d bytes", header.Size, maxImageSize)
} }
dataFunc := func(innerCtx context.Context) (io.Reader, int, error) { dataFunc := func(innerCtx context.Context) (io.Reader, int64, error) {
f, err := header.Open() f, err := header.Open()
return f, int(header.Size), err return f, header.Size, err
} }
isHeader := true isHeader := true

View file

@ -52,9 +52,9 @@ func (p *processor) EmojiCreate(ctx context.Context, account *gtsmodel.Account,
emojiURI := uris.GenerateURIForEmoji(emojiID) emojiURI := uris.GenerateURIForEmoji(emojiID)
data := func(innerCtx context.Context) (io.Reader, int, error) { data := func(innerCtx context.Context) (io.Reader, int64, error) {
f, err := form.Image.Open() f, err := form.Image.Open()
return f, int(form.Image.Size), err return f, form.Image.Size, err
} }
processingEmoji, err := p.mediaManager.ProcessEmoji(ctx, data, nil, form.Shortcode, emojiID, emojiURI, nil) processingEmoji, err := p.mediaManager.ProcessEmoji(ctx, data, nil, form.Shortcode, emojiID, emojiURI, nil)

View file

@ -30,9 +30,9 @@
) )
func (p *processor) Create(ctx context.Context, account *gtsmodel.Account, form *apimodel.AttachmentRequest) (*apimodel.Attachment, gtserror.WithCode) { func (p *processor) Create(ctx context.Context, account *gtsmodel.Account, form *apimodel.AttachmentRequest) (*apimodel.Attachment, gtserror.WithCode) {
data := func(innerCtx context.Context) (io.Reader, int, error) { data := func(innerCtx context.Context) (io.Reader, int64, error) {
f, err := form.File.Open() f, err := form.File.Open()
return f, int(form.File.Size), err return f, form.File.Size, err
} }
focusX, focusY, err := parseFocus(form.Focus) focusX, focusY, err := parseFocus(form.Focus)

View file

@ -138,7 +138,7 @@ func (p *processor) getAttachmentContent(ctx context.Context, requestingAccount
// if it's the thumbnail that's requested then the user will have to wait a bit while we process the // if it's the thumbnail that's requested then the user will have to wait a bit while we process the
// large version and derive a thumbnail from it, so use the normal recaching procedure: fetch the media, // large version and derive a thumbnail from it, so use the normal recaching procedure: fetch the media,
// process it, then return the thumbnail data // process it, then return the thumbnail data
data = func(innerCtx context.Context) (io.Reader, int, error) { data = func(innerCtx context.Context) (io.Reader, int64, error) {
transport, err := p.transportController.NewTransportForUsername(innerCtx, requestingUsername) transport, err := p.transportController.NewTransportForUsername(innerCtx, requestingUsername)
if err != nil { if err != nil {
return nil, 0, err return nil, 0, err
@ -169,7 +169,7 @@ func (p *processor) getAttachmentContent(ctx context.Context, requestingAccount
// the caller will read from the buffered reader, so it doesn't matter if they drop out without reading everything // the caller will read from the buffered reader, so it doesn't matter if they drop out without reading everything
attachmentContent.Content = bufferedReader attachmentContent.Content = bufferedReader
data = func(innerCtx context.Context) (io.Reader, int, error) { data = func(innerCtx context.Context) (io.Reader, int64, error) {
transport, err := p.transportController.NewTransportForUsername(innerCtx, requestingUsername) transport, err := p.transportController.NewTransportForUsername(innerCtx, requestingUsername)
if err != nil { if err != nil {
return nil, 0, err return nil, 0, err

View file

@ -26,7 +26,7 @@
"net/url" "net/url"
) )
func (t *transport) DereferenceMedia(ctx context.Context, iri *url.URL) (io.ReadCloser, int, error) { func (t *transport) DereferenceMedia(ctx context.Context, iri *url.URL) (io.ReadCloser, int64, error) {
// Build IRI just once // Build IRI just once
iriStr := iri.String() iriStr := iri.String()
@ -50,5 +50,5 @@ func (t *transport) DereferenceMedia(ctx context.Context, iri *url.URL) (io.Read
return nil, 0, fmt.Errorf("GET request to %s failed (%d): %s", iriStr, rsp.StatusCode, rsp.Status) return nil, 0, fmt.Errorf("GET request to %s failed (%d): %s", iriStr, rsp.StatusCode, rsp.Status)
} }
return rsp.Body, int(rsp.ContentLength), nil return rsp.Body, rsp.ContentLength, nil
} }

View file

@ -47,7 +47,7 @@
type Transport interface { type Transport interface {
pub.Transport pub.Transport
// DereferenceMedia fetches the given media attachment IRI, returning the reader and filesize. // DereferenceMedia fetches the given media attachment IRI, returning the reader and filesize.
DereferenceMedia(ctx context.Context, iri *url.URL) (io.ReadCloser, int, error) DereferenceMedia(ctx context.Context, iri *url.URL) (io.ReadCloser, int64, error)
// DereferenceInstance dereferences remote instance information, first by checking /api/v1/instance, and then by checking /.well-known/nodeinfo. // DereferenceInstance dereferences remote instance information, first by checking /api/v1/instance, and then by checking /.well-known/nodeinfo.
DereferenceInstance(ctx context.Context, iri *url.URL) (*gtsmodel.Instance, error) DereferenceInstance(ctx context.Context, iri *url.URL) (*gtsmodel.Instance, error)
// Finger performs a webfinger request with the given username and domain, and returns the bytes from the response body. // Finger performs a webfinger request with the given username and domain, and returns the bytes from the response body.

View file

@ -688,9 +688,9 @@ func (c *converter) InstanceToAPIInstance(ctx context.Context, i *gtsmodel.Insta
}, },
MediaAttachments: &model.InstanceConfigurationMediaAttachments{ MediaAttachments: &model.InstanceConfigurationMediaAttachments{
SupportedMimeTypes: media.AllSupportedMIMETypes(), SupportedMimeTypes: media.AllSupportedMIMETypes(),
ImageSizeLimit: config.GetMediaImageMaxSize(), ImageSizeLimit: int(config.GetMediaImageMaxSize()),
ImageMatrixLimit: instanceMediaAttachmentsImageMatrixLimit, // height*width ImageMatrixLimit: instanceMediaAttachmentsImageMatrixLimit, // height*width
VideoSizeLimit: config.GetMediaVideoMaxSize(), VideoSizeLimit: int(config.GetMediaVideoMaxSize()),
VideoFrameRateLimit: instanceMediaAttachmentsVideoFrameRateLimit, VideoFrameRateLimit: instanceMediaAttachmentsVideoFrameRateLimit,
VideoMatrixLimit: instanceMediaAttachmentsVideoMatrixLimit, // height*width VideoMatrixLimit: instanceMediaAttachmentsVideoMatrixLimit, // height*width
}, },

View file

@ -2,7 +2,7 @@
set -eu set -eu
EXPECTED='{"account-domain":"peepee","accounts-allow-custom-css":true,"accounts-approval-required":false,"accounts-reason-required":false,"accounts-registration-open":true,"advanced-cookies-samesite":"strict","application-name":"gts","bind-address":"127.0.0.1","config-path":"./test/test.yaml","db-address":":memory:","db-database":"gotosocial_prod","db-password":"hunter2","db-port":6969,"db-tls-ca-cert":"","db-tls-mode":"disable","db-type":"sqlite","db-user":"sex-haver","email":"","host":"example.com","instance-deliver-to-shared-inboxes":false,"instance-expose-peers":true,"instance-expose-suspended":true,"letsencrypt-cert-dir":"/gotosocial/storage/certs","letsencrypt-email-address":"","letsencrypt-enabled":true,"letsencrypt-port":80,"log-db-queries":true,"log-level":"info","media-description-max-chars":5000,"media-description-min-chars":69,"media-emoji-local-max-size":420,"media-emoji-remote-max-size":420,"media-image-max-size":420,"media-remote-cache-days":30,"media-video-max-size":420,"oidc-client-id":"1234","oidc-client-secret":"shhhh its a secret","oidc-enabled":true,"oidc-idp-name":"sex-haver","oidc-issuer":"whoknows","oidc-scopes":["read","write"],"oidc-skip-verification":true,"password":"","path":"","port":6969,"protocol":"http","smtp-from":"queen@terfisland.org","smtp-host":"example.com","smtp-password":"hunter2","smtp-port":4269,"smtp-username":"sex-haver","software-version":"","statuses-cw-max-chars":420,"statuses-max-chars":69,"statuses-media-max-files":1,"statuses-poll-max-options":1,"statuses-poll-option-max-chars":50,"storage-backend":"local","storage-local-base-path":"/root/store","storage-s3-access-key":"minio","storage-s3-bucket":"gts","storage-s3-endpoint":"localhost:9000","storage-s3-secret-key":"miniostorage","storage-s3-use-ssl":false,"syslog-address":"127.0.0.1:6969","syslog-enabled":true,"syslog-protocol":"udp","trusted-proxies":["127.0.0.1/32","docker.host.local"],"username":"","web-asset-base-dir":"/root","web-template-base-dir":"/root"}' EXPECT='{"account-domain":"peepee","accounts-allow-custom-css":true,"accounts-approval-required":false,"accounts-reason-required":false,"accounts-registration-open":true,"advanced-cookies-samesite":"strict","application-name":"gts","bind-address":"127.0.0.1","config-path":"./test/test.yaml","db-address":":memory:","db-database":"gotosocial_prod","db-password":"hunter2","db-port":6969,"db-tls-ca-cert":"","db-tls-mode":"disable","db-type":"sqlite","db-user":"sex-haver","email":"","host":"example.com","instance-deliver-to-shared-inboxes":false,"instance-expose-peers":true,"instance-expose-suspended":true,"letsencrypt-cert-dir":"/gotosocial/storage/certs","letsencrypt-email-address":"","letsencrypt-enabled":true,"letsencrypt-port":80,"log-db-queries":true,"log-level":"info","media-description-max-chars":5000,"media-description-min-chars":69,"media-emoji-local-max-size":420,"media-emoji-remote-max-size":420,"media-image-max-size":420,"media-remote-cache-days":30,"media-video-max-size":420,"oidc-client-id":"1234","oidc-client-secret":"shhhh its a secret","oidc-enabled":true,"oidc-idp-name":"sex-haver","oidc-issuer":"whoknows","oidc-scopes":["read","write"],"oidc-skip-verification":true,"password":"","path":"","port":6969,"protocol":"http","smtp-from":"queen.rip.in.piss@terfisland.org","smtp-host":"example.com","smtp-password":"hunter2","smtp-port":4269,"smtp-username":"sex-haver","software-version":"","statuses-cw-max-chars":420,"statuses-max-chars":69,"statuses-media-max-files":1,"statuses-poll-max-options":1,"statuses-poll-option-max-chars":50,"storage-backend":"local","storage-local-base-path":"/root/store","storage-s3-access-key":"minio","storage-s3-bucket":"gts","storage-s3-endpoint":"localhost:9000","storage-s3-secret-key":"miniostorage","storage-s3-use-ssl":false,"syslog-address":"127.0.0.1:6969","syslog-enabled":true,"syslog-protocol":"udp","trusted-proxies":["127.0.0.1/32","docker.host.local"],"username":"","web-asset-base-dir":"/root","web-template-base-dir":"/root"}'
# Set all the environment variables to # Set all the environment variables to
# ensure that these are parsed without panic # ensure that these are parsed without panic
@ -66,16 +66,24 @@ GTS_SMTP_HOST='example.com' \
GTS_SMTP_PORT=4269 \ GTS_SMTP_PORT=4269 \
GTS_SMTP_USERNAME='sex-haver' \ GTS_SMTP_USERNAME='sex-haver' \
GTS_SMTP_PASSWORD='hunter2' \ GTS_SMTP_PASSWORD='hunter2' \
GTS_SMTP_FROM='queen@terfisland.org' \ GTS_SMTP_FROM='queen.rip.in.piss@terfisland.org' \
GTS_SYSLOG_ENABLED=true \ GTS_SYSLOG_ENABLED=true \
GTS_SYSLOG_PROTOCOL='udp' \ GTS_SYSLOG_PROTOCOL='udp' \
GTS_SYSLOG_ADDRESS='127.0.0.1:6969' \ GTS_SYSLOG_ADDRESS='127.0.0.1:6969' \
GTS_ADVANCED_COOKIES_SAMESITE='strict' \ GTS_ADVANCED_COOKIES_SAMESITE='strict' \
go run ./cmd/gotosocial/... --config-path $(dirname ${0})/test.yaml debug config) go run ./cmd/gotosocial/... --config-path $(dirname ${0})/test.yaml debug config)
if [ "${OUTPUT}" != "${EXPECTED}" ]; then OUTPUT_OUT=$(mktemp)
echo "$OUTPUT" > "$OUTPUT_OUT"
EXPECT_OUT=$(mktemp)
echo "$EXPECT" > "$EXPECT_OUT"
if ! DIFF=$(diff "$OUTPUT_OUT" "$EXPECT_OUT"); then
echo "OUTPUT not equal EXPECTED" echo "OUTPUT not equal EXPECTED"
echo "$DIFF"
exit 1 exit 1
else else
echo "OK" echo "OK"
exit 0
fi fi

View file

@ -6,6 +6,24 @@
"unsafe" "unsafe"
) )
const (
// SI units
KB Size = 1e3
MB Size = 1e6
GB Size = 1e9
TB Size = 1e12
PB Size = 1e15
EB Size = 1e18
// IEC units
KiB Size = 1024
MiB Size = KiB * 1024
GiB Size = MiB * 1024
TiB Size = GiB * 1024
PiB Size = TiB * 1024
EiB Size = PiB * 1024
)
var ( var (
// ErrInvalidUnit is returned when an invalid IEC/SI is provided. // ErrInvalidUnit is returned when an invalid IEC/SI is provided.
ErrInvalidUnit = errors.New("bytesize: invalid unit") ErrInvalidUnit = errors.New("bytesize: invalid unit")
@ -13,42 +31,39 @@
// ErrInvalidFormat is returned when an invalid size value is provided. // ErrInvalidFormat is returned when an invalid size value is provided.
ErrInvalidFormat = errors.New("bytesize: invalid format") ErrInvalidFormat = errors.New("bytesize: invalid format")
// bunits are the binary unit chars.
units = `kMGTPE`
// iecpows is a precomputed table of 1024^n. // iecpows is a precomputed table of 1024^n.
iecpows = [...]float64{ iecpows = [...]float64{
float64(1024), // KiB float64(KiB),
float64(1024 * 1024), // MiB float64(MiB),
float64(1024 * 1024 * 1024), // GiB float64(GiB),
float64(1024 * 1024 * 1024 * 1024), // TiB float64(TiB),
float64(1024 * 1024 * 1024 * 1024 * 1024), // PiB float64(PiB),
float64(1024 * 1024 * 1024 * 1024 * 1024 * 1024), // EiB float64(EiB),
} }
// sipows is a precomputed table of 1000^n. // sipows is a precomputed table of 1000^n.
sipows = [...]float64{ sipows = [...]float64{
float64(1e3), // KB float64(KB),
float64(1e6), // MB float64(MB),
float64(1e9), // GB float64(GB),
float64(1e12), // TB float64(TB),
float64(1e15), // PB float64(PB),
float64(1e18), // EB float64(EB),
} }
// bvals is a precomputed table of IEC unit values. // bvals is a precomputed table of IEC unit values.
iecvals = [...]uint64{ iecvals = [...]Size{
'k': 1024, 'k': KiB,
'K': 1024, 'K': KiB,
'M': 1024 * 1024, 'M': MiB,
'G': 1024 * 1024 * 1024, 'G': GiB,
'T': 1024 * 1024 * 1024 * 1024, 'T': TiB,
'P': 1024 * 1024 * 1024 * 1024 * 1024, 'P': PiB,
'E': 1024 * 1024 * 1024 * 1024 * 1024 * 1024, 'E': EiB,
} }
// sivals is a precomputed table of SI unit values. // sivals is a precomputed table of SI unit values.
sivals = [...]uint64{ sivals = [...]Size{
// ASCII numbers _aren't_ valid SI unit values, // ASCII numbers _aren't_ valid SI unit values,
// BUT if the space containing a possible unit // BUT if the space containing a possible unit
// char is checked with this table -- it is valid // char is checked with this table -- it is valid
@ -64,12 +79,13 @@
'8': 1, '8': 1,
'9': 1, '9': 1,
'k': 1e3, 'k': KB,
'M': 1e6, 'K': KB,
'G': 1e9, 'M': MB,
'T': 1e12, 'G': GB,
'P': 1e15, 'T': TB,
'E': 1e18, 'P': PB,
'E': EB,
} }
) )
@ -91,7 +107,28 @@ func ParseSize(s string) (Size, error) {
return 0, ErrInvalidFormat return 0, ErrInvalidFormat
} }
return Size(uint64(f) * unit), nil return Size(f) * unit, nil
}
// Set implements flag.Value{}.
func (sz *Size) Set(in string) error {
s, err := ParseSize(in)
if err != nil {
return err
}
*sz = s
return nil
}
// MarshalText implements encoding.TextMarshaler{}.
func (sz *Size) MarshalText() ([]byte, error) {
const maxLen = 7 // max IEC string length
return sz.AppendFormatIEC(make([]byte, 0, maxLen)), nil
}
// UnmarshalText implements encoding.TextUnmarshaler{}.
func (sz *Size) UnmarshalText(text []byte) error {
return sz.Set(*(*string)(unsafe.Pointer(&text)))
} }
// AppendFormat defaults to using Size.AppendFormatIEC(). // AppendFormat defaults to using Size.AppendFormatIEC().
@ -121,7 +158,13 @@ func (sz Size) AppendFormatIEC(dst []byte) []byte {
// appendFormat will append formatted Size to 'dst', depending on base, powers table and single unit suffix. // appendFormat will append formatted Size to 'dst', depending on base, powers table and single unit suffix.
func (sz Size) appendFormat(dst []byte, base uint64, pows *[6]float64, sunit string) []byte { func (sz Size) appendFormat(dst []byte, base uint64, pows *[6]float64, sunit string) []byte {
const min = 0.75 const (
// min "small" unit threshold
min = 0.75
// binary unit chars.
units = `kMGTPE`
)
// Larger number: get value of // Larger number: get value of
// i / unit size. We have a 'min' // i / unit size. We have a 'min'
@ -143,13 +186,15 @@ func (sz Size) appendFormat(dst []byte, base uint64, pows *[6]float64, sunit str
// StringSI returns an SI unit string format of Size. // StringSI returns an SI unit string format of Size.
func (sz Size) StringSI() string { func (sz Size) StringSI() string {
b := sz.AppendFormatSI(make([]byte, 0, 6)) const maxLen = 6 // max SI string length
b := sz.AppendFormatSI(make([]byte, 0, maxLen))
return *(*string)(unsafe.Pointer(&b)) return *(*string)(unsafe.Pointer(&b))
} }
// StringIEC returns an IEC unit string format of Size. // StringIEC returns an IEC unit string format of Size.
func (sz Size) StringIEC() string { func (sz Size) StringIEC() string {
b := sz.AppendFormatIEC(make([]byte, 0, 7)) const maxLen = 7 // max IEC string length
b := sz.AppendFormatIEC(make([]byte, 0, maxLen))
return *(*string)(unsafe.Pointer(&b)) return *(*string)(unsafe.Pointer(&b))
} }
@ -159,9 +204,7 @@ func (sz Size) String() string {
} }
// parseUnit will parse the byte size unit from string 's'. // parseUnit will parse the byte size unit from string 's'.
func parseUnit(s string) (uint64, int, error) { func parseUnit(s string) (Size, int, error) {
var isIEC bool
// Check for string // Check for string
if len(s) < 1 { if len(s) < 1 {
return 0, 0, ErrInvalidFormat return 0, 0, ErrInvalidFormat
@ -171,8 +214,8 @@ func parseUnit(s string) (uint64, int, error) {
if l := len(s) - 1; s[l] == 'B' { if l := len(s) - 1; s[l] == 'B' {
s = s[:l] s = s[:l]
// Check str remains
if len(s) < 1 { if len(s) < 1 {
// No remaining str before unit suffix
return 0, 0, ErrInvalidFormat return 0, 0, ErrInvalidFormat
} }
} }
@ -180,38 +223,41 @@ func parseUnit(s string) (uint64, int, error) {
// Strip IEC binary unit suffix // Strip IEC binary unit suffix
if l := len(s) - 1; s[l] == 'i' { if l := len(s) - 1; s[l] == 'i' {
s = s[:l] s = s[:l]
isIEC = true
// Check str remains
if len(s) < 1 { if len(s) < 1 {
// No remaining str before unit suffix
return 0, 0, ErrInvalidFormat return 0, 0, ErrInvalidFormat
} }
// Location of unit char.
l := len(s) - 1
c := int(s[l])
// Check valid unit char was provided
if len(iecvals) < c || iecvals[c] == 0 {
return 0, 0, ErrInvalidUnit
}
// Return parsed IEC unit size
return iecvals[c], l, nil
} }
// Location of unit char. // Location of unit char.
l := len(s) - 1 l := len(s) - 1
c := int(s[l])
var unit uint64 switch {
switch c := int(s[l]); { // Check valid unit char provided
// Determine IEC unit in use case len(sivals) < c || sivals[c] == 0:
case isIEC && c < len(iecvals): return 0, 0, ErrInvalidUnit
unit = iecvals[c]
if unit == 0 {
return 0, 0, ErrInvalidUnit
}
// Determine SI unit in use // No unit char (only ascii number)
case c < len(sivals): case sivals[c] == 1:
unit = sivals[c] l++
switch unit {
case 0:
return 0, 0, ErrInvalidUnit
case 1:
l++
}
} }
return unit, l, nil // Return parsed SI unit size
return sivals[c], l, nil
} }
// ftoa appends string formatted 'f' to 'dst', assumed < ~800. // ftoa appends string formatted 'f' to 'dst', assumed < ~800.
@ -286,7 +332,7 @@ func ftoa(dst []byte, f float64) []byte {
// itoa appends string formatted 'i' to 'dst'. // itoa appends string formatted 'i' to 'dst'.
func itoa(dst []byte, i uint64) []byte { func itoa(dst []byte, i uint64) []byte {
// Assemble int in reverse order. // Assemble uint in reverse order.
var b [4]byte var b [4]byte
bp := len(b) - 1 bp := len(b) - 1
@ -302,5 +348,10 @@ func itoa(dst []byte, i uint64) []byte {
return append(dst, b[bp:]...) return append(dst, b[bp:]...)
} }
// We use the following internal strconv function usually
// used internally to parse float values, as we know that
// are value passed will always be of 64bit type, and knowing
// the returned float string length is very helpful!
//
//go:linkname atof64 strconv.atof64 //go:linkname atof64 strconv.atof64
func atof64(string) (float64, int, error) func atof64(string) (float64, int, error)

2
vendor/modules.txt vendored
View file

@ -7,7 +7,7 @@ codeberg.org/gruf/go-bitutil
# codeberg.org/gruf/go-bytes v1.0.2 # codeberg.org/gruf/go-bytes v1.0.2
## explicit; go 1.14 ## explicit; go 1.14
codeberg.org/gruf/go-bytes codeberg.org/gruf/go-bytes
# codeberg.org/gruf/go-bytesize v0.2.1 # codeberg.org/gruf/go-bytesize v1.0.0
## explicit; go 1.17 ## explicit; go 1.17
codeberg.org/gruf/go-bytesize codeberg.org/gruf/go-bytesize
# codeberg.org/gruf/go-byteutil v1.0.2 # codeberg.org/gruf/go-byteutil v1.0.2