commit 68736efd2039317b2ca80ccacfb683580da63379
parent 3e4e57d5543277cdf42da03b20229851eb24be69
Author: tobi <31960611+tsmethurst@users.noreply.github.com>
Date: Sun, 26 Jun 2022 10:58:45 +0200
[feature] add configuration to `/api/v1/instance` response (#670)
* add configuration object to api instance model
* regenerate swagger docs
* add func to return all supported mimes for media
* add instance configuration to api serialization
* fix json tags
* update instance endpoint tests
* fix typeutils tests
* final regen of swagger docs
* omitempty instance configuration
Diffstat:
5 files changed, 252 insertions(+), 5 deletions(-)
diff --git a/docs/api/swagger.yaml b/docs/api/swagger.yaml
@@ -975,6 +975,8 @@ definitions:
description: New account registrations require admin approval.
type: boolean
x-go-name: ApprovalRequired
+ configuration:
+ $ref: '#/definitions/instanceConfiguration'
contact_account:
$ref: '#/definitions/account'
description:
@@ -1062,6 +1064,126 @@ definitions:
type: object
x-go-name: Instance
x-go-package: github.com/superseriousbusiness/gotosocial/internal/api/model
+ instanceConfiguration:
+ properties:
+ media_attachments:
+ $ref: '#/definitions/instanceConfigurationMediaAttachments'
+ polls:
+ $ref: '#/definitions/instanceConfigurationPolls'
+ statuses:
+ $ref: '#/definitions/instanceConfigurationStatuses'
+ title: InstanceConfiguration models instance configuration parameters.
+ type: object
+ x-go-name: InstanceConfiguration
+ x-go-package: github.com/superseriousbusiness/gotosocial/internal/api/model
+ instanceConfigurationMediaAttachments:
+ properties:
+ image_matrix_limit:
+ description: |-
+ Max allowed image size in pixels as height*width.
+
+ GtS doesn't set a limit on this, but for compatibility
+ we give Mastodon's 4096x4096px value here.
+ example: 16777216
+ format: int64
+ type: integer
+ x-go-name: ImageMatrixLimit
+ image_size_limit:
+ description: Max allowed image size in bytes
+ example: 2097152
+ format: int64
+ type: integer
+ x-go-name: ImageSizeLimit
+ supported_mime_types:
+ description: List of mime types that it's possible to upload to this instance.
+ example:
+ - image/jpeg
+ - image/gif
+ items:
+ type: string
+ type: array
+ x-go-name: SupportedMimeTypes
+ video_frame_rate_limit:
+ description: Max allowed video frame rate.
+ example: 60
+ format: int64
+ type: integer
+ x-go-name: VideoFrameRateLimit
+ video_matrix_limit:
+ description: |-
+ Max allowed video size in pixels as height*width.
+
+ GtS doesn't set a limit on this, but for compatibility
+ we give Mastodon's 4096x4096px value here.
+ example: 16777216
+ format: int64
+ type: integer
+ x-go-name: VideoMatrixLimit
+ video_size_limit:
+ description: Max allowed video size in bytes
+ example: 10485760
+ format: int64
+ type: integer
+ x-go-name: VideoSizeLimit
+ title: InstanceConfigurationMediaAttachments models instance media attachment
+ config parameters.
+ type: object
+ x-go-name: InstanceConfigurationMediaAttachments
+ x-go-package: github.com/superseriousbusiness/gotosocial/internal/api/model
+ instanceConfigurationPolls:
+ properties:
+ max_characters_per_option:
+ description: Number of characters allowed per option in the poll.
+ example: 50
+ format: int64
+ type: integer
+ x-go-name: MaxCharactersPerOption
+ max_expiration:
+ description: Maximum expiration time of the poll in seconds.
+ example: 2629746
+ format: int64
+ type: integer
+ x-go-name: MaxExpiration
+ max_options:
+ description: Number of options permitted in a poll on this instance.
+ example: 4
+ format: int64
+ type: integer
+ x-go-name: MaxOptions
+ min_expiration:
+ description: Minimum expiration time of the poll in seconds.
+ example: 300
+ format: int64
+ type: integer
+ x-go-name: MinExpiration
+ title: InstanceConfigurationPolls models instance poll config parameters.
+ type: object
+ x-go-name: InstanceConfigurationPolls
+ x-go-package: github.com/superseriousbusiness/gotosocial/internal/api/model
+ instanceConfigurationStatuses:
+ properties:
+ characters_reserved_per_url:
+ description: Amount of characters that a URL will be compressed to.
+ example: 999
+ format: int64
+ type: integer
+ x-go-name: CharactersReservedPerURL
+ max_characters:
+ description: Maximum allowed length of a post on this instance, in characters.
+ example: 5000
+ format: int64
+ type: integer
+ x-go-name: MaxCharacters
+ max_media_attachments:
+ description: Max number of attachments allowed on a status.
+ example: 4
+ format: int64
+ type: integer
+ x-go-name: MaxMediaAttachments
+ title: InstanceConfigurationStatuses models instance status config parameters.
+ type: object
+ x-go-name: InstanceConfigurationStatuses
+ x-go-package: github.com/superseriousbusiness/gotosocial/internal/api/model
instanceURLs:
properties:
streaming_api:
diff --git a/internal/api/client/instance/instancepatch_test.go b/internal/api/client/instance/instancepatch_test.go
@@ -63,7 +63,7 @@ func (suite *InstancePatchTestSuite) TestInstancePatch1() {
b, err := io.ReadAll(result.Body)
suite.NoError(err)
- suite.Equal(`{"uri":"http://localhost:8080","title":"Example Instance","description":"","short_description":"","email":"someone@example.org","version":"","registrations":true,"approval_required":true,"invites_enabled":false,"urls":{"streaming_api":"wss://localhost:8080"},"stats":{"domain_count":2,"status_count":16,"user_count":4},"thumbnail":"","contact_account":{"id":"01F8MH17FWEB39HZJ76B6VXSKF","username":"admin","acct":"admin","display_name":"","locked":false,"bot":false,"created_at":"2022-05-17T13:10:59.000Z","note":"","url":"http://localhost:8080/@admin","avatar":"","avatar_static":"","header":"","header_static":"","followers_count":1,"following_count":1,"statuses_count":4,"last_status_at":"2021-10-20T10:41:37.000Z","emojis":[],"fields":[]},"max_toot_chars":5000}`, string(b))
+ suite.Equal(`{"uri":"http://localhost:8080","title":"Example Instance","description":"","short_description":"","email":"someone@example.org","version":"","registrations":true,"approval_required":true,"invites_enabled":false,"configuration":{"statuses":{"max_characters":5000,"max_media_attachments":6,"characters_reserved_per_url":999},"media_attachments":{"supported_mime_types":["image/jpeg","image/gif","image/png"],"image_size_limit":1048576,"image_matrix_limit":16777216,"video_size_limit":5242880,"video_frame_rate_limit":60,"video_matrix_limit":16777216},"polls":{"max_options":6,"max_characters_per_option":50,"min_expiration":300,"max_expiration":2629746}},"urls":{"streaming_api":"wss://localhost:8080"},"stats":{"domain_count":2,"status_count":16,"user_count":4},"thumbnail":"","contact_account":{"id":"01F8MH17FWEB39HZJ76B6VXSKF","username":"admin","acct":"admin","display_name":"","locked":false,"bot":false,"created_at":"2022-05-17T13:10:59.000Z","note":"","url":"http://localhost:8080/@admin","avatar":"","avatar_static":"","header":"","header_static":"","followers_count":1,"following_count":1,"statuses_count":4,"last_status_at":"2021-10-20T10:41:37.000Z","emojis":[],"fields":[]},"max_toot_chars":5000}`, string(b))
}
func (suite *InstancePatchTestSuite) TestInstancePatch2() {
@@ -93,7 +93,7 @@ func (suite *InstancePatchTestSuite) TestInstancePatch2() {
b, err := io.ReadAll(result.Body)
suite.NoError(err)
- suite.Equal(`{"uri":"http://localhost:8080","title":"Geoff's Instance","description":"","short_description":"","email":"","version":"","registrations":true,"approval_required":true,"invites_enabled":false,"urls":{"streaming_api":"wss://localhost:8080"},"stats":{"domain_count":2,"status_count":16,"user_count":4},"thumbnail":"","max_toot_chars":5000}`, string(b))
+ suite.Equal(`{"uri":"http://localhost:8080","title":"Geoff's Instance","description":"","short_description":"","email":"","version":"","registrations":true,"approval_required":true,"invites_enabled":false,"configuration":{"statuses":{"max_characters":5000,"max_media_attachments":6,"characters_reserved_per_url":999},"media_attachments":{"supported_mime_types":["image/jpeg","image/gif","image/png"],"image_size_limit":1048576,"image_matrix_limit":16777216,"video_size_limit":5242880,"video_frame_rate_limit":60,"video_matrix_limit":16777216},"polls":{"max_options":6,"max_characters_per_option":50,"min_expiration":300,"max_expiration":2629746}},"urls":{"streaming_api":"wss://localhost:8080"},"stats":{"domain_count":2,"status_count":16,"user_count":4},"thumbnail":"","max_toot_chars":5000}`, string(b))
}
func (suite *InstancePatchTestSuite) TestInstancePatch3() {
@@ -123,7 +123,7 @@ func (suite *InstancePatchTestSuite) TestInstancePatch3() {
b, err := io.ReadAll(result.Body)
suite.NoError(err)
- suite.Equal(`{"uri":"http://localhost:8080","title":"localhost:8080","description":"","short_description":"\u003cp\u003eThis is some html, which is \u003cem\u003eallowed\u003c/em\u003e in short descriptions.\u003c/p\u003e","email":"","version":"","registrations":true,"approval_required":true,"invites_enabled":false,"urls":{"streaming_api":"wss://localhost:8080"},"stats":{"domain_count":2,"status_count":16,"user_count":4},"thumbnail":"","max_toot_chars":5000}`, string(b))
+ suite.Equal(`{"uri":"http://localhost:8080","title":"localhost:8080","description":"","short_description":"\u003cp\u003eThis is some html, which is \u003cem\u003eallowed\u003c/em\u003e in short descriptions.\u003c/p\u003e","email":"","version":"","registrations":true,"approval_required":true,"invites_enabled":false,"configuration":{"statuses":{"max_characters":5000,"max_media_attachments":6,"characters_reserved_per_url":999},"media_attachments":{"supported_mime_types":["image/jpeg","image/gif","image/png"],"image_size_limit":1048576,"image_matrix_limit":16777216,"video_size_limit":5242880,"video_frame_rate_limit":60,"video_matrix_limit":16777216},"polls":{"max_options":6,"max_characters_per_option":50,"min_expiration":300,"max_expiration":2629746}},"urls":{"streaming_api":"wss://localhost:8080"},"stats":{"domain_count":2,"status_count":16,"user_count":4},"thumbnail":"","max_toot_chars":5000}`, string(b))
}
func (suite *InstancePatchTestSuite) TestInstancePatch4() {
@@ -214,7 +214,7 @@ func (suite *InstancePatchTestSuite) TestInstancePatch6() {
b, err := io.ReadAll(result.Body)
suite.NoError(err)
- suite.Equal(`{"uri":"http://localhost:8080","title":"localhost:8080","description":"","short_description":"","email":"","version":"","registrations":true,"approval_required":true,"invites_enabled":false,"urls":{"streaming_api":"wss://localhost:8080"},"stats":{"domain_count":2,"status_count":16,"user_count":4},"thumbnail":"","max_toot_chars":5000}`, string(b))
+ suite.Equal(`{"uri":"http://localhost:8080","title":"localhost:8080","description":"","short_description":"","email":"","version":"","registrations":true,"approval_required":true,"invites_enabled":false,"configuration":{"statuses":{"max_characters":5000,"max_media_attachments":6,"characters_reserved_per_url":999},"media_attachments":{"supported_mime_types":["image/jpeg","image/gif","image/png"],"image_size_limit":1048576,"image_matrix_limit":16777216,"video_size_limit":5242880,"video_frame_rate_limit":60,"video_matrix_limit":16777216},"polls":{"max_options":6,"max_characters_per_option":50,"min_expiration":300,"max_expiration":2629746}},"urls":{"streaming_api":"wss://localhost:8080"},"stats":{"domain_count":2,"status_count":16,"user_count":4},"thumbnail":"","max_toot_chars":5000}`, string(b))
}
func (suite *InstancePatchTestSuite) TestInstancePatch7() {
diff --git a/internal/api/model/instance.go b/internal/api/model/instance.go
@@ -62,6 +62,9 @@ type Instance struct {
ApprovalRequired bool `json:"approval_required"`
// Invites are enabled on this instance.
InvitesEnabled bool `json:"invites_enabled"`
+ // Configuration object containing values about status limits etc.
+ // This key/value will be omitted for remote instances.
+ Configuration *InstanceConfiguration `json:"configuration,omitempty"`
// URLs of interest for client applications.
URLS *InstanceURLs `json:"urls,omitempty"`
// Statistics about the instance: number of posts, accounts, etc.
@@ -79,6 +82,94 @@ type Instance struct {
MaxTootChars uint `json:"max_toot_chars"`
}
+// InstanceConfiguration models instance configuration parameters.
+//
+// swagger:model instanceConfiguration
+type InstanceConfiguration struct {
+ // Instance configuration pertaining to status limits.
+ Statuses *InstanceConfigurationStatuses `json:"statuses"`
+ // Instance configuration pertaining to media attachment types + size limits.
+ MediaAttachments *InstanceConfigurationMediaAttachments `json:"media_attachments"`
+ // Instance configuration pertaining to poll limits.
+ Polls *InstanceConfigurationPolls `json:"polls"`
+}
+
+// InstanceConfigurationStatuses models instance status config parameters.
+//
+// swagger:model instanceConfigurationStatuses
+type InstanceConfigurationStatuses struct {
+ // Maximum allowed length of a post on this instance, in characters.
+ //
+ // example: 5000
+ MaxCharacters int `json:"max_characters"`
+ // Max number of attachments allowed on a status.
+ //
+ // example: 4
+ MaxMediaAttachments int `json:"max_media_attachments"`
+ // Amount of characters that a URL will be compressed to.
+ //
+ // example: 999
+ CharactersReservedPerURL int `json:"characters_reserved_per_url"`
+}
+
+// InstanceConfigurationMediaAttachments models instance media attachment config parameters.
+//
+// swagger:model instanceConfigurationMediaAttachments
+type InstanceConfigurationMediaAttachments struct {
+ // List of mime types that it's possible to upload to this instance.
+ //
+ // example: ["image/jpeg","image/gif"]
+ SupportedMimeTypes []string `json:"supported_mime_types"`
+ // Max allowed image size in bytes
+ //
+ // example: 2097152
+ ImageSizeLimit int `json:"image_size_limit"`
+ // Max allowed image size in pixels as height*width.
+ //
+ // GtS doesn't set a limit on this, but for compatibility
+ // we give Mastodon's 4096x4096px value here.
+ //
+ // example: 16777216
+ ImageMatrixLimit int `json:"image_matrix_limit"`
+ // Max allowed video size in bytes
+ //
+ // example: 10485760
+ VideoSizeLimit int `json:"video_size_limit"`
+ // Max allowed video frame rate.
+ //
+ // example: 60
+ VideoFrameRateLimit int `json:"video_frame_rate_limit"`
+ // Max allowed video size in pixels as height*width.
+ //
+ // GtS doesn't set a limit on this, but for compatibility
+ // we give Mastodon's 4096x4096px value here.
+ //
+ // example: 16777216
+ VideoMatrixLimit int `json:"video_matrix_limit"`
+}
+
+// InstanceConfigurationPolls models instance poll config parameters.
+//
+// swagger:model instanceConfigurationPolls
+type InstanceConfigurationPolls struct {
+ // Number of options permitted in a poll on this instance.
+ //
+ // example: 4
+ MaxOptions int `json:"max_options"`
+ // Number of characters allowed per option in the poll.
+ //
+ // example: 50
+ MaxCharactersPerOption int `json:"max_characters_per_option"`
+ // Minimum expiration time of the poll in seconds.
+ //
+ // example: 300
+ MinExpiration int `json:"min_expiration"`
+ // Maximum expiration time of the poll in seconds.
+ //
+ // example: 2629746
+ MaxExpiration int `json:"max_expiration"`
+}
+
// InstanceURLs models instance-relevant URLs for client application consumption.
//
// swagger:model instanceURLs
diff --git a/internal/media/util.go b/internal/media/util.go
@@ -26,6 +26,16 @@ import (
"github.com/sirupsen/logrus"
)
+// AllSupportedMIMETypes just returns all media
+// MIME types supported by this instance.
+func AllSupportedMIMETypes() []string {
+ return []string{
+ mimeImageJpeg,
+ mimeImageGif,
+ mimeImagePng,
+ }
+}
+
// parseContentType parses the MIME content type from a file, returning it as a string in the form (eg., "image/jpeg").
// Returns an error if the content type is not something we can process.
//
diff --git a/internal/typeutils/internaltofrontend.go b/internal/typeutils/internaltofrontend.go
@@ -29,6 +29,7 @@ import (
"github.com/superseriousbusiness/gotosocial/internal/config"
"github.com/superseriousbusiness/gotosocial/internal/db"
"github.com/superseriousbusiness/gotosocial/internal/gtsmodel"
+ "github.com/superseriousbusiness/gotosocial/internal/media"
"github.com/superseriousbusiness/gotosocial/internal/util"
)
@@ -595,9 +596,32 @@ func (c *converter) InstanceToAPIInstance(ctx context.Context, i *gtsmodel.Insta
mi.InvitesEnabled = false // TODO
mi.MaxTootChars = uint(config.GetStatusesMaxChars())
mi.URLS = &model.InstanceURLs{
- StreamingAPI: fmt.Sprintf("wss://%s", host),
+ StreamingAPI: "wss://" + host,
}
mi.Version = config.GetSoftwareVersion()
+
+ // todo: remove hardcoded values and put them in config somewhere
+ mi.Configuration = &model.InstanceConfiguration{
+ Statuses: &model.InstanceConfigurationStatuses{
+ MaxCharacters: config.GetStatusesMaxChars(),
+ MaxMediaAttachments: config.GetStatusesMediaMaxFiles(),
+ CharactersReservedPerURL: 999,
+ },
+ MediaAttachments: &model.InstanceConfigurationMediaAttachments{
+ SupportedMimeTypes: media.AllSupportedMIMETypes(),
+ ImageSizeLimit: config.GetMediaImageMaxSize(),
+ ImageMatrixLimit: 16777216, // height*width
+ VideoSizeLimit: config.GetMediaVideoMaxSize(),
+ VideoFrameRateLimit: 60,
+ VideoMatrixLimit: 16777216, // height*width
+ },
+ Polls: &model.InstanceConfigurationPolls{
+ MaxOptions: config.GetStatusesPollMaxOptions(),
+ MaxCharactersPerOption: config.GetStatusesPollOptionMaxChars(),
+ MinExpiration: 300, // seconds
+ MaxExpiration: 2629746, // seconds
+ },
+ }
}
// get the instance account if it exists and just skip if it doesn't