Frodo swaggins (#126)

* more swagger fun

* document a whole bunch more stuff

* more swagger yayyyyyyy

* progress + go fmt
This commit is contained in:
Tobi Smethurst 2021-08-02 19:06:44 +02:00 committed by GitHub
parent cb85f65cca
commit 0386a28b5a
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
58 changed files with 3289 additions and 469 deletions

View file

@ -11,6 +11,7 @@ WORKDIR /go/src/github.com/superseriousbusiness/gotosocial
ADD cmd /go/src/github.com/superseriousbusiness/gotosocial/cmd ADD cmd /go/src/github.com/superseriousbusiness/gotosocial/cmd
ADD internal /go/src/github.com/superseriousbusiness/gotosocial/internal ADD internal /go/src/github.com/superseriousbusiness/gotosocial/internal
ADD testrig /go/src/github.com/superseriousbusiness/gotosocial/testrig ADD testrig /go/src/github.com/superseriousbusiness/gotosocial/testrig
ADD docs/swagger.go /go/src/github.com/superseriousbusiness/gotosocial/docs/swagger.go
ADD go.mod /go/src/github.com/superseriousbusiness/gotosocial/go.mod ADD go.mod /go/src/github.com/superseriousbusiness/gotosocial/go.mod
ADD go.sum /go/src/github.com/superseriousbusiness/gotosocial/go.sum ADD go.sum /go/src/github.com/superseriousbusiness/gotosocial/go.sum
@ -56,6 +57,9 @@ COPY --from=binary_builder /go/src/github.com/superseriousbusiness/gotosocial/go
# copy over the web directory with templates etc # copy over the web directory with templates etc
COPY --from=web_builder web /gotosocial/web COPY --from=web_builder web /gotosocial/web
# put the swagger yaml in the web assets directory so it can be accessed
COPY docs/api/swagger.yaml /gotosocial/web/assets/swagger.yaml
# copy over the admin directory # copy over the admin directory
COPY --from=admin_builder /gotosocial-admin/public /gotosocial/web/assets/admin COPY --from=admin_builder /gotosocial-admin/public /gotosocial/web/assets/admin

View file

@ -225,7 +225,7 @@ Things are moving on the project! As of July 2021 you can now:
* [ ] Scope middleware * [ ] Scope middleware
* [ ] Permissions/acl middleware for admins+moderators * [ ] Permissions/acl middleware for admins+moderators
* [ ] Documentation * [ ] Documentation
* [ ] Swagger API documentation * [x] Swagger API documentation
* [ ] ReadTheDocs.io documentation * [ ] ReadTheDocs.io documentation
* [ ] Deployment documentation * [ ] Deployment documentation
* [ ] App creation guide * [ ] App creation guide

File diff suppressed because it is too large Load diff

View file

@ -35,10 +35,17 @@
// scopes: // scopes:
// read: grants read access to everything // read: grants read access to everything
// read:accounts: grants read access to accounts // read:accounts: grants read access to accounts
// read:blocks: grant read access to blocks
// read:media: grant read access to media
// read:search: grant read access to searches
// read:statuses: grants read access to statuses
// read:streaming: grants read access to streaming api
// write: grants write access to everything // write: grants write access to everything
// write:accounts: grants write access to accounts // write:accounts: grants write access to accounts
// write:blocks: grants write access to blocks // write:blocks: grants write access to blocks
// write:follows: grants write access to follows // write:follows: grants write access to follows
// write:media: grants write access to media
// write:statuses: grants write access to statuses
// admin: grants admin access to everything // admin: grants admin access to everything
// admin:accounts: grants admin access to accounts // admin:accounts: grants admin access to accounts
// OAuth2 Application: // OAuth2 Application:

View file

@ -30,13 +30,13 @@
"github.com/superseriousbusiness/gotosocial/internal/util" "github.com/superseriousbusiness/gotosocial/internal/util"
) )
// AccountCreatePOSTHandler handles create account requests, validates them, // AccountCreatePOSTHandler swagger:operation POST /api/v1/accounts accountCreate
// and puts them in the database if they're valid.
//
// swagger:operation POST /api/v1/accounts accountCreate
// //
// Create a new account using an application token. // Create a new account using an application token.
// //
// The parameters can also be given in the body of the request, as JSON, if the content-type is set to 'application/json'.
// The parameters can also be given in the body of the request, as XML, if the content-type is set to 'application/xml'.
//
// --- // ---
// tags: // tags:
// - accounts // - accounts
@ -45,17 +45,10 @@
// - application/json // - application/json
// - application/xml // - application/xml
// - application/x-www-form-urlencoded // - application/x-www-form-urlencoded
// - multipart/form-data
// //
// produces: // produces:
// - application/json // - application/json
// //
// parameters:
// - name: Account Create Request
// in: body
// schema:
// "$ref": "#/definitions/accountCreateRequest"
//
// security: // security:
// - OAuth2 Application: // - OAuth2 Application:
// - write:accounts // - write:accounts

View file

@ -25,9 +25,7 @@
"github.com/superseriousbusiness/gotosocial/internal/oauth" "github.com/superseriousbusiness/gotosocial/internal/oauth"
) )
// AccountGETHandler returns info about the given account. // AccountGETHandler swagger:operation GET /api/v1/accounts/{id} accountGet
//
// swagger:operation GET /api/v1/accounts/{id} accountGet
// //
// Get information about an account with the given ID. // Get information about an account with the given ID.
// //

View file

@ -26,10 +26,7 @@
"github.com/superseriousbusiness/gotosocial/internal/oauth" "github.com/superseriousbusiness/gotosocial/internal/oauth"
) )
// AccountUpdateCredentialsPATCHHandler allows a user to modify their account/profile settings. // AccountUpdateCredentialsPATCHHandler swagger:operation PATCH /api/v1/accounts/update_credentials accountUpdate
// It should be served as a PATCH at /api/v1/accounts/update_credentials
//
// swagger:operation PATCH /api/v1/accounts/update_credentials accountUpdate
// //
// Update your account. // Update your account.
// //
@ -56,10 +53,12 @@
// in: formData // in: formData
// description: The display name to use for the account. // description: The display name to use for the account.
// type: string // type: string
// allowEmptyValue: true
// - name: note // - name: note
// in: formData // in: formData
// description: Bio/description of this account. // description: Bio/description of this account.
// type: string // type: string
// allowEmptyValue: true
// - name: avatar // - name: avatar
// in: formData // in: formData
// description: Avatar of the user. // description: Avatar of the user.
@ -72,15 +71,15 @@
// in: formData // in: formData
// description: Require manual approval of follow requests. // description: Require manual approval of follow requests.
// type: boolean // type: boolean
// - name: source.privacy // - name: source[privacy]
// in: formData // in: formData
// description: Default post privacy for authored statuses. // description: Default post privacy for authored statuses.
// type: string // type: string
// - name: source.sensitive // - name: source[sensitive]
// in: formData // in: formData
// description: Mark authored statuses as sensitive by default. // description: Mark authored statuses as sensitive by default.
// type: boolean // type: boolean
// - name: source.language // - name: source[language]
// in: formData // in: formData
// description: Default language to use for authored statuses (ISO 6391). // description: Default language to use for authored statuses (ISO 6391).
// type: string // type: string

View file

@ -25,11 +25,7 @@
"github.com/superseriousbusiness/gotosocial/internal/oauth" "github.com/superseriousbusiness/gotosocial/internal/oauth"
) )
// AccountVerifyGETHandler serves a user's account details to them IF they reached this // AccountVerifyGETHandler swagger:operation GET /api/v1/accounts/verify_credentials accountVerify
// handler while in possession of a valid token, according to the oauth middleware.
// It should be served as a GET at /api/v1/accounts/verify_credentials.
//
// swagger:operation GET /api/v1/accounts/verify_credentials accountVerify
// //
// Verify a token by returning account details pertaining to it. // Verify a token by returning account details pertaining to it.
// //

View file

@ -25,9 +25,7 @@
"github.com/superseriousbusiness/gotosocial/internal/oauth" "github.com/superseriousbusiness/gotosocial/internal/oauth"
) )
// AccountBlockPOSTHandler handles the creation of a block from the authed account targeting the given account ID. // AccountBlockPOSTHandler swagger:operation POST /api/v1/accounts/{id}/block accountBlock
//
// swagger:operation POST /api/v1/accounts/{id}/block accountBlock
// //
// Block account with id. // Block account with id.
// //

View file

@ -26,25 +26,43 @@
"github.com/superseriousbusiness/gotosocial/internal/oauth" "github.com/superseriousbusiness/gotosocial/internal/oauth"
) )
// AccountFollowPOSTHandler is the endpoint for creating a new follow request to the target account // AccountFollowPOSTHandler swagger:operation POST /api/v1/accounts/{id}/follow accountFollow
//
// swagger:operation POST /api/v1/accounts/{id}/follow accountFollow
// //
// Follow account with id. // Follow account with id.
// //
// The parameters can also be given in the body of the request, as JSON, if the content-type is set to 'application/json'.
// The parameters can also be given in the body of the request, as XML, if the content-type is set to 'application/xml'.
//
// --- // ---
// tags: // tags:
// - accounts // - accounts
// //
// produces: // consumes:
// - application/json // - application/json
// - application/xml
// - application/x-www-form-urlencoded
// //
// parameters: // parameters:
// - name: id // - name: id
// type: string
// description: The id of the account to follow.
// in: path
// required: true // required: true
// in: path
// description: ID of the account to follow.
// type: string
// - default: true
// description: Show reblogs from this account.
// in: formData
// name: reblogs
// type: boolean
// x-go-name: Reblogs
// - default: false
// description: Notify when this account posts.
// in: formData
// name: notify
// type: boolean
// x-go-name: Notify
//
// produces:
// - application/json
// //
// security: // security:
// - OAuth2 Bearer: // - OAuth2 Bearer:
@ -79,7 +97,7 @@ func (m *Module) AccountFollowPOSTHandler(c *gin.Context) {
c.JSON(http.StatusBadRequest, gin.H{"error": err.Error()}) c.JSON(http.StatusBadRequest, gin.H{"error": err.Error()})
return return
} }
form.TargetAccountID = targetAcctID form.ID = targetAcctID
relationship, errWithCode := m.processor.AccountFollowCreate(authed, form) relationship, errWithCode := m.processor.AccountFollowCreate(authed, form)
if errWithCode != nil { if errWithCode != nil {

View file

@ -25,9 +25,7 @@
"github.com/superseriousbusiness/gotosocial/internal/oauth" "github.com/superseriousbusiness/gotosocial/internal/oauth"
) )
// AccountFollowersGETHandler serves the followers of the requested account, if they're visible to the requester. // AccountFollowersGETHandler swagger:operation GET /api/v1/accounts/{id}/followers accountFollowers
//
// swagger:operation GET /api/v1/accounts/{id}/followers accountFollowers
// //
// See followers of account with given id. // See followers of account with given id.
// //

View file

@ -25,9 +25,7 @@
"github.com/superseriousbusiness/gotosocial/internal/oauth" "github.com/superseriousbusiness/gotosocial/internal/oauth"
) )
// AccountFollowingGETHandler serves the following of the requested account, if they're visible to the requester. // AccountFollowingGETHandler swagger:operation GET /api/v1/accounts/{id}/following accountFollowing
//
// swagger:operation GET /api/v1/accounts/{id}/following accountFollowing
// //
// See accounts followed by given account id. // See accounts followed by given account id.
// //

View file

@ -8,9 +8,7 @@
"github.com/superseriousbusiness/gotosocial/internal/oauth" "github.com/superseriousbusiness/gotosocial/internal/oauth"
) )
// AccountRelationshipsGETHandler serves the relationship of the requesting account with one or more requested account IDs. // AccountRelationshipsGETHandler swagger:operation GET /api/v1/accounts/relationships accountRelationships
//
// swagger:operation GET /api/v1/accounts/relationships accountRelationships
// //
// See your account's relationships with the given account IDs. // See your account's relationships with the given account IDs.
// //

View file

@ -26,9 +26,7 @@
"github.com/superseriousbusiness/gotosocial/internal/oauth" "github.com/superseriousbusiness/gotosocial/internal/oauth"
) )
// AccountStatusesGETHandler serves the statuses of the requested account, if they're visible to the requester. // AccountStatusesGETHandler swagger:operation GET /api/v1/accounts/{id}/statuses accountStatuses
//
// swagger:operation GET /api/v1/accounts/{id}/statuses accountStatuses
// //
// See statuses posted by the requested account. // See statuses posted by the requested account.
// //
@ -86,7 +84,7 @@
// responses: // responses:
// '200': // '200':
// name: statuses // name: statuses
// description: Array of statuses.. // description: Array of statuses.
// schema: // schema:
// type: array // type: array
// items: // items:

