mirror of
https://github.com/superseriousbusiness/gotosocial.git
synced 2025-11-26 02:53:32 -06:00
[feature] Implement /api/v2/instance endpoint (#1409)
* interim: start adding /api/v2/instance * finish up
This commit is contained in:
parent
4ee4cd2da1
commit
382512a5a6
109 changed files with 1660 additions and 944 deletions
|
|
@ -79,8 +79,10 @@ type TypeConverter interface {
|
|||
StatusToAPIStatus(ctx context.Context, s *gtsmodel.Status, requestingAccount *gtsmodel.Account) (*apimodel.Status, error)
|
||||
// VisToAPIVis converts a gts visibility into its api equivalent
|
||||
VisToAPIVis(ctx context.Context, m gtsmodel.Visibility) apimodel.Visibility
|
||||
// InstanceToAPIInstance converts a gts instance into its api equivalent for serving at /api/v1/instance
|
||||
InstanceToAPIInstance(ctx context.Context, i *gtsmodel.Instance) (*apimodel.Instance, error)
|
||||
// InstanceToAPIV1Instance converts a gts instance into its api equivalent for serving at /api/v1/instance
|
||||
InstanceToAPIV1Instance(ctx context.Context, i *gtsmodel.Instance) (*apimodel.InstanceV1, error)
|
||||
// InstanceToAPIV2Instance converts a gts instance into its api equivalent for serving at /api/v2/instance
|
||||
InstanceToAPIV2Instance(ctx context.Context, i *gtsmodel.Instance) (*apimodel.InstanceV2, error)
|
||||
// RelationshipToAPIRelationship converts a gts relationship into its api equivalent for serving in various places
|
||||
RelationshipToAPIRelationship(ctx context.Context, r *gtsmodel.Relationship) (*apimodel.Relationship, error)
|
||||
// NotificationToAPINotification converts a gts notification into a api notification
|
||||
|
|
|
|||
|
|
@ -20,7 +20,6 @@ package typeutils
|
|||
|
||||
import (
|
||||
"context"
|
||||
"errors"
|
||||
"fmt"
|
||||
"math"
|
||||
"strconv"
|
||||
|
|
@ -43,6 +42,8 @@ const (
|
|||
instanceMediaAttachmentsVideoFrameRateLimit = 60
|
||||
instancePollsMinExpiration = 300 // seconds
|
||||
instancePollsMaxExpiration = 2629746 // seconds
|
||||
instanceAccountsMaxFeaturedTags = 10
|
||||
instanceSourceURL = "https://github.com/superseriousbusiness/gotosocial"
|
||||
)
|
||||
|
||||
func (c *converter) AccountToAPIAccountSensitive(ctx context.Context, a *gtsmodel.Account) (*apimodel.Account, error) {
|
||||
|
|
@ -675,113 +676,189 @@ func (c *converter) VisToAPIVis(ctx context.Context, m gtsmodel.Visibility) apim
|
|||
return ""
|
||||
}
|
||||
|
||||
func (c *converter) InstanceToAPIInstance(ctx context.Context, i *gtsmodel.Instance) (*apimodel.Instance, error) {
|
||||
mi := &apimodel.Instance{
|
||||
func (c *converter) InstanceToAPIV1Instance(ctx context.Context, i *gtsmodel.Instance) (*apimodel.InstanceV1, error) {
|
||||
instance := &apimodel.InstanceV1{
|
||||
URI: i.URI,
|
||||
AccountDomain: config.GetAccountDomain(),
|
||||
Title: i.Title,
|
||||
Description: i.Description,
|
||||
ShortDescription: i.ShortDescription,
|
||||
Email: i.ContactEmail,
|
||||
Version: i.Version,
|
||||
Stats: make(map[string]int),
|
||||
Version: config.GetSoftwareVersion(),
|
||||
Languages: []string{}, // todo: not supported yet
|
||||
Registrations: config.GetAccountsRegistrationOpen(),
|
||||
ApprovalRequired: config.GetAccountsApprovalRequired(),
|
||||
InvitesEnabled: false, // todo: not supported yet
|
||||
MaxTootChars: uint(config.GetStatusesMaxChars()),
|
||||
}
|
||||
|
||||
// if the requested instance is *this* instance, we can add some extra information
|
||||
if host := config.GetHost(); i.Domain == host {
|
||||
mi.AccountDomain = config.GetAccountDomain()
|
||||
// configuration
|
||||
instance.Configuration.Statuses.MaxCharacters = config.GetStatusesMaxChars()
|
||||
instance.Configuration.Statuses.MaxMediaAttachments = config.GetStatusesMediaMaxFiles()
|
||||
instance.Configuration.Statuses.CharactersReservedPerURL = instanceStatusesCharactersReservedPerURL
|
||||
instance.Configuration.MediaAttachments.SupportedMimeTypes = media.SupportedMIMETypes
|
||||
instance.Configuration.MediaAttachments.ImageSizeLimit = int(config.GetMediaImageMaxSize())
|
||||
instance.Configuration.MediaAttachments.ImageMatrixLimit = instanceMediaAttachmentsImageMatrixLimit
|
||||
instance.Configuration.MediaAttachments.VideoSizeLimit = int(config.GetMediaVideoMaxSize())
|
||||
instance.Configuration.MediaAttachments.VideoFrameRateLimit = instanceMediaAttachmentsVideoFrameRateLimit
|
||||
instance.Configuration.MediaAttachments.VideoMatrixLimit = instanceMediaAttachmentsVideoMatrixLimit
|
||||
instance.Configuration.Polls.MaxOptions = config.GetStatusesPollMaxOptions()
|
||||
instance.Configuration.Polls.MaxCharactersPerOption = config.GetStatusesPollOptionMaxChars()
|
||||
instance.Configuration.Polls.MinExpiration = instancePollsMinExpiration
|
||||
instance.Configuration.Polls.MaxExpiration = instancePollsMaxExpiration
|
||||
instance.Configuration.Accounts.AllowCustomCSS = config.GetAccountsAllowCustomCSS()
|
||||
instance.Configuration.Accounts.MaxFeaturedTags = instanceAccountsMaxFeaturedTags
|
||||
instance.Configuration.Emojis.EmojiSizeLimit = int(config.GetMediaEmojiLocalMaxSize())
|
||||
|
||||
if ia, err := c.db.GetInstanceAccount(ctx, ""); err == nil {
|
||||
// assume default logo
|
||||
mi.Thumbnail = config.GetProtocol() + "://" + host + "/assets/logo.png"
|
||||
// URLs
|
||||
instance.URLs.StreamingAPI = "wss://" + i.Domain
|
||||
|
||||
// take instance account avatar as instance thumbnail if we can
|
||||
if ia.AvatarMediaAttachmentID != "" {
|
||||
if ia.AvatarMediaAttachment == nil {
|
||||
avi, err := c.db.GetAttachmentByID(ctx, ia.AvatarMediaAttachmentID)
|
||||
if err == nil {
|
||||
ia.AvatarMediaAttachment = avi
|
||||
} else if !errors.Is(err, db.ErrNoEntries) {
|
||||
log.Errorf("InstanceToAPIInstance: error getting instance avatar attachment with id %s: %s", ia.AvatarMediaAttachmentID, err)
|
||||
}
|
||||
}
|
||||
// statistics
|
||||
stats := make(map[string]int, 3)
|
||||
userCount, err := c.db.CountInstanceUsers(ctx, i.Domain)
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("InstanceToAPIV1Instance: db error getting counting instance users: %w", err)
|
||||
}
|
||||
stats["user_count"] = userCount
|
||||
|
||||
if ia.AvatarMediaAttachment != nil {
|
||||
mi.Thumbnail = ia.AvatarMediaAttachment.URL
|
||||
mi.ThumbnailType = ia.AvatarMediaAttachment.File.ContentType
|
||||
mi.ThumbnailDescription = ia.AvatarMediaAttachment.Description
|
||||
}
|
||||
statusCount, err := c.db.CountInstanceStatuses(ctx, i.Domain)
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("InstanceToAPIV1Instance: db error getting counting instance statuses: %w", err)
|
||||
}
|
||||
stats["status_count"] = statusCount
|
||||
|
||||
domainCount, err := c.db.CountInstanceDomains(ctx, i.Domain)
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("InstanceToAPIV1Instance: db error getting counting instance domains: %w", err)
|
||||
}
|
||||
stats["domain_count"] = domainCount
|
||||
instance.Stats = stats
|
||||
|
||||
// thumbnail
|
||||
iAccount, err := c.db.GetInstanceAccount(ctx, "")
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("InstanceToAPIV1Instance: db error getting instance account: %w", err)
|
||||
}
|
||||
|
||||
if iAccount.AvatarMediaAttachmentID != "" {
|
||||
if iAccount.AvatarMediaAttachment == nil {
|
||||
avi, err := c.db.GetAttachmentByID(ctx, iAccount.AvatarMediaAttachmentID)
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("InstanceToAPIInstance: error getting instance avatar attachment with id %s: %w", iAccount.AvatarMediaAttachmentID, err)
|
||||
}
|
||||
iAccount.AvatarMediaAttachment = avi
|
||||
}
|
||||
|
||||
userCount, err := c.db.CountInstanceUsers(ctx, host)
|
||||
if err == nil {
|
||||
mi.Stats["user_count"] = userCount
|
||||
}
|
||||
|
||||
statusCount, err := c.db.CountInstanceStatuses(ctx, host)
|
||||
if err == nil {
|
||||
mi.Stats["status_count"] = statusCount
|
||||
}
|
||||
|
||||
domainCount, err := c.db.CountInstanceDomains(ctx, host)
|
||||
if err == nil {
|
||||
mi.Stats["domain_count"] = domainCount
|
||||
}
|
||||
|
||||
mi.Registrations = config.GetAccountsRegistrationOpen()
|
||||
mi.ApprovalRequired = config.GetAccountsApprovalRequired()
|
||||
mi.InvitesEnabled = false // TODO
|
||||
mi.MaxTootChars = uint(config.GetStatusesMaxChars())
|
||||
mi.URLS = &apimodel.InstanceURLs{
|
||||
StreamingAPI: "wss://" + host,
|
||||
}
|
||||
mi.Version = config.GetSoftwareVersion()
|
||||
|
||||
// todo: remove hardcoded values and put them in config somewhere
|
||||
mi.Configuration = &apimodel.InstanceConfiguration{
|
||||
Statuses: &apimodel.InstanceConfigurationStatuses{
|
||||
MaxCharacters: config.GetStatusesMaxChars(),
|
||||
MaxMediaAttachments: config.GetStatusesMediaMaxFiles(),
|
||||
CharactersReservedPerURL: instanceStatusesCharactersReservedPerURL,
|
||||
},
|
||||
MediaAttachments: &apimodel.InstanceConfigurationMediaAttachments{
|
||||
SupportedMimeTypes: media.SupportedMIMETypes,
|
||||
ImageSizeLimit: int(config.GetMediaImageMaxSize()), // bytes
|
||||
ImageMatrixLimit: instanceMediaAttachmentsImageMatrixLimit, // height*width
|
||||
VideoSizeLimit: int(config.GetMediaVideoMaxSize()), // bytes
|
||||
VideoFrameRateLimit: instanceMediaAttachmentsVideoFrameRateLimit,
|
||||
VideoMatrixLimit: instanceMediaAttachmentsVideoMatrixLimit, // height*width
|
||||
},
|
||||
Polls: &apimodel.InstanceConfigurationPolls{
|
||||
MaxOptions: config.GetStatusesPollMaxOptions(),
|
||||
MaxCharactersPerOption: config.GetStatusesPollOptionMaxChars(),
|
||||
MinExpiration: instancePollsMinExpiration, // seconds
|
||||
MaxExpiration: instancePollsMaxExpiration, // seconds
|
||||
},
|
||||
Accounts: &apimodel.InstanceConfigurationAccounts{
|
||||
AllowCustomCSS: config.GetAccountsAllowCustomCSS(),
|
||||
},
|
||||
Emojis: &apimodel.InstanceConfigurationEmojis{
|
||||
EmojiSizeLimit: int(config.GetMediaEmojiLocalMaxSize()), // bytes
|
||||
},
|
||||
}
|
||||
instance.Thumbnail = iAccount.AvatarMediaAttachment.URL
|
||||
instance.ThumbnailType = iAccount.AvatarMediaAttachment.File.ContentType
|
||||
instance.ThumbnailDescription = iAccount.AvatarMediaAttachment.Description
|
||||
} else {
|
||||
instance.Thumbnail = config.GetProtocol() + "://" + i.Domain + "/assets/logo.png" // default thumb
|
||||
}
|
||||
|
||||
// contact account is optional but let's try to get it
|
||||
// contact account
|
||||
if i.ContactAccountID != "" {
|
||||
if i.ContactAccount == nil {
|
||||
contactAccount, err := c.db.GetAccountByID(ctx, i.ContactAccountID)
|
||||
if err == nil {
|
||||
i.ContactAccount = contactAccount
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("InstanceToAPIV1Instance: db error getting instance contact account %s: %w", i.ContactAccountID, err)
|
||||
}
|
||||
i.ContactAccount = contactAccount
|
||||
}
|
||||
ma, err := c.AccountToAPIAccountPublic(ctx, i.ContactAccount)
|
||||
if err == nil {
|
||||
mi.ContactAccount = ma
|
||||
|
||||
account, err := c.AccountToAPIAccountPublic(ctx, i.ContactAccount)
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("InstanceToAPIV1Instance: error converting instance contact account %s: %w", i.ContactAccountID, err)
|
||||
}
|
||||
instance.ContactAccount = account
|
||||
}
|
||||
|
||||
return mi, nil
|
||||
return instance, nil
|
||||
}
|
||||
|
||||
func (c *converter) InstanceToAPIV2Instance(ctx context.Context, i *gtsmodel.Instance) (*apimodel.InstanceV2, error) {
|
||||
instance := &apimodel.InstanceV2{
|
||||
Domain: i.Domain,
|
||||
AccountDomain: config.GetAccountDomain(),
|
||||
Title: i.Title,
|
||||
Version: config.GetSoftwareVersion(),
|
||||
SourceURL: instanceSourceURL,
|
||||
Description: i.Description,
|
||||
Usage: apimodel.InstanceV2Usage{}, // todo: not implemented
|
||||
Languages: []string{}, // todo: not implemented
|
||||
Rules: []interface{}{}, // todo: not implemented
|
||||
}
|
||||
|
||||
// thumbnail
|
||||
thumbnail := apimodel.InstanceV2Thumbnail{}
|
||||
|
||||
iAccount, err := c.db.GetInstanceAccount(ctx, "")
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("InstanceToAPIV2Instance: db error getting instance account: %w", err)
|
||||
}
|
||||
|
||||
if iAccount.AvatarMediaAttachmentID != "" {
|
||||
if iAccount.AvatarMediaAttachment == nil {
|
||||
avi, err := c.db.GetAttachmentByID(ctx, iAccount.AvatarMediaAttachmentID)
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("InstanceToAPIV2Instance: error getting instance avatar attachment with id %s: %w", iAccount.AvatarMediaAttachmentID, err)
|
||||
}
|
||||
iAccount.AvatarMediaAttachment = avi
|
||||
}
|
||||
|
||||
thumbnail.URL = iAccount.AvatarMediaAttachment.URL
|
||||
thumbnail.Type = iAccount.AvatarMediaAttachment.File.ContentType
|
||||
thumbnail.Description = iAccount.AvatarMediaAttachment.Description
|
||||
thumbnail.Blurhash = iAccount.AvatarMediaAttachment.Blurhash
|
||||
} else {
|
||||
thumbnail.URL = config.GetProtocol() + "://" + i.Domain + "/assets/logo.png" // default thumb
|
||||
}
|
||||
|
||||
instance.Thumbnail = thumbnail
|
||||
|
||||
// configuration
|
||||
instance.Configuration.URLs.Streaming = "wss://" + i.Domain
|
||||
instance.Configuration.Statuses.MaxCharacters = config.GetStatusesMaxChars()
|
||||
instance.Configuration.Statuses.MaxMediaAttachments = config.GetStatusesMediaMaxFiles()
|
||||
instance.Configuration.Statuses.CharactersReservedPerURL = instanceStatusesCharactersReservedPerURL
|
||||
instance.Configuration.MediaAttachments.SupportedMimeTypes = media.SupportedMIMETypes
|
||||
instance.Configuration.MediaAttachments.ImageSizeLimit = int(config.GetMediaImageMaxSize())
|
||||
instance.Configuration.MediaAttachments.ImageMatrixLimit = instanceMediaAttachmentsImageMatrixLimit
|
||||
instance.Configuration.MediaAttachments.VideoSizeLimit = int(config.GetMediaVideoMaxSize())
|
||||
instance.Configuration.MediaAttachments.VideoFrameRateLimit = instanceMediaAttachmentsVideoFrameRateLimit
|
||||
instance.Configuration.MediaAttachments.VideoMatrixLimit = instanceMediaAttachmentsVideoMatrixLimit
|
||||
instance.Configuration.Polls.MaxOptions = config.GetStatusesPollMaxOptions()
|
||||
instance.Configuration.Polls.MaxCharactersPerOption = config.GetStatusesPollOptionMaxChars()
|
||||
instance.Configuration.Polls.MinExpiration = instancePollsMinExpiration
|
||||
instance.Configuration.Polls.MaxExpiration = instancePollsMaxExpiration
|
||||
instance.Configuration.Accounts.AllowCustomCSS = config.GetAccountsAllowCustomCSS()
|
||||
instance.Configuration.Accounts.MaxFeaturedTags = instanceAccountsMaxFeaturedTags
|
||||
instance.Configuration.Emojis.EmojiSizeLimit = int(config.GetMediaEmojiLocalMaxSize())
|
||||
|
||||
// registrations
|
||||
instance.Registrations.Enabled = config.GetAccountsRegistrationOpen()
|
||||
instance.Registrations.ApprovalRequired = config.GetAccountsApprovalRequired()
|
||||
instance.Registrations.Message = nil // todo: not implemented
|
||||
|
||||
// contact
|
||||
instance.Contact.Email = i.ContactEmail
|
||||
if i.ContactAccountID != "" {
|
||||
if i.ContactAccount == nil {
|
||||
contactAccount, err := c.db.GetAccountByID(ctx, i.ContactAccountID)
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("InstanceToAPIV2Instance: db error getting instance contact account %s: %w", i.ContactAccountID, err)
|
||||
}
|
||||
i.ContactAccount = contactAccount
|
||||
}
|
||||
|
||||
account, err := c.AccountToAPIAccountPublic(ctx, i.ContactAccount)
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("InstanceToAPIV2Instance: error converting instance contact account %s: %w", i.ContactAccountID, err)
|
||||
}
|
||||
instance.Contact.Account = account
|
||||
}
|
||||
|
||||
return instance, nil
|
||||
}
|
||||
|
||||
func (c *converter) RelationshipToAPIRelationship(ctx context.Context, r *gtsmodel.Relationship) (*apimodel.Relationship, error) {
|
||||
|
|
|
|||
|
|
@ -24,8 +24,9 @@ import (
|
|||
"testing"
|
||||
|
||||
"github.com/stretchr/testify/suite"
|
||||
"github.com/superseriousbusiness/gotosocial/internal/config"
|
||||
"github.com/superseriousbusiness/gotosocial/internal/db"
|
||||
"github.com/superseriousbusiness/gotosocial/internal/gtsmodel"
|
||||
"github.com/superseriousbusiness/gotosocial/testrig"
|
||||
)
|
||||
|
||||
type InternalToFrontendTestSuite struct {
|
||||
|
|
@ -454,93 +455,207 @@ func (suite *InternalToFrontendTestSuite) TestVideoAttachmentToFrontend() {
|
|||
}`, string(b))
|
||||
}
|
||||
|
||||
func (suite *InternalToFrontendTestSuite) TestInstanceToFrontend() {
|
||||
testInstance := >smodel.Instance{
|
||||
CreatedAt: testrig.TimeMustParse("2021-10-20T11:36:45Z"),
|
||||
UpdatedAt: testrig.TimeMustParse("2021-10-20T11:36:45Z"),
|
||||
Domain: "example.org",
|
||||
Title: "example instance",
|
||||
URI: "https://example.org",
|
||||
ShortDescription: "a little description",
|
||||
Description: "a much longer description",
|
||||
ContactEmail: "someone@example.org",
|
||||
Version: "software-from-hell 0.666",
|
||||
func (suite *InternalToFrontendTestSuite) TestInstanceV1ToFrontend() {
|
||||
ctx := context.Background()
|
||||
|
||||
i := >smodel.Instance{}
|
||||
if err := suite.db.GetWhere(ctx, []db.Where{{Key: "domain", Value: config.GetHost()}}, i); err != nil {
|
||||
suite.FailNow(err.Error())
|
||||
}
|
||||
|
||||
apiInstance, err := suite.typeconverter.InstanceToAPIInstance(context.Background(), testInstance)
|
||||
suite.NoError(err)
|
||||
instance, err := suite.typeconverter.InstanceToAPIV1Instance(ctx, i)
|
||||
if err != nil {
|
||||
suite.FailNow(err.Error())
|
||||
}
|
||||
|
||||
b, err := json.MarshalIndent(apiInstance, "", " ")
|
||||
b, err := json.MarshalIndent(instance, "", " ")
|
||||
suite.NoError(err)
|
||||
|
||||
suite.Equal(`{
|
||||
"uri": "https://example.org",
|
||||
"title": "example instance",
|
||||
"description": "a much longer description",
|
||||
"short_description": "a little description",
|
||||
"email": "someone@example.org",
|
||||
"version": "software-from-hell 0.666",
|
||||
"registrations": false,
|
||||
"approval_required": false,
|
||||
"uri": "http://localhost:8080",
|
||||
"account_domain": "localhost:8080",
|
||||
"title": "GoToSocial Testrig Instance",
|
||||
"description": "\u003cp\u003eThis is the GoToSocial testrig. It doesn't federate or anything.\u003c/p\u003e\u003cp\u003eWhen the testrig is shut down, all data on it will be deleted.\u003c/p\u003e\u003cp\u003eDon't use this in production!\u003c/p\u003e",
|
||||
"short_description": "\u003cp\u003eThis is the GoToSocial testrig. It doesn't federate or anything.\u003c/p\u003e\u003cp\u003eWhen the testrig is shut down, all data on it will be deleted.\u003c/p\u003e\u003cp\u003eDon't use this in production!\u003c/p\u003e",
|
||||
"email": "admin@example.org",
|
||||
"version": "0.0.0-testrig",
|
||||
"registrations": true,
|
||||
"approval_required": true,
|
||||
"invites_enabled": false,
|
||||
"thumbnail": "",
|
||||
"max_toot_chars": 0
|
||||
}`, string(b))
|
||||
}
|
||||
|
||||
func (suite *InternalToFrontendTestSuite) TestInstanceToFrontendWithAdminAccount() {
|
||||
testInstance := >smodel.Instance{
|
||||
CreatedAt: testrig.TimeMustParse("2021-10-20T11:36:45Z"),
|
||||
UpdatedAt: testrig.TimeMustParse("2021-10-20T11:36:45Z"),
|
||||
Domain: "example.org",
|
||||
Title: "example instance",
|
||||
URI: "https://example.org",
|
||||
ShortDescription: "a little description",
|
||||
Description: "a much longer description",
|
||||
ContactEmail: "someone@example.org",
|
||||
ContactAccountID: suite.testAccounts["remote_account_2"].ID,
|
||||
Version: "software-from-hell 0.666",
|
||||
}
|
||||
|
||||
apiInstance, err := suite.typeconverter.InstanceToAPIInstance(context.Background(), testInstance)
|
||||
suite.NoError(err)
|
||||
|
||||
b, err := json.MarshalIndent(apiInstance, "", " ")
|
||||
suite.NoError(err)
|
||||
|
||||
suite.Equal(`{
|
||||
"uri": "https://example.org",
|
||||
"title": "example instance",
|
||||
"description": "a much longer description",
|
||||
"short_description": "a little description",
|
||||
"email": "someone@example.org",
|
||||
"version": "software-from-hell 0.666",
|
||||
"registrations": false,
|
||||
"approval_required": false,
|
||||
"invites_enabled": false,
|
||||
"thumbnail": "",
|
||||
"configuration": {
|
||||
"statuses": {
|
||||
"max_characters": 5000,
|
||||
"max_media_attachments": 6,
|
||||
"characters_reserved_per_url": 25
|
||||
},
|
||||
"media_attachments": {
|
||||
"supported_mime_types": [
|
||||
"image/jpeg",
|
||||
"image/gif",
|
||||
"image/png",
|
||||
"image/webp",
|
||||
"video/mp4"
|
||||
],
|
||||
"image_size_limit": 10485760,
|
||||
"image_matrix_limit": 16777216,
|
||||
"video_size_limit": 41943040,
|
||||
"video_frame_rate_limit": 60,
|
||||
"video_matrix_limit": 16777216
|
||||
},
|
||||
"polls": {
|
||||
"max_options": 6,
|
||||
"max_characters_per_option": 50,
|
||||
"min_expiration": 300,
|
||||
"max_expiration": 2629746
|
||||
},
|
||||
"accounts": {
|
||||
"allow_custom_css": true,
|
||||
"max_featured_tags": 10
|
||||
},
|
||||
"emojis": {
|
||||
"emoji_size_limit": 51200
|
||||
}
|
||||
},
|
||||
"urls": {
|
||||
"streaming_api": "wss://localhost:8080"
|
||||
},
|
||||
"stats": {
|
||||
"domain_count": 2,
|
||||
"status_count": 16,
|
||||
"user_count": 4
|
||||
},
|
||||
"thumbnail": "http://localhost:8080/assets/logo.png",
|
||||
"contact_account": {
|
||||
"id": "01FHMQX3GAABWSM0S2VZEC2SWC",
|
||||
"username": "Some_User",
|
||||
"acct": "Some_User@example.org",
|
||||
"display_name": "some user",
|
||||
"locked": true,
|
||||
"id": "01F8MH17FWEB39HZJ76B6VXSKF",
|
||||
"username": "admin",
|
||||
"acct": "admin",
|
||||
"display_name": "",
|
||||
"locked": false,
|
||||
"bot": false,
|
||||
"created_at": "2020-08-10T12:13:28.000Z",
|
||||
"note": "i'm a real son of a gun",
|
||||
"url": "http://example.org/@Some_User",
|
||||
"created_at": "2022-05-17T13:10:59.000Z",
|
||||
"note": "",
|
||||
"url": "http://localhost:8080/@admin",
|
||||
"avatar": "",
|
||||
"avatar_static": "",
|
||||
"header": "http://localhost:8080/assets/default_header.png",
|
||||
"header_static": "http://localhost:8080/assets/default_header.png",
|
||||
"followers_count": 0,
|
||||
"following_count": 0,
|
||||
"statuses_count": 0,
|
||||
"last_status_at": null,
|
||||
"followers_count": 1,
|
||||
"following_count": 1,
|
||||
"statuses_count": 4,
|
||||
"last_status_at": "2021-10-20T10:41:37.000Z",
|
||||
"emojis": [],
|
||||
"fields": []
|
||||
"fields": [],
|
||||
"enable_rss": true,
|
||||
"role": "admin"
|
||||
},
|
||||
"max_toot_chars": 0
|
||||
"max_toot_chars": 5000
|
||||
}`, string(b))
|
||||
}
|
||||
|
||||
func (suite *InternalToFrontendTestSuite) TestInstanceV2ToFrontend() {
|
||||
ctx := context.Background()
|
||||
|
||||
i := >smodel.Instance{}
|
||||
if err := suite.db.GetWhere(ctx, []db.Where{{Key: "domain", Value: config.GetHost()}}, i); err != nil {
|
||||
suite.FailNow(err.Error())
|
||||
}
|
||||
|
||||
instance, err := suite.typeconverter.InstanceToAPIV2Instance(ctx, i)
|
||||
if err != nil {
|
||||
suite.FailNow(err.Error())
|
||||
}
|
||||
|
||||
b, err := json.MarshalIndent(instance, "", " ")
|
||||
suite.NoError(err)
|
||||
|
||||
suite.Equal(`{
|
||||
"domain": "localhost:8080",
|
||||
"account_domain": "localhost:8080",
|
||||
"title": "GoToSocial Testrig Instance",
|
||||
"version": "0.0.0-testrig",
|
||||
"source_url": "https://github.com/superseriousbusiness/gotosocial",
|
||||
"description": "\u003cp\u003eThis is the GoToSocial testrig. It doesn't federate or anything.\u003c/p\u003e\u003cp\u003eWhen the testrig is shut down, all data on it will be deleted.\u003c/p\u003e\u003cp\u003eDon't use this in production!\u003c/p\u003e",
|
||||
"usage": {
|
||||
"users": {
|
||||
"active_month": 0
|
||||
}
|
||||
},
|
||||
"thumbnail": {
|
||||
"url": "http://localhost:8080/assets/logo.png"
|
||||
},
|
||||
"languages": [],
|
||||
"configuration": {
|
||||
"urls": {
|
||||
"streaming": "wss://localhost:8080"
|
||||
},
|
||||
"accounts": {
|
||||
"allow_custom_css": true,
|
||||
"max_featured_tags": 10
|
||||
},
|
||||
"statuses": {
|
||||
"max_characters": 5000,
|
||||
"max_media_attachments": 6,
|
||||
"characters_reserved_per_url": 25
|
||||
},
|
||||
"media_attachments": {
|
||||
"supported_mime_types": [
|
||||
"image/jpeg",
|
||||
"image/gif",
|
||||
"image/png",
|
||||
"image/webp",
|
||||
"video/mp4"
|
||||
],
|
||||
"image_size_limit": 10485760,
|
||||
"image_matrix_limit": 16777216,
|
||||
"video_size_limit": 41943040,
|
||||
"video_frame_rate_limit": 60,
|
||||
"video_matrix_limit": 16777216
|
||||
},
|
||||
"polls": {
|
||||
"max_options": 6,
|
||||
"max_characters_per_option": 50,
|
||||
"min_expiration": 300,
|
||||
"max_expiration": 2629746
|
||||
},
|
||||
"translation": {
|
||||
"enabled": false
|
||||
},
|
||||
"emojis": {
|
||||
"emoji_size_limit": 51200
|
||||
}
|
||||
},
|
||||
"registrations": {
|
||||
"enabled": true,
|
||||
"approval_required": true,
|
||||
"message": null
|
||||
},
|
||||
"contact": {
|
||||
"email": "admin@example.org",
|
||||
"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": "http://localhost:8080/assets/default_header.png",
|
||||
"header_static": "http://localhost:8080/assets/default_header.png",
|
||||
"followers_count": 1,
|
||||
"following_count": 1,
|
||||
"statuses_count": 4,
|
||||
"last_status_at": "2021-10-20T10:41:37.000Z",
|
||||
"emojis": [],
|
||||
"fields": [],
|
||||
"enable_rss": true,
|
||||
"role": "admin"
|
||||
}
|
||||
},
|
||||
"rules": []
|
||||
}`, string(b))
|
||||
}
|
||||
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue