Add `Accept` header negotiation to relevant API endpoints (#337)
* start centralizing negotiation logic for API * swagger document nodeinfo endpoint * go fmt * document negotiate function * use content negotiation * tidy up negotiation logic * negotiate content throughout client api * swagger * remove attachment on Content * add accept header to test requests
This commit is contained in:
parent
0884f89431
commit
e2daf0f012
|
@ -12,6 +12,24 @@ definitions:
|
|||
title: A FileHeader describes a file part of a multipart request.
|
||||
type: object
|
||||
x-go-package: mime/multipart
|
||||
Link:
|
||||
description: See https://webfinger.net/
|
||||
properties:
|
||||
href:
|
||||
type: string
|
||||
x-go-name: Href
|
||||
rel:
|
||||
type: string
|
||||
x-go-name: Rel
|
||||
template:
|
||||
type: string
|
||||
x-go-name: Template
|
||||
type:
|
||||
type: string
|
||||
x-go-name: Type
|
||||
title: Link represents one 'link' in a slice of links returned from a lookup request.
|
||||
type: object
|
||||
x-go-package: github.com/superseriousbusiness/gotosocial/internal/api/model
|
||||
MIMEHeader:
|
||||
additionalProperties:
|
||||
items:
|
||||
|
@ -49,6 +67,48 @@ definitions:
|
|||
title: Mention represents a mention of another account.
|
||||
type: object
|
||||
x-go-package: github.com/superseriousbusiness/gotosocial/internal/api/model
|
||||
NodeInfoServices:
|
||||
properties:
|
||||
inbound:
|
||||
items:
|
||||
type: string
|
||||
type: array
|
||||
x-go-name: Inbound
|
||||
outbound:
|
||||
items:
|
||||
type: string
|
||||
type: array
|
||||
x-go-name: Outbound
|
||||
title: NodeInfoServices represents inbound and outbound services that this node
|
||||
offers connections to.
|
||||
type: object
|
||||
x-go-package: github.com/superseriousbusiness/gotosocial/internal/api/model
|
||||
NodeInfoSoftware:
|
||||
properties:
|
||||
name:
|
||||
example: gotosocial
|
||||
type: string
|
||||
x-go-name: Name
|
||||
version:
|
||||
example: 0.1.2 1234567
|
||||
type: string
|
||||
x-go-name: Version
|
||||
title: NodeInfoSoftware represents the name and version number of the software
|
||||
of this node.
|
||||
type: object
|
||||
x-go-package: github.com/superseriousbusiness/gotosocial/internal/api/model
|
||||
NodeInfoUsage:
|
||||
properties:
|
||||
users:
|
||||
$ref: '#/definitions/NodeInfoUsers'
|
||||
title: NodeInfoUsage represents usage information about this server, such as number
|
||||
of users.
|
||||
type: object
|
||||
x-go-package: github.com/superseriousbusiness/gotosocial/internal/api/model
|
||||
NodeInfoUsers:
|
||||
title: NodeInfoUsers is a stub for usage information, currently empty.
|
||||
type: object
|
||||
x-go-package: github.com/superseriousbusiness/gotosocial/internal/api/model
|
||||
Source:
|
||||
description: Returned as an additional entity when verifying and updated credentials,
|
||||
as an attribute of Account.
|
||||
|
@ -1122,6 +1182,42 @@ definitions:
|
|||
type: object
|
||||
x-go-name: MediaMeta
|
||||
x-go-package: github.com/superseriousbusiness/gotosocial/internal/api/model
|
||||
nodeinfo:
|
||||
description: 'See: https://nodeinfo.diaspora.software/schema.html'
|
||||
properties:
|
||||
metadata:
|
||||
additionalProperties:
|
||||
type: object
|
||||
description: Free form key value pairs for software specific values. Clients
|
||||
should not rely on any specific key present.
|
||||
type: object
|
||||
x-go-name: Metadata
|
||||
openRegistrations:
|
||||
description: Whether this server allows open self-registration.
|
||||
example: false
|
||||
type: boolean
|
||||
x-go-name: OpenRegistrations
|
||||
protocols:
|
||||
description: The protocols supported on this server.
|
||||
items:
|
||||
type: string
|
||||
type: array
|
||||
x-go-name: Protocols
|
||||
services:
|
||||
$ref: '#/definitions/NodeInfoServices'
|
||||
software:
|
||||
$ref: '#/definitions/NodeInfoSoftware'
|
||||
usage:
|
||||
$ref: '#/definitions/NodeInfoUsage'
|
||||
version:
|
||||
description: The schema version
|
||||
example: "2.0"
|
||||
type: string
|
||||
x-go-name: Version
|
||||
title: Nodeinfo represents a version 2.1 or version 2.0 nodeinfo schema.
|
||||
type: object
|
||||
x-go-name: Nodeinfo
|
||||
x-go-package: github.com/superseriousbusiness/gotosocial/internal/api/model
|
||||
oauthToken:
|
||||
properties:
|
||||
access_token:
|
||||
|
@ -1670,6 +1766,28 @@ definitions:
|
|||
type: object
|
||||
x-go-name: UpdateSource
|
||||
x-go-package: github.com/superseriousbusiness/gotosocial/internal/api/model
|
||||
wellKnownResponse:
|
||||
description: See https://webfinger.net/
|
||||
properties:
|
||||
aliases:
|
||||
items:
|
||||
type: string
|
||||
type: array
|
||||
x-go-name: Aliases
|
||||
links:
|
||||
items:
|
||||
$ref: '#/definitions/Link'
|
||||
type: array
|
||||
x-go-name: Links
|
||||
subject:
|
||||
type: string
|
||||
x-go-name: Subject
|
||||
title: |-
|
||||
WellKnownResponse represents the response to either a webfinger request for an 'acct' resource, or a request to nodeinfo.
|
||||
For example, it would be returned from https://example.org/.well-known/webfinger?resource=acct:some_username@example.org
|
||||
type: object
|
||||
x-go-name: WellKnownResponse
|
||||
x-go-package: github.com/superseriousbusiness/gotosocial/internal/api/model
|
||||
host: example.org
|
||||
info:
|
||||
contact:
|
||||
|
@ -1682,6 +1800,43 @@ info:
|
|||
title: GoToSocial
|
||||
version: 0.0.1
|
||||
paths:
|
||||
/.well-known/nodeinfo:
|
||||
get:
|
||||
description: |-
|
||||
eg. `{"links":[{"rel":"http://nodeinfo.diaspora.software/ns/schema/2.0","href":"http://example.org/nodeinfo/2.0"}]}`
|
||||
See: https://nodeinfo.diaspora.software/protocol.html
|
||||
operationId: nodeInfoWellKnownGet
|
||||
produces:
|
||||
- application/json
|
||||
responses:
|
||||
"200":
|
||||
description: ""
|
||||
schema:
|
||||
$ref: '#/definitions/wellKnownResponse'
|
||||
summary: Directs callers to /nodeinfo/2.0.
|
||||
tags:
|
||||
- nodeinfo
|
||||
/.well-known/webfinger:
|
||||
get:
|
||||
description: |-
|
||||
For example, a GET to `https://goblin.technology/.well-known/webfinger?resource=acct:tobi@goblin.technology` would return:
|
||||
|
||||
```
|
||||
{"subject":"acct:tobi@goblin.technology","aliases":["https://goblin.technology/users/tobi","https://goblin.technology/@tobi"],"links":[{"rel":"http://webfinger.net/rel/profile-page","type":"text/html","href":"https://goblin.technology/@tobi"},{"rel":"self","type":"application/activity+json","href":"https://goblin.technology/users/tobi"}]}
|
||||
```
|
||||
|
||||
See: https://webfinger.net/
|
||||
operationId: webfingerGet
|
||||
produces:
|
||||
- application/json
|
||||
responses:
|
||||
"200":
|
||||
description: ""
|
||||
schema:
|
||||
$ref: '#/definitions/wellKnownResponse'
|
||||
summary: Handles webfinger account lookup requests.
|
||||
tags:
|
||||
- webfinger
|
||||
/api/v1/accounts:
|
||||
post:
|
||||
consumes:
|
||||
|
@ -3529,6 +3684,20 @@ paths:
|
|||
summary: Change the password of authenticated user.
|
||||
tags:
|
||||
- user
|
||||
/nodeinfo/2.0:
|
||||
get:
|
||||
description: 'See: https://nodeinfo.diaspora.software/schema.html'
|
||||
operationId: nodeInfoGet
|
||||
produces:
|
||||
- application/json; profile="http://nodeinfo.diaspora.software/ns/schema/2.0#"
|
||||
responses:
|
||||
"200":
|
||||
description: ""
|
||||
schema:
|
||||
$ref: '#/definitions/nodeinfo'
|
||||
summary: Returns a compliant nodeinfo response to node info queries.
|
||||
tags:
|
||||
- nodeinfo
|
||||
/users/{username}/outbox:
|
||||
get:
|
||||
description: |-
|
||||
|
|
|
@ -95,5 +95,7 @@ func (suite *AccountStandardTestSuite) newContext(recorder *httptest.ResponseRec
|
|||
ctx.Request.Header.Set("Content-Type", bodyContentType)
|
||||
}
|
||||
|
||||
ctx.Request.Header.Set("accept", "application/json")
|
||||
|
||||
return ctx
|
||||
}
|
||||
|
|
|
@ -27,6 +27,7 @@ import (
|
|||
"github.com/spf13/viper"
|
||||
|
||||
"github.com/gin-gonic/gin"
|
||||
"github.com/superseriousbusiness/gotosocial/internal/api"
|
||||
"github.com/superseriousbusiness/gotosocial/internal/api/model"
|
||||
"github.com/superseriousbusiness/gotosocial/internal/config"
|
||||
"github.com/superseriousbusiness/gotosocial/internal/oauth"
|
||||
|
@ -78,6 +79,11 @@ func (m *Module) AccountCreatePOSTHandler(c *gin.Context) {
|
|||
return
|
||||
}
|
||||
|
||||
if _, err := api.NegotiateAccept(c, api.JSONAcceptHeaders...); err != nil {
|
||||
c.JSON(http.StatusNotAcceptable, gin.H{"error": err.Error()})
|
||||
return
|
||||
}
|
||||
|
||||
l.Trace("parsing request form")
|
||||
form := &model.AccountCreateRequest{}
|
||||
if err := c.ShouldBind(form); err != nil || form == nil {
|
||||
|
|
|
@ -22,6 +22,7 @@ import (
|
|||
"net/http"
|
||||
|
||||
"github.com/gin-gonic/gin"
|
||||
"github.com/superseriousbusiness/gotosocial/internal/api"
|
||||
"github.com/superseriousbusiness/gotosocial/internal/oauth"
|
||||
)
|
||||
|
||||
|
@ -64,6 +65,11 @@ func (m *Module) AccountGETHandler(c *gin.Context) {
|
|||
return
|
||||
}
|
||||
|
||||
if _, err := api.NegotiateAccept(c, api.JSONAcceptHeaders...); err != nil {
|
||||
c.JSON(http.StatusNotAcceptable, gin.H{"error": err.Error()})
|
||||
return
|
||||
}
|
||||
|
||||
targetAcctID := c.Param(IDKey)
|
||||
if targetAcctID == "" {
|
||||
c.JSON(http.StatusBadRequest, gin.H{"error": "no account id specified"})
|
||||
|
|
|
@ -20,11 +20,13 @@ package account
|
|||
|
||||
import (
|
||||
"fmt"
|
||||
"github.com/sirupsen/logrus"
|
||||
"net/http"
|
||||
"strconv"
|
||||
|
||||
"github.com/sirupsen/logrus"
|
||||
|
||||
"github.com/gin-gonic/gin"
|
||||
"github.com/superseriousbusiness/gotosocial/internal/api"
|
||||
"github.com/superseriousbusiness/gotosocial/internal/api/model"
|
||||
"github.com/superseriousbusiness/gotosocial/internal/oauth"
|
||||
)
|
||||
|
@ -110,6 +112,11 @@ func (m *Module) AccountUpdateCredentialsPATCHHandler(c *gin.Context) {
|
|||
}
|
||||
l.Tracef("retrieved account %+v", authed.Account.ID)
|
||||
|
||||
if _, err := api.NegotiateAccept(c, api.JSONAcceptHeaders...); err != nil {
|
||||
c.JSON(http.StatusNotAcceptable, gin.H{"error": err.Error()})
|
||||
return
|
||||
}
|
||||
|
||||
form, err := parseUpdateAccountForm(c)
|
||||
if err != nil {
|
||||
c.JSON(http.StatusBadRequest, gin.H{"error": err.Error()})
|
||||
|
|
|
@ -19,10 +19,12 @@
|
|||
package account
|
||||
|
||||
import (
|
||||
"github.com/sirupsen/logrus"
|
||||
"net/http"
|
||||
|
||||
"github.com/sirupsen/logrus"
|
||||
|
||||
"github.com/gin-gonic/gin"
|
||||
"github.com/superseriousbusiness/gotosocial/internal/api"
|
||||
"github.com/superseriousbusiness/gotosocial/internal/oauth"
|
||||
)
|
||||
|
||||
|
@ -60,6 +62,11 @@ func (m *Module) AccountVerifyGETHandler(c *gin.Context) {
|
|||
return
|
||||
}
|
||||
|
||||
if _, err := api.NegotiateAccept(c, api.JSONAcceptHeaders...); err != nil {
|
||||
c.JSON(http.StatusNotAcceptable, gin.H{"error": err.Error()})
|
||||
return
|
||||
}
|
||||
|
||||
acctSensitive, err := m.processor.AccountGet(c.Request.Context(), authed, authed.Account.ID)
|
||||
if err != nil {
|
||||
l.Debugf("error getting account from processor: %s", err)
|
||||
|
|
|
@ -22,6 +22,7 @@ import (
|
|||
"net/http"
|
||||
|
||||
"github.com/gin-gonic/gin"
|
||||
"github.com/superseriousbusiness/gotosocial/internal/api"
|
||||
"github.com/superseriousbusiness/gotosocial/internal/oauth"
|
||||
)
|
||||
|
||||
|
@ -66,6 +67,11 @@ func (m *Module) AccountBlockPOSTHandler(c *gin.Context) {
|
|||
return
|
||||
}
|
||||
|
||||
if _, err := api.NegotiateAccept(c, api.JSONAcceptHeaders...); err != nil {
|
||||
c.JSON(http.StatusNotAcceptable, gin.H{"error": err.Error()})
|
||||
return
|
||||
}
|
||||
|
||||
targetAcctID := c.Param(IDKey)
|
||||
if targetAcctID == "" {
|
||||
c.JSON(http.StatusBadRequest, gin.H{"error": "no account id specified"})
|
||||
|
|
|
@ -22,6 +22,7 @@ import (
|
|||
"net/http"
|
||||
|
||||
"github.com/gin-gonic/gin"
|
||||
"github.com/superseriousbusiness/gotosocial/internal/api"
|
||||
"github.com/superseriousbusiness/gotosocial/internal/api/model"
|
||||
"github.com/superseriousbusiness/gotosocial/internal/oauth"
|
||||
)
|
||||
|
@ -87,6 +88,11 @@ func (m *Module) AccountFollowPOSTHandler(c *gin.Context) {
|
|||
return
|
||||
}
|
||||
|
||||
if _, err := api.NegotiateAccept(c, api.JSONAcceptHeaders...); err != nil {
|
||||
c.JSON(http.StatusNotAcceptable, gin.H{"error": err.Error()})
|
||||
return
|
||||
}
|
||||
|
||||
targetAcctID := c.Param(IDKey)
|
||||
if targetAcctID == "" {
|
||||
c.JSON(http.StatusBadRequest, gin.H{"error": "no account id specified"})
|
||||
|
|
|
@ -22,6 +22,7 @@ import (
|
|||
"net/http"
|
||||
|
||||
"github.com/gin-gonic/gin"
|
||||
"github.com/superseriousbusiness/gotosocial/internal/api"
|
||||
"github.com/superseriousbusiness/gotosocial/internal/oauth"
|
||||
)
|
||||
|
||||
|
@ -68,6 +69,11 @@ func (m *Module) AccountFollowersGETHandler(c *gin.Context) {
|
|||
return
|
||||
}
|
||||
|
||||
if _, err := api.NegotiateAccept(c, api.JSONAcceptHeaders...); err != nil {
|
||||
c.JSON(http.StatusNotAcceptable, gin.H{"error": err.Error()})
|
||||
return
|
||||
}
|
||||
|
||||
targetAcctID := c.Param(IDKey)
|
||||
if targetAcctID == "" {
|
||||
c.JSON(http.StatusBadRequest, gin.H{"error": "no account id specified"})
|
||||
|
|
|
@ -22,6 +22,7 @@ import (
|
|||
"net/http"
|
||||
|
||||
"github.com/gin-gonic/gin"
|
||||
"github.com/superseriousbusiness/gotosocial/internal/api"
|
||||
"github.com/superseriousbusiness/gotosocial/internal/oauth"
|
||||
)
|
||||
|
||||
|
@ -68,6 +69,11 @@ func (m *Module) AccountFollowingGETHandler(c *gin.Context) {
|
|||
return
|
||||
}
|
||||
|
||||
if _, err := api.NegotiateAccept(c, api.JSONAcceptHeaders...); err != nil {
|
||||
c.JSON(http.StatusNotAcceptable, gin.H{"error": err.Error()})
|
||||
return
|
||||
}
|
||||
|
||||
targetAcctID := c.Param(IDKey)
|
||||
if targetAcctID == "" {
|
||||
c.JSON(http.StatusBadRequest, gin.H{"error": "no account id specified"})
|
||||
|
|
|
@ -1,10 +1,12 @@
|
|||
package account
|
||||
|
||||
import (
|
||||
"github.com/sirupsen/logrus"
|
||||
"net/http"
|
||||
|
||||
"github.com/sirupsen/logrus"
|
||||
|
||||
"github.com/gin-gonic/gin"
|
||||
"github.com/superseriousbusiness/gotosocial/internal/api"
|
||||
"github.com/superseriousbusiness/gotosocial/internal/api/model"
|
||||
"github.com/superseriousbusiness/gotosocial/internal/oauth"
|
||||
)
|
||||
|
@ -57,6 +59,11 @@ func (m *Module) AccountRelationshipsGETHandler(c *gin.Context) {
|
|||
return
|
||||
}
|
||||
|
||||
if _, err := api.NegotiateAccept(c, api.JSONAcceptHeaders...); err != nil {
|
||||
c.JSON(http.StatusNotAcceptable, gin.H{"error": err.Error()})
|
||||
return
|
||||
}
|
||||
|
||||
targetAccountIDs := c.QueryArray("id[]")
|
||||
if len(targetAccountIDs) == 0 {
|
||||
// check fallback -- let's be generous and see if maybe it's just set as 'id'?
|
||||
|
|
|
@ -19,11 +19,13 @@
|
|||
package account
|
||||
|
||||
import (
|
||||
"github.com/sirupsen/logrus"
|
||||
"net/http"
|
||||
"strconv"
|
||||
|
||||
"github.com/sirupsen/logrus"
|
||||
|
||||
"github.com/gin-gonic/gin"
|
||||
"github.com/superseriousbusiness/gotosocial/internal/api"
|
||||
"github.com/superseriousbusiness/gotosocial/internal/oauth"
|
||||
)
|
||||
|
||||
|
@ -118,6 +120,11 @@ func (m *Module) AccountStatusesGETHandler(c *gin.Context) {
|
|||
return
|
||||
}
|
||||
|
||||
if _, err := api.NegotiateAccept(c, api.JSONAcceptHeaders...); err != nil {
|
||||
c.JSON(http.StatusNotAcceptable, gin.H{"error": err.Error()})
|
||||
return
|
||||
}
|
||||
|
||||
targetAcctID := c.Param(IDKey)
|
||||
if targetAcctID == "" {
|
||||
l.Debug("no account id specified in query")
|
||||
|
|
|
@ -22,6 +22,7 @@ import (
|
|||
"net/http"
|
||||
|
||||
"github.com/gin-gonic/gin"
|
||||
"github.com/superseriousbusiness/gotosocial/internal/api"
|
||||
"github.com/superseriousbusiness/gotosocial/internal/oauth"
|
||||
)
|
||||
|
||||
|
@ -66,6 +67,11 @@ func (m *Module) AccountUnblockPOSTHandler(c *gin.Context) {
|
|||
return
|
||||
}
|
||||
|
||||
if _, err := api.NegotiateAccept(c, api.JSONAcceptHeaders...); err != nil {
|
||||
c.JSON(http.StatusNotAcceptable, gin.H{"error": err.Error()})
|
||||
return
|
||||
}
|
||||
|
||||
targetAcctID := c.Param(IDKey)
|
||||
if targetAcctID == "" {
|
||||
c.JSON(http.StatusBadRequest, gin.H{"error": "no account id specified"})
|
||||
|
|
|
@ -19,10 +19,12 @@
|
|||
package account
|
||||
|
||||
import (
|
||||
"github.com/sirupsen/logrus"
|
||||
"net/http"
|
||||
|
||||
"github.com/sirupsen/logrus"
|
||||
|
||||
"github.com/gin-gonic/gin"
|
||||
"github.com/superseriousbusiness/gotosocial/internal/api"
|
||||
"github.com/superseriousbusiness/gotosocial/internal/oauth"
|
||||
)
|
||||
|
||||
|
@ -69,6 +71,11 @@ func (m *Module) AccountUnfollowPOSTHandler(c *gin.Context) {
|
|||
return
|
||||
}
|
||||
|
||||
if _, err := api.NegotiateAccept(c, api.JSONAcceptHeaders...); err != nil {
|
||||
c.JSON(http.StatusNotAcceptable, gin.H{"error": err.Error()})
|
||||
return
|
||||
}
|
||||
|
||||
targetAcctID := c.Param(IDKey)
|
||||
if targetAcctID == "" {
|
||||
l.Debug(err)
|
||||
|
|
|
@ -8,6 +8,7 @@ import (
|
|||
|
||||
"github.com/gin-gonic/gin"
|
||||
"github.com/sirupsen/logrus"
|
||||
"github.com/superseriousbusiness/gotosocial/internal/api"
|
||||
"github.com/superseriousbusiness/gotosocial/internal/api/model"
|
||||
"github.com/superseriousbusiness/gotosocial/internal/oauth"
|
||||
)
|
||||
|
@ -110,6 +111,11 @@ func (m *Module) DomainBlocksPOSTHandler(c *gin.Context) {
|
|||
return
|
||||
}
|
||||
|
||||
if _, err := api.NegotiateAccept(c, api.JSONAcceptHeaders...); err != nil {
|
||||
c.JSON(http.StatusNotAcceptable, gin.H{"error": err.Error()})
|
||||
return
|
||||
}
|
||||
|
||||
imp := false
|
||||
importString := c.Query(ImportQueryKey)
|
||||
if importString != "" {
|
||||
|
|
|
@ -5,6 +5,7 @@ import (
|
|||
|
||||
"github.com/gin-gonic/gin"
|
||||
"github.com/sirupsen/logrus"
|
||||
"github.com/superseriousbusiness/gotosocial/internal/api"
|
||||
"github.com/superseriousbusiness/gotosocial/internal/oauth"
|
||||
)
|
||||
|
||||
|
@ -62,6 +63,11 @@ func (m *Module) DomainBlockDELETEHandler(c *gin.Context) {
|
|||
return
|
||||
}
|
||||
|
||||
if _, err := api.NegotiateAccept(c, api.JSONAcceptHeaders...); err != nil {
|
||||
c.JSON(http.StatusNotAcceptable, gin.H{"error": err.Error()})
|
||||
return
|
||||
}
|
||||
|
||||
domainBlockID := c.Param(IDKey)
|
||||
if domainBlockID == "" {
|
||||
c.JSON(http.StatusBadRequest, gin.H{"error": "no domain block id provided"})
|
||||
|
|
|
@ -6,6 +6,7 @@ import (
|
|||
|
||||
"github.com/gin-gonic/gin"
|
||||
"github.com/sirupsen/logrus"
|
||||
"github.com/superseriousbusiness/gotosocial/internal/api"
|
||||
"github.com/superseriousbusiness/gotosocial/internal/oauth"
|
||||
)
|
||||
|
||||
|
@ -63,6 +64,11 @@ func (m *Module) DomainBlockGETHandler(c *gin.Context) {
|
|||
return
|
||||
}
|
||||
|
||||
if _, err := api.NegotiateAccept(c, api.JSONAcceptHeaders...); err != nil {
|
||||
c.JSON(http.StatusNotAcceptable, gin.H{"error": err.Error()})
|
||||
return
|
||||
}
|
||||
|
||||
domainBlockID := c.Param(IDKey)
|
||||
if domainBlockID == "" {
|
||||
c.JSON(http.StatusBadRequest, gin.H{"error": "no domain block id provided"})
|
||||
|
|
|
@ -6,6 +6,7 @@ import (
|
|||
|
||||
"github.com/gin-gonic/gin"
|
||||
"github.com/sirupsen/logrus"
|
||||
"github.com/superseriousbusiness/gotosocial/internal/api"
|
||||
"github.com/superseriousbusiness/gotosocial/internal/oauth"
|
||||
)
|
||||
|
||||
|
@ -69,6 +70,11 @@ func (m *Module) DomainBlocksGETHandler(c *gin.Context) {
|
|||
return
|
||||
}
|
||||
|
||||
if _, err := api.NegotiateAccept(c, api.JSONAcceptHeaders...); err != nil {
|
||||
c.JSON(http.StatusNotAcceptable, gin.H{"error": err.Error()})
|
||||
return
|
||||
}
|
||||
|
||||
export := false
|
||||
exportString := c.Query(ExportQueryKey)
|
||||
if exportString != "" {
|
||||
|
|
|
@ -25,6 +25,7 @@ import (
|
|||
|
||||
"github.com/gin-gonic/gin"
|
||||
"github.com/sirupsen/logrus"
|
||||
"github.com/superseriousbusiness/gotosocial/internal/api"
|
||||
"github.com/superseriousbusiness/gotosocial/internal/api/model"
|
||||
"github.com/superseriousbusiness/gotosocial/internal/media"
|
||||
"github.com/superseriousbusiness/gotosocial/internal/oauth"
|
||||
|
@ -94,6 +95,11 @@ func (m *Module) emojiCreatePOSTHandler(c *gin.Context) {
|
|||
return
|
||||
}
|
||||
|
||||
if _, err := api.NegotiateAccept(c, api.JSONAcceptHeaders...); err != nil {
|
||||
c.JSON(http.StatusNotAcceptable, gin.H{"error": err.Error()})
|
||||
return
|
||||
}
|
||||
|
||||
// extract the media create form from the request context
|
||||
l.Tracef("parsing request form: %+v", c.Request.Form)
|
||||
form := &model.EmojiCreateRequest{}
|
||||
|
|
|
@ -25,6 +25,7 @@ import (
|
|||
"github.com/sirupsen/logrus"
|
||||
|
||||
"github.com/gin-gonic/gin"
|
||||
"github.com/superseriousbusiness/gotosocial/internal/api"
|
||||
"github.com/superseriousbusiness/gotosocial/internal/api/model"
|
||||
"github.com/superseriousbusiness/gotosocial/internal/oauth"
|
||||
)
|
||||
|
@ -81,6 +82,11 @@ func (m *Module) AppsPOSTHandler(c *gin.Context) {
|
|||
return
|
||||
}
|
||||
|
||||
if _, err := api.NegotiateAccept(c, api.JSONAcceptHeaders...); err != nil {
|
||||
c.JSON(http.StatusNotAcceptable, gin.H{"error": err.Error()})
|
||||
return
|
||||
}
|
||||
|
||||
form := &model.ApplicationCreateRequest{}
|
||||
if err := c.ShouldBind(form); err != nil {
|
||||
c.JSON(http.StatusUnprocessableEntity, gin.H{"error": err.Error()})
|
||||
|
|
|
@ -21,14 +21,16 @@ package auth
|
|||
import (
|
||||
"errors"
|
||||
"fmt"
|
||||
"github.com/sirupsen/logrus"
|
||||
"net/http"
|
||||
"net/url"
|
||||
"strings"
|
||||
|
||||
"github.com/sirupsen/logrus"
|
||||
|
||||
"github.com/gin-contrib/sessions"
|
||||
"github.com/gin-gonic/gin"
|
||||
"github.com/google/uuid"
|
||||
"github.com/superseriousbusiness/gotosocial/internal/api"
|
||||
"github.com/superseriousbusiness/gotosocial/internal/api/model"
|
||||
"github.com/superseriousbusiness/gotosocial/internal/db"
|
||||
"github.com/superseriousbusiness/gotosocial/internal/gtsmodel"
|
||||
|
@ -41,6 +43,11 @@ func (m *Module) AuthorizeGETHandler(c *gin.Context) {
|
|||
l := logrus.WithField("func", "AuthorizeGETHandler")
|
||||
s := sessions.Default(c)
|
||||
|
||||
if _, err := api.NegotiateAccept(c, api.HTMLAcceptHeaders...); err != nil {
|
||||
c.JSON(http.StatusNotAcceptable, gin.H{"error": err.Error()})
|
||||
return
|
||||
}
|
||||
|
||||
// UserID will be set in the session by AuthorizePOSTHandler if the caller has already gone through the authentication flow
|
||||
// If it's not set, then we don't know yet who the user is, so we need to redirect them to the sign in page.
|
||||
userID, ok := s.Get(sessionUserID).(string)
|
||||
|
|
|
@ -21,11 +21,13 @@ package auth
|
|||
import (
|
||||
"context"
|
||||
"errors"
|
||||
"github.com/sirupsen/logrus"
|
||||
"net/http"
|
||||
|
||||
"github.com/sirupsen/logrus"
|
||||
|
||||
"github.com/gin-contrib/sessions"
|
||||
"github.com/gin-gonic/gin"
|
||||
"github.com/superseriousbusiness/gotosocial/internal/api"
|
||||
"github.com/superseriousbusiness/gotosocial/internal/db"
|
||||
"github.com/superseriousbusiness/gotosocial/internal/gtsmodel"
|
||||
"golang.org/x/crypto/bcrypt"
|
||||
|
@ -43,6 +45,12 @@ type login struct {
|
|||
func (m *Module) SignInGETHandler(c *gin.Context) {
|
||||
l := logrus.WithField("func", "SignInGETHandler")
|
||||
l.Trace("entering sign in handler")
|
||||
|
||||
if _, err := api.NegotiateAccept(c, api.HTMLAcceptHeaders...); err != nil {
|
||||
c.JSON(http.StatusNotAcceptable, gin.H{"error": err.Error()})
|
||||
return
|
||||
}
|
||||
|
||||
if m.idp != nil {
|
||||
s := sessions.Default(c)
|
||||
|
||||
|
|
|
@ -19,10 +19,12 @@
|
|||
package auth
|
||||
|
||||
import (
|
||||
"github.com/sirupsen/logrus"
|
||||
"net/http"
|
||||
"net/url"
|
||||
|
||||
"github.com/sirupsen/logrus"
|
||||
"github.com/superseriousbusiness/gotosocial/internal/api"
|
||||
|
||||
"github.com/gin-gonic/gin"
|
||||
)
|
||||
|
||||
|
@ -41,6 +43,11 @@ func (m *Module) TokenPOSTHandler(c *gin.Context) {
|
|||
l := logrus.WithField("func", "TokenPOSTHandler")
|
||||
l.Trace("entered TokenPOSTHandler")
|
||||
|
||||
if _, err := api.NegotiateAccept(c, api.JSONAcceptHeaders...); err != nil {
|
||||
c.JSON(http.StatusNotAcceptable, gin.H{"error": err.Error()})
|
||||
return
|
||||
}
|
||||
|
||||
form := &tokenBody{}
|
||||
if err := c.ShouldBind(form); err == nil {
|
||||
c.Request.Form = url.Values{}
|
||||
|
|
|
@ -19,11 +19,13 @@
|
|||
package blocks
|
||||
|
||||
import (
|
||||
"github.com/sirupsen/logrus"
|
||||
"net/http"
|
||||
"strconv"
|
||||
|
||||
"github.com/sirupsen/logrus"
|
||||
|
||||
"github.com/gin-gonic/gin"
|
||||
"github.com/superseriousbusiness/gotosocial/internal/api"
|
||||
"github.com/superseriousbusiness/gotosocial/internal/oauth"
|
||||
)
|
||||
|
||||
|
@ -94,6 +96,11 @@ func (m *Module) BlocksGETHandler(c *gin.Context) {
|
|||
return
|
||||
}
|
||||
|
||||
if _, err := api.NegotiateAccept(c, api.JSONAcceptHeaders...); err != nil {
|
||||
c.JSON(http.StatusNotAcceptable, gin.H{"error": err.Error()})
|
||||
return
|
||||
}
|
||||
|
||||
maxID := ""
|
||||
maxIDString := c.Query(MaxIDKey)
|
||||
if maxIDString != "" {
|
||||
|
|
|
@ -4,9 +4,15 @@ import (
|
|||
"net/http"
|
||||
|
||||
"github.com/gin-gonic/gin"
|
||||
"github.com/superseriousbusiness/gotosocial/internal/api"
|
||||
)
|
||||
|
||||
// EmojisGETHandler returns a list of custom emojis enabled on the instance
|
||||
func (m *Module) EmojisGETHandler(c *gin.Context) {
|
||||
if _, err := api.NegotiateAccept(c, api.JSONAcceptHeaders...); err != nil {
|
||||
c.JSON(http.StatusNotAcceptable, gin.H{"error": err.Error()})
|
||||
return
|
||||
}
|
||||
|
||||
c.JSON(http.StatusOK, []string{})
|
||||
}
|
||||
|
|
|
@ -1,11 +1,13 @@
|
|||
package favourites
|
||||
|
||||
import (
|
||||
"github.com/sirupsen/logrus"
|
||||
"net/http"
|
||||
"strconv"
|
||||
|
||||
"github.com/sirupsen/logrus"
|
||||
|
||||
"github.com/gin-gonic/gin"
|
||||
"github.com/superseriousbusiness/gotosocial/internal/api"
|
||||
"github.com/superseriousbusiness/gotosocial/internal/oauth"
|
||||
)
|
||||
|
||||
|
@ -20,6 +22,11 @@ func (m *Module) FavouritesGETHandler(c *gin.Context) {
|
|||
return
|
||||
}
|
||||
|
||||
if _, err := api.NegotiateAccept(c, api.JSONAcceptHeaders...); err != nil {
|
||||
c.JSON(http.StatusNotAcceptable, gin.H{"error": err.Error()})
|
||||
return
|
||||
}
|
||||
|
||||
maxID := ""
|
||||
maxIDString := c.Query(MaxIDKey)
|
||||
if maxIDString != "" {
|
||||
|
|
|
@ -24,6 +24,7 @@ import (
|
|||
|
||||
"github.com/gin-gonic/gin"
|
||||
"github.com/sirupsen/logrus"
|
||||
"github.com/superseriousbusiness/gotosocial/internal/api"
|
||||
"github.com/superseriousbusiness/gotosocial/internal/api/model"
|
||||
"github.com/superseriousbusiness/gotosocial/internal/oauth"
|
||||
)
|
||||
|
@ -90,14 +91,14 @@ func (m *FileServer) ServeFile(c *gin.Context) {
|
|||
return
|
||||
}
|
||||
|
||||
// TODO: do proper content negotiation here -- if the requester only accepts text/html we should try to serve them *something*
|
||||
// TODO: if the requester only accepts text/html we should try to serve them *something*.
|
||||
// This is mostly needed because when sharing a link to a gts-hosted file on something like mastodon, the masto servers will
|
||||
// attempt to look up the content to provide a preview of the link, and they ask for text/html.
|
||||
if c.NegotiateFormat(content.ContentType) == "" {
|
||||
l.Debugf("couldn't negotiate content for Accept headers %+v: we have content type %s", c.Request.Header.Get("Accepted"), content.ContentType)
|
||||
c.AbortWithStatus(http.StatusNotAcceptable)
|
||||
format, err := api.NegotiateAccept(c, api.Offer(content.ContentType))
|
||||
if err != nil {
|
||||
c.JSON(http.StatusNotAcceptable, gin.H{"error": err.Error()})
|
||||
return
|
||||
}
|
||||
|
||||
c.DataFromReader(http.StatusOK, content.ContentLength, content.ContentType, bytes.NewReader(content.Content), nil)
|
||||
c.DataFromReader(http.StatusOK, content.ContentLength, format, bytes.NewReader(content.Content), nil)
|
||||
}
|
||||
|
|
|
@ -123,6 +123,7 @@ func (suite *ServeFileTestSuite) TestServeOriginalFileSuccessful() {
|
|||
recorder := httptest.NewRecorder()
|
||||
ctx, _ := gin.CreateTestContext(recorder)
|
||||
ctx.Request = httptest.NewRequest(http.MethodGet, targetAttachment.URL, nil)
|
||||
ctx.Request.Header.Set("accept", "*/*")
|
||||
|
||||
// normally the router would populate these params from the path values,
|
||||
// but because we're calling the ServeFile function directly, we need to set them manually.
|
||||
|
|
|
@ -4,9 +4,15 @@ import (
|
|||
"net/http"
|
||||
|
||||
"github.com/gin-gonic/gin"
|
||||
"github.com/superseriousbusiness/gotosocial/internal/api"
|
||||
)
|
||||
|
||||
// FiltersGETHandler returns a list of filters set by/for the authed account
|
||||
func (m *Module) FiltersGETHandler(c *gin.Context) {
|
||||
if _, err := api.NegotiateAccept(c, api.JSONAcceptHeaders...); err != nil {
|
||||
c.JSON(http.StatusNotAcceptable, gin.H{"error": err.Error()})
|
||||
return
|
||||
}
|
||||
|
||||
c.JSON(http.StatusOK, []string{})
|
||||
}
|
||||
|
|
|
@ -19,10 +19,12 @@
|
|||
package followrequest
|
||||
|
||||
import (
|
||||
"github.com/sirupsen/logrus"
|
||||
"net/http"
|
||||
|
||||
"github.com/sirupsen/logrus"
|
||||
|
||||
"github.com/gin-gonic/gin"
|
||||
"github.com/superseriousbusiness/gotosocial/internal/api"
|
||||
"github.com/superseriousbusiness/gotosocial/internal/oauth"
|
||||
)
|
||||
|
||||
|
@ -76,6 +78,11 @@ func (m *Module) FollowRequestAuthorizePOSTHandler(c *gin.Context) {
|
|||
return
|
||||
}
|
||||
|
||||
if _, err := api.NegotiateAccept(c, api.JSONAcceptHeaders...); err != nil {
|
||||
c.JSON(http.StatusNotAcceptable, gin.H{"error": err.Error()})
|
||||
return
|
||||
}
|
||||
|
||||
if authed.User.Disabled || !authed.User.Approved || !authed.Account.SuspendedAt.IsZero() {
|
||||
l.Debugf("couldn't auth: %s", err)
|
||||
c.JSON(http.StatusForbidden, gin.H{"error": "account is disabled, not yet approved, or suspended"})
|
||||
|
|
|
@ -106,6 +106,7 @@ func (suite *FollowRequestStandardTestSuite) newContext(recorder *httptest.Respo
|
|||
if bodyContentType != "" {
|
||||
ctx.Request.Header.Set("Content-Type", bodyContentType)
|
||||
}
|
||||
ctx.Request.Header.Set("accept", "application/json")
|
||||
|
||||
return ctx
|
||||
}
|
||||
|
|
|
@ -19,10 +19,12 @@
|
|||
package followrequest
|
||||
|
||||
import (
|
||||
"github.com/sirupsen/logrus"
|
||||
"net/http"
|
||||
|
||||
"github.com/sirupsen/logrus"
|
||||
|
||||
"github.com/gin-gonic/gin"
|
||||
"github.com/superseriousbusiness/gotosocial/internal/api"
|
||||
"github.com/superseriousbusiness/gotosocial/internal/oauth"
|
||||
)
|
||||
|
||||
|
@ -83,6 +85,11 @@ func (m *Module) FollowRequestGETHandler(c *gin.Context) {
|
|||
return
|
||||
}
|
||||
|
||||
if _, err := api.NegotiateAccept(c, api.JSONAcceptHeaders...); err != nil {
|
||||
c.JSON(http.StatusNotAcceptable, gin.H{"error": err.Error()})
|
||||
return
|
||||
}
|
||||
|
||||
if authed.User.Disabled || !authed.User.Approved || !authed.Account.SuspendedAt.IsZero() {
|
||||
l.Debugf("couldn't auth: %s", err)
|
||||
c.JSON(http.StatusForbidden, gin.H{"error": "account is disabled, not yet approved, or suspended"})
|
||||
|
|
|
@ -23,6 +23,7 @@ import (
|
|||
|
||||
"github.com/gin-gonic/gin"
|
||||
"github.com/sirupsen/logrus"
|
||||
"github.com/superseriousbusiness/gotosocial/internal/api"
|
||||
"github.com/superseriousbusiness/gotosocial/internal/oauth"
|
||||
)
|
||||
|
||||
|
@ -74,6 +75,11 @@ func (m *Module) FollowRequestRejectPOSTHandler(c *gin.Context) {
|
|||
return
|
||||
}
|
||||
|
||||
if _, err := api.NegotiateAccept(c, api.JSONAcceptHeaders...); err != nil {
|
||||
c.JSON(http.StatusNotAcceptable, gin.H{"error": err.Error()})
|
||||
return
|
||||
}
|
||||
|
||||
if authed.User.Disabled || !authed.User.Approved || !authed.Account.SuspendedAt.IsZero() {
|
||||
l.Debugf("couldn't auth: %s", err)
|
||||
c.JSON(http.StatusForbidden, gin.H{"error": "account is disabled, not yet approved, or suspended"})
|
||||
|
|
|
@ -5,6 +5,7 @@ import (
|
|||
|
||||
"github.com/sirupsen/logrus"
|
||||
"github.com/spf13/viper"
|
||||
"github.com/superseriousbusiness/gotosocial/internal/api"
|
||||
"github.com/superseriousbusiness/gotosocial/internal/config"
|
||||
|
||||
"github.com/gin-gonic/gin"
|
||||
|
@ -35,6 +36,11 @@ import (
|
|||
func (m *Module) InstanceInformationGETHandler(c *gin.Context) {
|
||||
l := logrus.WithField("func", "InstanceInformationGETHandler")
|
||||
|
||||
if _, err := api.NegotiateAccept(c, api.JSONAcceptHeaders...); err != nil {
|
||||
c.JSON(http.StatusNotAcceptable, gin.H{"error": err.Error()})
|
||||
return
|
||||
}
|
||||
|
||||
host := viper.GetString(config.Keys.Host)
|
||||
|
||||
instance, err := m.processor.InstanceGet(c.Request.Context(), host)
|
||||
|
|
|
@ -1,10 +1,12 @@
|
|||
package instance
|
||||
|
||||
import (
|
||||
"github.com/sirupsen/logrus"
|
||||
"net/http"
|
||||
|
||||
"github.com/sirupsen/logrus"
|
||||
|
||||
"github.com/gin-gonic/gin"
|
||||
"github.com/superseriousbusiness/gotosocial/internal/api"
|
||||
"github.com/superseriousbusiness/gotosocial/internal/api/model"
|
||||
"github.com/superseriousbusiness/gotosocial/internal/oauth"
|
||||
)
|
||||
|
@ -93,6 +95,11 @@ func (m *Module) InstanceUpdatePATCHHandler(c *gin.Context) {
|
|||
return
|
||||
}
|
||||
|
||||
if _, err := api.NegotiateAccept(c, api.JSONAcceptHeaders...); err != nil {
|
||||
c.JSON(http.StatusNotAcceptable, gin.H{"error": err.Error()})
|
||||
return
|
||||
}
|
||||
|
||||
// only admins can update instance settings
|
||||
if !authed.User.Admin {
|
||||
l.Debug("user is not an admin so cannot update instance settings")
|
||||
|
|
|
@ -4,9 +4,15 @@ import (
|
|||
"net/http"
|
||||
|
||||
"github.com/gin-gonic/gin"
|
||||
"github.com/superseriousbusiness/gotosocial/internal/api"
|
||||
)
|
||||
|
||||
// ListsGETHandler returns a list of lists created by/for the authed account
|
||||
func (m *Module) ListsGETHandler(c *gin.Context) {
|
||||
if _, err := api.NegotiateAccept(c, api.JSONAcceptHeaders...); err != nil {
|
||||
c.JSON(http.StatusNotAcceptable, gin.H{"error": err.Error()})
|
||||
return
|
||||
}
|
||||
|
||||
c.JSON(http.StatusOK, []string{})
|
||||
}
|
||||
|
|
|
@ -27,6 +27,7 @@ import (
|
|||
"github.com/spf13/viper"
|
||||
|
||||
"github.com/gin-gonic/gin"
|
||||
"github.com/superseriousbusiness/gotosocial/internal/api"
|
||||
"github.com/superseriousbusiness/gotosocial/internal/api/model"
|
||||
"github.com/superseriousbusiness/gotosocial/internal/config"
|
||||
"github.com/superseriousbusiness/gotosocial/internal/oauth"
|
||||
|
@ -93,6 +94,11 @@ func (m *Module) MediaCreatePOSTHandler(c *gin.Context) {
|
|||
return
|
||||
}
|
||||
|
||||
if _, err := api.NegotiateAccept(c, api.JSONAcceptHeaders...); err != nil {
|
||||
c.JSON(http.StatusNotAcceptable, gin.H{"error": err.Error()})
|
||||
return
|
||||
}
|
||||
|
||||
// extract the media create form from the request context
|
||||
l.Tracef("parsing request form: %s", c.Request.Form)
|
||||
form := &model.AttachmentRequest{}
|
||||
|
|
|
@ -149,6 +149,7 @@ func (suite *MediaCreateTestSuite) TestStatusCreatePOSTImageHandlerSuccessful()
|
|||
}
|
||||
ctx.Request = httptest.NewRequest(http.MethodPost, fmt.Sprintf("http://localhost:8080/%s", mediamodule.BasePath), bytes.NewReader(buf.Bytes())) // the endpoint we're hitting
|
||||
ctx.Request.Header.Set("Content-Type", w.FormDataContentType())
|
||||
ctx.Request.Header.Set("accept", "application/json")
|
||||
|
||||
// do the actual request
|
||||
suite.mediaModule.MediaCreatePOSTHandler(ctx)
|
||||
|
|
|
@ -19,10 +19,12 @@
|
|||
package media
|
||||
|
||||
import (
|
||||
"github.com/sirupsen/logrus"
|
||||
"net/http"
|
||||
|
||||
"github.com/sirupsen/logrus"
|
||||
|
||||
"github.com/gin-gonic/gin"
|
||||
"github.com/superseriousbusiness/gotosocial/internal/api"
|
||||
"github.com/superseriousbusiness/gotosocial/internal/oauth"
|
||||
)
|
||||
|
||||
|
@ -70,6 +72,11 @@ func (m *Module) MediaGETHandler(c *gin.Context) {
|
|||
return
|
||||
}
|
||||
|
||||
if _, err := api.NegotiateAccept(c, api.JSONAcceptHeaders...); err != nil {
|
||||
c.JSON(http.StatusNotAcceptable, gin.H{"error": err.Error()})
|
||||
return
|
||||
}
|
||||
|
||||
attachmentID := c.Param(IDKey)
|
||||
if attachmentID == "" {
|
||||
c.JSON(http.StatusBadRequest, gin.H{"error": "no attachment ID given in request"})
|
||||
|
|
|
@ -27,6 +27,7 @@ import (
|
|||
"github.com/spf13/viper"
|
||||
|
||||
"github.com/gin-gonic/gin"
|
||||
"github.com/superseriousbusiness/gotosocial/internal/api"
|
||||
"github.com/superseriousbusiness/gotosocial/internal/api/model"
|
||||
"github.com/superseriousbusiness/gotosocial/internal/config"
|
||||
"github.com/superseriousbusiness/gotosocial/internal/oauth"
|
||||
|
@ -102,6 +103,11 @@ func (m *Module) MediaPUTHandler(c *gin.Context) {
|
|||
return
|
||||
}
|
||||
|
||||
if _, err := api.NegotiateAccept(c, api.JSONAcceptHeaders...); err != nil {
|
||||
c.JSON(http.StatusNotAcceptable, gin.H{"error": err.Error()})
|
||||
return
|
||||
}
|
||||
|
||||
attachmentID := c.Param(IDKey)
|
||||
if attachmentID == "" {
|
||||
c.JSON(http.StatusBadRequest, gin.H{"error": "no attachment ID given in request"})
|
||||
|
|
|
@ -24,6 +24,7 @@ import (
|
|||
|
||||
"github.com/gin-gonic/gin"
|
||||
"github.com/sirupsen/logrus"
|
||||
"github.com/superseriousbusiness/gotosocial/internal/api"
|
||||
"github.com/superseriousbusiness/gotosocial/internal/oauth"
|
||||
)
|
||||
|
||||
|
@ -44,6 +45,11 @@ func (m *Module) NotificationsGETHandler(c *gin.Context) {
|
|||
return
|
||||
}
|
||||
|
||||
if _, err := api.NegotiateAccept(c, api.JSONAcceptHeaders...); err != nil {
|
||||
c.JSON(http.StatusNotAcceptable, gin.H{"error": err.Error()})
|
||||
return
|
||||
}
|
||||
|
||||
limit := 20
|
||||
limitString := c.Query(LimitKey)
|
||||
if limitString != "" {
|
||||
|
|
|
@ -25,6 +25,7 @@ import (
|
|||
|
||||
"github.com/gin-gonic/gin"
|
||||
"github.com/sirupsen/logrus"
|
||||
"github.com/superseriousbusiness/gotosocial/internal/api"
|
||||
"github.com/superseriousbusiness/gotosocial/internal/api/model"
|
||||
"github.com/superseriousbusiness/gotosocial/internal/oauth"
|
||||
)
|
||||
|
@ -71,6 +72,11 @@ func (m *Module) SearchGETHandler(c *gin.Context) {
|
|||
return
|
||||
}
|
||||
|
||||
if _, err := api.NegotiateAccept(c, api.JSONAcceptHeaders...); err != nil {
|
||||
c.JSON(http.StatusNotAcceptable, gin.H{"error": err.Error()})
|
||||
return
|
||||
}
|
||||
|
||||
accountID := c.Query(AccountIDKey)
|
||||
maxID := c.Query(MaxIDKey)
|
||||
minID := c.Query(MinIDKey)
|
||||
|
|
|
@ -23,6 +23,7 @@ import (
|
|||
|
||||
"github.com/gin-gonic/gin"
|
||||
"github.com/sirupsen/logrus"
|
||||
"github.com/superseriousbusiness/gotosocial/internal/api"
|
||||
"github.com/superseriousbusiness/gotosocial/internal/oauth"
|
||||
)
|
||||
|
||||
|
@ -74,13 +75,18 @@ func (m *Module) StatusBoostPOSTHandler(c *gin.Context) {
|
|||
})
|
||||
l.Debugf("entering function")
|
||||
|
||||
authed, err := oauth.Authed(c, true, false, true, true) // we don't really need an app here but we want everything else
|
||||
authed, err := oauth.Authed(c, true, true, true, true)
|
||||
if err != nil {
|
||||
l.Debug("not authed so can't boost status")
|
||||
c.JSON(http.StatusUnauthorized, gin.H{"error": "not authorized"})
|
||||
return
|
||||
}
|
||||
|
||||
if _, err := api.NegotiateAccept(c, api.JSONAcceptHeaders...); err != nil {
|
||||
c.JSON(http.StatusNotAcceptable, gin.H{"error": err.Error()})
|
||||
return
|
||||
}
|
||||
|
||||
targetStatusID := c.Param(IDKey)
|
||||
if targetStatusID == "" {
|
||||
c.JSON(http.StatusBadRequest, gin.H{"error": "no status id provided"})
|
||||
|
|
|
@ -51,6 +51,7 @@ func (suite *StatusBoostTestSuite) TestPostBoost() {
|
|||
ctx.Set(oauth.SessionAuthorizedUser, suite.testUsers["local_account_1"])
|
||||
ctx.Set(oauth.SessionAuthorizedAccount, suite.testAccounts["local_account_1"])
|
||||
ctx.Request = httptest.NewRequest(http.MethodPost, fmt.Sprintf("http://localhost:8080%s", strings.Replace(status.ReblogPath, ":id", targetStatus.ID, 1)), nil) // the endpoint we're hitting
|
||||
ctx.Request.Header.Set("accept", "application/json")
|
||||
|
||||
// normally the router would populate these params from the path values,
|
||||
// but because we're calling the function directly, we need to set them manually.
|
||||
|
@ -117,6 +118,7 @@ func (suite *StatusBoostTestSuite) TestPostUnboostable() {
|
|||
ctx.Set(oauth.SessionAuthorizedUser, suite.testUsers["local_account_1"])
|
||||
ctx.Set(oauth.SessionAuthorizedAccount, suite.testAccounts["local_account_1"])
|
||||
ctx.Request = httptest.NewRequest(http.MethodPost, fmt.Sprintf("http://localhost:8080%s", strings.Replace(status.ReblogPath, ":id", targetStatus.ID, 1)), nil) // the endpoint we're hitting
|
||||
ctx.Request.Header.Set("accept", "application/json")
|
||||
|
||||
// normally the router would populate these params from the path values,
|
||||
// but because we're calling the function directly, we need to set them manually.
|
||||
|
@ -155,6 +157,7 @@ func (suite *StatusBoostTestSuite) TestPostNotVisible() {
|
|||
ctx.Set(oauth.SessionAuthorizedUser, suite.testUsers["local_account_2"])
|
||||
ctx.Set(oauth.SessionAuthorizedAccount, suite.testAccounts["local_account_2"])
|
||||
ctx.Request = httptest.NewRequest(http.MethodPost, fmt.Sprintf("http://localhost:8080%s", strings.Replace(status.ReblogPath, ":id", targetStatus.ID, 1)), nil) // the endpoint we're hitting
|
||||
ctx.Request.Header.Set("accept", "application/json")
|
||||
|
||||
// normally the router would populate these params from the path values,
|
||||
// but because we're calling the function directly, we need to set them manually.
|
||||
|
|
|
@ -23,6 +23,7 @@ import (
|
|||
|
||||
"github.com/gin-gonic/gin"
|
||||
"github.com/sirupsen/logrus"
|
||||
"github.com/superseriousbusiness/gotosocial/internal/api"
|
||||
"github.com/superseriousbusiness/gotosocial/internal/oauth"
|
||||
)
|
||||
|
||||
|
@ -80,6 +81,11 @@ func (m *Module) StatusContextGETHandler(c *gin.Context) {
|
|||
return
|
||||
}
|
||||
|
||||
if _, err := api.NegotiateAccept(c, api.JSONAcceptHeaders...); err != nil {
|
||||
c.JSON(http.StatusNotAcceptable, gin.H{"error": err.Error()})
|
||||
return
|
||||
}
|
||||
|
||||
targetStatusID := c.Param(IDKey)
|
||||
if targetStatusID == "" {
|
||||
c.JSON(http.StatusBadRequest, gin.H{"error": "no status id provided"})
|
||||
|
|
|
@ -27,6 +27,7 @@ import (
|
|||
"github.com/spf13/viper"
|
||||
|
||||
"github.com/gin-gonic/gin"
|
||||
"github.com/superseriousbusiness/gotosocial/internal/api"
|
||||
"github.com/superseriousbusiness/gotosocial/internal/api/model"
|
||||
"github.com/superseriousbusiness/gotosocial/internal/config"
|
||||
"github.com/superseriousbusiness/gotosocial/internal/oauth"
|
||||
|
@ -71,13 +72,18 @@ import (
|
|||
// description: internal error
|
||||
func (m *Module) StatusCreatePOSTHandler(c *gin.Context) {
|
||||
l := logrus.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)
|
||||
if err != nil {
|
||||
l.Debugf("couldn't auth: %s", err)
|
||||
c.JSON(http.StatusForbidden, gin.H{"error": err.Error()})
|
||||
return
|
||||
}
|
||||
|
||||
if _, err := api.NegotiateAccept(c, api.JSONAcceptHeaders...); err != nil {
|
||||
c.JSON(http.StatusNotAcceptable, gin.H{"error": err.Error()})
|
||||
return
|
||||
}
|
||||
|
||||
// First check this user/account is permitted to post new statuses.
|
||||
// There's no point continuing otherwise.
|
||||
if authed.User.Disabled || !authed.User.Approved || !authed.Account.SuspendedAt.IsZero() {
|
||||
|
|
|
@ -65,6 +65,7 @@ func (suite *StatusCreateTestSuite) TestPostNewStatus() {
|
|||
ctx.Set(oauth.SessionAuthorizedUser, suite.testUsers["local_account_1"])
|
||||
ctx.Set(oauth.SessionAuthorizedAccount, suite.testAccounts["local_account_1"])
|
||||
ctx.Request = httptest.NewRequest(http.MethodPost, fmt.Sprintf("http://localhost:8080/%s", status.BasePath), nil) // the endpoint we're hitting
|
||||
ctx.Request.Header.Set("accept", "application/json")
|
||||
ctx.Request.Form = url.Values{
|
||||
"status": {"this is a brand new status! #helloworld"},
|
||||
"spoiler_text": {"hello hello"},
|
||||
|
@ -119,6 +120,7 @@ func (suite *StatusCreateTestSuite) TestPostAnotherNewStatus() {
|
|||
ctx.Set(oauth.SessionAuthorizedUser, suite.testUsers["local_account_1"])
|
||||
ctx.Set(oauth.SessionAuthorizedAccount, suite.testAccounts["local_account_1"])
|
||||
ctx.Request = httptest.NewRequest(http.MethodPost, fmt.Sprintf("http://localhost:8080/%s", status.BasePath), nil) // the endpoint we're hitting
|
||||
ctx.Request.Header.Set("accept", "application/json")
|
||||
ctx.Request.Form = url.Values{
|
||||
"status": {statusWithLinksAndTags},
|
||||
}
|
||||
|
@ -154,6 +156,7 @@ func (suite *StatusCreateTestSuite) TestPostNewStatusWithEmoji() {
|
|||
ctx.Set(oauth.SessionAuthorizedUser, suite.testUsers["local_account_1"])
|
||||
ctx.Set(oauth.SessionAuthorizedAccount, suite.testAccounts["local_account_1"])
|
||||
ctx.Request = httptest.NewRequest(http.MethodPost, fmt.Sprintf("http://localhost:8080/%s", status.BasePath), nil) // the endpoint we're hitting
|
||||
ctx.Request.Header.Set("accept", "application/json")
|
||||
ctx.Request.Form = url.Values{
|
||||
"status": {"here is a rainbow emoji a few times! :rainbow: :rainbow: :rainbow: \n here's an emoji that isn't in the db: :test_emoji: "},
|
||||
}
|
||||
|
@ -195,6 +198,7 @@ func (suite *StatusCreateTestSuite) TestReplyToNonexistentStatus() {
|
|||
ctx.Set(oauth.SessionAuthorizedUser, suite.testUsers["local_account_1"])
|
||||
ctx.Set(oauth.SessionAuthorizedAccount, suite.testAccounts["local_account_1"])
|
||||
ctx.Request = httptest.NewRequest(http.MethodPost, fmt.Sprintf("http://localhost:8080/%s", status.BasePath), nil) // the endpoint we're hitting
|
||||
ctx.Request.Header.Set("accept", "application/json")
|
||||
ctx.Request.Form = url.Values{
|
||||
"status": {"this is a reply to a status that doesn't exist"},
|
||||
"spoiler_text": {"don't open cuz it won't work"},
|
||||
|
@ -226,6 +230,7 @@ func (suite *StatusCreateTestSuite) TestReplyToLocalStatus() {
|
|||
ctx.Set(oauth.SessionAuthorizedUser, suite.testUsers["local_account_1"])
|
||||
ctx.Set(oauth.SessionAuthorizedAccount, suite.testAccounts["local_account_1"])
|
||||
ctx.Request = httptest.NewRequest(http.MethodPost, fmt.Sprintf("http://localhost:8080/%s", status.BasePath), nil) // the endpoint we're hitting
|
||||
ctx.Request.Header.Set("accept", "application/json")
|
||||
ctx.Request.Form = url.Values{
|
||||
"status": {fmt.Sprintf("hello @%s this reply should work!", testrig.NewTestAccounts()["local_account_2"].Username)},
|
||||
"in_reply_to_id": {testrig.NewTestStatuses()["local_account_2_status_1"].ID},
|
||||
|
@ -268,6 +273,7 @@ func (suite *StatusCreateTestSuite) TestAttachNewMediaSuccess() {
|
|||
ctx.Set(oauth.SessionAuthorizedUser, suite.testUsers["local_account_1"])
|
||||
ctx.Set(oauth.SessionAuthorizedAccount, suite.testAccounts["local_account_1"])
|
||||
ctx.Request = httptest.NewRequest(http.MethodPost, fmt.Sprintf("http://localhost:8080/%s", status.BasePath), nil) // the endpoint we're hitting
|
||||
ctx.Request.Header.Set("accept", "application/json")
|
||||
ctx.Request.Form = url.Values{
|
||||
"status": {"here's an image attachment"},
|
||||
"media_ids": {attachment.ID},
|
||||
|
|
|
@ -23,6 +23,7 @@ import (
|
|||
|
||||
"github.com/gin-gonic/gin"
|
||||
"github.com/sirupsen/logrus"
|
||||
"github.com/superseriousbusiness/gotosocial/internal/api"
|
||||
"github.com/superseriousbusiness/gotosocial/internal/oauth"
|
||||
)
|
||||
|
||||
|
@ -73,13 +74,18 @@ func (m *Module) StatusDELETEHandler(c *gin.Context) {
|
|||
})
|
||||
l.Debugf("entering function")
|
||||
|
||||
authed, err := oauth.Authed(c, true, false, true, true) // we don't really need an app here but we want everything else
|
||||
authed, err := oauth.Authed(c, true, true, true, true)
|
||||
if err != nil {
|
||||
l.Debug("not authed so can't delete status")
|
||||
c.JSON(http.StatusUnauthorized, gin.H{"error": "not authorized"})
|
||||
return
|
||||
}
|
||||
|
||||
if _, err := api.NegotiateAccept(c, api.JSONAcceptHeaders...); err != nil {
|
||||
c.JSON(http.StatusNotAcceptable, gin.H{"error": err.Error()})
|
||||
return
|
||||
}
|
||||
|
||||
targetStatusID := c.Param(IDKey)
|
||||
if targetStatusID == "" {
|
||||
c.JSON(http.StatusBadRequest, gin.H{"error": "no status id provided"})
|
||||
|
|
|
@ -23,6 +23,7 @@ import (
|
|||
|
||||
"github.com/gin-gonic/gin"
|
||||
"github.com/sirupsen/logrus"
|
||||
"github.com/superseriousbusiness/gotosocial/internal/api"
|
||||
"github.com/superseriousbusiness/gotosocial/internal/oauth"
|
||||
)
|
||||
|
||||
|
@ -70,13 +71,18 @@ func (m *Module) StatusFavePOSTHandler(c *gin.Context) {
|
|||
})
|
||||
l.Debugf("entering function")
|
||||
|
||||
authed, err := oauth.Authed(c, true, false, true, true) // we don't really need an app here but we want everything else
|
||||
authed, err := oauth.Authed(c, true, true, true, true)
|
||||
if err != nil {
|
||||
l.Debug("not authed so can't fave status")
|
||||
c.JSON(http.StatusUnauthorized, gin.H{"error": "not authorized"})
|
||||
return
|
||||
}
|
||||
|
||||
if _, err := api.NegotiateAccept(c, api.JSONAcceptHeaders...); err != nil {
|
||||
c.JSON(http.StatusNotAcceptable, gin.H{"error": err.Error()})
|
||||
return
|
||||
}
|
||||
|
||||
targetStatusID := c.Param(IDKey)
|
||||
if targetStatusID == "" {
|
||||
c.JSON(http.StatusBadRequest, gin.H{"error": "no status id provided"})
|
||||
|
|
|
@ -55,6 +55,7 @@ func (suite *StatusFaveTestSuite) TestPostFave() {
|
|||
ctx.Set(oauth.SessionAuthorizedUser, suite.testUsers["local_account_1"])
|
||||
ctx.Set(oauth.SessionAuthorizedAccount, suite.testAccounts["local_account_1"])
|
||||
ctx.Request = httptest.NewRequest(http.MethodPost, fmt.Sprintf("http://localhost:8080%s", strings.Replace(status.FavouritePath, ":id", targetStatus.ID, 1)), nil) // the endpoint we're hitting
|
||||
ctx.Request.Header.Set("accept", "application/json")
|
||||
|
||||
// normally the router would populate these params from the path values,
|
||||
// but because we're calling the function directly, we need to set them manually.
|
||||
|
@ -103,6 +104,7 @@ func (suite *StatusFaveTestSuite) TestPostUnfaveable() {
|
|||
ctx.Set(oauth.SessionAuthorizedUser, suite.testUsers["local_account_1"])
|
||||
ctx.Set(oauth.SessionAuthorizedAccount, suite.testAccounts["local_account_1"])
|
||||
ctx.Request = httptest.NewRequest(http.MethodPost, fmt.Sprintf("http://localhost:8080%s", strings.Replace(status.FavouritePath, ":id", targetStatus.ID, 1)), nil) // the endpoint we're hitting
|
||||
ctx.Request.Header.Set("accept", "application/json")
|
||||
|
||||
// normally the router would populate these params from the path values,
|
||||
// but because we're calling the function directly, we need to set them manually.
|
||||
|
|
|
@ -23,6 +23,7 @@ import (
|
|||
|
||||
"github.com/gin-gonic/gin"
|
||||
"github.com/sirupsen/logrus"
|
||||
"github.com/superseriousbusiness/gotosocial/internal/api"
|
||||
"github.com/superseriousbusiness/gotosocial/internal/oauth"
|
||||
)
|
||||
|
||||
|
@ -71,13 +72,18 @@ func (m *Module) StatusFavedByGETHandler(c *gin.Context) {
|
|||
})
|
||||
l.Debugf("entering function")
|
||||
|
||||
authed, err := oauth.Authed(c, false, false, false, false) // we don't really need an app here but we want everything else
|
||||
authed, err := oauth.Authed(c, true, true, true, true) // we don't really need an app here but we want everything else
|
||||
if err != nil {
|
||||
l.Errorf("error authing status faved by request: %s", err)
|
||||
c.JSON(http.StatusBadRequest, gin.H{"error": "not authed"})
|
||||
return
|
||||
}
|
||||
|
||||
if _, err := api.NegotiateAccept(c, api.JSONAcceptHeaders...); err != nil {
|
||||
c.JSON(http.StatusNotAcceptable, gin.H{"error": err.Error()})
|
||||
return
|
||||
}
|
||||
|
||||
targetStatusID := c.Param(IDKey)
|
||||
if targetStatusID == "" {
|
||||
c.JSON(http.StatusBadRequest, gin.H{"error": "no status id provided"})
|
||||
|
|
|
@ -53,6 +53,7 @@ func (suite *StatusFavedByTestSuite) TestGetFavedBy() {
|
|||
ctx.Set(oauth.SessionAuthorizedUser, suite.testUsers["local_account_2"])
|
||||
ctx.Set(oauth.SessionAuthorizedAccount, suite.testAccounts["local_account_2"])
|
||||
ctx.Request = httptest.NewRequest(http.MethodPost, fmt.Sprintf("http://localhost:8080%s", strings.Replace(status.FavouritedPath, ":id", targetStatus.ID, 1)), nil) // the endpoint we're hitting
|
||||
ctx.Request.Header.Set("accept", "application/json")
|
||||
|
||||
// normally the router would populate these params from the path values,
|
||||
// but because we're calling the function directly, we need to set them manually.
|
||||
|
|
|
@ -23,6 +23,7 @@ import (
|
|||
|
||||
"github.com/gin-gonic/gin"
|
||||
"github.com/sirupsen/logrus"
|
||||
"github.com/superseriousbusiness/gotosocial/internal/api"
|
||||
"github.com/superseriousbusiness/gotosocial/internal/oauth"
|
||||
)
|
||||
|
||||
|
@ -70,13 +71,18 @@ func (m *Module) StatusGETHandler(c *gin.Context) {
|
|||
})
|
||||
l.Debugf("entering function")
|
||||
|
||||
authed, err := oauth.Authed(c, false, false, false, false) // we don't really need an app here but we want everything else
|
||||
authed, err := oauth.Authed(c, false, false, false, false)
|
||||
if err != nil {
|
||||
l.Errorf("error authing status faved by request: %s", err)
|
||||
c.JSON(http.StatusBadRequest, gin.H{"error": "not authed"})
|
||||
return
|
||||
}
|
||||
|
||||
if _, err := api.NegotiateAccept(c, api.JSONAcceptHeaders...); err != nil {
|
||||
c.JSON(http.StatusNotAcceptable, gin.H{"error": err.Error()})
|
||||
return
|
||||
}
|
||||
|
||||
targetStatusID := c.Param(IDKey)
|
||||
if targetStatusID == "" {
|
||||
c.JSON(http.StatusBadRequest, gin.H{"error": "no status id provided"})
|
||||
|
|
|
@ -23,6 +23,7 @@ import (
|
|||
|
||||
"github.com/gin-gonic/gin"
|
||||
"github.com/sirupsen/logrus"
|
||||
"github.com/superseriousbusiness/gotosocial/internal/api"
|
||||
"github.com/superseriousbusiness/gotosocial/internal/oauth"
|
||||
)
|
||||
|
||||
|
@ -71,13 +72,18 @@ func (m *Module) StatusUnboostPOSTHandler(c *gin.Context) {
|
|||
})
|
||||
l.Debugf("entering function")
|
||||
|
||||
authed, err := oauth.Authed(c, true, false, true, true) // we don't really need an app here but we want everything else
|
||||
authed, err := oauth.Authed(c, true, true, true, true)
|
||||
if err != nil {
|
||||
l.Debug("not authed so can't unboost status")
|
||||
c.JSON(http.StatusUnauthorized, gin.H{"error": "not authorized"})
|
||||
return
|
||||
}
|
||||
|
||||
if _, err := api.NegotiateAccept(c, api.JSONAcceptHeaders...); err != nil {
|
||||
c.JSON(http.StatusNotAcceptable, gin.H{"error": err.Error()})
|
||||
return
|
||||
}
|
||||
|
||||
targetStatusID := c.Param(IDKey)
|
||||
if targetStatusID == "" {
|
||||
c.JSON(http.StatusBadRequest, gin.H{"error": "no status id provided"})
|
||||
|
|
|
@ -23,6 +23,7 @@ import (
|
|||
|
||||
"github.com/gin-gonic/gin"
|
||||
"github.com/sirupsen/logrus"
|
||||
"github.com/superseriousbusiness/gotosocial/internal/api"
|
||||
"github.com/superseriousbusiness/gotosocial/internal/oauth"
|
||||
)
|
||||
|
||||
|
@ -70,13 +71,18 @@ func (m *Module) StatusUnfavePOSTHandler(c *gin.Context) {
|
|||
})
|
||||
l.Debugf("entering function")
|
||||
|
||||
authed, err := oauth.Authed(c, true, false, true, true) // we don't really need an app here but we want everything else
|
||||
authed, err := oauth.Authed(c, true, true, true, true)
|
||||
if err != nil {
|
||||
l.Debug("not authed so can't unfave status")
|
||||
c.JSON(http.StatusUnauthorized, gin.H{"error": "not authorized"})
|
||||
return
|
||||
}
|
||||
|
||||
if _, err := api.NegotiateAccept(c, api.JSONAcceptHeaders...); err != nil {
|
||||
c.JSON(http.StatusNotAcceptable, gin.H{"error": err.Error()})
|
||||
return
|
||||
}
|
||||
|
||||
targetStatusID := c.Param(IDKey)
|
||||
if targetStatusID == "" {
|
||||
c.JSON(http.StatusBadRequest, gin.H{"error": "no status id provided"})
|
||||
|
|
|
@ -56,6 +56,7 @@ func (suite *StatusUnfaveTestSuite) TestPostUnfave() {
|
|||
ctx.Set(oauth.SessionAuthorizedUser, suite.testUsers["local_account_1"])
|
||||
ctx.Set(oauth.SessionAuthorizedAccount, suite.testAccounts["local_account_1"])
|
||||
ctx.Request = httptest.NewRequest(http.MethodPost, fmt.Sprintf("http://localhost:8080%s", strings.Replace(status.UnfavouritePath, ":id", targetStatus.ID, 1)), nil) // the endpoint we're hitting
|
||||
ctx.Request.Header.Set("accept", "application/json")
|
||||
|
||||
// normally the router would populate these params from the path values,
|
||||
// but because we're calling the function directly, we need to set them manually.
|
||||
|
@ -105,6 +106,7 @@ func (suite *StatusUnfaveTestSuite) TestPostAlreadyNotFaved() {
|
|||
ctx.Set(oauth.SessionAuthorizedUser, suite.testUsers["local_account_1"])
|
||||
ctx.Set(oauth.SessionAuthorizedAccount, suite.testAccounts["local_account_1"])
|
||||
ctx.Request = httptest.NewRequest(http.MethodPost, fmt.Sprintf("http://localhost:8080%s", strings.Replace(status.UnfavouritePath, ":id", targetStatus.ID, 1)), nil) // the endpoint we're hitting
|
||||
ctx.Request.Header.Set("accept", "application/json")
|
||||
|
||||
// normally the router would populate these params from the path values,
|
||||
// but because we're calling the function directly, we need to set them manually.
|
||||
|
|
|
@ -19,11 +19,13 @@
|
|||
package timeline
|
||||
|
||||
import (
|
||||
"github.com/sirupsen/logrus"
|
||||
"net/http"
|
||||
"strconv"
|
||||
|
||||
"github.com/sirupsen/logrus"
|
||||
|
||||
"github.com/gin-gonic/gin"
|
||||
"github.com/superseriousbusiness/gotosocial/internal/api"
|
||||
"github.com/superseriousbusiness/gotosocial/internal/oauth"
|
||||
)
|
||||
|
||||
|
@ -112,6 +114,11 @@ func (m *Module) HomeTimelineGETHandler(c *gin.Context) {
|
|||
return
|
||||
}
|
||||
|
||||
if _, err := api.NegotiateAccept(c, api.JSONAcceptHeaders...); err != nil {
|
||||
c.JSON(http.StatusNotAcceptable, gin.H{"error": err.Error()})
|
||||
return
|
||||
}
|
||||
|
||||
maxID := ""
|
||||
maxIDString := c.Query(MaxIDKey)
|
||||
if maxIDString != "" {
|
||||
|
|
|
@ -19,11 +19,13 @@
|
|||
package timeline
|
||||
|
||||
import (
|
||||
"github.com/sirupsen/logrus"
|
||||
"net/http"
|
||||
"strconv"
|
||||
|
||||
"github.com/sirupsen/logrus"
|
||||
|
||||
"github.com/gin-gonic/gin"
|
||||
"github.com/superseriousbusiness/gotosocial/internal/api"
|
||||
"github.com/superseriousbusiness/gotosocial/internal/oauth"
|
||||
)
|
||||
|
||||
|
@ -112,6 +114,11 @@ func (m *Module) PublicTimelineGETHandler(c *gin.Context) {
|
|||
return
|
||||
}
|
||||
|
||||
if _, err := api.NegotiateAccept(c, api.JSONAcceptHeaders...); err != nil {
|
||||
c.JSON(http.StatusNotAcceptable, gin.H{"error": err.Error()})
|
||||
return
|
||||
}
|
||||
|
||||
maxID := ""
|
||||
maxIDString := c.Query(MaxIDKey)
|
||||
if maxIDString != "" {
|
||||
|
|
|
@ -23,6 +23,7 @@ import (
|
|||
|
||||
"github.com/gin-gonic/gin"
|
||||
"github.com/sirupsen/logrus"
|
||||
"github.com/superseriousbusiness/gotosocial/internal/api"
|
||||
"github.com/superseriousbusiness/gotosocial/internal/api/model"
|
||||
"github.com/superseriousbusiness/gotosocial/internal/oauth"
|
||||
)
|
||||
|
@ -71,6 +72,11 @@ func (m *Module) PasswordChangePOSTHandler(c *gin.Context) {
|
|||
return
|
||||
}
|
||||
|
||||
if _, err := api.NegotiateAccept(c, api.JSONAcceptHeaders...); err != nil {
|
||||
c.JSON(http.StatusNotAcceptable, gin.H{"error": err.Error()})
|
||||
return
|
||||
}
|
||||
|
||||
// First check this user/account is active.
|
||||
if authed.User.Disabled || !authed.User.Approved || !authed.Account.SuspendedAt.IsZero() {
|
||||
l.Debugf("couldn't auth: %s", err)
|
||||
|
|
|
@ -50,6 +50,7 @@ func (suite *PasswordChangeTestSuite) TestPasswordChangePOST() {
|
|||
ctx.Set(oauth.SessionAuthorizedUser, suite.testUsers["local_account_1"])
|
||||
ctx.Set(oauth.SessionAuthorizedAccount, suite.testAccounts["local_account_1"])
|
||||
ctx.Request = httptest.NewRequest(http.MethodPost, fmt.Sprintf("http://localhost:8080/%s", user.PasswordChangePath), nil)
|
||||
ctx.Request.Header.Set("accept", "application/json")
|
||||
ctx.Request.Form = url.Values{
|
||||
"old_password": {"password"},
|
||||
"new_password": {"peepeepoopoopassword"},
|
||||
|
@ -83,6 +84,7 @@ func (suite *PasswordChangeTestSuite) TestPasswordMissingOldPassword() {
|
|||
ctx.Set(oauth.SessionAuthorizedUser, suite.testUsers["local_account_1"])
|
||||
ctx.Set(oauth.SessionAuthorizedAccount, suite.testAccounts["local_account_1"])
|
||||
ctx.Request = httptest.NewRequest(http.MethodPost, fmt.Sprintf("http://localhost:8080/%s", user.PasswordChangePath), nil)
|
||||
ctx.Request.Header.Set("accept", "application/json")
|
||||
ctx.Request.Form = url.Values{
|
||||
"new_password": {"peepeepoopoopassword"},
|
||||
}
|
||||
|
@ -109,6 +111,7 @@ func (suite *PasswordChangeTestSuite) TestPasswordIncorrectOldPassword() {
|
|||
ctx.Set(oauth.SessionAuthorizedUser, suite.testUsers["local_account_1"])
|
||||
ctx.Set(oauth.SessionAuthorizedAccount, suite.testAccounts["local_account_1"])
|
||||
ctx.Request = httptest.NewRequest(http.MethodPost, fmt.Sprintf("http://localhost:8080/%s", user.PasswordChangePath), nil)
|
||||
ctx.Request.Header.Set("accept", "application/json")
|
||||
ctx.Request.Form = url.Values{
|
||||
"old_password": {"notright"},
|
||||
"new_password": {"peepeepoopoopassword"},
|
||||
|
@ -136,6 +139,7 @@ func (suite *PasswordChangeTestSuite) TestPasswordWeakNewPassword() {
|
|||
ctx.Set(oauth.SessionAuthorizedUser, suite.testUsers["local_account_1"])
|
||||
ctx.Set(oauth.SessionAuthorizedAccount, suite.testAccounts["local_account_1"])
|
||||
ctx.Request = httptest.NewRequest(http.MethodPost, fmt.Sprintf("http://localhost:8080/%s", user.PasswordChangePath), nil)
|
||||
ctx.Request.Header.Set("accept", "application/json")
|
||||
ctx.Request.Form = url.Values{
|
||||
"old_password": {"password"},
|
||||
"new_password": {"peepeepoopoo"},
|
||||
|
|
|
@ -22,6 +22,8 @@ package model
|
|||
// For example, it would be returned from https://example.org/.well-known/webfinger?resource=acct:some_username@example.org
|
||||
//
|
||||
// See https://webfinger.net/
|
||||
//
|
||||
// swagger:model wellKnownResponse
|
||||
type WellKnownResponse struct {
|
||||
Subject string `json:"subject,omitempty"`
|
||||
Aliases []string `json:"aliases,omitempty"`
|
||||
|
@ -40,8 +42,11 @@ type Link struct {
|
|||
|
||||
// Nodeinfo represents a version 2.1 or version 2.0 nodeinfo schema.
|
||||
// See: https://nodeinfo.diaspora.software/schema.html
|
||||
//
|
||||
// swagger:model nodeinfo
|
||||
type Nodeinfo struct {
|
||||
// The schema version
|
||||
// example: 2.0
|
||||
Version string `json:"version"`
|
||||
// Metadata about server software in use.
|
||||
Software NodeInfoSoftware `json:"software"`
|
||||
|
@ -50,6 +55,7 @@ type Nodeinfo struct {
|
|||
// The third party sites this server can connect to via their application API.
|
||||
Services NodeInfoServices `json:"services"`
|
||||
// Whether this server allows open self-registration.
|
||||
// example: false
|
||||
OpenRegistrations bool `json:"openRegistrations"`
|
||||
// Usage statistics for this server.
|
||||
Usage NodeInfoUsage `json:"usage"`
|
||||
|
@ -59,7 +65,9 @@ type Nodeinfo struct {
|
|||
|
||||
// NodeInfoSoftware represents the name and version number of the software of this node.
|
||||
type NodeInfoSoftware struct {
|
||||
// example: gotosocial
|
||||
Name string `json:"name"`
|
||||
// example: 0.1.2 1234567
|
||||
Version string `json:"version"`
|
||||
}
|
||||
|
||||
|
|
|
@ -0,0 +1,96 @@
|
|||
/*
|
||||
GoToSocial
|
||||
Copyright (C) 2021 GoToSocial Authors admin@gotosocial.org
|
||||
|
||||
This program is free software: you can redistribute it and/or modify
|
||||
it under the terms of the GNU Affero General Public License as published by
|
||||
the Free Software Foundation, either version 3 of the License, or
|
||||
(at your option) any later version.
|
||||
|
||||
This program is distributed in the hope that it will be useful,
|
||||
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
GNU Affero General Public License for more details.
|
||||
|
||||
You should have received a copy of the GNU Affero General Public License
|
||||
along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
package api
|
||||
|
||||
import (
|
||||
"errors"
|
||||
"fmt"
|
||||
|
||||
"github.com/gin-gonic/gin"
|
||||
)
|
||||
|
||||
// Offer represents an offered mime-type.
|
||||
type Offer string
|
||||
|
||||
const (
|
||||
AppJSON Offer = `application/json` // AppJSON is the mime type for 'application/json'.
|
||||
AppActivityJSON Offer = `application/activity+json` // AppActivityJSON is the mime type for 'application/activity+json'.
|
||||
AppActivityLDJSON Offer = `application/ld+json; profile="https://www.w3.org/ns/activitystreams"` // AppActivityLDJSON is the mime type for 'application/ld+json; profile="https://www.w3.org/ns/activitystreams"'
|
||||
TextHTML Offer = `text/html` // TextHTML is the mime type for 'text/html'.
|
||||
)
|
||||
|
||||
// ActivityPubAcceptHeaders represents the Accept headers mentioned here:
|
||||
// https://www.w3.org/TR/activitypub/#retrieving-objects
|
||||
var ActivityPubAcceptHeaders = []Offer{
|
||||
AppActivityJSON,
|
||||
AppActivityLDJSON,
|
||||
}
|
||||
|
||||
// JSONAcceptHeaders is a slice of offers that just contains application/json types.
|
||||
var JSONAcceptHeaders = []Offer{
|
||||
AppJSON,
|
||||
}
|
||||
|
||||
// HTMLAcceptHeaders is a slice of offers that just contains text/html types.
|
||||
var HTMLAcceptHeaders = []Offer{
|
||||
TextHTML,
|
||||
}
|
||||
|
||||
// NegotiateAccept takes the *gin.Context from an incoming request, and a
|
||||
// slice of Offers, and performs content negotiation for the given request
|
||||
// with the given content-type offers. It will return a string representation
|
||||
// of the first suitable content-type, or an error if something goes wrong or
|
||||
// a suiteable content-type cannot be matched.
|
||||
//
|
||||
// For example, if the request in the *gin.Context has Accept headers of value
|
||||
// [application/json, text/html], and the provided offers are of value
|
||||
// [application/json, application/xml], then the returned string will be
|
||||
// 'application/json', which indicates the content-type that should be returned.
|
||||
//
|
||||
// If there are no Accept headers in the request, or the length of offers is 0,
|
||||
// then an error will be returned, so this function should only be called in places
|
||||
// where format negotiation is actually needed and headers are expected to be present
|
||||
// on incoming requests.
|
||||
//
|
||||
// Callers can use the offer slices exported in this package as shortcuts for
|
||||
// often-used Accept types.
|
||||
//
|
||||
// See https://developer.mozilla.org/en-US/docs/Web/HTTP/Content_negotiation#server-driven_content_negotiation
|
||||
func NegotiateAccept(c *gin.Context, offers ...Offer) (string, error) {
|
||||
if len(offers) == 0 {
|
||||
return "", errors.New("no format offered")
|
||||
}
|
||||
|
||||
accepts := c.Request.Header.Values("Accept")
|
||||
if len(accepts) == 0 {
|
||||
return "", fmt.Errorf("no Accept header(s) set on incoming request; this endpoint offers %s", offers)
|
||||
}
|
||||
|
||||
strings := []string{}
|
||||
for _, o := range offers {
|
||||
strings = append(strings, string(o))
|
||||
}
|
||||
|
||||
format := c.NegotiateFormat(strings...)
|
||||
if format == "" {
|
||||
return "", fmt.Errorf("no format can be offered for requested Accept header(s) %s; this endpoint offers %s", accepts, offers)
|
||||
}
|
||||
|
||||
return format, nil
|
||||
}
|
|
@ -19,20 +19,42 @@
|
|||
package nodeinfo
|
||||
|
||||
import (
|
||||
"encoding/json"
|
||||
"net/http"
|
||||
|
||||
"github.com/gin-gonic/gin"
|
||||
"github.com/sirupsen/logrus"
|
||||
"github.com/superseriousbusiness/gotosocial/internal/api"
|
||||
)
|
||||
|
||||
// NodeInfoGETHandler returns a compliant nodeinfo response to node info queries.
|
||||
// See: https://nodeinfo.diaspora.software/
|
||||
// NodeInfoGETHandler swagger:operation GET /nodeinfo/2.0 nodeInfoGet
|
||||
//
|
||||
// Returns a compliant nodeinfo response to node info queries.
|
||||
//
|
||||
// See: https://nodeinfo.diaspora.software/schema.html
|
||||
//
|
||||
// ---
|
||||
// tags:
|
||||
// - nodeinfo
|
||||
//
|
||||
// produces:
|
||||
// - application/json; profile="http://nodeinfo.diaspora.software/ns/schema/2.0#"
|
||||
//
|
||||
// responses:
|
||||
// '200':
|
||||
// schema:
|
||||
// "$ref": "#/definitions/nodeinfo"
|
||||
func (m *Module) NodeInfoGETHandler(c *gin.Context) {
|
||||
l := logrus.WithFields(logrus.Fields{
|
||||
"func": "NodeInfoGETHandler",
|
||||
"user-agent": c.Request.UserAgent(),
|
||||
})
|
||||
|
||||
if _, err := api.NegotiateAccept(c, api.JSONAcceptHeaders...); err != nil {
|
||||
c.JSON(http.StatusNotAcceptable, gin.H{"error": err.Error()})
|
||||
return
|
||||
}
|
||||
|
||||
ni, err := m.processor.GetNodeInfo(c.Request.Context(), c.Request)
|
||||
if err != nil {
|
||||
l.Debugf("error with get node info request: %s", err)
|
||||
|
@ -40,5 +62,10 @@ func (m *Module) NodeInfoGETHandler(c *gin.Context) {
|
|||
return
|
||||
}
|
||||
|
||||
c.JSON(http.StatusOK, ni)
|
||||
b, jsonErr := json.Marshal(ni)
|
||||
if jsonErr != nil {
|
||||
c.JSON(http.StatusInternalServerError, gin.H{"error": jsonErr.Error()})
|
||||
}
|
||||
|
||||
c.Data(http.StatusOK, `application/json; profile="http://nodeinfo.diaspora.software/ns/schema/2.0#"`, b)
|
||||
}
|
||||
|
|
|
@ -23,16 +23,37 @@ import (
|
|||
|
||||
"github.com/gin-gonic/gin"
|
||||
"github.com/sirupsen/logrus"
|
||||
"github.com/superseriousbusiness/gotosocial/internal/api"
|
||||
)
|
||||
|
||||
// NodeInfoWellKnownGETHandler returns a well known response to a query to /.well-known/nodeinfo,
|
||||
// directing (but not redirecting...) callers to the NodeInfoGETHandler.
|
||||
// NodeInfoWellKnownGETHandler swagger:operation GET /.well-known/nodeinfo nodeInfoWellKnownGet
|
||||
//
|
||||
// Directs callers to /nodeinfo/2.0.
|
||||
//
|
||||
// eg. `{"links":[{"rel":"http://nodeinfo.diaspora.software/ns/schema/2.0","href":"http://example.org/nodeinfo/2.0"}]}`
|
||||
// See: https://nodeinfo.diaspora.software/protocol.html
|
||||
//
|
||||
// ---
|
||||
// tags:
|
||||
// - nodeinfo
|
||||
//
|
||||
// produces:
|
||||
// - application/json
|
||||
//
|
||||
// responses:
|
||||
// '200':
|
||||
// schema:
|
||||
// "$ref": "#/definitions/wellKnownResponse"
|
||||
func (m *Module) NodeInfoWellKnownGETHandler(c *gin.Context) {
|
||||
l := logrus.WithFields(logrus.Fields{
|
||||
"func": "NodeInfoWellKnownGETHandler",
|
||||
"user-agent": c.Request.UserAgent(),
|
||||
})
|
||||
|
||||
if _, err := api.NegotiateAccept(c, api.JSONAcceptHeaders...); err != nil {
|
||||
c.JSON(http.StatusNotAcceptable, gin.H{"error": err.Error()})
|
||||
return
|
||||
}
|
||||
|
||||
niRel, err := m.processor.GetNodeInfoRel(c.Request.Context(), c.Request)
|
||||
if err != nil {
|
||||
l.Debugf("error with get node info rel request: %s", err)
|
||||
|
|
|
@ -20,19 +20,11 @@ package user
|
|||
|
||||
import (
|
||||
"context"
|
||||
"fmt"
|
||||
|
||||
"github.com/gin-gonic/gin"
|
||||
"github.com/superseriousbusiness/gotosocial/internal/util"
|
||||
)
|
||||
|
||||
// ActivityPubAcceptHeaders represents the Accept headers mentioned here:
|
||||
// https://www.w3.org/TR/activitypub/#retrieving-objects
|
||||
var ActivityPubAcceptHeaders = []string{
|
||||
`application/activity+json`,
|
||||
`application/ld+json; profile="https://www.w3.org/ns/activitystreams"`,
|
||||
}
|
||||
|
||||
// transferContext transfers the signature verifier and signature from the gin context to the request context
|
||||
func transferContext(c *gin.Context) context.Context {
|
||||
ctx := c.Request.Context()
|
||||
|
@ -50,14 +42,6 @@ func transferContext(c *gin.Context) context.Context {
|
|||
return ctx
|
||||
}
|
||||
|
||||
func negotiateFormat(c *gin.Context) (string, error) {
|
||||
format := c.NegotiateFormat(ActivityPubAcceptHeaders...)
|
||||
if format == "" {
|
||||
return "", fmt.Errorf("no format can be offered for Accept headers %s", c.Request.Header.Get("Accept"))
|
||||
}
|
||||
return format, nil
|
||||
}
|
||||
|
||||
// SwaggerCollection represents an activitypub collection.
|
||||
// swagger:model swaggerCollection
|
||||
type SwaggerCollection struct {
|
||||
|
|
|
@ -25,6 +25,7 @@ import (
|
|||
|
||||
"github.com/gin-gonic/gin"
|
||||
"github.com/sirupsen/logrus"
|
||||
"github.com/superseriousbusiness/gotosocial/internal/api"
|
||||
)
|
||||
|
||||
// FollowersGETHandler returns a collection of URIs for followers of the target user, formatted so that other AP servers can understand it.
|
||||
|
@ -40,9 +41,9 @@ func (m *Module) FollowersGETHandler(c *gin.Context) {
|
|||
return
|
||||
}
|
||||
|
||||
format, err := negotiateFormat(c)
|
||||
format, err := api.NegotiateAccept(c, api.ActivityPubAcceptHeaders...)
|
||||
if err != nil {
|
||||
c.JSON(http.StatusNotAcceptable, gin.H{"error": fmt.Sprintf("could not negotiate format with given Accept header(s): %s", err)})
|
||||
c.JSON(http.StatusNotAcceptable, gin.H{"error": err.Error()})
|
||||
return
|
||||
}
|
||||
l.Tracef("negotiated format: %s", format)
|
||||
|
|
|
@ -25,6 +25,7 @@ import (
|
|||
|
||||
"github.com/gin-gonic/gin"
|
||||
"github.com/sirupsen/logrus"
|
||||
"github.com/superseriousbusiness/gotosocial/internal/api"
|
||||
)
|
||||
|
||||
// FollowingGETHandler returns a collection of URIs for accounts that the target user follows, formatted so that other AP servers can understand it.
|
||||
|
@ -40,9 +41,9 @@ func (m *Module) FollowingGETHandler(c *gin.Context) {
|
|||
return
|
||||
}
|
||||
|
||||
format, err := negotiateFormat(c)
|
||||
format, err := api.NegotiateAccept(c, api.ActivityPubAcceptHeaders...)
|
||||
if err != nil {
|
||||
c.JSON(http.StatusNotAcceptable, gin.H{"error": fmt.Sprintf("could not negotiate format with given Accept header(s): %s", err)})
|
||||
c.JSON(http.StatusNotAcceptable, gin.H{"error": err.Error()})
|
||||
return
|
||||
}
|
||||
l.Tracef("negotiated format: %s", format)
|
||||
|
|
|
@ -26,6 +26,7 @@ import (
|
|||
|
||||
"github.com/gin-gonic/gin"
|
||||
"github.com/sirupsen/logrus"
|
||||
"github.com/superseriousbusiness/gotosocial/internal/api"
|
||||
)
|
||||
|
||||
// OutboxGETHandler swagger:operation GET /users/{username}/outbox s2sOutboxGet
|
||||
|
@ -113,9 +114,9 @@ func (m *Module) OutboxGETHandler(c *gin.Context) {
|
|||
maxID = maxIDString
|
||||
}
|
||||
|
||||
format, err := negotiateFormat(c)
|
||||
format, err := api.NegotiateAccept(c, api.ActivityPubAcceptHeaders...)
|
||||
if err != nil {
|
||||
c.JSON(http.StatusNotAcceptable, gin.H{"error": fmt.Sprintf("could not negotiate format with given Accept header(s): %s", err)})
|
||||
c.JSON(http.StatusNotAcceptable, gin.H{"error": err.Error()})
|
||||
return
|
||||
}
|
||||
l.Tracef("negotiated format: %s", format)
|
||||
|
|
|
@ -54,6 +54,7 @@ func (suite *OutboxGetTestSuite) TestGetOutbox() {
|
|||
recorder := httptest.NewRecorder()
|
||||
ctx, _ := gin.CreateTestContext(recorder)
|
||||
ctx.Request = httptest.NewRequest(http.MethodGet, targetAccount.OutboxURI, nil) // the endpoint we're hitting
|
||||
ctx.Request.Header.Set("accept", "application/activity+json")
|
||||
ctx.Request.Header.Set("Signature", signedRequest.SignatureHeader)
|
||||
ctx.Request.Header.Set("Date", signedRequest.DateHeader)
|
||||
|
||||
|
@ -108,6 +109,7 @@ func (suite *OutboxGetTestSuite) TestGetOutboxFirstPage() {
|
|||
recorder := httptest.NewRecorder()
|
||||
ctx, _ := gin.CreateTestContext(recorder)
|
||||
ctx.Request = httptest.NewRequest(http.MethodGet, targetAccount.OutboxURI+"?page=true", nil) // the endpoint we're hitting
|
||||
ctx.Request.Header.Set("accept", "application/activity+json")
|
||||
ctx.Request.Header.Set("Signature", signedRequest.SignatureHeader)
|
||||
ctx.Request.Header.Set("Date", signedRequest.DateHeader)
|
||||
|
||||
|
@ -162,6 +164,7 @@ func (suite *OutboxGetTestSuite) TestGetOutboxNextPage() {
|
|||
recorder := httptest.NewRecorder()
|
||||
ctx, _ := gin.CreateTestContext(recorder)
|
||||
ctx.Request = httptest.NewRequest(http.MethodGet, targetAccount.OutboxURI+"?page=true&max_id=01F8MHAMCHF6Y650WCRSCP4WMY", nil) // the endpoint we're hitting
|
||||
ctx.Request.Header.Set("accept", "application/activity+json")
|
||||
ctx.Request.Header.Set("Signature", signedRequest.SignatureHeader)
|
||||
ctx.Request.Header.Set("Date", signedRequest.DateHeader)
|
||||
|
||||
|
|
|
@ -25,6 +25,7 @@ import (
|
|||
|
||||
"github.com/gin-gonic/gin"
|
||||
"github.com/sirupsen/logrus"
|
||||
"github.com/superseriousbusiness/gotosocial/internal/api"
|
||||
)
|
||||
|
||||
// PublicKeyGETHandler should be served at eg https://example.org/users/:username/main-key.
|
||||
|
@ -44,9 +45,9 @@ func (m *Module) PublicKeyGETHandler(c *gin.Context) {
|
|||
return
|
||||
}
|
||||
|
||||
format, err := negotiateFormat(c)
|
||||
format, err := api.NegotiateAccept(c, api.ActivityPubAcceptHeaders...)
|
||||
if err != nil {
|
||||
c.JSON(http.StatusNotAcceptable, gin.H{"error": fmt.Sprintf("could not negotiate format with given Accept header(s): %s", err)})
|
||||
c.JSON(http.StatusNotAcceptable, gin.H{"error": err.Error()})
|
||||
return
|
||||
}
|
||||
l.Tracef("negotiated format: %s", format)
|
||||
|
|
|
@ -26,6 +26,7 @@ import (
|
|||
|
||||
"github.com/gin-gonic/gin"
|
||||
"github.com/sirupsen/logrus"
|
||||
"github.com/superseriousbusiness/gotosocial/internal/api"
|
||||
)
|
||||
|
||||
// StatusRepliesGETHandler swagger:operation GET /users/{username}/statuses/{status}/replies s2sRepliesGet
|
||||
|
@ -131,9 +132,9 @@ func (m *Module) StatusRepliesGETHandler(c *gin.Context) {
|
|||
minID = minIDString
|
||||
}
|
||||
|
||||
format, err := negotiateFormat(c)
|
||||
format, err := api.NegotiateAccept(c, api.ActivityPubAcceptHeaders...)
|
||||
if err != nil {
|
||||
c.JSON(http.StatusNotAcceptable, gin.H{"error": fmt.Sprintf("could not negotiate format with given Accept header(s): %s", err)})
|
||||
c.JSON(http.StatusNotAcceptable, gin.H{"error": err.Error()})
|
||||
return
|
||||
}
|
||||
l.Tracef("negotiated format: %s", format)
|
||||
|
|
|
@ -57,6 +57,7 @@ func (suite *RepliesGetTestSuite) TestGetReplies() {
|
|||
recorder := httptest.NewRecorder()
|
||||
ctx, _ := gin.CreateTestContext(recorder)
|
||||
ctx.Request = httptest.NewRequest(http.MethodGet, targetStatus.URI+"/replies", nil) // the endpoint we're hitting
|
||||
ctx.Request.Header.Set("accept", "application/activity+json")
|
||||
ctx.Request.Header.Set("Signature", signedRequest.SignatureHeader)
|
||||
ctx.Request.Header.Set("Date", signedRequest.DateHeader)
|
||||
|
||||
|
@ -117,6 +118,7 @@ func (suite *RepliesGetTestSuite) TestGetRepliesNext() {
|
|||
recorder := httptest.NewRecorder()
|
||||
ctx, _ := gin.CreateTestContext(recorder)
|
||||
ctx.Request = httptest.NewRequest(http.MethodGet, targetStatus.URI+"/replies?only_other_accounts=false&page=true", nil) // the endpoint we're hitting
|
||||
ctx.Request.Header.Set("accept", "application/activity+json")
|
||||
ctx.Request.Header.Set("Signature", signedRequest.SignatureHeader)
|
||||
ctx.Request.Header.Set("Date", signedRequest.DateHeader)
|
||||
|
||||
|
@ -180,6 +182,7 @@ func (suite *RepliesGetTestSuite) TestGetRepliesLast() {
|
|||
recorder := httptest.NewRecorder()
|
||||
ctx, _ := gin.CreateTestContext(recorder)
|
||||
ctx.Request = httptest.NewRequest(http.MethodGet, targetStatus.URI+"/replies?only_other_accounts=false&page=true&min_id=01FF25D5Q0DH7CHD57CTRS6WK0", nil) // the endpoint we're hitting
|
||||
ctx.Request.Header.Set("accept", "application/activity+json")
|
||||
ctx.Request.Header.Set("Signature", signedRequest.SignatureHeader)
|
||||
ctx.Request.Header.Set("Date", signedRequest.DateHeader)
|
||||
|
||||
|
|
|
@ -25,6 +25,7 @@ import (
|
|||
|
||||
"github.com/gin-gonic/gin"
|
||||
"github.com/sirupsen/logrus"
|
||||
"github.com/superseriousbusiness/gotosocial/internal/api"
|
||||
)
|
||||
|
||||
// StatusGETHandler serves the target status as an activitystreams NOTE so that other AP servers can parse it.
|
||||
|
@ -46,9 +47,9 @@ func (m *Module) StatusGETHandler(c *gin.Context) {
|
|||
return
|
||||
}
|
||||
|
||||
format, err := negotiateFormat(c)
|
||||
format, err := api.NegotiateAccept(c, api.ActivityPubAcceptHeaders...)
|
||||
if err != nil {
|
||||
c.JSON(http.StatusNotAcceptable, gin.H{"error": fmt.Sprintf("could not negotiate format with given Accept header(s): %s", err)})
|
||||
c.JSON(http.StatusNotAcceptable, gin.H{"error": err.Error()})
|
||||
return
|
||||
}
|
||||
l.Tracef("negotiated format: %s", format)
|
||||
|
|
|
@ -25,6 +25,7 @@ import (
|
|||
|
||||
"github.com/gin-gonic/gin"
|
||||
"github.com/sirupsen/logrus"
|
||||
"github.com/superseriousbusiness/gotosocial/internal/api"
|
||||
)
|
||||
|
||||
// UsersGETHandler should be served at https://example.org/users/:username.
|
||||
|
@ -48,9 +49,9 @@ func (m *Module) UsersGETHandler(c *gin.Context) {
|
|||
return
|
||||
}
|
||||
|
||||
format, err := negotiateFormat(c)
|
||||
format, err := api.NegotiateAccept(c, api.ActivityPubAcceptHeaders...)
|
||||
if err != nil {
|
||||
c.JSON(http.StatusNotAcceptable, gin.H{"error": fmt.Sprintf("could not negotiate format with given Accept header(s): %s", err)})
|
||||
c.JSON(http.StatusNotAcceptable, gin.H{"error": err.Error()})
|
||||
return
|
||||
}
|
||||
l.Tracef("negotiated format: %s", format)
|
||||
|
|
|
@ -55,6 +55,7 @@ func (suite *UserGetTestSuite) TestGetUser() {
|
|||
recorder := httptest.NewRecorder()
|
||||
ctx, _ := gin.CreateTestContext(recorder)
|
||||
ctx.Request = httptest.NewRequest(http.MethodGet, targetAccount.URI, nil) // the endpoint we're hitting
|
||||
ctx.Request.Header.Set("accept", "application/activity+json")
|
||||
ctx.Request.Header.Set("Signature", signedRequest.SignatureHeader)
|
||||
ctx.Request.Header.Set("Date", signedRequest.DateHeader)
|
||||
|
||||
|
|
|
@ -27,26 +27,54 @@ import (
|
|||
"github.com/gin-gonic/gin"
|
||||
"github.com/sirupsen/logrus"
|
||||
"github.com/spf13/viper"
|
||||
"github.com/superseriousbusiness/gotosocial/internal/api"
|
||||
"github.com/superseriousbusiness/gotosocial/internal/config"
|
||||
"github.com/superseriousbusiness/gotosocial/internal/util"
|
||||
)
|
||||
|
||||
// WebfingerGETRequest handles requests to, for example, https://example.org/.well-known/webfinger?resource=acct:some_user@example.org
|
||||
// WebfingerGETRequest swagger:operation GET /.well-known/webfinger webfingerGet
|
||||
//
|
||||
// Handles webfinger account lookup requests.
|
||||
//
|
||||
// For example, a GET to `https://goblin.technology/.well-known/webfinger?resource=acct:tobi@goblin.technology` would return:
|
||||
//
|
||||
// ```
|
||||
// {"subject":"acct:tobi@goblin.technology","aliases":["https://goblin.technology/users/tobi","https://goblin.technology/@tobi"],"links":[{"rel":"http://webfinger.net/rel/profile-page","type":"text/html","href":"https://goblin.technology/@tobi"},{"rel":"self","type":"application/activity+json","href":"https://goblin.technology/users/tobi"}]}
|
||||
// ```
|
||||
//
|
||||
// See: https://webfinger.net/
|
||||
//
|
||||
// ---
|
||||
// tags:
|
||||
// - webfinger
|
||||
//
|
||||
// produces:
|
||||
// - application/json
|
||||
//
|
||||
// responses:
|
||||
// '200':
|
||||
// schema:
|
||||
// "$ref": "#/definitions/wellKnownResponse"
|
||||
func (m *Module) WebfingerGETRequest(c *gin.Context) {
|
||||
l := logrus.WithFields(logrus.Fields{
|
||||
"func": "WebfingerGETRequest",
|
||||
"user-agent": c.Request.UserAgent(),
|
||||
})
|
||||
|
||||
q, set := c.GetQuery("resource")
|
||||
if !set || q == "" {
|
||||
resourceQuery, set := c.GetQuery("resource")
|
||||
if !set || resourceQuery == "" {
|
||||
l.Debug("aborting request because no resource was set in query")
|
||||
c.JSON(http.StatusBadRequest, gin.H{"error": "no 'resource' in request query"})
|
||||
return
|
||||
}
|
||||
|
||||
if _, err := api.NegotiateAccept(c, api.JSONAcceptHeaders...); err != nil {
|
||||
c.JSON(http.StatusNotAcceptable, gin.H{"error": err.Error()})
|
||||
return
|
||||
}
|
||||
|
||||
// remove the acct: prefix if it's present
|
||||
trimAcct := strings.TrimPrefix(q, "acct:")
|
||||
trimAcct := strings.TrimPrefix(resourceQuery, "acct:")
|
||||
// remove the first @ in @whatever@example.org if it's present
|
||||
namestring := strings.TrimPrefix(trimAcct, "@")
|
||||
|
||||
|
|
|
@ -50,6 +50,7 @@ func (suite *WebfingerGetTestSuite) TestFingerUser() {
|
|||
recorder := httptest.NewRecorder()
|
||||
ctx, _ := gin.CreateTestContext(recorder)
|
||||
ctx.Request = httptest.NewRequest(http.MethodGet, requestPath, nil) // the endpoint we're hitting
|
||||
ctx.Request.Header.Set("accept", "application/json")
|
||||
|
||||
// trigger the function being tested
|
||||
suite.webfingerModule.WebfingerGETRequest(ctx)
|
||||
|
@ -83,6 +84,7 @@ func (suite *WebfingerGetTestSuite) TestFingerUserWithDifferentAccountDomainByHo
|
|||
recorder := httptest.NewRecorder()
|
||||
ctx, _ := gin.CreateTestContext(recorder)
|
||||
ctx.Request = httptest.NewRequest(http.MethodGet, requestPath, nil) // the endpoint we're hitting
|
||||
ctx.Request.Header.Set("accept", "application/json")
|
||||
|
||||
// trigger the function being tested
|
||||
suite.webfingerModule.WebfingerGETRequest(ctx)
|
||||
|
@ -116,6 +118,7 @@ func (suite *WebfingerGetTestSuite) TestFingerUserWithDifferentAccountDomainByAc
|
|||
recorder := httptest.NewRecorder()
|
||||
ctx, _ := gin.CreateTestContext(recorder)
|
||||
ctx.Request = httptest.NewRequest(http.MethodGet, requestPath, nil) // the endpoint we're hitting
|
||||
ctx.Request.Header.Set("accept", "application/json")
|
||||
|
||||
// trigger the function being tested
|
||||
suite.webfingerModule.WebfingerGETRequest(ctx)
|
||||
|
@ -141,6 +144,7 @@ func (suite *WebfingerGetTestSuite) TestFingerUserWithoutAcct() {
|
|||
recorder := httptest.NewRecorder()
|
||||
ctx, _ := gin.CreateTestContext(recorder)
|
||||
ctx.Request = httptest.NewRequest(http.MethodGet, requestPath, nil) // the endpoint we're hitting
|
||||
ctx.Request.Header.Set("accept", "application/json")
|
||||
|
||||
// trigger the function being tested
|
||||
suite.webfingerModule.WebfingerGETRequest(ctx)
|
||||
|
|
|
@ -21,7 +21,7 @@ nav:
|
|||
- "configuration/index.md"
|
||||
- "configuration/general.md"
|
||||
- "configuration/database.md"
|
||||
- "configuration/template.md"
|
||||
- "configuration/web.md"
|
||||
- "configuration/accounts.md"
|
||||
- "configuration/media.md"
|
||||
- "configuration/storage.md"
|
||||
|
|
Loading…
Reference in New Issue