Merge remote-tracking branch 'origin/main' into HEAD

This commit is contained in:
S0yKaf 2025-01-18 13:55:15 -05:00
commit 0e137c0f2d
1759 changed files with 864109 additions and 314186 deletions

View file

@ -68,6 +68,10 @@ type Account struct {
// Description of this account's avatar, for alt text.
// example: A cute drawing of a smiling sloth.
AvatarDescription string `json:"avatar_description,omitempty"`
// Database ID of the media attachment for this account's avatar image.
// Omitted if no avatar uploaded for this account (ie., default avatar).
// example: 01JAJ3XCD66K3T99JZESCR137W
AvatarMediaID string `json:"avatar_media_id,omitempty"`
// Web location of the account's header image.
// example: https://example.org/media/some_user/header/original/header.jpeg
Header string `json:"header"`
@ -78,14 +82,18 @@ type Account struct {
// Description of this account's header, for alt text.
// example: A sunlit field with purple flowers.
HeaderDescription string `json:"header_description,omitempty"`
// Database ID of the media attachment for this account's header image.
// Omitted if no header uploaded for this account (ie., default header).
// example: 01JAJ3XCD66K3T99JZESCR137W
HeaderMediaID string `json:"header_media_id,omitempty"`
// Number of accounts following this account, according to our instance.
FollowersCount int `json:"followers_count"`
// Number of account's followed by this account, according to our instance.
FollowingCount int `json:"following_count"`
// Number of statuses posted by this account, according to our instance.
StatusesCount int `json:"statuses_count"`
// When the account's most recent status was posted (ISO 8601 Datetime).
// example: 2021-07-30T09:20:25+00:00
// When the account's most recent status was posted (ISO 8601 Date).
// example: 2021-07-30
LastStatusAt *string `json:"last_status_at"`
// Array of custom emojis used in this account's note or display name.
// Empty for blocked accounts.

View file

@ -23,12 +23,15 @@ import "mime/multipart"
//
// swagger: ignore
type AttachmentRequest struct {
// Media file.
File *multipart.FileHeader `form:"file" binding:"required"`
// Description of the media file. Optional.
// This will be used as alt-text for users of screenreaders etc.
// example: This is an image of some kittens, they are very cute and fluffy.
Description string `form:"description"`
// Focus of the media file. Optional.
// If present, it should be in the form of two comma-separated floats between -1 and 1.
// example: -0.5,0.565
@ -39,16 +42,38 @@ type AttachmentRequest struct {
//
// swagger:ignore
type AttachmentUpdateRequest struct {
// Description of the media file.
// This will be used as alt-text for users of screenreaders etc.
// allowEmptyValue: true
Description *string `form:"description" json:"description" xml:"description"`
// Focus of the media file.
// If present, it should be in the form of two comma-separated floats between -1 and 1.
// allowEmptyValue: true
Focus *string `form:"focus" json:"focus" xml:"focus"`
}
// AttachmentAttributesRequest models an edit request for attachment attributes.
//
// swagger:ignore
type AttachmentAttributesRequest struct {
// The ID of the attachment.
// example: 01FC31DZT1AYWDZ8XTCRWRBYRK
ID string `form:"id" json:"id"`
// Description of the media file.
// This will be used as alt-text for users of screenreaders etc.
// allowEmptyValue: true
Description string `form:"description" json:"description"`
// Focus of the media file.
// If present, it should be in the form of two comma-separated floats between -1 and 1.
// allowEmptyValue: true
Focus string `form:"focus" json:"focus"`
}
// Attachment models a media attachment.
//
// swagger:model attachment
@ -160,7 +185,7 @@ type MediaDimensions struct {
Duration float32 `json:"duration,omitempty"`
// Bitrate of the media in bits per second.
// example: 1000000
Bitrate int `json:"bitrate,omitempty"`
Bitrate uint64 `json:"bitrate,omitempty"`
// Size of the media, in the format `[width]x[height]`.
// Not set for audio.
// example: 1920x1080

View file

@ -19,7 +19,6 @@ package model
import (
"io"
"time"
"github.com/superseriousbusiness/gotosocial/internal/storage"
)
@ -30,8 +29,6 @@ type Content struct {
ContentType string
// ContentLength in bytes
ContentLength int64
// Time when the content was last updated.
ContentUpdated time.Time
// Actual content
Content io.ReadCloser
// Resource URL to forward to if the file can be fetched from the storage directly (e.g signed S3 URL)

View file

@ -27,6 +27,10 @@ type Conversation struct {
// Is the conversation currently marked as unread?
Unread bool `json:"unread"`
// Participants in the conversation.
//
// If this is a conversation between no accounts (ie., a self-directed DM),
// this will include only the requesting account itself. Otherwise, it will
// include every other account in the conversation *except* the requester.
Accounts []Account `json:"accounts"`
// The last status in the conversation. May be `null`.
LastStatus *Status `json:"last_status"`

View file

@ -61,6 +61,9 @@ type DomainPermission struct {
// Time at which the permission entry was created (ISO 8601 Datetime).
// example: 2021-07-30T09:20:25+00:00
CreatedAt string `json:"created_at,omitempty"`
// Permission type of this entry (block, allow).
// Only set for domain permission drafts.
PermissionType string `json:"permission_type,omitempty"`
}
// DomainPermissionRequest is the form submitted as a POST to create a new domain permission entry (allow/block).
@ -69,22 +72,24 @@ type DomainPermission struct {
type DomainPermissionRequest struct {
// A list of domains for which this permission request should apply.
// Only used if import=true is specified.
Domains *multipart.FileHeader `form:"domains" json:"domains" xml:"domains"`
Domains *multipart.FileHeader `form:"domains" json:"domains"`
// A single domain for which this permission request should apply.
// Only used if import=true is NOT specified or if import=false.
// example: example.org
Domain string `form:"domain" json:"domain" xml:"domain"`
Domain string `form:"domain" json:"domain"`
// Obfuscate the domain name when displaying this permission entry publicly.
// Ie., instead of 'example.org' show something like 'e**mpl*.or*'.
// example: false
Obfuscate bool `form:"obfuscate" json:"obfuscate" xml:"obfuscate"`
Obfuscate bool `form:"obfuscate" json:"obfuscate"`
// Private comment for other admins on why this permission entry was created.
// example: don't like 'em!!!!
PrivateComment string `form:"private_comment" json:"private_comment" xml:"private_comment"`
PrivateComment string `form:"private_comment" json:"private_comment"`
// Public comment on why this permission entry was created.
// Will be visible to requesters at /api/v1/instance/peers if this endpoint is exposed.
// example: foss dorks 😫
PublicComment string `form:"public_comment" json:"public_comment" xml:"public_comment"`
PublicComment string `form:"public_comment" json:"public_comment"`
// Permission type to create (only applies to domain permission drafts, not explicit blocks and allows).
PermissionType string `form:"permission_type" json:"permission_type"`
}
// DomainKeysExpireRequest is the form submitted as a POST to /api/v1/admin/domain_keys_expire to expire a domain's public keys.
@ -92,5 +97,103 @@ type DomainPermissionRequest struct {
// swagger:parameters domainKeysExpire
type DomainKeysExpireRequest struct {
// hostname/domain to expire keys for.
Domain string `form:"domain" json:"domain" xml:"domain"`
Domain string `form:"domain" json:"domain"`
}
// DomainPermissionSubscription represents an auto-refreshing subscription to a list of domain permissions (allows, blocks).
//
// swagger:model domainPermissionSubscription
type DomainPermissionSubscription struct {
// The ID of the domain permission subscription.
// example: 01FBW21XJA09XYX51KV5JVBW0F
// readonly: true
ID string `json:"id"`
// Priority of this subscription compared to others of the same permission type. 0-255 (higher = higher priority).
// example: 100
Priority uint8 `json:"priority"`
// Title of this subscription, as set by admin who created or updated it.
// example: really cool list of neato pals
Title string `json:"title"`
// The type of domain permission subscription (allow, block).
// example: block
PermissionType string `json:"permission_type"`
// If true, domain permissions arising from this subscription will be created as drafts that must be approved by a moderator to take effect. If false, domain permissions from this subscription will come into force immediately.
// example: true
AsDraft bool `json:"as_draft"`
// If true, this domain permission subscription will "adopt" domain permissions which already exist on the instance, and which meet the following conditions: 1) they have no subscription ID (ie., they're "orphaned") and 2) they are present in the subscribed list. Such orphaned domain permissions will be given this subscription's subscription ID value.
// example: false
AdoptOrphans bool `json:"adopt_orphans"`
// Time at which the subscription was created (ISO 8601 Datetime).
// example: 2021-07-30T09:20:25+00:00
CreatedAt string `json:"created_at"`
// ID of the account that created this subscription.
// example: 01FBW21XJA09XYX51KV5JVBW0F
// readonly: true
CreatedBy string `json:"created_by"`
// URI to call in order to fetch the permissions list.
// example: https://www.example.org/blocklists/list1.csv
URI string `json:"uri"`
// MIME content type to use when parsing the permissions list.
// example: text/csv
ContentType string `json:"content_type"`
// (Optional) username to set for basic auth when doing a fetch of URI.
// example: admin123
FetchUsername string `json:"fetch_username,omitempty"`
// (Optional) password to set for basic auth when doing a fetch of URI.
// example: admin123
FetchPassword string `json:"fetch_password,omitempty"`
// Time of the most recent fetch attempt (successful or otherwise) (ISO 8601 Datetime).
// example: 2021-07-30T09:20:25+00:00
// readonly: true
FetchedAt string `json:"fetched_at,omitempty"`
// Time of the most recent successful fetch (ISO 8601 Datetime).
// example: 2021-07-30T09:20:25+00:00
// readonly: true
SuccessfullyFetchedAt string `json:"successfully_fetched_at,omitempty"`
// If most recent fetch attempt failed, this field will contain an error message related to the fetch attempt.
// example: Oopsie doopsie, we made a fucky wucky.
// readonly: true
Error string `json:"error,omitempty"`
// Count of domain permission entries discovered at URI on last (successful) fetch.
// example: 53
// readonly: true
Count uint64 `json:"count"`
}
// DomainPermissionSubscriptionRequest represents a request to create or update a domain permission subscription..
//
// swagger:ignore
type DomainPermissionSubscriptionRequest struct {
// Priority of this subscription compared to others of the same permission type. 0-255 (higher = higher priority).
// example: 100
Priority *int `form:"priority" json:"priority"`
// Title of this subscription, as set by admin who created or updated it.
// example: really cool list of neato pals
Title *string `form:"title" json:"title"`
// The type of domain permission subscription (allow, block).
// example: block
PermissionType *string `form:"permission_type" json:"permission_type"`
// URI to call in order to fetch the permissions list.
// example: https://www.example.org/blocklists/list1.csv
URI *string `form:"uri" json:"uri"`
// MIME content type to use when parsing the permissions list.
// example: text/csv
ContentType *string `form:"content_type" json:"content_type"`
// If true, domain permissions arising from this subscription will be
// created as drafts that must be approved by a moderator to take effect.
// If false, domain permissions from this subscription will come into force immediately.
// example: true
AsDraft *bool `form:"as_draft" json:"as_draft"`
// If true, this domain permission subscription will "adopt" domain permissions
// which already exist on the instance, and which meet the following conditions:
// 1) they have no subscription ID (ie., they're "orphaned") and 2) they are present
// in the subscribed list. Such orphaned domain permissions will be given this
// subscription's subscription ID value and be managed by this subscription.
AdoptOrphans *bool `form:"adopt_orphans" json:"adopt_orphans"`
// (Optional) username to set for basic auth when doing a fetch of URI.
// example: admin123
FetchUsername *string `form:"fetch_username" json:"fetch_username"`
// (Optional) password to set for basic auth when doing a fetch of URI.
// example: admin123
FetchPassword *string `form:"fetch_password" json:"fetch_password"`
}

View file

@ -95,5 +95,5 @@ type FilterCreateUpdateRequestV1 struct {
// Number of seconds from now that the filter should expire. If omitted, filter never expires.
//
// Example: 86400
ExpiresInI interface{} `json:"expires_in"`
ExpiresInI Nullable[any] `json:"expires_in"`
}

View file

@ -134,7 +134,7 @@ type FilterCreateRequestV2 struct {
// Number of seconds from now that the filter should expire. If omitted, filter never expires.
//
// Example: 86400
ExpiresInI interface{} `json:"expires_in"`
ExpiresInI Nullable[any] `json:"expires_in"`
// Keywords to be added to the newly created filter.
Keywords []FilterKeywordCreateUpdateRequest `form:"-" json:"keywords_attributes" xml:"keywords_attributes"`
@ -199,7 +199,7 @@ type FilterUpdateRequestV2 struct {
// Number of seconds from now that the filter should expire. If omitted, filter never expires.
//
// Example: 86400
ExpiresInI interface{} `json:"expires_in"`
ExpiresInI Nullable[any] `json:"expires_in"`
// Keywords to be added to the filter, modified, or removed.
Keywords []FilterKeywordCreateUpdateDeleteRequest `form:"-" json:"keywords_attributes" xml:"keywords_attributes"`

View file

@ -33,6 +33,8 @@ type InstanceSettingsUpdateRequest struct {
ShortDescription *string `form:"short_description" json:"short_description" xml:"short_description"`
// Longer description of the instance, max 5,000 chars. HTML formatting accepted.
Description *string `form:"description" json:"description" xml:"description"`
// Custom CSS for the instance.
CustomCSS *string `form:"custom_css" json:"custom_css,omitempty" xml:"custom_css"`
// Terms and conditions of the instance, max 5,000 chars. HTML formatting accepted.
Terms *string `form:"terms" json:"terms" xml:"terms"`
// Image to use as the instance thumbnail.

View file

@ -38,6 +38,8 @@ type InstanceV1 struct {
//
// This should be displayed on the 'about' page for an instance.
Description string `json:"description"`
// Custom CSS for the instance.
CustomCSS string `json:"custom_css,omitempty"`
// Raw (unparsed) version of description.
DescriptionText string `json:"description_text,omitempty"`
// A shorter description of the instance.

View file

@ -53,6 +53,8 @@ type InstanceV2 struct {
Description string `json:"description"`
// Raw (unparsed) version of description.
DescriptionText string `json:"description_text,omitempty"`
// Instance Custom Css
CustomCSS string `json:"custom_css,omitempty"`
// Basic anonymous usage data for this instance.
Usage InstanceV2Usage `json:"usage"`
// An image used to represent this instance.

View file

@ -0,0 +1,107 @@
// GoToSocial
// Copyright (C) GoToSocial Authors admin@gotosocial.org
// SPDX-License-Identifier: AGPL-3.0-or-later
//
// This program is free software: you can redistribute it and/or modify
// it under the terms of the GNU Affero General Public License as published by
// the Free Software Foundation, either version 3 of the License, or
// (at your option) any later version.
//
// This program is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
// GNU Affero General Public License for more details.
//
// You should have received a copy of the GNU Affero General Public License
// along with this program. If not, see <http://www.gnu.org/licenses/>.
package model
import (
"bytes"
"encoding/json"
"errors"
)
// Nullable is a generic type, which implements a field that can be one of three states:
//
// - field is not set in the request
// - field is explicitly set to `null` in the request
// - field is explicitly set to a valid value in the request
//
// Nullable is intended to be used with JSON unmarshalling.
//
// Adapted from https://github.com/oapi-codegen/nullable/blob/main/nullable.go
type Nullable[T any] struct {
state nullableState
value T
}
type nullableState uint8
const (
nullableStateUnspecified nullableState = 0
nullableStateNull nullableState = 1
nullableStateSet nullableState = 2
)
// Get retrieves the underlying value, if present,
// and returns an error if the value was not present.
func (t Nullable[T]) Get() (T, error) {
var empty T
if t.IsNull() {
return empty, errors.New("value is null")
}
if !t.IsSpecified() {
return empty, errors.New("value is not specified")
}
return t.value, nil
}
// IsNull indicates whether the field
// was sent, and had a value of `null`
func (t Nullable[T]) IsNull() bool {
return t.state == nullableStateNull
}
// IsSpecified indicates whether the field
// was sent either as a value or as `null`.
func (t Nullable[T]) IsSpecified() bool {
return t.state != nullableStateUnspecified
}
// If field is unspecified,
// UnmarshalJSON won't be called.
func (t *Nullable[T]) UnmarshalJSON(data []byte) error {
// If field is specified as `null`.
if bytes.Equal(data, []byte("null")) {
t.setNull()
return nil
}
// Otherwise, we have an
// actual value, so parse it.
var v T
if err := json.Unmarshal(data, &v); err != nil {
return err
}
t.set(v)
return nil
}
// setNull indicates that the field
// was sent, and had a value of `null`
func (t *Nullable[T]) setNull() {
*t = Nullable[T]{state: nullableStateNull}
}
// set the underlying value to given value.
func (t *Nullable[T]) set(value T) {
*t = Nullable[T]{
state: nullableStateSet,
value: value,
}
}

View file

@ -29,6 +29,10 @@ type Status struct {
// The date when this status was created (ISO 8601 Datetime).
// example: 2021-07-30T09:20:25+00:00
CreatedAt string `json:"created_at"`
// Timestamp of when the status was last edited (ISO 8601 Datetime).
// example: 2021-07-30T09:20:25+00:00
// nullable: true
EditedAt *string `json:"edited_at"`
// ID of the status being replied to.
// example: 01FBVD42CQ3ZEEVMW180SBX03B
// nullable: true
@ -197,36 +201,50 @@ type StatusReblogged struct {
//
// swagger:ignore
type StatusCreateRequest struct {
// Text content of the status.
// If media_ids is provided, this becomes optional.
// Attaching a poll is optional while status is provided.
Status string `form:"status" json:"status"`
// Array of Attachment ids to be attached as media.
// If provided, status becomes optional, and poll cannot be used.
MediaIDs []string `form:"media_ids[]" json:"media_ids"`
// Poll to include with this status.
Poll *PollRequest `form:"poll" json:"poll"`
// ID of the status being replied to, if status is a reply.
InReplyToID string `form:"in_reply_to_id" json:"in_reply_to_id"`
// Status and attached media should be marked as sensitive.
Sensitive bool `form:"sensitive" json:"sensitive"`
// Text to be shown as a warning or subject before the actual content.
// Statuses are generally collapsed behind this field.
SpoilerText string `form:"spoiler_text" json:"spoiler_text"`
// Visibility of the posted status.
Visibility Visibility `form:"visibility" json:"visibility"`
// Set to "true" if this status should not be federated, ie. it should be a "local only" status.
// Set to "true" if this status should not be
// federated,ie. it should be a "local only" status.
LocalOnly *bool `form:"local_only" json:"local_only"`
// Deprecated: Only used if LocalOnly is not set.
Federated *bool `form:"federated" json:"federated"`
// ISO 8601 Datetime at which to schedule a status.
// Providing this parameter will cause ScheduledStatus to be returned instead of Status.
// Must be at least 5 minutes in the future.
ScheduledAt string `form:"scheduled_at" json:"scheduled_at"`
// ISO 639 language code for this status.
Language string `form:"language" json:"language"`
// Content type to use when parsing this status.
ContentType StatusContentType `form:"content_type" json:"content_type"`
// Interaction policy to use for this status.
InteractionPolicy *InteractionPolicy `form:"-" json:"interaction_policy"`
}
@ -236,6 +254,7 @@ type StatusCreateRequest struct {
//
// swagger:ignore
type StatusInteractionPolicyForm struct {
// Interaction policy to use for this status.
InteractionPolicy *InteractionPolicy `form:"interaction_policy" json:"-"`
}
@ -250,13 +269,18 @@ const (
// VisibilityNone is visible to nobody. This is only used for the visibility of web statuses.
VisibilityNone Visibility = "none"
// VisibilityPublic is visible to everyone, and will be available via the web even for nonauthenticated users.
VisibilityPublic Visibility = "public"
// VisibilityUnlisted is visible to everyone, but only on home timelines, lists, etc.
VisibilityUnlisted Visibility = "unlisted"
// VisibilityPrivate is visible only to followers of the account that posted the status.
VisibilityPrivate Visibility = "private"
// VisibilityMutualsOnly is visible only to mutual followers of the account that posted the status.
VisibilityMutualsOnly Visibility = "mutuals_only"
// VisibilityDirect is visible only to accounts tagged in the status. It is equivalent to a direct message.
VisibilityDirect Visibility = "direct"
)
@ -268,7 +292,8 @@ const (
// swagger:type string
type StatusContentType string
// Content type to use when parsing submitted status into an html-formatted status
// Content type to use when parsing submitted
// status into an html-formatted status.
const (
StatusContentTypePlain StatusContentType = "text/plain"
StatusContentTypeMarkdown StatusContentType = "text/markdown"
@ -280,11 +305,14 @@ const (
//
// swagger:model statusSource
type StatusSource struct {
// ID of the status.
// example: 01FBVD42CQ3ZEEVMW180SBX03B
ID string `json:"id"`
// Plain-text source of a status.
Text string `json:"text"`
// Plain-text version of spoiler text.
SpoilerText string `json:"spoiler_text"`
}
@ -294,27 +322,69 @@ type StatusSource struct {
//
// swagger:model statusEdit
type StatusEdit struct {
// The content of this status at this revision.
// Should be HTML, but might also be plaintext in some cases.
// example: <p>Hey this is a status!</p>
Content string `json:"content"`
// Subject, summary, or content warning for the status at this revision.
// example: warning nsfw
SpoilerText string `json:"spoiler_text"`
// Status marked sensitive at this revision.
// example: false
Sensitive bool `json:"sensitive"`
// The date when this revision was created (ISO 8601 Datetime).
// example: 2021-07-30T09:20:25+00:00
CreatedAt string `json:"created_at"`
// The account that authored this status.
Account *Account `json:"account"`
// The poll attached to the status at this revision.
// Note that edits changing the poll options will be collapsed together into one edit, since this action resets the poll.
// nullable: true
Poll *Poll `json:"poll"`
// Media that is attached to this status.
MediaAttachments []*Attachment `json:"media_attachments"`
// Custom emoji to be used when rendering status content.
Emojis []Emoji `json:"emojis"`
}
// StatusEditRequest models status edit parameters.
//
// swagger:ignore
type StatusEditRequest struct {
// Text content of the status.
// If media_ids is provided, this becomes optional.
// Attaching a poll is optional while status is provided.
Status string `form:"status" json:"status"`
// Text to be shown as a warning or subject before the actual content.
// Statuses are generally collapsed behind this field.
SpoilerText string `form:"spoiler_text" json:"spoiler_text"`
// Content type to use when parsing this status.
ContentType StatusContentType `form:"content_type" json:"content_type"`
// Status and attached media should be marked as sensitive.
Sensitive bool `form:"sensitive" json:"sensitive"`
// ISO 639 language code for this status.
Language string `form:"language" json:"language"`
// Array of Attachment ids to be attached as media.
// If provided, status becomes optional, and poll cannot be used.
MediaIDs []string `form:"media_ids[]" json:"media_ids"`
// Array of Attachment attributes to be updated in attached media.
MediaAttributes []AttachmentAttributesRequest `form:"media_attributes[]" json:"media_attributes"`
// Poll to include with this status.
Poll *PollRequest `form:"poll" json:"poll"`
}