View file

@ -25,9 +25,7 @@
"github.com/superseriousbusiness/gotosocial/internal/oauth" "github.com/superseriousbusiness/gotosocial/internal/oauth"
) )
// AccountUnblockPOSTHandler handles the removal of a block from the authed account targeting the given account ID. // AccountUnblockPOSTHandler swagger:operation POST /api/v1/accounts/{id}/unblock accountUnblock
//
// swagger:operation POST /api/v1/accounts/{id}/unblock accountUnblock
// //
// Unblock account with ID. // Unblock account with ID.
// //

View file

@ -25,9 +25,7 @@
"github.com/superseriousbusiness/gotosocial/internal/oauth" "github.com/superseriousbusiness/gotosocial/internal/oauth"
) )
// AccountUnfollowPOSTHandler is the endpoint for removing a follow and/or follow request to the target account // AccountUnfollowPOSTHandler swagger:operation POST /api/v1/accounts/{id}/unfollow accountUnfollow
//
// swagger:operation POST /api/v1/accounts/{id}/unfollow accountUnfollow
// //
// Unfollow account with id. // Unfollow account with id.
// //

View file

@ -12,13 +12,11 @@
"github.com/superseriousbusiness/gotosocial/internal/oauth" "github.com/superseriousbusiness/gotosocial/internal/oauth"
) )
// DomainBlocksPOSTHandler deals with the creation of one or more domain blocks. // DomainBlocksPOSTHandler swagger:operation POST /api/v1/admin/domain_blocks domainBlockCreate
//
// swagger:operation PATCH /api/v1/admin/domain_blocks domainBlockCreate
// //
// Create one or more domain blocks, from a string or a file. // Create one or more domain blocks, from a string or a file.
// //
// Note that you have two options when using this endpoint: either you can set 'import' to true // Note that you have two options when using this endpoint: either you can set `import` to true
// and upload a file containing multiple domain blocks, JSON-formatted, or you can leave import as // and upload a file containing multiple domain blocks, JSON-formatted, or you can leave import as
// false, and just add one domain block. // false, and just add one domain block.
// //
@ -46,38 +44,35 @@
// in: formData // in: formData
// description: |- // description: |-
// JSON-formatted list of domain blocks to import. // JSON-formatted list of domain blocks to import.
// This is only used if 'import' is set to true. // This is only used if `import` is set to true.
// type: file // type: file
// - name: domain // - name: domain
// in: formData // in: formData
// description: |- // description: |-
// Single domain to block. // Single domain to block.
// Used only if 'import' is not true. // Used only if `import` is not true.
// type: string // type: string
// example: example.org
// - name: obfuscate // - name: obfuscate
// in: formData // in: formData
// description: |- // description: |-
// Obfuscate the name of the domain when serving it publicly. // Obfuscate the name of the domain when serving it publicly.
// Eg., 'example.org' becomes something like 'ex***e.org'. // Eg., 'example.org' becomes something like 'ex***e.org'.
// Used only if 'import' is not true. // Used only if `import` is not true.
// type: boolean // type: boolean
// - name: public_comment // - name: public_comment
// in: formData // in: formData
// description: |- // description: |-
// Public comment about this domain block. // Public comment about this domain block.
// Will be displayed alongside the domain block if you choose to share blocks. // Will be displayed alongside the domain block if you choose to share blocks.
// Used only if 'import' is not true. // Used only if `import` is not true.
// type: string // type: string
// example: "harassment, transphobia"
// - name: private_comment // - name: private_comment
// in: formData // in: formData
// description: |- // description: |-
// Private comment about this domain block. Will only be shown to other admins, so this // Private comment about this domain block. Will only be shown to other admins, so this
// is a useful way of internally keeping track of why a certain domain ended up blocked. // is a useful way of internally keeping track of why a certain domain ended up blocked.
// Used only if 'import' is not true. // Used only if `import` is not true.
// type: string // type: string
// example: "harassment, transphobia, and some stuff only other admins need to know about"
// //
// security: // security:
// - OAuth2 Bearer: // - OAuth2 Bearer:
@ -86,9 +81,8 @@
// responses: // responses:
// '200': // '200':
// description: |- // description: |-
// The newly created domain block, if import != true. // The newly created domain block, if `import` != `true`.
// Note that if a list has been imported, then an `array` of // Note that if a list has been imported, then an `array` of newly created domain blocks will be returned instead.
// newly created domain blocks will be returned instead.
// schema: // schema:
// "$ref": "#/definitions/domainBlock" // "$ref": "#/definitions/domainBlock"
// '403': // '403':

View file

@ -8,9 +8,7 @@
"github.com/superseriousbusiness/gotosocial/internal/oauth" "github.com/superseriousbusiness/gotosocial/internal/oauth"
) )
// DomainBlockDELETEHandler deals with the delete of an existing domain block. // DomainBlockDELETEHandler swagger:operation DELETE /api/v1/admin/domain_blocks/{id} domainBlockDelete
//
// swagger:operation DELETE /api/v1/admin/domain_blocks/{id} domainBlockDelete
// //
// Delete domain block with the given ID. // Delete domain block with the given ID.
// //

View file

@ -9,9 +9,7 @@
"github.com/superseriousbusiness/gotosocial/internal/oauth" "github.com/superseriousbusiness/gotosocial/internal/oauth"
) )
// DomainBlockGETHandler returns one existing domain block, identified by its id. // DomainBlockGETHandler swagger:operation GET /api/v1/admin/domain_blocks/{id} domainBlockGet
//
// swagger:operation GET /api/v1/admin/domain_blocks/{id} domainBlockGet
// //
// View domain block with the given ID. // View domain block with the given ID.
// //

View file

@ -9,9 +9,7 @@
"github.com/superseriousbusiness/gotosocial/internal/oauth" "github.com/superseriousbusiness/gotosocial/internal/oauth"
) )
// DomainBlocksGETHandler returns a list of all existing domain blocks. // DomainBlocksGETHandler swagger:operation GET /api/v1/admin/domain_blocks domainBlocksGet
//
// swagger:operation GET /api/v1/admin/domain_blocks domainBlocksGet
// //
// View all domain blocks currently in place. // View all domain blocks currently in place.
// //

View file

@ -31,9 +31,7 @@
"github.com/superseriousbusiness/gotosocial/internal/util" "github.com/superseriousbusiness/gotosocial/internal/util"
) )
// emojiCreateRequest handles the creation of a new instance emoji. // emojiCreateRequest swagger:operation POST /api/v1/admin/custom_emojis emojiCreate
//
// swagger:operation POST /api/v1/admin/custom_emojis emojiCreate
// //
// Upload and create a new instance emoji. // Upload and create a new instance emoji.
// //
@ -55,11 +53,12 @@
// This must be unique on the instance. // This must be unique on the instance.
// type: string // type: string
// pattern: \w{2,30} // pattern: \w{2,30}
// example: blobcat_uwu // required: true
// - name: domains // - name: image
// in: formData // in: formData
// description: A png or gif image of the emoji. Animated pngs work too! // description: A png or gif image of the emoji. Animated pngs work too!
// type: file // type: file
// required: true
// //
// security: // security:
// - OAuth2 Bearer: // - OAuth2 Bearer:

View file

@ -27,8 +27,41 @@
"github.com/superseriousbusiness/gotosocial/internal/oauth" "github.com/superseriousbusiness/gotosocial/internal/oauth"
) )
// AppsPOSTHandler should be served at https://example.org/api/v1/apps // AppsPOSTHandler swagger:operation POST /api/v1/apps appCreate
// It is equivalent to: https://docs.joinmastodon.org/methods/apps/ //
// Register a new application on this instance.
//
// The registered application can be used to obtain an application token.
// This can then be used to register a new account, or (through user auth) obtain an access token.
//
// The parameters can also be given in the body of the request, as JSON, if the content-type is set to 'application/json'.
// The parameters can also be given in the body of the request, as XML, if the content-type is set to 'application/xml'.
//
// ---
// tags:
// - apps
//
// consumes:
// - application/json
// - application/xml
// - application/x-www-form-urlencoded
//
// produces:
// - application/json
//
// responses:
// '200':
// description: "The newly-created application."
// schema:
// "$ref": "#/definitions/application"
// '401':
// description: unauthorized
// '400':
// description: bad request
// '422':
// description: unprocessable
// '500':
// description: internal error
func (m *Module) AppsPOSTHandler(c *gin.Context) { func (m *Module) AppsPOSTHandler(c *gin.Context) {
l := m.log.WithField("func", "AppsPOSTHandler") l := m.log.WithField("func", "AppsPOSTHandler")
l.Trace("entering AppsPOSTHandler") l.Trace("entering AppsPOSTHandler")

View file

@ -26,7 +26,63 @@
"github.com/superseriousbusiness/gotosocial/internal/oauth" "github.com/superseriousbusiness/gotosocial/internal/oauth"
) )
// BlocksGETHandler handles GETting blocks. // BlocksGETHandler swagger:operation GET /api/v1/blocks blocksGet
//
// Get an array of accounts that requesting account has blocked.
//
// The next and previous queries can be parsed from the returned Link header.
// Example:
//
// ```
// <https://example.org/api/v1/blocks?limit=80&max_id=01FC0SKA48HNSVR6YKZCQGS2V8>; rel="next", <https://example.org/api/v1/blocks?limit=80&min_id=01FC0SKW5JK2Q4EVAV2B462YY0>; rel="prev"
// ````
//
// ---
// tags:
// - blocks
//
// produces:
// - application/json
//
// parameters:
// - name: limit
// type: integer
// description: Number of blocks to return.
// default: 20
// in: query
// - name: max_id
// type: string
// description: |-
// Return only blocks *OLDER* than the given max block ID.
// The block with the specified ID will not be included in the response.
// in: query
// - name: since_id
// type: string
// description: |-
// Return only blocks *NEWER* than the given since block ID.
// The block with the specified ID will not be included in the response.
// in: query
//
// security:
// - OAuth2 Bearer:
// - read:blocks
//
// responses:
// '200':
// headers:
// Link:
// type: string
// description: Links to the next and previous queries.
// schema:
// type: array
// items:
// "$ref": "#/definitions/account"
// '401':
// description: unauthorized
// '400':
// description: bad request
// '404':
// description: not found
func (m *Module) BlocksGETHandler(c *gin.Context) { func (m *Module) BlocksGETHandler(c *gin.Context) {
l := m.log.WithField("func", "PublicTimelineGETHandler") l := m.log.WithField("func", "PublicTimelineGETHandler")

View file

@ -6,7 +6,28 @@
"github.com/gin-gonic/gin" "github.com/gin-gonic/gin"
) )
// InstanceInformationGETHandler is for serving instance information at /api/v1/instance // InstanceInformationGETHandler swagger:operation GET /api/v1/instance instanceGet
//
// View instance information.
//
// This is mostly provided for Mastodon application compatibility, since many apps that work with Mastodon use `/api/v1/instance` to inform their connection parameters.
//
// However, it can also be used by other instances for gathering instance information and representing instances in some UI or other.
//
// ---
// tags:
// - instance
//
// produces:
// - application/json
//
// responses:
// '200':
// description: "Instance information."
// schema:
// "$ref": "#/definitions/instance"
// '500':
// description: internal error
func (m *Module) InstanceInformationGETHandler(c *gin.Context) { func (m *Module) InstanceInformationGETHandler(c *gin.Context) {
l := m.log.WithField("func", "InstanceInformationGETHandler") l := m.log.WithField("func", "InstanceInformationGETHandler")

View file

@ -8,7 +8,81 @@
"github.com/superseriousbusiness/gotosocial/internal/oauth" "github.com/superseriousbusiness/gotosocial/internal/oauth"
) )
// InstanceUpdatePATCHHandler allows an admin to update the instance information served at /api/v1/instance // InstanceUpdatePATCHHandler swagger:operation PATCH /api/v1/instance instanceUpdate
//
// Update your instance information and/or upload a new avatar/header for the instance.
//
// This requires admin permissions on the instance.
//
// ---
// tags:
// - instance
//
// consumes:
// - multipart/form-data
//
// produces:
// - application/json
//
// parameters:
// - name: title
// in: formData
// description: Title to use for the instance.
// type: string
// maximum: 40
// allowEmptyValue: true
// - name: contact_username
// in: formData
// description: |-
// Username of the contact account.
// This must be the username of an instance admin.
// type: string
// allowEmptyValue: true
// - name: contact_email
// in: formData
// description: Email address to use as the instance contact.
// type: string
// allowEmptyValue: true
// - name: short_description
// in: formData
// description: Short description of the instance.
// type: string
// maximum: 500
// allowEmptyValue: true
// - name: description
// in: formData
// description: Longer description of the instance.
// type: string
// maximum: 5000
// allowEmptyValue: true
// - name: terms
// in: formData
// description: Terms and conditions of the instance.
// type: string
// maximum: 5000
// allowEmptyValue: true
// - name: avatar
// in: formData
// description: Avatar of the instance.
// type: file
// - name: header
// in: formData
// description: Header of the instance.
// type: file
//
// security:
// - OAuth2 Bearer:
// - admin
//
// responses:
// '200':
// description: "The newly updated instance."
// schema:
// "$ref": "#/definitions/instance"
// '401':
// description: unauthorized
// '400':
// description: bad request
func (m *Module) InstanceUpdatePATCHHandler(c *gin.Context) { func (m *Module) InstanceUpdatePATCHHandler(c *gin.Context) {
l := m.log.WithField("func", "InstanceUpdatePATCHHandler") l := m.log.WithField("func", "InstanceUpdatePATCHHandler")
authed, err := oauth.Authed(c, true, true, true, true) authed, err := oauth.Authed(c, true, true, true, true)

View file

@ -29,7 +29,58 @@
"github.com/superseriousbusiness/gotosocial/internal/oauth" "github.com/superseriousbusiness/gotosocial/internal/oauth"
) )
// MediaCreatePOSTHandler handles requests to create/upload media attachments // MediaCreatePOSTHandler swagger:operation POST /api/v1/media mediaCreate
//
// Upload a new media attachment.
//
// ---
// tags:
// - media
//
// consumes:
// - multipart/form-data
//
// produces:
// - application/json
//
// parameters:
// - name: description
// in: formData
// description: |-
// Image or media description to use as alt-text on the attachment.
// This is very useful for users of screenreaders.
// May or may not be required, depending on your instance settings.
// type: string
// - name: focus
// in: formData
// description: |-
// Focus of the media file.
// If present, it should be in the form of two comma-separated floats between -1 and 1.
// For example: `-0.5,0.25`.
// type: string
// - name: file
// in: formData
// description: The media attachment to upload.
// type: file
// required: true
//
// security:
// - OAuth2 Bearer:
// - write:media
//
// responses:
// '200':
// description: The newly-created media attachment.
// schema:
// "$ref": "#/definitions/attachment"
// '400':
// description: bad request
// '401':
// description: unauthorized
// '403':
// description: forbidden
// '422':
// description: unprocessable
func (m *Module) MediaCreatePOSTHandler(c *gin.Context) { func (m *Module) MediaCreatePOSTHandler(c *gin.Context) {
l := m.log.WithField("func", "statusCreatePOSTHandler") l := m.log.WithField("func", "statusCreatePOSTHandler")
authed, err := oauth.Authed(c, true, true, true, true) // posting new media is serious business so we want *everything* authed, err := oauth.Authed(c, true, true, true, true) // posting new media is serious business so we want *everything*

View file

@ -1,12 +1,3 @@
package media
import (
"net/http"
"github.com/gin-gonic/gin"
"github.com/superseriousbusiness/gotosocial/internal/oauth"
)
/* /*
GoToSocial GoToSocial
Copyright (C) 2021 GoToSocial Authors admin@gotosocial.org Copyright (C) 2021 GoToSocial Authors admin@gotosocial.org
@ -25,7 +16,50 @@
along with this program. If not, see <http://www.gnu.org/licenses/>. along with this program. If not, see <http://www.gnu.org/licenses/>.
*/ */
// MediaGETHandler allows the owner of an attachment to get information about that attachment before it's used in a status. package media
import (
"net/http"
"github.com/gin-gonic/gin"
"github.com/superseriousbusiness/gotosocial/internal/oauth"
)
// MediaGETHandler swagger:operation GET /api/v1/media/{id} mediaGet
//
// Get a media attachment that you own.
//
// ---
// tags:
// - media
//
// produces:
// - application/json
//
// parameters:
// - name: id
// description: id of the attachment
// type: string
// in: path
// required: true
//
// security:
// - OAuth2 Bearer:
// - read:media
//
// responses:
// '200':
// description: The requested media attachment.
// schema:
// "$ref": "#/definitions/attachment"
// '400':
// description: bad request
// '401':
// description: unauthorized
// '403':
// description: forbidden
// '422':
// description: unprocessable
func (m *Module) MediaGETHandler(c *gin.Context) { func (m *Module) MediaGETHandler(c *gin.Context) {
l := m.log.WithField("func", "MediaGETHandler") l := m.log.WithField("func", "MediaGETHandler")
authed, err := oauth.Authed(c, true, true, true, true) authed, err := oauth.Authed(c, true, true, true, true)

View file

@ -1,16 +1,3 @@
package media
import (
"errors"
"fmt"
"net/http"
"github.com/gin-gonic/gin"
"github.com/superseriousbusiness/gotosocial/internal/api/model"
"github.com/superseriousbusiness/gotosocial/internal/config"
"github.com/superseriousbusiness/gotosocial/internal/oauth"
)
/* /*
GoToSocial GoToSocial
Copyright (C) 2021 GoToSocial Authors admin@gotosocial.org Copyright (C) 2021 GoToSocial Authors admin@gotosocial.org
@ -29,7 +16,80 @@
along with this program. If not, see <http://www.gnu.org/licenses/>. along with this program. If not, see <http://www.gnu.org/licenses/>.
*/ */
// MediaPUTHandler allows the owner of an attachment to update information about that attachment before it's used in a status. package media
import (
"errors"
"fmt"
"net/http"
"github.com/gin-gonic/gin"
"github.com/superseriousbusiness/gotosocial/internal/api/model"
"github.com/superseriousbusiness/gotosocial/internal/config"
"github.com/superseriousbusiness/gotosocial/internal/oauth"
)
// MediaPUTHandler swagger:operation PUT /api/v1/media/{id} mediaUpdate
//
// Update a media attachment.
//
// You must own the media attachment, and the attachment must not yet be attached to a status.
//
// The parameters can also be given in the body of the request, as JSON, if the content-type is set to 'application/json'.
// The parameters can also be given in the body of the request, as XML, if the content-type is set to 'application/xml'.
//
// ---
// tags:
// - media
//
// consumes:
// - application/json
// - application/xml
// - application/x-www-form-urlencoded
//
// produces:
// - application/json
//
// parameters:
// - name: id
// description: id of the attachment to update
// type: string
// in: path
// required: true
// - name: description
// in: formData
// description: |-
// Image or media description to use as alt-text on the attachment.
// This is very useful for users of screenreaders.
// May or may not be required, depending on your instance settings.
// type: string
// allowEmptyValue: true
// - name: focus
// in: formData
// description: |-
// Focus of the media file.
// If present, it should be in the form of two comma-separated floats between -1 and 1.
// For example: `-0.5,0.25`.
// type: string
// allowEmptyValue: true
//
// security:
// - OAuth2 Bearer:
// - write:media
//
// responses:
// '200':
// description: The newly-updated media attachment.
// schema:
// "$ref": "#/definitions/attachment"
// '400':
// description: bad request
// '401':
// description: unauthorized
// '403':
// description: forbidden
// '422':
// description: unprocessable
func (m *Module) MediaPUTHandler(c *gin.Context) { func (m *Module) MediaPUTHandler(c *gin.Context) {
l := m.log.WithField("func", "MediaGETHandler") l := m.log.WithField("func", "MediaGETHandler")
authed, err := oauth.Authed(c, true, true, true, true) authed, err := oauth.Authed(c, true, true, true, true)

View file

@ -29,8 +29,32 @@
"github.com/superseriousbusiness/gotosocial/internal/oauth" "github.com/superseriousbusiness/gotosocial/internal/oauth"
) )
// SearchGETHandler handles searches for local and remote accounts, statuses, and hashtags. // SearchGETHandler swagger:operation GET /api/v1/search searchGet
// It corresponds to the mastodon endpoint described here: https://docs.joinmastodon.org/methods/search/ //
// Search for statuses, accounts, or hashtags, on this instance or elsewhere.
//
// If statuses are in the result, they will be returned in descending chronological order (newest first), with sequential IDs (bigger = newer).
//
// ---
// tags:
// - search
//
// security:
// - OAuth2 Bearer:
// - read:search
//
// responses:
// '200':
// name: search results
// description: Results of the search.
// schema:
// type: array
// items:
// "$ref": "#/definitions/searchResult"
// '401':
// description: unauthorized
// '400':
// description: bad request
func (m *Module) SearchGETHandler(c *gin.Context) { func (m *Module) SearchGETHandler(c *gin.Context) {
l := m.log.WithFields(logrus.Fields{ l := m.log.WithFields(logrus.Fields{
"func": "SearchGETHandler", "func": "SearchGETHandler",

View file

@ -26,7 +26,45 @@
"github.com/superseriousbusiness/gotosocial/internal/oauth" "github.com/superseriousbusiness/gotosocial/internal/oauth"
) )
// StatusBoostPOSTHandler handles boost requests against a given status ID // StatusBoostPOSTHandler swagger:operation POST /api/v1/statuses/{id}/reblog statusReblog
//
// Reblog/boost status with the given ID.
//
// If the target status is rebloggable/boostable, it will be shared with your followers.
// This is equivalent to an activitypub 'announce' activity.
//
// ---
// tags:
// - statuses
//
// produces:
// - application/json
//
// parameters:
// - name: id
// type: string
// description: Target status ID.
// in: path
// required: true
//
// security:
// - OAuth2 Bearer:
// - write:statuses
//
// responses:
// '200':
// name: status
// description: The boost of the status.
// schema:
// "$ref": "#/definitions/status"
// '400':
// description: bad request
// '401':
// description: unauthorized
// '403':
// description: forbidden
// '404':
// description: not found
func (m *Module) StatusBoostPOSTHandler(c *gin.Context) { func (m *Module) StatusBoostPOSTHandler(c *gin.Context) {
l := m.log.WithFields(logrus.Fields{ l := m.log.WithFields(logrus.Fields{
"func": "StatusBoostPOSTHandler", "func": "StatusBoostPOSTHandler",

View file

@ -26,7 +26,42 @@
"github.com/superseriousbusiness/gotosocial/internal/oauth" "github.com/superseriousbusiness/gotosocial/internal/oauth"
) )
// StatusBoostedByGETHandler is for serving a list of accounts that have boosted/reblogged a given status // StatusBoostedByGETHandler swagger:operation GET /api/v1/statuses/{id}/reblogged_by statusBoostedBy
//
// View accounts that have reblogged/boosted the target status.
//
// ---
// tags:
// - statuses
//
// produces:
// - application/json
//
// parameters:
// - name: id
// type: string
// description: Target status ID.
// in: path
// required: true
//
// security:
// - OAuth2 Bearer:
// - read:accounts
//
// responses:
// '200':
// schema:
// type: array
// items:
// "$ref": "#/definitions/account"
// '400':
// description: bad request
// '401':
// description: unauthorized
// '403':
// description: forbidden
// '404':
// description: not found
func (m *Module) StatusBoostedByGETHandler(c *gin.Context) { func (m *Module) StatusBoostedByGETHandler(c *gin.Context) {
l := m.log.WithFields(logrus.Fields{ l := m.log.WithFields(logrus.Fields{
"func": "StatusBoostedByGETHandler", "func": "StatusBoostedByGETHandler",

View file

@ -26,7 +26,44 @@
"github.com/superseriousbusiness/gotosocial/internal/oauth" "github.com/superseriousbusiness/gotosocial/internal/oauth"
) )
// StatusContextGETHandler returns the context around the given status ID. // StatusContextGETHandler swagger:operation GET /api/v1/statuses/{id}/context statusContext
//
// Return ancestors and descendants of the given status.
//
// The returned statuses will be ordered in a thread structure, so they are suitable to be displayed in the order in which they were returned.
//
// ---
// tags:
// - statuses
//
// produces:
// - application/json
//
// parameters:
// - name: id
// type: string
// description: Target status ID.
// in: path
// required: true
//
// security:
// - OAuth2 Bearer:
// - read:statuses
//
// responses:
// '200':
// name: statuses
// description: Status context object.
// schema:
// "$ref": "#/definitions/statusContext"
// '400':
// description: bad request
// '401':
// description: unauthorized
// '403':
// description: forbidden
// '404':
// description: not found
func (m *Module) StatusContextGETHandler(c *gin.Context) { func (m *Module) StatusContextGETHandler(c *gin.Context) {
l := m.log.WithFields(logrus.Fields{ l := m.log.WithFields(logrus.Fields{
"func": "StatusContextGETHandler", "func": "StatusContextGETHandler",

View file

@ -30,7 +30,42 @@
"github.com/superseriousbusiness/gotosocial/internal/util" "github.com/superseriousbusiness/gotosocial/internal/util"
) )
// StatusCreatePOSTHandler deals with the creation of new statuses // StatusCreatePOSTHandler swagger:operation POST /api/v1/statuses statusCreate
//
// Create a new status.
//
// The parameters can also be given in the body of the request, as JSON, if the content-type is set to 'application/json'.
// The parameters can also be given in the body of the request, as XML, if the content-type is set to 'application/xml'.
//
// ---
// tags:
// - statuses
//
// consumes:
// - application/json
// - application/xml
// - application/x-www-form-urlencoded
//
// produces:
// - application/json
//
// security:
// - OAuth2 Bearer:
// - write:statuses
//
// responses:
// '200':
// description: "The newly created status."
// schema:
// "$ref": "#/definitions/status"
// '401':
// description: unauthorized
// '400':
// description: bad request
// '404':
// description: not found
// '500':
// description: internal error
func (m *Module) StatusCreatePOSTHandler(c *gin.Context) { func (m *Module) StatusCreatePOSTHandler(c *gin.Context) {
l := m.log.WithField("func", "statusCreatePOSTHandler") l := m.log.WithField("func", "statusCreatePOSTHandler")
authed, err := oauth.Authed(c, true, true, true, true) // posting a status is serious business so we want *everything* authed, err := oauth.Authed(c, true, true, true, true) // posting a status is serious business so we want *everything*

View file

@ -26,7 +26,44 @@
"github.com/superseriousbusiness/gotosocial/internal/oauth" "github.com/superseriousbusiness/gotosocial/internal/oauth"
) )
// StatusDELETEHandler verifies and handles deletion of a status // StatusDELETEHandler swagger:operation DELETE /api/v1/statuses/{id} statusDelete
//
// Delete status with the given ID. The status must belong to you.
//
// The deleted status will be returned in the response. The `text` field will contain the original text of the status as it was submitted.
// This is useful when doing a 'delete and redraft' type operation.
//
// ---
// tags:
// - statuses
//
// produces:
// - application/json
//
// parameters:
// - name: id
// type: string
// description: Target status ID.
// in: path
// required: true
//
// security:
// - OAuth2 Bearer:
// - write:statuses
//
// responses:
// '200':
// description: "The newly deleted status."
// schema:
// "$ref": "#/definitions/status"
// '400':
// description: bad request
// '401':
// description: unauthorized
// '403':
// description: forbidden
// '404':
// description: not found
func (m *Module) StatusDELETEHandler(c *gin.Context) { func (m *Module) StatusDELETEHandler(c *gin.Context) {
l := m.log.WithFields(logrus.Fields{ l := m.log.WithFields(logrus.Fields{
"func": "StatusDELETEHandler", "func": "StatusDELETEHandler",

View file

@ -26,7 +26,41 @@
"github.com/superseriousbusiness/gotosocial/internal/oauth" "github.com/superseriousbusiness/gotosocial/internal/oauth"
) )
// StatusFavePOSTHandler handles fave requests against a given status ID // StatusFavePOSTHandler swagger:operation POST /api/v1/statuses/{id}/favourite statusFave
//
// Star/like/favourite the given status, if permitted.
//
// ---
// tags:
// - statuses
//
// produces:
// - application/json
//
// parameters:
// - name: id
// type: string
// description: Target status ID.
// in: path
// required: true
//
// security:
// - OAuth2 Bearer:
// - write:statuses
//
// responses:
// '200':
// description: "The newly faved status."
// schema:
// "$ref": "#/definitions/status"
// '400':
// description: bad request
// '401':
// description: unauthorized
// '403':
// description: forbidden
// '404':
// description: not found
func (m *Module) StatusFavePOSTHandler(c *gin.Context) { func (m *Module) StatusFavePOSTHandler(c *gin.Context) {
l := m.log.WithFields(logrus.Fields{ l := m.log.WithFields(logrus.Fields{
"func": "StatusFavePOSTHandler", "func": "StatusFavePOSTHandler",

View file

@ -26,7 +26,42 @@
"github.com/superseriousbusiness/gotosocial/internal/oauth" "github.com/superseriousbusiness/gotosocial/internal/oauth"
) )
// StatusFavedByGETHandler is for serving a list of accounts that have faved a given status // StatusFavedByGETHandler swagger:operation GET /api/v1/statuses/{id}/favourited_by statusFavedBy
//
// View accounts that have faved/starred/liked the target status.
//
// ---
// tags:
// - statuses
//
// produces:
// - application/json
//
// parameters:
// - name: id
// type: string
// description: Target status ID.
// in: path
// required: true
//
// security:
// - OAuth2 Bearer:
// - read:accounts
//
// responses:
// '200':
// schema:
// type: array
// items:
// "$ref": "#/definitions/account"
// '400':
// description: bad request
// '401':
// description: unauthorized
// '403':
// description: forbidden
// '404':
// description: not found
func (m *Module) StatusFavedByGETHandler(c *gin.Context) { func (m *Module) StatusFavedByGETHandler(c *gin.Context) {
l := m.log.WithFields(logrus.Fields{ l := m.log.WithFields(logrus.Fields{
"func": "statusGETHandler", "func": "statusGETHandler",

View file

@ -26,7 +26,41 @@
"github.com/superseriousbusiness/gotosocial/internal/oauth" "github.com/superseriousbusiness/gotosocial/internal/oauth"
) )
// StatusGETHandler is for handling requests to just get one status based on its ID // StatusGETHandler swagger:operation GET /api/v1/statuses/{id} statusGet
//
// View status with the given ID.
//
// ---
// tags:
// - statuses
//
// produces:
// - application/json
//
// parameters:
// - name: id
// type: string
// description: Target status ID.
// in: path
// required: true
//
// security:
// - OAuth2 Bearer:
// - read:statuses
//
// responses:
// '200':
// description: "The requested created status."
// schema:
// "$ref": "#/definitions/status"
// '401':
// description: unauthorized
// '400':
// description: bad request
// '404':
// description: not found
// '500':
// description: internal error
func (m *Module) StatusGETHandler(c *gin.Context) { func (m *Module) StatusGETHandler(c *gin.Context) {
l := m.log.WithFields(logrus.Fields{ l := m.log.WithFields(logrus.Fields{
"func": "statusGETHandler", "func": "statusGETHandler",

View file

@ -26,7 +26,42 @@
"github.com/superseriousbusiness/gotosocial/internal/oauth" "github.com/superseriousbusiness/gotosocial/internal/oauth"
) )
// StatusUnboostPOSTHandler handles unboost requests against a given status ID // StatusUnboostPOSTHandler swagger:operation POST /api/v1/statuses/{id}/unreblog statusUnreblog
//
// Unreblog/unboost status with the given ID.
//
// ---
// tags:
// - statuses
//
// produces:
// - application/json
//
// parameters:
// - name: id
// type: string
// description: Target status ID.
// in: path
// required: true
//
// security:
// - OAuth2 Bearer:
// - write:statuses
//
// responses:
// '200':
// name: status
// description: The unboosted status.
// schema:
// "$ref": "#/definitions/status"
// '400':
// description: bad request
// '401':
// description: unauthorized
// '403':
// description: forbidden
// '404':
// description: not found
func (m *Module) StatusUnboostPOSTHandler(c *gin.Context) { func (m *Module) StatusUnboostPOSTHandler(c *gin.Context) {
l := m.log.WithFields(logrus.Fields{ l := m.log.WithFields(logrus.Fields{
"func": "StatusUnboostPOSTHandler", "func": "StatusUnboostPOSTHandler",

View file

@ -26,7 +26,41 @@
"github.com/superseriousbusiness/gotosocial/internal/oauth" "github.com/superseriousbusiness/gotosocial/internal/oauth"
) )
// StatusUnfavePOSTHandler is for undoing a fave on a status with a given ID // StatusUnfavePOSTHandler swagger:operation POST /api/v1/statuses/{id}/unfavourite statusUnfave
//
// Unstar/unlike/unfavourite the given status.
//
// ---
// tags:
// - statuses
//
// produces:
// - application/json
//
// parameters:
// - name: id
// type: string
// description: Target status ID.
// in: path
// required: true
//
// security:
// - OAuth2 Bearer:
// - write:statuses
//
// responses:
// '200':
// description: "The unfaved status."
// schema:
// "$ref": "#/definitions/status"
// '400':
// description: bad request
// '401':
// description: unauthorized
// '403':
// description: forbidden
// '404':
// description: not found
func (m *Module) StatusUnfavePOSTHandler(c *gin.Context) { func (m *Module) StatusUnfavePOSTHandler(c *gin.Context) {
l := m.log.WithFields(logrus.Fields{ l := m.log.WithFields(logrus.Fields{
"func": "StatusUnfavePOSTHandler", "func": "StatusUnfavePOSTHandler",

View file

@ -9,7 +9,103 @@
"github.com/gorilla/websocket" "github.com/gorilla/websocket"
) )
// StreamGETHandler handles the creation of a new websocket streaming request. // StreamGETHandler swagger:operation GET /api/v1/streaming streamGet
//
// Initiate a websocket connection for live streaming of statuses and notifications.
//
// The scheme used should *always* be `wss`. The streaming basepath can be viewed at `/api/v1/instance`.
//
// On a successful connection, a code `101` will be returned, which indicates that the connection is being upgraded to a secure websocket connection.
//
// As long as the connection is open, various message types will be streamed into it.
//
// GoToSocial will ping the connection every 30 seconds to check whether the client is still receiving.
//
// If the ping fails, or something else goes wrong during transmission, then the connection will be dropped, and the client will be expected to start it again.
//
// ---
// tags:
// - streaming
//
// produces:
// - application/json
//
// schemes:
// - wss
//
// parameters:
// - name: access_token
// type: string
// description: Access token for the requesting account.
// in: query
// required: true
// - name: stream
// type: string
// description: |-
// Type of stream to request.
//
// Options are:
//
// `user`: receive updates for the account's home timeline.
// `public`: receive updates for the public timeline.
// `public:local`: receive updates for the local timeline.
// `hashtag`: receive updates for a given hashtag.
// `hashtag:local`: receive local updates for a given hashtag.
// `list`: receive updates for a certain list of accounts.
// `direct`: receive updates for direct messages.
// in: query
// required: true
// security:
// - OAuth2 Bearer:
// - read:streaming
//
// responses:
// '101':
// schema:
// type: object
// properties:
// stream:
// type: array
// items:
// type: string
// enum:
// - user
// - public
// - public:local
// - hashtag
// - hashtag:local
// - list
// - direct
// event:
// description: |-
// The type of event being received.
//
// `update`: a new status has been received.
// `notification`: a new notification has been received.
// `delete`: a status has been deleted.
// `filters_changed`: not implemented.
// type: string
// enum:
// - update
// - notification
// - delete
// - filters_changed
// payload:
// description: |-
// The payload of the streamed message.
// Different depending on the `event` type.
//
// If present, it should be parsed as a string.
//
// If `event` = `update`, then the payload will be a JSON string of a status.
// If `event` = `notification`, then the payload will be a JSON string of a notification.
// If `event` = `delete`, then the payload will be a status ID.
// type: string
// example: "{\"id\":\"01FC3TZ5CFG6H65GCKCJRKA669\",\"created_at\":\"2021-08-02T16:25:52Z\",\"sensitive\":false,\"spoiler_text\":\"\",\"visibility\":\"public\",\"language\":\"en\",\"uri\":\"https://gts.superseriousbusiness.org/users/dumpsterqueer/statuses/01FC3TZ5CFG6H65GCKCJRKA669\",\"url\":\"https://gts.superseriousbusiness.org/@dumpsterqueer/statuses/01FC3TZ5CFG6H65GCKCJRKA669\",\"replies_count\":0,\"reblogs_count\":0,\"favourites_count\":0,\"favourited\":false,\"reblogged\":false,\"muted\":false,\"bookmarked\":fals…//gts.superseriousbusiness.org/fileserver/01JNN207W98SGG3CBJ76R5MVDN/header/original/019036W043D8FXPJKSKCX7G965.png\",\"header_static\":\"https://gts.superseriousbusiness.org/fileserver/01JNN207W98SGG3CBJ76R5MVDN/header/small/019036W043D8FXPJKSKCX7G965.png\",\"followers_count\":33,\"following_count\":28,\"statuses_count\":126,\"last_status_at\":\"2021-08-02T16:25:52Z\",\"emojis\":[],\"fields\":[]},\"media_attachments\":[],\"mentions\":[],\"tags\":[],\"emojis\":[],\"card\":null,\"poll\":null,\"text\":\"a\"}"
// '401':
// description: unauthorized
// '400':
// description: bad request
func (m *Module) StreamGETHandler(c *gin.Context) { func (m *Module) StreamGETHandler(c *gin.Context) {
l := m.log.WithField("func", "StreamGETHandler") l := m.log.WithField("func", "StreamGETHandler")

View file

@ -26,15 +26,81 @@
"github.com/superseriousbusiness/gotosocial/internal/oauth" "github.com/superseriousbusiness/gotosocial/internal/oauth"
) )
// HomeTimelineGETHandler serves status from the HOME timeline. // HomeTimelineGETHandler swagger:operation GET /api/v1/timelines/home homeTimeline
// //
// Several different filters might be passed into this function in the query: // See statuses/posts by accounts you follow.
// //
// max_id -- the maximum ID of the status to show // The statuses will be returned in descending chronological order (newest first), with sequential IDs (bigger = newer).
// since_id -- Return results newer than id //
// min_id -- Return results immediately newer than id // The returned Link header can be used to generate the previous and next queries when scrolling up or down a timeline.
// limit -- show only limit number of statuses //
// local -- Return only local statuses? // Example:
//
// ```
// <https://example.org/api/v1/timelines/home?limit=20&max_id=01FC3GSQ8A3MMJ43BPZSGEG29M>; rel="next", <https://example.org/api/v1/timelines/home?limit=20&min_id=01FC3KJW2GYXSDDRA6RWNDM46M>; rel="prev"
// ````
//
// ---
// tags:
// - timelines
//
// produces:
// - application/json
//
// parameters:
// - name: max_id
// type: string
// description: |-
// Return only statuses *OLDER* than the given max status ID.
// The status with the specified ID will not be included in the response.
// in: query
// required: false
// - name: since_id
// type: string
// description: |-
// Return only statuses *NEWER* than the given since status ID.
// The status with the specified ID will not be included in the response.
// in: query
// - name: min_id
// type: string
// description: |-
// Return only statuses *NEWER* than the given since status ID.
// The status with the specified ID will not be included in the response.
// in: query
// required: false
// - name: limit
// type: integer
// description: Number of statuses to return.
// default: 20
// in: query
// required: false
// - name: local
// type: boolean
// description: Show only statuses posted by local accounts.
// default: false
// in: query
// required: false
//
// security:
// - OAuth2 Bearer:
// - read:statuses
//
// responses:
// '200':
// name: statuses
// description: Array of statuses.
// schema:
// type: array
// items:
// "$ref": "#/definitions/status"
// headers:
// Link:
// type: string
// description: Links to the next and previous queries.
// '401':
// description: unauthorized
// '400':
// description: bad request
func (m *Module) HomeTimelineGETHandler(c *gin.Context) { func (m *Module) HomeTimelineGETHandler(c *gin.Context) {
l := m.log.WithField("func", "HomeTimelineGETHandler") l := m.log.WithField("func", "HomeTimelineGETHandler")

View file

@ -26,9 +26,81 @@
"github.com/superseriousbusiness/gotosocial/internal/oauth" "github.com/superseriousbusiness/gotosocial/internal/oauth"
) )
// PublicTimelineGETHandler handles PUBLIC timeline requests. // PublicTimelineGETHandler swagger:operation GET /api/v1/timelines/public publicTimeline
// This includes requests to local, which are actually just public //
// requests with a filter. // See public statuses/posts that your instance is aware of.
//
// The statuses will be returned in descending chronological order (newest first), with sequential IDs (bigger = newer).
//
// The returned Link header can be used to generate the previous and next queries when scrolling up or down a timeline.
//
// Example:
//
// ```
// <https://example.org/api/v1/timelines/public?limit=20&max_id=01FC3GSQ8A3MMJ43BPZSGEG29M>; rel="next", <https://example.org/api/v1/timelines/public?limit=20&min_id=01FC3KJW2GYXSDDRA6RWNDM46M>; rel="prev"
// ````
//
// ---
// tags:
// - timelines
//
// produces:
// - application/json
//
// parameters:
// - name: max_id
// type: string
// description: |-
// Return only statuses *OLDER* than the given max status ID.
// The status with the specified ID will not be included in the response.
// in: query
// required: false
// - name: since_id
// type: string
// description: |-
// Return only statuses *NEWER* than the given since status ID.
// The status with the specified ID will not be included in the response.
// in: query
// - name: min_id
// type: string
// description: |-
// Return only statuses *NEWER* than the given since status ID.
// The status with the specified ID will not be included in the response.
// in: query
// required: false
// - name: limit
// type: integer
// description: Number of statuses to return.
// default: 20
// in: query
// required: false
// - name: local
// type: boolean
// description: Show only statuses posted by local accounts.
// default: false
// in: query
// required: false
//
// security:
// - OAuth2 Bearer:
// - read:statuses
//
// responses:
// '200':
// name: statuses
// description: Array of statuses.
// schema:
// type: array
// items:
// "$ref": "#/definitions/status"
// headers:
// Link:
// type: string
// description: Links to the next and previous queries.
// '401':
// description: unauthorized
// '400':
// description: bad request
func (m *Module) PublicTimelineGETHandler(c *gin.Context) { func (m *Module) PublicTimelineGETHandler(c *gin.Context) {
l := m.log.WithField("func", "PublicTimelineGETHandler") l := m.log.WithField("func", "PublicTimelineGETHandler")

View file

@ -23,7 +23,9 @@
"net" "net"
) )
// Account represents a fediverse account of some kind, either a remote one or one on this instance. // Account models a fediverse account.
//
// The modelled account can be either a remote account, or one on this instance.
// //
// swagger:model account // swagger:model account
type Account struct { type Account struct {
@ -42,7 +44,7 @@ type Account struct {
DisplayName string `json:"display_name"` DisplayName string `json:"display_name"`
// Account manually approves follow requests. // Account manually approves follow requests.
Locked bool `json:"locked"` Locked bool `json:"locked"`
// Account has opted into discovery features such as the profile directory. // Account has opted into discovery features.
Discoverable bool `json:"discoverable,omitempty"` Discoverable bool `json:"discoverable,omitempty"`
// Account identifies as a bot. // Account identifies as a bot.
Bot bool `json:"bot"` Bot bool `json:"bot"`
@ -90,9 +92,9 @@ type Account struct {
Source *Source `json:"source,omitempty"` Source *Source `json:"source,omitempty"`
} }
// AccountCreateRequest represents the form submitted during a POST request to /api/v1/accounts. // AccountCreateRequest models account creation parameters.
// //
// swagger:model accountCreateRequest // swagger:parameters accountCreate
type AccountCreateRequest struct { type AccountCreateRequest struct {
// Text that will be reviewed by moderators if registrations require manual approval. // Text that will be reviewed by moderators if registrations require manual approval.
Reason string `form:"reason" json:"reason" xml:"reason"` Reason string `form:"reason" json:"reason" xml:"reason"`
@ -127,7 +129,8 @@ type AccountCreateRequest struct {
IP net.IP `form:"-"` IP net.IP `form:"-"`
} }
// UpdateCredentialsRequest represents the form submitted during a PATCH request to /api/v1/accounts/update_credentials. // UpdateCredentialsRequest models an update to an account, by the account owner.
//
// swagger:ignore // swagger:ignore
type UpdateCredentialsRequest struct { type UpdateCredentialsRequest struct {
// Account should be made discoverable and shown in the profile directory (if enabled). // Account should be made discoverable and shown in the profile directory (if enabled).
@ -151,7 +154,8 @@ type UpdateCredentialsRequest struct {
} }
// UpdateSource is to be used specifically in an UpdateCredentialsRequest. // UpdateSource is to be used specifically in an UpdateCredentialsRequest.
// swagger:ignore //
// swagger:model updateSource
type UpdateSource struct { type UpdateSource struct {
// Default post privacy for authored statuses. // Default post privacy for authored statuses.
Privacy *string `form:"privacy" json:"privacy" xml:"privacy"` Privacy *string `form:"privacy" json:"privacy" xml:"privacy"`
@ -172,15 +176,14 @@ type UpdateField struct {
Value *string `form:"value" json:"value" xml:"value"` Value *string `form:"value" json:"value" xml:"value"`
} }
// AccountFollowRequest is for parsing requests at /api/v1/accounts/:id/follow // AccountFollowRequest models a request to follow an account.
// //
// swagger:model accountFollowRequest // swagger:ignore
type AccountFollowRequest struct { type AccountFollowRequest struct {
// ID of the account to follow request // The id of the account to follow.
// This should be a URL parameter not a form field ID string `form:"-" json:"-" xml:"-"`
TargetAccountID string `form:"-"` // Show reblogs from this account.
// Show reblogs for this account?
Reblogs *bool `form:"reblogs" json:"reblogs" xml:"reblogs"` Reblogs *bool `form:"reblogs" json:"reblogs" xml:"reblogs"`
// Notify when this account posts? // Notify when this account posts.
Notify *bool `form:"notify" json:"notify" xml:"notify"` Notify *bool `form:"notify" json:"notify" xml:"notify"`
} }

View file

@ -18,7 +18,7 @@
package model package model
// AdminAccountInfo represents the *admin* view of an account's details. See here: https://docs.joinmastodon.org/entities/admin-account/ // AdminAccountInfo models the admin view of an account's details.
type AdminAccountInfo struct { type AdminAccountInfo struct {
// The ID of the account in the database. // The ID of the account in the database.
ID string `json:"id"` ID string `json:"id"`
@ -56,7 +56,7 @@ type AdminAccountInfo struct {
InvitedByAccountID string `json:"invited_by_account_id"` InvitedByAccountID string `json:"invited_by_account_id"`
} }
// AdminReportInfo represents the *admin* view of a report. See here: https://docs.joinmastodon.org/entities/admin-report/ // AdminReportInfo models the admin view of a report.
type AdminReportInfo struct { type AdminReportInfo struct {
// The ID of the report in the database. // The ID of the report in the database.
ID string `json:"id"` ID string `json:"id"`

View file

@ -18,20 +18,46 @@
package model package model
// Announcement represents an admin/moderator announcement for local users. See here: https://docs.joinmastodon.org/entities/announcement/ // Announcement models an admin announcement for the instance.
//
// swagger:model announcement
type Announcement struct { type Announcement struct {
ID string `json:"id"` // The ID of the announcement.
Content string `json:"content"` // example: 01FC30T7X4TNCZK0TH90QYF3M4
StartsAt string `json:"starts_at"` ID string `json:"id"`
EndsAt string `json:"ends_at"` // The body of the announcement.
AllDay bool `json:"all_day"` // Should be HTML formatted.
PublishedAt string `json:"published_at"` // example: <p>This is an announcement. No malarky.</p>
UpdatedAt string `json:"updated_at"` Content string `json:"content"`
Published bool `json:"published"` // When the announcement should begin to be displayed (ISO 8601 Datetime).
Read bool `json:"read"` // If the announcement has no start time, this will be omitted or empty.
Mentions []Mention `json:"mentions"` // example: 2021-07-30T09:20:25+00:00
Statuses []Status `json:"statuses"` StartsAt string `json:"starts_at"`
Tags []Tag `json:"tags"` // When the announcement should stop being displayed (ISO 8601 Datetime).
Emojis []Emoji `json:"emoji"` // If the announcement has no end time, this will be omitted or empty.
Reactions []AnnouncementReaction `json:"reactions"` // example: 2021-07-30T09:20:25+00:00
EndsAt string `json:"ends_at"`
// Announcement doesn't have begin time and end time, but begin day and end day.
AllDay bool `json:"all_day"`
// When the announcement was first published (ISO 8601 Datetime).
// example: 2021-07-30T09:20:25+00:00
PublishedAt string `json:"published_at"`
// When the announcement was last updated (ISO 8601 Datetime).
// example: 2021-07-30T09:20:25+00:00
UpdatedAt string `json:"updated_at"`
// Announcement is 'published', ie., visible to users.
// Announcements that are not published should be shown only to admins.
Published bool `json:"published"`
// Requesting account has seen this announcement.
Read bool `json:"read"`
// Mentions this announcement contains.
Mentions []Mention `json:"mentions"`
// Statuses contained in this announcement.
Statuses []Status `json:"statuses"`
// Tags used in this announcement.
Tags []Tag `json:"tags"`
// Emojis used in this announcement.
Emojis []Emoji `json:"emoji"`
// Reactions to this announcement.
Reactions []AnnouncementReaction `json:"reactions"`
} }

View file

@ -18,16 +18,24 @@
package model package model
// AnnouncementReaction represents a user reaction to admin/moderator announcement. See here: https://docs.joinmastodon.org/entities/announcementreaction/ // AnnouncementReaction models a user reaction to an announcement.
//
// swagger:model announcementReaction
type AnnouncementReaction struct { type AnnouncementReaction struct {
// The emoji used for the reaction. Either a unicode emoji, or a custom emoji's shortcode. // The emoji used for the reaction. Either a unicode emoji, or a custom emoji's shortcode.
// example: blobcat_uwu
Name string `json:"name"` Name string `json:"name"`
// The total number of users who have added this reaction. // The total number of users who have added this reaction.
// example: 5
Count int `json:"count"` Count int `json:"count"`
// Whether the authorized user has added this reaction to the announcement. // This reaction belongs to the account viewing it.
Me bool `json:"me"` Me bool `json:"me"`
// A link to the custom emoji. // Web link to the image of the custom emoji.
// Empty for unicode emojis.
// example: https://example.org/custom_emojis/original/blobcat_uwu.png
URL string `json:"url,omitempty"` URL string `json:"url,omitempty"`
// A link to a non-animated version of the custom emoji. // Web link to a non-animated image of the custom emoji.
// Empty for unicode emojis.
// example: https://example.org/custom_emojis/statuc/blobcat_uwu.png
StaticURL string `json:"static_url,omitempty"` StaticURL string `json:"static_url,omitempty"`
} }

View file

@ -18,8 +18,7 @@
package model package model
// Application represents an api Application, as defined here. // Application models an api application.
// Primarily, application is used for allowing apps like Tusky etc to connect to Mastodon on behalf of a user.
// //
// swagger:model application // swagger:model application
type Application struct { type Application struct {
@ -43,18 +42,30 @@ type Application struct {
VapidKey string `json:"vapid_key,omitempty"` VapidKey string `json:"vapid_key,omitempty"`
} }
// ApplicationCreateRequest represents a POST request to https://example.org/api/v1/apps. // ApplicationCreateRequest models app create parameters.
// See here: https://docs.joinmastodon.org/methods/apps/ //
// And here: https://docs.joinmastodon.org/client/token/ // swagger:parameters appCreate
type ApplicationCreateRequest struct { type ApplicationCreateRequest struct {
// A name for your application // The name of the application.
//
// in: formData
// required: true
ClientName string `form:"client_name" json:"client_name" xml:"client_name" binding:"required"` ClientName string `form:"client_name" json:"client_name" xml:"client_name" binding:"required"`
// Where the user should be redirected after authorization. // Where the user should be redirected after authorization.
// To display the authorization code to the user instead of redirecting //
// to a web page, use urn:ietf:wg:oauth:2.0:oob in this parameter. // To display the authorization code to the user instead of redirecting to a web page, use `urn:ietf:wg:oauth:2.0:oob` in this parameter.
//
// in: formData
// required: true
RedirectURIs string `form:"redirect_uris" json:"redirect_uris" xml:"redirect_uris" binding:"required"` RedirectURIs string `form:"redirect_uris" json:"redirect_uris" xml:"redirect_uris" binding:"required"`
// Space separated list of scopes. If none is provided, defaults to read. // Space separated list of scopes.
//
// If no scopes are provided, defaults to `read`.
//
// in: formData
Scopes string `form:"scopes" json:"scopes" xml:"scopes"` Scopes string `form:"scopes" json:"scopes" xml:"scopes"`
// A URL to the homepage of your app // A URL to the web page of the app (optional).
//
// in: formData
Website string `form:"website" json:"website" xml:"website"` Website string `form:"website" json:"website" xml:"website"`
} }

View file

@ -20,33 +20,50 @@
import "mime/multipart" import "mime/multipart"
// AttachmentRequest represents the form data parameters submitted by a client during a media upload request. // AttachmentRequest models media attachment creation parameters.
// See: https://docs.joinmastodon.org/methods/statuses/media/ //
// swagger: ignore
type AttachmentRequest struct { type AttachmentRequest struct {
File *multipart.FileHeader `form:"file" binding:"required"` // Media file.
Description string `form:"description"` File *multipart.FileHeader `form:"file" binding:"required"`
Focus string `form:"focus"` // 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
Focus string `form:"focus"`
} }
// AttachmentUpdateRequest represents the form data parameters submitted by a client during a media update/PUT request. // AttachmentUpdateRequest models an update request for an attachment.
// See: https://docs.joinmastodon.org/methods/statuses/media/ //
// swagger:ignore
type AttachmentUpdateRequest struct { 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"` Description *string `form:"description" json:"description" xml:"description"`
Focus *string `form:"focus" json:"focus" xml:"focus"` // 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"`
} }
// Attachment represents the object returned to a client after a successful media upload request. // Attachment models a media attachment.
// //
// swagger:model attachment // swagger:model attachment
type Attachment struct { type Attachment struct {
// The ID of the attachment. // The ID of the attachment.
// example: 01FC31DZT1AYWDZ8XTCRWRBYRK
ID string `json:"id"` ID string `json:"id"`
// The type of the attachment. // The type of the attachment.
// unknown = unsupported or unrecognized file type. // enum:
// image = Static image. // - unknown
// gifv = Looping, soundless animation. // - image
// video = Video clip. // - gifv
// audio = Audio track. // - video
// - audio
// example: image // example: image
Type string `json:"type"` Type string `json:"type"`
// The location of the original full-size attachment. // The location of the original full-size attachment.
@ -64,6 +81,7 @@ type Attachment struct {
// example: https://some-other-server.org/attachments/small/ahhhhh.jpeg // example: https://some-other-server.org/attachments/small/ahhhhh.jpeg
PreviewRemoteURL string `json:"preview_remote_url,omitempty"` PreviewRemoteURL string `json:"preview_remote_url,omitempty"`
// A shorter URL for the attachment. // A shorter URL for the attachment.
// Not currently used.
TextURL string `json:"text_url,omitempty"` TextURL string `json:"text_url,omitempty"`
// Metadata for this attachment. // Metadata for this attachment.
Meta MediaMeta `json:"meta,omitempty"` Meta MediaMeta `json:"meta,omitempty"`
@ -75,42 +93,88 @@ type Attachment struct {
Blurhash string `json:"blurhash,omitempty"` Blurhash string `json:"blurhash,omitempty"`
} }
// MediaMeta describes the returned media // MediaMeta models media metadata.
// This can be metadata about an image, an audio file, video, etc.
// //
// swagger:model mediaMeta // swagger:model mediaMeta
type MediaMeta struct { type MediaMeta struct {
Length string `json:"length,omitempty"` Length string `json:"length,omitempty"`
Duration float32 `json:"duration,omitempty"` // Duration of the media in seconds.
FPS uint16 `json:"fps,omitempty"` // Only set for video and audio.
Size string `json:"size,omitempty"` // example: 5.43
Width int `json:"width,omitempty"` Duration float32 `json:"duration,omitempty"`
Height int `json:"height,omitempty"` // Framerate of the media.
Aspect float32 `json:"aspect,omitempty"` // Only set for video and gifs.
AudioEncode string `json:"audio_encode,omitempty"` // example: 30
AudioBitrate string `json:"audio_bitrate,omitempty"` FPS uint16 `json:"fps,omitempty"`
AudioChannels string `json:"audio_channels,omitempty"` // Size of the media, in the format `[width]x[height]`.
Original MediaDimensions `json:"original"` // Not set for audio.
Small MediaDimensions `json:"small,omitempty"` // example: 1920x1080
Focus MediaFocus `json:"focus,omitempty"` Size string `json:"size,omitempty"`
// Width of the media in pixels.
// Not set for audio.
// example: 1920
Width int `json:"width,omitempty"`
// Height of the media in pixels.
// Not set for audio.
// example: 1080
Height int `json:"height,omitempty"`
// Aspect ratio of the media.
// Equal to width / height.
// example: 1.777777778
Aspect float32 `json:"aspect,omitempty"`
AudioEncode string `json:"audio_encode,omitempty"`
AudioBitrate string `json:"audio_bitrate,omitempty"`
AudioChannels string `json:"audio_channels,omitempty"`
// Dimensions of the original media.
Original MediaDimensions `json:"original"`
// Dimensions of the thumbnail/small version of the media.
Small MediaDimensions `json:"small,omitempty"`
// Focus data for the media.
Focus MediaFocus `json:"focus,omitempty"`
} }
// MediaFocus describes the focal point of a piece of media. It should be returned to the caller as part of MediaMeta. // MediaFocus models the focal point of a piece of media.
// //
// swagger:model mediaFocus // swagger:model mediaFocus
type MediaFocus struct { type MediaFocus struct {
X float32 `json:"x"` // should be between -1 and 1 // x position of the focus
Y float32 `json:"y"` // should be between -1 and 1 // should be between -1 and 1
X float32 `json:"x"`
// y position of the focus
// should be between -1 and 1
Y float32 `json:"y"`
} }
// MediaDimensions describes the physical properties of a piece of media. It should be returned to the caller as part of MediaMeta. // MediaDimensions models detailed properties of a piece of media.
// //
// swagger:model mediaDimensions // swagger:model mediaDimensions
type MediaDimensions struct { type MediaDimensions struct {
Width int `json:"width,omitempty"` // Width of the media in pixels.
Height int `json:"height,omitempty"` // Not set for audio.
FrameRate string `json:"frame_rate,omitempty"` // example: 1920
Duration float32 `json:"duration,omitempty"` Width int `json:"width,omitempty"`
Bitrate int `json:"bitrate,omitempty"` // Height of the media in pixels.
Size string `json:"size,omitempty"` // Not set for audio.
Aspect float32 `json:"aspect,omitempty"` // example: 1080
Height int `json:"height,omitempty"`
// Framerate of the media.
// Only set for video and gifs.
// example: 30
FrameRate string `json:"frame_rate,omitempty"`
// Duration of the media in seconds.
// Only set for video and audio.
// example: 5.43
Duration float32 `json:"duration,omitempty"`
// Bitrate of the media in bits per second.
// example: 1000000
Bitrate int `json:"bitrate,omitempty"`
// Size of the media, in the format `[width]x[height]`.
// Not set for audio.
// example: 1920x1080
Size string `json:"size,omitempty"`
// Aspect ratio of the media.
// Equal to width / height.
// example: 1.777777778
Aspect float32 `json:"aspect,omitempty"`
} }

View file

@ -32,11 +32,11 @@ type Card struct {
// example: Is water wet? We're not sure. In this article, we ask an expert... // example: Is water wet? We're not sure. In this article, we ask an expert...
Description string `json:"description"` Description string `json:"description"`
// The type of the preview card. // The type of the preview card.
// String (Enumerable, oneOf) // enum:
// link = Link OEmbed // - link
// photo = Photo OEmbed // - photo
// video = Video OEmbed // - video
// rich = iframe OEmbed. Not currently accepted, so won't show up in practice. // - rich
// example: link // example: link
Type string `json:"type"` Type string `json:"type"`
// The author of the original resource. // The author of the original resource.

View file

@ -18,7 +18,9 @@
package model package model
// Context represents the tree around a given status. Used for reconstructing threads of statuses. See: https://docs.joinmastodon.org/entities/context/ // Context models the tree around a given status.
//
// swagger:model statusContext
type Context struct { type Context struct {
// Parents in the thread. // Parents in the thread.
Ancestors []Status `json:"ancestors"` Ancestors []Status `json:"ancestors"`

View file

@ -20,60 +20,77 @@
import "mime/multipart" import "mime/multipart"
// Instance represents the software instance of Mastodon running on this domain. See https://docs.joinmastodon.org/entities/instance/ // Instance models information about this or another instance.
//
// swagger:model instance
type Instance struct { type Instance struct {
// REQUIRED // The URI of the instance.
// example: https://example.org
// The domain name of the instance.
URI string `json:"uri,omitempty"` URI string `json:"uri,omitempty"`
// The title of the website. // The title of the instance.
// example: GoToSocial Example Instance
Title string `json:"title,omitempty"` Title string `json:"title,omitempty"`
// Admin-defined description of the Mastodon site. // Description of the instance.
//
// Should be HTML formatted, but might be plaintext.
//
// This should be displayed on the 'about' page for an instance.
Description string `json:"description"` Description string `json:"description"`
// A shorter description defined by the admin. // A shorter description of the instance.
//
// Should be HTML formatted, but might be plaintext.
//
// This should be displayed on the instance splash/landing page.
ShortDescription string `json:"short_description"` ShortDescription string `json:"short_description"`
// An email that may be contacted for any inquiries. // An email address that may be used for inquiries.
// example: admin@example.org
Email string `json:"email"` Email string `json:"email"`
// The version of Mastodon installed on the instance. // The version of GoToSocial installed on the instance.
//
// This will contain at least a semantic version number.
//
// It may also contain, after a space, the short git commit ID of the running software.
//
// example: 0.1.1 cb85f65
Version string `json:"version"` Version string `json:"version"`
// Primary langauges of the website and its staff. // Primary language of the instance.
// example: en
Languages []string `json:"languages,omitempty"` Languages []string `json:"languages,omitempty"`
// Whether registrations are enabled. // New account registrations are enabled on this instance.
Registrations bool `json:"registrations"` Registrations bool `json:"registrations"`
// Whether registrations require moderator approval. // New account registrations require admin approval.
ApprovalRequired bool `json:"approval_required"` ApprovalRequired bool `json:"approval_required"`
// Whether invites are enabled. // Invites are enabled on this instance.
InvitesEnabled bool `json:"invites_enabled"` InvitesEnabled bool `json:"invites_enabled"`
// URLs of interest for clients apps. // URLs of interest for client applications.
URLS *InstanceURLs `json:"urls,omitempty"` URLS *InstanceURLs `json:"urls,omitempty"`
// Statistics about how much information the instance contains. // Statistics about the instance: number of posts, accounts, etc.
Stats map[string]int `json:"stats,omitempty"` Stats map[string]int `json:"stats,omitempty"`
// Banner image for the website. // URL of the instance avatar/banner image.
// example: https://example.org/files/instance/thumbnail.jpeg
Thumbnail string `json:"thumbnail"` Thumbnail string `json:"thumbnail"`
// A user that can be contacted, as an alternative to email. // Contact account for the instance.
ContactAccount *Account `json:"contact_account,omitempty"` ContactAccount *Account `json:"contact_account,omitempty"`
// What's the maximum allowed length of a post on this instance? // Maximum allowed length of a post on this instance, in characters.
// This is provided for compatibility with Tusky. //
// This is provided for compatibility with Tusky and other apps.
//
// example: 5000
MaxTootChars uint `json:"max_toot_chars"` MaxTootChars uint `json:"max_toot_chars"`
} }
// InstanceURLs represents URLs necessary for successfully connecting to the instance as a user. See https://docs.joinmastodon.org/entities/instance/ // InstanceURLs models instance-relevant URLs for client application consumption.
//
// swagger:model instanceURLs
type InstanceURLs struct { type InstanceURLs struct {
// Websockets address for push streaming. // Websockets address for status and notification streaming.
// example: wss://example.org
StreamingAPI string `json:"streaming_api"` StreamingAPI string `json:"streaming_api"`
} }
// InstanceStats represents some public-facing stats about the instance. See https://docs.joinmastodon.org/entities/instance/ // InstanceSettingsUpdateRequest models an instance update request.
type InstanceStats struct { //
// Users registered on this instance. // swagger:ignore
UserCount int `json:"user_count"`
// Statuses authored by users on instance.
StatusCount int `json:"status_count"`
// Domains federated with this instance.
DomainCount int `json:"domain_count"`
}
// InstanceSettingsUpdateRequest is the form to be parsed on a PATCH to /api/v1/instance
type InstanceSettingsUpdateRequest struct { type InstanceSettingsUpdateRequest struct {
// Title to use for the instance. Max 40 characters. // Title to use for the instance. Max 40 characters.
Title *string `form:"title" json:"title" xml:"title"` Title *string `form:"title" json:"title" xml:"title"`

View file

@ -51,19 +51,24 @@ type Poll struct {
type PollOptions struct { type PollOptions struct {
// The text value of the poll option. String. // The text value of the poll option. String.
Title string `json:"title"` Title string `json:"title"`
// The number of received votes for this option. Number, or null if results are not published yet. // The number of received votes for this option.
// Number, or null if results are not published yet.
VotesCount int `json:"votes_count,omitempty"` VotesCount int `json:"votes_count,omitempty"`
} }
// PollRequest represents a mastodon-api poll attached to a status POST request, as defined here: https://docs.joinmastodon.org/methods/statuses/ // PollRequest models a request to create a poll.
// It should be used at the path https://example.org/api/v1/statuses //
// swagger:parameters createStatus
type PollRequest struct { type PollRequest struct {
// Array of possible answers. If provided, media_ids cannot be used, and poll[expires_in] must be provided. // Array of possible answers.
Options []string `form:"options"` // If provided, media_ids cannot be used, and poll[expires_in] must be provided.
// Duration the poll should be open, in seconds. If provided, media_ids cannot be used, and poll[options] must be provided. // name: poll[options]
ExpiresIn int `form:"expires_in"` Options []string `form:"options" json:"options" xml:"options"`
// Allow multiple choices? // Duration the poll should be open, in seconds.
Multiple bool `form:"multiple"` // If provided, media_ids cannot be used, and poll[options] must be provided.
// Hide vote counts until the poll ends? ExpiresIn int `form:"expires_in" json:"expires_in" xml:"expires_in"`
HideTotals bool `form:"hide_totals"` // Allow multiple choices on this poll.
Multiple bool `form:"multiple" json:"multiple" xml:"multiple"`
// Hide vote counts until the poll ends.
HideTotals bool `form:"hide_totals" json:"hide_totals" xml:"hide_totals"`
} }

View file

@ -18,33 +18,72 @@
package model package model
// SearchQuery corresponds to search parameters as submitted through the client API. // SearchQuery models a search request.
// See https://docs.joinmastodon.org/methods/search/ //
// swagger:parameters searchGet
type SearchQuery struct { type SearchQuery struct {
// If provided, statuses returned will be authored only by this account // If type is `statuses`, then statuses returned will be authored only by this account.
AccountID string //
// Return results older than this id // in: query
MaxID string AccountID string `json:"account_id"`
// Return results immediately newer than this id // Return results *older* than this id.
MinID string //
// Enum(accounts, hashtags, statuses) // The entry with this ID will not be included in the search results.
Type string // in: query
// Filter out unreviewed tags? Defaults to false. Use true when trying to find trending tags. MaxID string `json:"max_id"`
ExcludeUnreviewed bool // Return results *newer* than this id.
// The search query //
Query string // The entry with this ID will not be included in the search results.
// Attempt WebFinger lookup. Defaults to false. // in: query
Resolve bool MinID string `json:"min_id"`
// Maximum number of results to load, per type. Defaults to 20. Max 40. // Type of the search query to perform.
Limit int //
// Offset in search results. Used for pagination. Defaults to 0. // Must be one of: `accounts`, `hashtags`, `statuses`.
Offset int //
// Only include accounts that the user is following. Defaults to false. // enum:
Following bool // - accounts
// - hashtags
// - statuses
// required: true
// in: query
Type string `json:"type"`
// Filter out tags that haven't been reviewed and approved by an instance admin.
//
// default: false
// in: query
ExcludeUnreviewed bool `json:"exclude_unreviewed"`
// String to use as a search query.
//
// For accounts, this should be in the format `@someaccount@some.instance.com`, or the format `https://some.instance.com/@someaccount`
//
// For a status, this can be in the format: `https://some.instance.com/@someaccount/SOME_ID_OF_A_STATUS`
//
// required: true
// in: query
Query string `json:"q"`
// Attempt to resolve the query by performing a remote webfinger lookup, if the query includes a remote host.
// default: false
Resolve bool `json:"resolve"`
// Maximum number of results to load, per type.
// default: 20
// minimum: 1
// maximum: 40
// in: query
Limit int `json:"limit"`
// Offset for paginating search results.
//
// default: 0
// in: query
Offset int `json:"offset"`
// Only include accounts that the searching account is following.
// default: false
// in: query
Following bool `json:"following"`
} }
// SearchResult corresponds to a search result, containing accounts, statuses, and hashtags. // SearchResult models a search result.
// See https://docs.joinmastodon.org/methods/search/ //
// swagger:model searchResult
type SearchResult struct { type SearchResult struct {
Accounts []Account `json:"accounts"` Accounts []Account `json:"accounts"`
Statuses []Status `json:"statuses"` Statuses []Status `json:"statuses"`

View file

@ -18,7 +18,7 @@
package model package model
// Status represents a status or post. // Status models a status or post.
// //
// swagger:model status // swagger:model status
type Status struct { type Status struct {
@ -71,8 +71,9 @@ type Status struct {
// The content of this status. Should be HTML, but might also be plaintext in some cases. // The content of this status. Should be HTML, but might also be plaintext in some cases.
// example: <p>Hey this is a status!</p> // example: <p>Hey this is a status!</p>
Content string `json:"content"` Content string `json:"content"`
// The status that this status is a reblog/boost of. // The status that this status reblogs/boosts.
Reblog *Status `json:"reblog,omitempty"` // nullable: true
Reblog *StatusReblogged `json:"reblog,omitempty"`
// The application used to post this status, if visible. // The application used to post this status, if visible.
Application *Application `json:"application"` Application *Application `json:"application"`
// The account that authored this status. // The account that authored this status.
@ -95,35 +96,71 @@ type Status struct {
Text string `json:"text"` Text string `json:"text"`
} }
// StatusCreateRequest represents a mastodon-api status POST request, as defined here: https://docs.joinmastodon.org/methods/statuses/ // StatusReblogged represents a reblogged status.
// It should be used at the path https://mastodon.example/api/v1/statuses //
// swagger:model statusReblogged
type StatusReblogged struct {
*Status
}
// StatusCreateRequest models status creation parameters.
//
// swagger:parameters statusCreate
type StatusCreateRequest struct { 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. // Text content of the status.
// If media_ids is provided, this becomes optional.
// Attaching a poll is optional while status is provided.
// in: formData
Status string `form:"status" json:"status" xml:"status"` Status string `form:"status" json:"status" xml:"status"`
// Array of Attachment ids to be attached as media. If provided, status becomes optional, and poll cannot be used. // Array of Attachment ids to be attached as media.
// If provided, status becomes optional, and poll cannot be used.
// in: formData
MediaIDs []string `form:"media_ids" json:"media_ids" xml:"media_ids"` MediaIDs []string `form:"media_ids" json:"media_ids" xml:"media_ids"`
// Poll to include with this status. // Poll to include with this status.
// swagger:ignore
Poll *PollRequest `form:"poll" json:"poll" xml:"poll"` Poll *PollRequest `form:"poll" json:"poll" xml:"poll"`
// ID of the status being replied to, if status is a reply // ID of the status being replied to, if status is a reply.
// in: formData
InReplyToID string `form:"in_reply_to_id" json:"in_reply_to_id" xml:"in_reply_to_id"` InReplyToID string `form:"in_reply_to_id" json:"in_reply_to_id" xml:"in_reply_to_id"`
// Mark status and attached media as sensitive? // Status and attached media should be marked as sensitive.
// in: formData
Sensitive bool `form:"sensitive" json:"sensitive" xml:"sensitive"` Sensitive bool `form:"sensitive" json:"sensitive" xml:"sensitive"`
// Text to be shown as a warning or subject before the actual content. Statuses are generally collapsed behind this field. // Text to be shown as a warning or subject before the actual content.
// Statuses are generally collapsed behind this field.
// in: formData
SpoilerText string `form:"spoiler_text" json:"spoiler_text" xml:"spoiler_text"` SpoilerText string `form:"spoiler_text" json:"spoiler_text" xml:"spoiler_text"`
// Visibility of the posted status. Enumerable oneOf public, unlisted, private, direct. // Visibility of the posted status.
// enum:
// - public
// - unlisted
// - private
// - direct
// in: formData
Visibility Visibility `form:"visibility" json:"visibility" xml:"visibility"` Visibility Visibility `form:"visibility" json:"visibility" xml:"visibility"`
// ISO 8601 Datetime at which to schedule a status. Providing this paramter will cause ScheduledStatus to be returned instead of Status. Must be at least 5 minutes in the future. // ISO 8601 Datetime at which to schedule a status.
// Providing this paramter will cause ScheduledStatus to be returned instead of Status.
// Must be at least 5 minutes in the future.
// in: formData
ScheduledAt string `form:"scheduled_at" json:"scheduled_at" xml:"scheduled_at"` ScheduledAt string `form:"scheduled_at" json:"scheduled_at" xml:"scheduled_at"`
// ISO 639 language code for this status. // ISO 639 language code for this status.
// in: formData
Language string `form:"language" json:"language" xml:"language"` Language string `form:"language" json:"language" xml:"language"`
// Format in which to parse the submitted status. // Format to use when parsing this status.
// Can be either plain or markdown. Empty will default to plain. // enum:
// - markdown
// - plain
// in: formData
Format StatusFormat `form:"format" json:"format" xml:"format"` Format StatusFormat `form:"format" json:"format" xml:"format"`
} }
// Visibility denotes the visibility of a status to other users. // Visibility models the visibility of a status.
// //
// swagger:model statusVisibility // swagger:model statusVisibility
// enum:
// - public
// - unlisted
// - private
// - direct
type Visibility string type Visibility string
const ( const (
@ -141,6 +178,8 @@ type StatusCreateRequest struct {
// AdvancedStatusCreateForm wraps the mastodon status create form along with the GTS advanced // AdvancedStatusCreateForm wraps the mastodon status create form along with the GTS advanced
// visibility settings. // visibility settings.
//
// swagger:model advancedStatusCreateForm
type AdvancedStatusCreateForm struct { type AdvancedStatusCreateForm struct {
StatusCreateRequest StatusCreateRequest
AdvancedVisibilityFlagsForm AdvancedVisibilityFlagsForm
@ -148,20 +187,27 @@ type AdvancedStatusCreateForm struct {
// AdvancedVisibilityFlagsForm allows a few more advanced flags to be set on new statuses, in addition // AdvancedVisibilityFlagsForm allows a few more advanced flags to be set on new statuses, in addition
// to the standard mastodon-compatible ones. // to the standard mastodon-compatible ones.
//
// swagger:model advancedVisibilityFlagsForm
type AdvancedVisibilityFlagsForm struct { type AdvancedVisibilityFlagsForm struct {
// The gotosocial visibility model // This status will be federated beyond the local timeline(s).
VisibilityAdvanced *string `form:"visibility_advanced" json:"visibility_advanced" xml:"visibility_advanced"`
// This status will be federated beyond the local timeline(s)
Federated *bool `form:"federated" json:"federated" xml:"federated"` Federated *bool `form:"federated" json:"federated" xml:"federated"`
// This status can be boosted/reblogged // This status can be boosted/reblogged.
Boostable *bool `form:"boostable" json:"boostable" xml:"boostable"` Boostable *bool `form:"boostable" json:"boostable" xml:"boostable"`
// This status can be replied to // This status can be replied to.
Replyable *bool `form:"replyable" json:"replyable" xml:"replyable"` Replyable *bool `form:"replyable" json:"replyable" xml:"replyable"`
// This status can be liked/faved // This status can be liked/faved.
Likeable *bool `form:"likeable" json:"likeable" xml:"likeable"` Likeable *bool `form:"likeable" json:"likeable" xml:"likeable"`
} }
// StatusFormat determines what kind of format a submitted status should be parsed in // StatusFormat is the format in which to parse the submitted status.
// Can be either plain or markdown. Empty will default to plain.
//
// swagger:model statusFormat
// enum:
// - plain
// - markdown
// example: plain
type StatusFormat string type StatusFormat string
// StatusFormatPlain expects a plaintext status which will then be formatted into html. // StatusFormatPlain expects a plaintext status which will then be formatted into html.

View file

@ -117,22 +117,24 @@ type Status struct {
VisibilityFollowersOnly Visibility = "followers_only" VisibilityFollowersOnly Visibility = "followers_only"
// VisibilityMutualsOnly means this status is visible to mutual followers only. // VisibilityMutualsOnly means this status is visible to mutual followers only.
VisibilityMutualsOnly Visibility = "mutuals_only" VisibilityMutualsOnly Visibility = "mutuals_only"
// VisibilityDirect means this status is visible only to mentioned recipients // VisibilityDirect means this status is visible only to mentioned recipients.
VisibilityDirect Visibility = "direct" VisibilityDirect Visibility = "direct"
// VisibilityDefault is used when no other setting can be found // VisibilityDefault is used when no other setting can be found.
VisibilityDefault Visibility = "public" VisibilityDefault Visibility = VisibilityUnlocked
) )
// VisibilityAdvanced denotes a set of flags that can be set on a status for fine-tuning visibility and interactivity of the status. // VisibilityAdvanced models flags for fine-tuning visibility and interactivity of a status.
//
// All flags default to true.
//
// If PUBLIC is selected, flags will all be overwritten to TRUE regardless of what is selected.
//
// If UNLOCKED is selected, any flags can be turned on or off in any combination.
//
// If FOLLOWERS-ONLY or MUTUALS-ONLY are selected, boostable will always be FALSE. Other flags can be turned on or off as desired.
//
// If DIRECT is selected, boostable will be FALSE, and all other flags will be TRUE.
type VisibilityAdvanced struct { type VisibilityAdvanced struct {
/*
ADVANCED SETTINGS -- These should all default to TRUE.
If PUBLIC is selected, they will all be overwritten to TRUE regardless of what is selected.
If UNLOCKED is selected, any of them can be turned on or off in any combination.
If FOLLOWERS-ONLY or MUTUALS-ONLY are selected, boostable will always be FALSE. The others can be turned on or off as desired.
If DIRECT is selected, boostable will be FALSE, and all other flags will be TRUE.
*/
// This status will be federated beyond the local timeline(s) // This status will be federated beyond the local timeline(s)
Federated bool `pg:"default:true"` Federated bool `pg:"default:true"`
// This status can be boosted/reblogged // This status can be boosted/reblogged

View file

@ -31,7 +31,7 @@
func (p *processor) FollowCreate(requestingAccount *gtsmodel.Account, form *apimodel.AccountFollowRequest) (*apimodel.Relationship, gtserror.WithCode) { func (p *processor) FollowCreate(requestingAccount *gtsmodel.Account, form *apimodel.AccountFollowRequest) (*apimodel.Relationship, gtserror.WithCode) {
// if there's a block between the accounts we shouldn't create the request ofc // if there's a block between the accounts we shouldn't create the request ofc
blocked, err := p.db.Blocked(requestingAccount.ID, form.TargetAccountID) blocked, err := p.db.Blocked(requestingAccount.ID, form.ID)
if err != nil { if err != nil {
return nil, gtserror.NewErrorInternalError(err) return nil, gtserror.NewErrorInternalError(err)
} }
@ -41,9 +41,9 @@ func (p *processor) FollowCreate(requestingAccount *gtsmodel.Account, form *apim
// make sure the target account actually exists in our db // make sure the target account actually exists in our db
targetAcct := &gtsmodel.Account{} targetAcct := &gtsmodel.Account{}
if err := p.db.GetByID(form.TargetAccountID, targetAcct); err != nil { if err := p.db.GetByID(form.ID, targetAcct); err != nil {
if _, ok := err.(db.ErrNoEntries); ok { if _, ok := err.(db.ErrNoEntries); ok {
return nil, gtserror.NewErrorNotFound(fmt.Errorf("accountfollowcreate: account %s not found in the db: %s", form.TargetAccountID, err)) return nil, gtserror.NewErrorNotFound(fmt.Errorf("accountfollowcreate: account %s not found in the db: %s", form.ID, err))
} }
} }
@ -54,7 +54,7 @@ func (p *processor) FollowCreate(requestingAccount *gtsmodel.Account, form *apim
} }
if follows { if follows {
// already follows so just return the relationship // already follows so just return the relationship
return p.RelationshipGet(requestingAccount, form.TargetAccountID) return p.RelationshipGet(requestingAccount, form.ID)
} }
// check if a follow exists already // check if a follow exists already
@ -64,7 +64,7 @@ func (p *processor) FollowCreate(requestingAccount *gtsmodel.Account, form *apim
} }
if followRequested { if followRequested {
// already follow requested so just return the relationship // already follow requested so just return the relationship
return p.RelationshipGet(requestingAccount, form.TargetAccountID) return p.RelationshipGet(requestingAccount, form.ID)
} }
// make the follow request // make the follow request
@ -76,7 +76,7 @@ func (p *processor) FollowCreate(requestingAccount *gtsmodel.Account, form *apim
fr := &gtsmodel.FollowRequest{ fr := &gtsmodel.FollowRequest{
ID: newFollowID, ID: newFollowID,
AccountID: requestingAccount.ID, AccountID: requestingAccount.ID,
TargetAccountID: form.TargetAccountID, TargetAccountID: form.ID,
ShowReblogs: true, ShowReblogs: true,
URI: util.GenerateURIForFollow(requestingAccount.Username, p.config.Protocol, p.config.Host, newFollowID), URI: util.GenerateURIForFollow(requestingAccount.Username, p.config.Protocol, p.config.Host, newFollowID),
Notify: false, Notify: false,
@ -95,11 +95,11 @@ func (p *processor) FollowCreate(requestingAccount *gtsmodel.Account, form *apim
// if it's a local account that's not locked we can just straight up accept the follow request // if it's a local account that's not locked we can just straight up accept the follow request
if !targetAcct.Locked && targetAcct.Domain == "" { if !targetAcct.Locked && targetAcct.Domain == "" {
if _, err := p.db.AcceptFollowRequest(requestingAccount.ID, form.TargetAccountID); err != nil { if _, err := p.db.AcceptFollowRequest(requestingAccount.ID, form.ID); err != nil {
return nil, gtserror.NewErrorInternalError(fmt.Errorf("accountfollowcreate: error accepting folow request for local unlocked account: %s", err)) return nil, gtserror.NewErrorInternalError(fmt.Errorf("accountfollowcreate: error accepting folow request for local unlocked account: %s", err))
} }
// return the new relationship // return the new relationship
return p.RelationshipGet(requestingAccount, form.TargetAccountID) return p.RelationshipGet(requestingAccount, form.ID)
} }
// otherwise we leave the follow request as it is and we handle the rest of the process asynchronously // otherwise we leave the follow request as it is and we handle the rest of the process asynchronously
@ -112,5 +112,5 @@ func (p *processor) FollowCreate(requestingAccount *gtsmodel.Account, form *apim
} }
// return whatever relationship results from this // return whatever relationship results from this
return p.RelationshipGet(requestingAccount, form.TargetAccountID) return p.RelationshipGet(requestingAccount, form.ID)
} }

View file

@ -21,22 +21,18 @@ func (p *processor) processVisibility(form *apimodel.AdvancedStatusCreateForm, a
Likeable: true, Likeable: true,
} }
var gtsBasicVis gtsmodel.Visibility var vis gtsmodel.Visibility
// Advanced takes priority if it's set. // If visibility isn't set on the form, then just take the account default.
// If it's not set, take whatever masto visibility is set.
// If *that's* not set either, then just take the account default.
// If that's also not set, take the default for the whole instance. // If that's also not set, take the default for the whole instance.
if form.VisibilityAdvanced != nil { if form.Visibility != "" {
gtsBasicVis = gtsmodel.Visibility(*form.VisibilityAdvanced) vis = p.tc.MastoVisToVis(form.Visibility)
} else if form.Visibility != "" {
gtsBasicVis = p.tc.MastoVisToVis(form.Visibility)
} else if accountDefaultVis != "" { } else if accountDefaultVis != "" {
gtsBasicVis = accountDefaultVis vis = accountDefaultVis
} else { } else {
gtsBasicVis = gtsmodel.VisibilityDefault vis = gtsmodel.VisibilityDefault
} }
switch gtsBasicVis { switch vis {
case gtsmodel.VisibilityPublic: case gtsmodel.VisibilityPublic:
// for public, there's no need to change any of the advanced flags from true regardless of what the user filled out // for public, there's no need to change any of the advanced flags from true regardless of what the user filled out
break break
@ -82,7 +78,7 @@ func (p *processor) processVisibility(form *apimodel.AdvancedStatusCreateForm, a
gtsAdvancedVis.Likeable = true gtsAdvancedVis.Likeable = true
} }
status.Visibility = gtsBasicVis status.Visibility = vis
status.VisibilityAdvanced = gtsAdvancedVis status.VisibilityAdvanced = gtsAdvancedVis
return nil return nil
} }

View file

@ -488,7 +488,7 @@ func (c *converter) StatusToMasto(s *gtsmodel.Status, requestingAccount *gtsmode
statusInteractions = si statusInteractions = si
} }
return &model.Status{ apiStatus := &model.Status{
ID: s.ID, ID: s.ID,
CreatedAt: s.CreatedAt.Format(time.RFC3339), CreatedAt: s.CreatedAt.Format(time.RFC3339),
InReplyToID: s.InReplyToID, InReplyToID: s.InReplyToID,
@ -508,7 +508,6 @@ func (c *converter) StatusToMasto(s *gtsmodel.Status, requestingAccount *gtsmode
Reblogged: statusInteractions.Reblogged, Reblogged: statusInteractions.Reblogged,
Pinned: s.Pinned, Pinned: s.Pinned,
Content: s.Content, Content: s.Content,
Reblog: mastoRebloggedStatus,
Application: mastoApplication, Application: mastoApplication,
Account: mastoAuthorAccount, Account: mastoAuthorAccount,
MediaAttachments: mastoAttachments, MediaAttachments: mastoAttachments,
@ -518,7 +517,13 @@ func (c *converter) StatusToMasto(s *gtsmodel.Status, requestingAccount *gtsmode
Card: mastoCard, // TODO: implement cards Card: mastoCard, // TODO: implement cards
Poll: mastoPoll, // TODO: implement polls Poll: mastoPoll, // TODO: implement polls
Text: s.Text, Text: s.Text,
}, nil }
if mastoRebloggedStatus != nil {
apiStatus.Reblog = &model.StatusReblogged{Status: mastoRebloggedStatus}
}
return apiStatus, nil
} }
// VisToMasto converts a gts visibility into its mastodon equivalent // VisToMasto converts a gts visibility into its mastodon equivalent