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.
|
title: A FileHeader describes a file part of a multipart request.
|
||||||
type: object
|
type: object
|
||||||
x-go-package: mime/multipart
|
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:
|
MIMEHeader:
|
||||||
additionalProperties:
|
additionalProperties:
|
||||||
items:
|
items:
|
||||||
|
@ -49,6 +67,48 @@ definitions:
|
||||||
title: Mention represents a mention of another account.
|
title: Mention represents a mention of another account.
|
||||||
type: object
|
type: object
|
||||||
x-go-package: github.com/superseriousbusiness/gotosocial/internal/api/model
|
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:
|
Source:
|
||||||
description: Returned as an additional entity when verifying and updated credentials,
|
description: Returned as an additional entity when verifying and updated credentials,
|
||||||
as an attribute of Account.
|
as an attribute of Account.
|
||||||
|
@ -1122,6 +1182,42 @@ definitions:
|
||||||
type: object
|
type: object
|
||||||
x-go-name: MediaMeta
|
x-go-name: MediaMeta
|
||||||
x-go-package: github.com/superseriousbusiness/gotosocial/internal/api/model
|
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:
|
oauthToken:
|
||||||
properties:
|
properties:
|
||||||
access_token:
|
access_token:
|
||||||
|
@ -1670,6 +1766,28 @@ definitions:
|
||||||
type: object
|
type: object
|
||||||
x-go-name: UpdateSource
|
x-go-name: UpdateSource
|
||||||
x-go-package: github.com/superseriousbusiness/gotosocial/internal/api/model
|
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
|
host: example.org
|
||||||
info:
|
info:
|
||||||
contact:
|
contact:
|
||||||
|
@ -1682,6 +1800,43 @@ info:
|
||||||
title: GoToSocial
|
title: GoToSocial
|
||||||
version: 0.0.1
|
version: 0.0.1
|
||||||
paths:
|
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:
|
/api/v1/accounts:
|
||||||
post:
|
post:
|
||||||
consumes:
|
consumes:
|
||||||
|
@ -3529,6 +3684,20 @@ paths:
|
||||||
summary: Change the password of authenticated user.
|
summary: Change the password of authenticated user.
|
||||||
tags:
|
tags:
|
||||||
- user
|
- 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:
|
/users/{username}/outbox:
|
||||||
get:
|
get:
|
||||||
description: |-
|
description: |-
|
||||||
|
|
|
@ -95,5 +95,7 @@ func (suite *AccountStandardTestSuite) newContext(recorder *httptest.ResponseRec
|
||||||
ctx.Request.Header.Set("Content-Type", bodyContentType)
|
ctx.Request.Header.Set("Content-Type", bodyContentType)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
ctx.Request.Header.Set("accept", "application/json")
|
||||||
|
|
||||||
return ctx
|
return ctx
|
||||||
}
|
}
|
||||||
|
|
|
@ -27,6 +27,7 @@ import (
|
||||||
"github.com/spf13/viper"
|
"github.com/spf13/viper"
|
||||||
|
|
||||||
"github.com/gin-gonic/gin"
|
"github.com/gin-gonic/gin"
|
||||||
|
"github.com/superseriousbusiness/gotosocial/internal/api"
|
||||||
"github.com/superseriousbusiness/gotosocial/internal/api/model"
|
"github.com/superseriousbusiness/gotosocial/internal/api/model"
|
||||||
"github.com/superseriousbusiness/gotosocial/internal/config"
|
"github.com/superseriousbusiness/gotosocial/internal/config"
|
||||||
"github.com/superseriousbusiness/gotosocial/internal/oauth"
|
"github.com/superseriousbusiness/gotosocial/internal/oauth"
|
||||||
|
@ -78,6 +79,11 @@ func (m *Module) AccountCreatePOSTHandler(c *gin.Context) {
|
||||||
return
|
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")
|
l.Trace("parsing request form")
|
||||||
form := &model.AccountCreateRequest{}
|
form := &model.AccountCreateRequest{}
|
||||||
if err := c.ShouldBind(form); err != nil || form == nil {
|
if err := c.ShouldBind(form); err != nil || form == nil {
|
||||||
|
|
|
@ -22,6 +22,7 @@ import (
|
||||||
"net/http"
|
"net/http"
|
||||||
|
|
||||||
"github.com/gin-gonic/gin"
|
"github.com/gin-gonic/gin"
|
||||||
|
"github.com/superseriousbusiness/gotosocial/internal/api"
|
||||||
"github.com/superseriousbusiness/gotosocial/internal/oauth"
|
"github.com/superseriousbusiness/gotosocial/internal/oauth"
|
||||||
)
|
)
|
||||||
|
|
||||||
|
@ -64,6 +65,11 @@ func (m *Module) AccountGETHandler(c *gin.Context) {
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if _, err := api.NegotiateAccept(c, api.JSONAcceptHeaders...); err != nil {
|
||||||
|
c.JSON(http.StatusNotAcceptable, gin.H{"error": err.Error()})
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
targetAcctID := c.Param(IDKey)
|
targetAcctID := c.Param(IDKey)
|
||||||
if targetAcctID == "" {
|
if targetAcctID == "" {
|
||||||
c.JSON(http.StatusBadRequest, gin.H{"error": "no account id specified"})
|
c.JSON(http.StatusBadRequest, gin.H{"error": "no account id specified"})
|
||||||
|
|
|
@ -20,11 +20,13 @@ package account
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"fmt"
|
"fmt"
|
||||||
"github.com/sirupsen/logrus"
|
|
||||||
"net/http"
|
"net/http"
|
||||||
"strconv"
|
"strconv"
|
||||||
|
|
||||||
|
"github.com/sirupsen/logrus"
|
||||||
|
|
||||||
"github.com/gin-gonic/gin"
|
"github.com/gin-gonic/gin"
|
||||||
|
"github.com/superseriousbusiness/gotosocial/internal/api"
|
||||||
"github.com/superseriousbusiness/gotosocial/internal/api/model"
|
"github.com/superseriousbusiness/gotosocial/internal/api/model"
|
||||||
"github.com/superseriousbusiness/gotosocial/internal/oauth"
|
"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)
|
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)
|
form, err := parseUpdateAccountForm(c)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
c.JSON(http.StatusBadRequest, gin.H{"error": err.Error()})
|
c.JSON(http.StatusBadRequest, gin.H{"error": err.Error()})
|
||||||
|
|
|
@ -19,10 +19,12 @@
|
||||||
package account
|
package account
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"github.com/sirupsen/logrus"
|
|
||||||
"net/http"
|
"net/http"
|
||||||
|
|
||||||
|
"github.com/sirupsen/logrus"
|
||||||
|
|
||||||
"github.com/gin-gonic/gin"
|
"github.com/gin-gonic/gin"
|
||||||
|
"github.com/superseriousbusiness/gotosocial/internal/api"
|
||||||
"github.com/superseriousbusiness/gotosocial/internal/oauth"
|
"github.com/superseriousbusiness/gotosocial/internal/oauth"
|
||||||
)
|
)
|
||||||
|
|
||||||
|
@ -60,6 +62,11 @@ func (m *Module) AccountVerifyGETHandler(c *gin.Context) {
|
||||||
return
|
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)
|
acctSensitive, err := m.processor.AccountGet(c.Request.Context(), authed, authed.Account.ID)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
l.Debugf("error getting account from processor: %s", err)
|
l.Debugf("error getting account from processor: %s", err)
|
||||||
|
|
|
@ -22,6 +22,7 @@ import (
|
||||||
"net/http"
|
"net/http"
|
||||||
|
|
||||||
"github.com/gin-gonic/gin"
|
"github.com/gin-gonic/gin"
|
||||||
|
"github.com/superseriousbusiness/gotosocial/internal/api"
|
||||||
"github.com/superseriousbusiness/gotosocial/internal/oauth"
|
"github.com/superseriousbusiness/gotosocial/internal/oauth"
|
||||||
)
|
)
|
||||||
|
|
||||||
|
@ -66,6 +67,11 @@ func (m *Module) AccountBlockPOSTHandler(c *gin.Context) {
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if _, err := api.NegotiateAccept(c, api.JSONAcceptHeaders...); err != nil {
|
||||||
|
c.JSON(http.StatusNotAcceptable, gin.H{"error": err.Error()})
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
targetAcctID := c.Param(IDKey)
|
targetAcctID := c.Param(IDKey)
|
||||||
if targetAcctID == "" {
|
if targetAcctID == "" {
|
||||||
c.JSON(http.StatusBadRequest, gin.H{"error": "no account id specified"})
|
c.JSON(http.StatusBadRequest, gin.H{"error": "no account id specified"})
|
||||||
|
|
|
@ -22,6 +22,7 @@ import (
|
||||||
"net/http"
|
"net/http"
|
||||||
|
|
||||||
"github.com/gin-gonic/gin"
|
"github.com/gin-gonic/gin"
|
||||||
|
"github.com/superseriousbusiness/gotosocial/internal/api"
|
||||||
"github.com/superseriousbusiness/gotosocial/internal/api/model"
|
"github.com/superseriousbusiness/gotosocial/internal/api/model"
|
||||||
"github.com/superseriousbusiness/gotosocial/internal/oauth"
|
"github.com/superseriousbusiness/gotosocial/internal/oauth"
|
||||||
)
|
)
|
||||||
|
@ -87,6 +88,11 @@ func (m *Module) AccountFollowPOSTHandler(c *gin.Context) {
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if _, err := api.NegotiateAccept(c, api.JSONAcceptHeaders...); err != nil {
|
||||||
|
c.JSON(http.StatusNotAcceptable, gin.H{"error": err.Error()})
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
targetAcctID := c.Param(IDKey)
|
targetAcctID := c.Param(IDKey)
|
||||||
if targetAcctID == "" {
|
if targetAcctID == "" {
|
||||||
c.JSON(http.StatusBadRequest, gin.H{"error": "no account id specified"})
|
c.JSON(http.StatusBadRequest, gin.H{"error": "no account id specified"})
|
||||||
|
|
|
@ -22,6 +22,7 @@ import (
|
||||||
"net/http"
|
"net/http"
|
||||||
|
|
||||||
"github.com/gin-gonic/gin"
|
"github.com/gin-gonic/gin"
|
||||||
|
"github.com/superseriousbusiness/gotosocial/internal/api"
|
||||||
"github.com/superseriousbusiness/gotosocial/internal/oauth"
|
"github.com/superseriousbusiness/gotosocial/internal/oauth"
|
||||||
)
|
)
|
||||||
|
|
||||||
|
@ -68,6 +69,11 @@ func (m *Module) AccountFollowersGETHandler(c *gin.Context) {
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if _, err := api.NegotiateAccept(c, api.JSONAcceptHeaders...); err != nil {
|
||||||
|
c.JSON(http.StatusNotAcceptable, gin.H{"error": err.Error()})
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
targetAcctID := c.Param(IDKey)
|
targetAcctID := c.Param(IDKey)
|
||||||
if targetAcctID == "" {
|
if targetAcctID == "" {
|
||||||
c.JSON(http.StatusBadRequest, gin.H{"error": "no account id specified"})
|
c.JSON(http.StatusBadRequest, gin.H{"error": "no account id specified"})
|
||||||
|
|
|
@ -22,6 +22,7 @@ import (
|
||||||
"net/http"
|
"net/http"
|
||||||
|
|
||||||
"github.com/gin-gonic/gin"
|
"github.com/gin-gonic/gin"
|
||||||
|
"github.com/superseriousbusiness/gotosocial/internal/api"
|
||||||
"github.com/superseriousbusiness/gotosocial/internal/oauth"
|
"github.com/superseriousbusiness/gotosocial/internal/oauth"
|
||||||
)
|
)
|
||||||
|
|
||||||
|
@ -68,6 +69,11 @@ func (m *Module) AccountFollowingGETHandler(c *gin.Context) {
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if _, err := api.NegotiateAccept(c, api.JSONAcceptHeaders...); err != nil {
|
||||||
|
c.JSON(http.StatusNotAcceptable, gin.H{"error": err.Error()})
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
targetAcctID := c.Param(IDKey)
|
targetAcctID := c.Param(IDKey)
|
||||||
if targetAcctID == "" {
|
if targetAcctID == "" {
|
||||||
c.JSON(http.StatusBadRequest, gin.H{"error": "no account id specified"})
|
c.JSON(http.StatusBadRequest, gin.H{"error": "no account id specified"})
|
||||||
|
|
|
@ -1,10 +1,12 @@
|
||||||
package account
|
package account
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"github.com/sirupsen/logrus"
|
|
||||||
"net/http"
|
"net/http"
|
||||||
|
|
||||||
|
"github.com/sirupsen/logrus"
|
||||||
|
|
||||||
"github.com/gin-gonic/gin"
|
"github.com/gin-gonic/gin"
|
||||||
|
"github.com/superseriousbusiness/gotosocial/internal/api"
|
||||||
"github.com/superseriousbusiness/gotosocial/internal/api/model"
|
"github.com/superseriousbusiness/gotosocial/internal/api/model"
|
||||||
"github.com/superseriousbusiness/gotosocial/internal/oauth"
|
"github.com/superseriousbusiness/gotosocial/internal/oauth"
|
||||||
)
|
)
|
||||||
|
@ -57,6 +59,11 @@ func (m *Module) AccountRelationshipsGETHandler(c *gin.Context) {
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if _, err := api.NegotiateAccept(c, api.JSONAcceptHeaders...); err != nil {
|
||||||
|
c.JSON(http.StatusNotAcceptable, gin.H{"error": err.Error()})
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
targetAccountIDs := c.QueryArray("id[]")
|
targetAccountIDs := c.QueryArray("id[]")
|
||||||
if len(targetAccountIDs) == 0 {
|
if len(targetAccountIDs) == 0 {
|
||||||
// check fallback -- let's be generous and see if maybe it's just set as 'id'?
|
// check fallback -- let's be generous and see if maybe it's just set as 'id'?
|
||||||
|
|
|
@ -19,11 +19,13 @@
|
||||||
package account
|
package account
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"github.com/sirupsen/logrus"
|
|
||||||
"net/http"
|
"net/http"
|
||||||
"strconv"
|
"strconv"
|
||||||
|
|
||||||
|
"github.com/sirupsen/logrus"
|
||||||
|
|
||||||
"github.com/gin-gonic/gin"
|
"github.com/gin-gonic/gin"
|
||||||
|
"github.com/superseriousbusiness/gotosocial/internal/api"
|
||||||
"github.com/superseriousbusiness/gotosocial/internal/oauth"
|
"github.com/superseriousbusiness/gotosocial/internal/oauth"
|
||||||
)
|
)
|
||||||
|
|
||||||
|
@ -118,6 +120,11 @@ func (m *Module) AccountStatusesGETHandler(c *gin.Context) {
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if _, err := api.NegotiateAccept(c, api.JSONAcceptHeaders...); err != nil {
|
||||||
|
c.JSON(http.StatusNotAcceptable, gin.H{"error": err.Error()})
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
targetAcctID := c.Param(IDKey)
|
targetAcctID := c.Param(IDKey)
|
||||||
if targetAcctID == "" {
|
if targetAcctID == "" {
|
||||||
l.Debug("no account id specified in query")
|
l.Debug("no account id specified in query")
|
||||||
|
|
|
@ -22,6 +22,7 @@ import (
|
||||||
"net/http"
|
"net/http"
|
||||||
|
|
||||||
"github.com/gin-gonic/gin"
|
"github.com/gin-gonic/gin"
|
||||||
|
"github.com/superseriousbusiness/gotosocial/internal/api"
|
||||||
"github.com/superseriousbusiness/gotosocial/internal/oauth"
|
"github.com/superseriousbusiness/gotosocial/internal/oauth"
|
||||||
)
|
)
|
||||||
|
|
||||||
|
@ -66,6 +67,11 @@ func (m *Module) AccountUnblockPOSTHandler(c *gin.Context) {
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if _, err := api.NegotiateAccept(c, api.JSONAcceptHeaders...); err != nil {
|
||||||
|
c.JSON(http.StatusNotAcceptable, gin.H{"error": err.Error()})
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
targetAcctID := c.Param(IDKey)
|
targetAcctID := c.Param(IDKey)
|
||||||
if targetAcctID == "" {
|
if targetAcctID == "" {
|
||||||
c.JSON(http.StatusBadRequest, gin.H{"error": "no account id specified"})
|
c.JSON(http.StatusBadRequest, gin.H{"error": "no account id specified"})
|
||||||
|
|
|
@ -19,10 +19,12 @@
|
||||||
package account
|
package account
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"github.com/sirupsen/logrus"
|
|
||||||
"net/http"
|
"net/http"
|
||||||
|
|
||||||
|
"github.com/sirupsen/logrus"
|
||||||
|
|
||||||
"github.com/gin-gonic/gin"
|
"github.com/gin-gonic/gin"
|
||||||
|
"github.com/superseriousbusiness/gotosocial/internal/api"
|
||||||
"github.com/superseriousbusiness/gotosocial/internal/oauth"
|
"github.com/superseriousbusiness/gotosocial/internal/oauth"
|
||||||
)
|
)
|
||||||
|
|
||||||
|
@ -69,6 +71,11 @@ func (m *Module) AccountUnfollowPOSTHandler(c *gin.Context) {
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if _, err := api.NegotiateAccept(c, api.JSONAcceptHeaders...); err != nil {
|
||||||
|
c.JSON(http.StatusNotAcceptable, gin.H{"error": err.Error()})
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
targetAcctID := c.Param(IDKey)
|
targetAcctID := c.Param(IDKey)
|
||||||
if targetAcctID == "" {
|
if targetAcctID == "" {
|
||||||
l.Debug(err)
|
l.Debug(err)
|
||||||
|
|
|
@ -8,6 +8,7 @@ import (
|
||||||
|
|
||||||
"github.com/gin-gonic/gin"
|
"github.com/gin-gonic/gin"
|
||||||
"github.com/sirupsen/logrus"
|
"github.com/sirupsen/logrus"
|
||||||
|
"github.com/superseriousbusiness/gotosocial/internal/api"
|
||||||
"github.com/superseriousbusiness/gotosocial/internal/api/model"
|
"github.com/superseriousbusiness/gotosocial/internal/api/model"
|
||||||
"github.com/superseriousbusiness/gotosocial/internal/oauth"
|
"github.com/superseriousbusiness/gotosocial/internal/oauth"
|
||||||
)
|
)
|
||||||
|
@ -110,6 +111,11 @@ func (m *Module) DomainBlocksPOSTHandler(c *gin.Context) {
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if _, err := api.NegotiateAccept(c, api.JSONAcceptHeaders...); err != nil {
|
||||||
|
c.JSON(http.StatusNotAcceptable, gin.H{"error": err.Error()})
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
imp := false
|
imp := false
|
||||||
importString := c.Query(ImportQueryKey)
|
importString := c.Query(ImportQueryKey)
|
||||||
if importString != "" {
|
if importString != "" {
|
||||||
|
|
|
@ -5,6 +5,7 @@ import (
|
||||||
|
|
||||||
"github.com/gin-gonic/gin"
|
"github.com/gin-gonic/gin"
|
||||||
"github.com/sirupsen/logrus"
|
"github.com/sirupsen/logrus"
|
||||||
|
"github.com/superseriousbusiness/gotosocial/internal/api"
|
||||||
"github.com/superseriousbusiness/gotosocial/internal/oauth"
|
"github.com/superseriousbusiness/gotosocial/internal/oauth"
|
||||||
)
|
)
|
||||||
|
|
||||||
|
@ -62,6 +63,11 @@ func (m *Module) DomainBlockDELETEHandler(c *gin.Context) {
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if _, err := api.NegotiateAccept(c, api.JSONAcceptHeaders...); err != nil {
|
||||||
|
c.JSON(http.StatusNotAcceptable, gin.H{"error": err.Error()})
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
domainBlockID := c.Param(IDKey)
|
domainBlockID := c.Param(IDKey)
|
||||||
if domainBlockID == "" {
|
if domainBlockID == "" {
|
||||||
c.JSON(http.StatusBadRequest, gin.H{"error": "no domain block id provided"})
|
c.JSON(http.StatusBadRequest, gin.H{"error": "no domain block id provided"})
|
||||||
|
|
|
@ -6,6 +6,7 @@ import (
|
||||||
|
|
||||||
"github.com/gin-gonic/gin"
|
"github.com/gin-gonic/gin"
|
||||||
"github.com/sirupsen/logrus"
|
"github.com/sirupsen/logrus"
|
||||||
|
"github.com/superseriousbusiness/gotosocial/internal/api"
|
||||||
"github.com/superseriousbusiness/gotosocial/internal/oauth"
|
"github.com/superseriousbusiness/gotosocial/internal/oauth"
|
||||||
)
|
)
|
||||||
|
|
||||||
|
@ -63,6 +64,11 @@ func (m *Module) DomainBlockGETHandler(c *gin.Context) {
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if _, err := api.NegotiateAccept(c, api.JSONAcceptHeaders...); err != nil {
|
||||||
|
c.JSON(http.StatusNotAcceptable, gin.H{"error": err.Error()})
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
domainBlockID := c.Param(IDKey)
|
domainBlockID := c.Param(IDKey)
|
||||||
if domainBlockID == "" {
|
if domainBlockID == "" {
|
||||||
c.JSON(http.StatusBadRequest, gin.H{"error": "no domain block id provided"})
|
c.JSON(http.StatusBadRequest, gin.H{"error": "no domain block id provided"})
|
||||||
|
|
|
@ -6,6 +6,7 @@ import (
|
||||||
|
|
||||||
"github.com/gin-gonic/gin"
|
"github.com/gin-gonic/gin"
|
||||||
"github.com/sirupsen/logrus"
|
"github.com/sirupsen/logrus"
|
||||||
|
"github.com/superseriousbusiness/gotosocial/internal/api"
|
||||||
"github.com/superseriousbusiness/gotosocial/internal/oauth"
|
"github.com/superseriousbusiness/gotosocial/internal/oauth"
|
||||||
)
|
)
|
||||||
|
|
||||||
|
@ -69,6 +70,11 @@ func (m *Module) DomainBlocksGETHandler(c *gin.Context) {
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if _, err := api.NegotiateAccept(c, api.JSONAcceptHeaders...); err != nil {
|
||||||
|
c.JSON(http.StatusNotAcceptable, gin.H{"error": err.Error()})
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
export := false
|
export := false
|
||||||
exportString := c.Query(ExportQueryKey)
|
exportString := c.Query(ExportQueryKey)
|
||||||
if exportString != "" {
|
if exportString != "" {
|
||||||
|
|
|
@ -25,6 +25,7 @@ import (
|
||||||
|
|
||||||
"github.com/gin-gonic/gin"
|
"github.com/gin-gonic/gin"
|
||||||
"github.com/sirupsen/logrus"
|
"github.com/sirupsen/logrus"
|
||||||
|
"github.com/superseriousbusiness/gotosocial/internal/api"
|
||||||
"github.com/superseriousbusiness/gotosocial/internal/api/model"
|
"github.com/superseriousbusiness/gotosocial/internal/api/model"
|
||||||
"github.com/superseriousbusiness/gotosocial/internal/media"
|
"github.com/superseriousbusiness/gotosocial/internal/media"
|
||||||
"github.com/superseriousbusiness/gotosocial/internal/oauth"
|
"github.com/superseriousbusiness/gotosocial/internal/oauth"
|
||||||
|
@ -94,6 +95,11 @@ func (m *Module) emojiCreatePOSTHandler(c *gin.Context) {
|
||||||
return
|
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
|
// extract the media create form from the request context
|
||||||
l.Tracef("parsing request form: %+v", c.Request.Form)
|
l.Tracef("parsing request form: %+v", c.Request.Form)
|
||||||
form := &model.EmojiCreateRequest{}
|
form := &model.EmojiCreateRequest{}
|
||||||
|
|
|
@ -25,6 +25,7 @@ import (
|
||||||
"github.com/sirupsen/logrus"
|
"github.com/sirupsen/logrus"
|
||||||
|
|
||||||
"github.com/gin-gonic/gin"
|
"github.com/gin-gonic/gin"
|
||||||
|
"github.com/superseriousbusiness/gotosocial/internal/api"
|
||||||
"github.com/superseriousbusiness/gotosocial/internal/api/model"
|
"github.com/superseriousbusiness/gotosocial/internal/api/model"
|
||||||
"github.com/superseriousbusiness/gotosocial/internal/oauth"
|
"github.com/superseriousbusiness/gotosocial/internal/oauth"
|
||||||
)
|
)
|
||||||
|
@ -81,6 +82,11 @@ func (m *Module) AppsPOSTHandler(c *gin.Context) {
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if _, err := api.NegotiateAccept(c, api.JSONAcceptHeaders...); err != nil {
|
||||||
|
c.JSON(http.StatusNotAcceptable, gin.H{"error": err.Error()})
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
form := &model.ApplicationCreateRequest{}
|
form := &model.ApplicationCreateRequest{}
|
||||||
if err := c.ShouldBind(form); err != nil {
|
if err := c.ShouldBind(form); err != nil {
|
||||||
c.JSON(http.StatusUnprocessableEntity, gin.H{"error": err.Error()})
|
c.JSON(http.StatusUnprocessableEntity, gin.H{"error": err.Error()})
|
||||||
|
|
|
@ -21,14 +21,16 @@ package auth
|
||||||
import (
|
import (
|
||||||
"errors"
|
"errors"
|
||||||
"fmt"
|
"fmt"
|
||||||
"github.com/sirupsen/logrus"
|
|
||||||
"net/http"
|
"net/http"
|
||||||
"net/url"
|
"net/url"
|
||||||
"strings"
|
"strings"
|
||||||
|
|
||||||
|
"github.com/sirupsen/logrus"
|
||||||
|
|
||||||
"github.com/gin-contrib/sessions"
|
"github.com/gin-contrib/sessions"
|
||||||
"github.com/gin-gonic/gin"
|
"github.com/gin-gonic/gin"
|
||||||
"github.com/google/uuid"
|
"github.com/google/uuid"
|
||||||
|
"github.com/superseriousbusiness/gotosocial/internal/api"
|
||||||
"github.com/superseriousbusiness/gotosocial/internal/api/model"
|
"github.com/superseriousbusiness/gotosocial/internal/api/model"
|
||||||
"github.com/superseriousbusiness/gotosocial/internal/db"
|
"github.com/superseriousbusiness/gotosocial/internal/db"
|
||||||
"github.com/superseriousbusiness/gotosocial/internal/gtsmodel"
|
"github.com/superseriousbusiness/gotosocial/internal/gtsmodel"
|
||||||
|
@ -41,6 +43,11 @@ func (m *Module) AuthorizeGETHandler(c *gin.Context) {
|
||||||
l := logrus.WithField("func", "AuthorizeGETHandler")
|
l := logrus.WithField("func", "AuthorizeGETHandler")
|
||||||
s := sessions.Default(c)
|
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
|
// 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.
|
// 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)
|
userID, ok := s.Get(sessionUserID).(string)
|
||||||
|
|
|
@ -21,11 +21,13 @@ package auth
|
||||||
import (
|
import (
|
||||||
"context"
|
"context"
|
||||||
"errors"
|
"errors"
|
||||||
"github.com/sirupsen/logrus"
|
|
||||||
"net/http"
|
"net/http"
|
||||||
|
|
||||||
|
"github.com/sirupsen/logrus"
|
||||||
|
|
||||||
"github.com/gin-contrib/sessions"
|
"github.com/gin-contrib/sessions"
|
||||||
"github.com/gin-gonic/gin"
|
"github.com/gin-gonic/gin"
|
||||||
|
"github.com/superseriousbusiness/gotosocial/internal/api"
|
||||||
"github.com/superseriousbusiness/gotosocial/internal/db"
|
"github.com/superseriousbusiness/gotosocial/internal/db"
|
||||||
"github.com/superseriousbusiness/gotosocial/internal/gtsmodel"
|
"github.com/superseriousbusiness/gotosocial/internal/gtsmodel"
|
||||||
"golang.org/x/crypto/bcrypt"
|
"golang.org/x/crypto/bcrypt"
|
||||||
|
@ -43,6 +45,12 @@ type login struct {
|
||||||
func (m *Module) SignInGETHandler(c *gin.Context) {
|
func (m *Module) SignInGETHandler(c *gin.Context) {
|
||||||
l := logrus.WithField("func", "SignInGETHandler")
|
l := logrus.WithField("func", "SignInGETHandler")
|
||||||
l.Trace("entering sign in handler")
|
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 {
|
if m.idp != nil {
|
||||||
s := sessions.Default(c)
|
s := sessions.Default(c)
|
||||||
|
|
||||||
|
|
|
@ -19,10 +19,12 @@
|
||||||
package auth
|
package auth
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"github.com/sirupsen/logrus"
|
|
||||||
"net/http"
|
"net/http"
|
||||||
"net/url"
|
"net/url"
|
||||||
|
|
||||||
|
"github.com/sirupsen/logrus"
|
||||||
|
"github.com/superseriousbusiness/gotosocial/internal/api"
|
||||||
|
|
||||||
"github.com/gin-gonic/gin"
|
"github.com/gin-gonic/gin"
|
||||||
)
|
)
|
||||||
|
|
||||||
|
@ -41,6 +43,11 @@ func (m *Module) TokenPOSTHandler(c *gin.Context) {
|
||||||
l := logrus.WithField("func", "TokenPOSTHandler")
|
l := logrus.WithField("func", "TokenPOSTHandler")
|
||||||
l.Trace("entered 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{}
|
form := &tokenBody{}
|
||||||
if err := c.ShouldBind(form); err == nil {
|
if err := c.ShouldBind(form); err == nil {
|
||||||
c.Request.Form = url.Values{}
|
c.Request.Form = url.Values{}
|
||||||
|
|
|
@ -19,11 +19,13 @@
|
||||||
package blocks
|
package blocks
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"github.com/sirupsen/logrus"
|
|
||||||
"net/http"
|
"net/http"
|
||||||
"strconv"
|
"strconv"
|
||||||
|
|
||||||
|
"github.com/sirupsen/logrus"
|
||||||
|
|
||||||
"github.com/gin-gonic/gin"
|
"github.com/gin-gonic/gin"
|
||||||
|
"github.com/superseriousbusiness/gotosocial/internal/api"
|
||||||
"github.com/superseriousbusiness/gotosocial/internal/oauth"
|
"github.com/superseriousbusiness/gotosocial/internal/oauth"
|
||||||
)
|
)
|
||||||
|
|
||||||
|
@ -94,6 +96,11 @@ func (m *Module) BlocksGETHandler(c *gin.Context) {
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if _, err := api.NegotiateAccept(c, api.JSONAcceptHeaders...); err != nil {
|
||||||
|
c.JSON(http.StatusNotAcceptable, gin.H{"error": err.Error()})
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
maxID := ""
|
maxID := ""
|
||||||
maxIDString := c.Query(MaxIDKey)
|
maxIDString := c.Query(MaxIDKey)
|
||||||
if maxIDString != "" {
|
if maxIDString != "" {
|
||||||
|
|
|
@ -4,9 +4,15 @@ import (
|
||||||
"net/http"
|
"net/http"
|
||||||
|
|
||||||
"github.com/gin-gonic/gin"
|
"github.com/gin-gonic/gin"
|
||||||
|
"github.com/superseriousbusiness/gotosocial/internal/api"
|
||||||
)
|
)
|
||||||
|
|
||||||
// EmojisGETHandler returns a list of custom emojis enabled on the instance
|
// EmojisGETHandler returns a list of custom emojis enabled on the instance
|
||||||
func (m *Module) EmojisGETHandler(c *gin.Context) {
|
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{})
|
c.JSON(http.StatusOK, []string{})
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,11 +1,13 @@
|
||||||
package favourites
|
package favourites
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"github.com/sirupsen/logrus"
|
|
||||||
"net/http"
|
"net/http"
|
||||||
"strconv"
|
"strconv"
|
||||||
|
|
||||||
|
"github.com/sirupsen/logrus"
|
||||||
|
|
||||||
"github.com/gin-gonic/gin"
|
"github.com/gin-gonic/gin"
|
||||||
|
"github.com/superseriousbusiness/gotosocial/internal/api"
|
||||||
"github.com/superseriousbusiness/gotosocial/internal/oauth"
|
"github.com/superseriousbusiness/gotosocial/internal/oauth"
|
||||||
)
|
)
|
||||||
|
|
||||||
|
@ -20,6 +22,11 @@ func (m *Module) FavouritesGETHandler(c *gin.Context) {
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if _, err := api.NegotiateAccept(c, api.JSONAcceptHeaders...); err != nil {
|
||||||
|
c.JSON(http.StatusNotAcceptable, gin.H{"error": err.Error()})
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
maxID := ""
|
maxID := ""
|
||||||
maxIDString := c.Query(MaxIDKey)
|
maxIDString := c.Query(MaxIDKey)
|
||||||
if maxIDString != "" {
|
if maxIDString != "" {
|
||||||
|
|
|
@ -24,6 +24,7 @@ import (
|
||||||
|
|
||||||
"github.com/gin-gonic/gin"
|
"github.com/gin-gonic/gin"
|
||||||
"github.com/sirupsen/logrus"
|
"github.com/sirupsen/logrus"
|
||||||
|
"github.com/superseriousbusiness/gotosocial/internal/api"
|
||||||
"github.com/superseriousbusiness/gotosocial/internal/api/model"
|
"github.com/superseriousbusiness/gotosocial/internal/api/model"
|
||||||
"github.com/superseriousbusiness/gotosocial/internal/oauth"
|
"github.com/superseriousbusiness/gotosocial/internal/oauth"
|
||||||
)
|
)
|
||||||
|
@ -90,14 +91,14 @@ func (m *FileServer) ServeFile(c *gin.Context) {
|
||||||
return
|
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
|
// 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.
|
// attempt to look up the content to provide a preview of the link, and they ask for text/html.
|
||||||
if c.NegotiateFormat(content.ContentType) == "" {
|
format, err := api.NegotiateAccept(c, api.Offer(content.ContentType))
|
||||||
l.Debugf("couldn't negotiate content for Accept headers %+v: we have content type %s", c.Request.Header.Get("Accepted"), content.ContentType)
|
if err != nil {
|
||||||
c.AbortWithStatus(http.StatusNotAcceptable)
|
c.JSON(http.StatusNotAcceptable, gin.H{"error": err.Error()})
|
||||||
return
|
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()
|
recorder := httptest.NewRecorder()
|
||||||
ctx, _ := gin.CreateTestContext(recorder)
|
ctx, _ := gin.CreateTestContext(recorder)
|
||||||
ctx.Request = httptest.NewRequest(http.MethodGet, targetAttachment.URL, nil)
|
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,
|
// 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.
|
// but because we're calling the ServeFile function directly, we need to set them manually.
|
||||||
|
|
|
@ -4,9 +4,15 @@ import (
|
||||||
"net/http"
|
"net/http"
|
||||||
|
|
||||||
"github.com/gin-gonic/gin"
|
"github.com/gin-gonic/gin"
|
||||||
|
"github.com/superseriousbusiness/gotosocial/internal/api"
|
||||||
)
|
)
|
||||||
|
|
||||||
// FiltersGETHandler returns a list of filters set by/for the authed account
|
// FiltersGETHandler returns a list of filters set by/for the authed account
|
||||||
func (m *Module) FiltersGETHandler(c *gin.Context) {
|
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{})
|
c.JSON(http.StatusOK, []string{})
|
||||||
}
|
}
|
||||||
|
|
|
@ -19,10 +19,12 @@
|
||||||
package followrequest
|
package followrequest
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"github.com/sirupsen/logrus"
|
|
||||||
"net/http"
|
"net/http"
|
||||||
|
|
||||||
|
"github.com/sirupsen/logrus"
|
||||||
|
|
||||||
"github.com/gin-gonic/gin"
|
"github.com/gin-gonic/gin"
|
||||||
|
"github.com/superseriousbusiness/gotosocial/internal/api"
|
||||||
"github.com/superseriousbusiness/gotosocial/internal/oauth"
|
"github.com/superseriousbusiness/gotosocial/internal/oauth"
|
||||||
)
|
)
|
||||||
|
|
||||||
|
@ -76,6 +78,11 @@ func (m *Module) FollowRequestAuthorizePOSTHandler(c *gin.Context) {
|
||||||
return
|
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() {
|
if authed.User.Disabled || !authed.User.Approved || !authed.Account.SuspendedAt.IsZero() {
|
||||||
l.Debugf("couldn't auth: %s", err)
|
l.Debugf("couldn't auth: %s", err)
|
||||||
c.JSON(http.StatusForbidden, gin.H{"error": "account is disabled, not yet approved, or suspended"})
|
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 != "" {
|
if bodyContentType != "" {
|
||||||
ctx.Request.Header.Set("Content-Type", bodyContentType)
|
ctx.Request.Header.Set("Content-Type", bodyContentType)
|
||||||
}
|
}
|
||||||
|
ctx.Request.Header.Set("accept", "application/json")
|
||||||
|
|
||||||
return ctx
|
return ctx
|
||||||
}
|
}
|
||||||
|
|
|
@ -19,10 +19,12 @@
|
||||||
package followrequest
|
package followrequest
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"github.com/sirupsen/logrus"
|
|
||||||
"net/http"
|
"net/http"
|
||||||
|
|
||||||
|
"github.com/sirupsen/logrus"
|
||||||
|
|
||||||
"github.com/gin-gonic/gin"
|
"github.com/gin-gonic/gin"
|
||||||
|
"github.com/superseriousbusiness/gotosocial/internal/api"
|
||||||
"github.com/superseriousbusiness/gotosocial/internal/oauth"
|
"github.com/superseriousbusiness/gotosocial/internal/oauth"
|
||||||
)
|
)
|
||||||
|
|
||||||
|
@ -83,6 +85,11 @@ func (m *Module) FollowRequestGETHandler(c *gin.Context) {
|
||||||
return
|
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() {
|
if authed.User.Disabled || !authed.User.Approved || !authed.Account.SuspendedAt.IsZero() {
|
||||||
l.Debugf("couldn't auth: %s", err)
|
l.Debugf("couldn't auth: %s", err)
|
||||||
c.JSON(http.StatusForbidden, gin.H{"error": "account is disabled, not yet approved, or suspended"})
|
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/gin-gonic/gin"
|
||||||
"github.com/sirupsen/logrus"
|
"github.com/sirupsen/logrus"
|
||||||
|
"github.com/superseriousbusiness/gotosocial/internal/api"
|
||||||
"github.com/superseriousbusiness/gotosocial/internal/oauth"
|
"github.com/superseriousbusiness/gotosocial/internal/oauth"
|
||||||
)
|
)
|
||||||
|
|
||||||
|
@ -74,6 +75,11 @@ func (m *Module) FollowRequestRejectPOSTHandler(c *gin.Context) {
|
||||||
return
|
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() {
|
if authed.User.Disabled || !authed.User.Approved || !authed.Account.SuspendedAt.IsZero() {
|
||||||
l.Debugf("couldn't auth: %s", err)
|
l.Debugf("couldn't auth: %s", err)
|
||||||
c.JSON(http.StatusForbidden, gin.H{"error": "account is disabled, not yet approved, or suspended"})
|
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/sirupsen/logrus"
|
||||||
"github.com/spf13/viper"
|
"github.com/spf13/viper"
|
||||||
|
"github.com/superseriousbusiness/gotosocial/internal/api"
|
||||||
"github.com/superseriousbusiness/gotosocial/internal/config"
|
"github.com/superseriousbusiness/gotosocial/internal/config"
|
||||||
|
|
||||||
"github.com/gin-gonic/gin"
|
"github.com/gin-gonic/gin"
|
||||||
|
@ -35,6 +36,11 @@ import (
|
||||||
func (m *Module) InstanceInformationGETHandler(c *gin.Context) {
|
func (m *Module) InstanceInformationGETHandler(c *gin.Context) {
|
||||||
l := logrus.WithField("func", "InstanceInformationGETHandler")
|
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)
|
host := viper.GetString(config.Keys.Host)
|
||||||
|
|
||||||
instance, err := m.processor.InstanceGet(c.Request.Context(), host)
|
instance, err := m.processor.InstanceGet(c.Request.Context(), host)
|
||||||
|
|
|
@ -1,10 +1,12 @@
|
||||||
package instance
|
package instance
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"github.com/sirupsen/logrus"
|
|
||||||
"net/http"
|
"net/http"
|
||||||
|
|
||||||
|
"github.com/sirupsen/logrus"
|
||||||
|
|
||||||
"github.com/gin-gonic/gin"
|
"github.com/gin-gonic/gin"
|
||||||
|
"github.com/superseriousbusiness/gotosocial/internal/api"
|
||||||
"github.com/superseriousbusiness/gotosocial/internal/api/model"
|
"github.com/superseriousbusiness/gotosocial/internal/api/model"
|
||||||
"github.com/superseriousbusiness/gotosocial/internal/oauth"
|
"github.com/superseriousbusiness/gotosocial/internal/oauth"
|
||||||
)
|
)
|
||||||
|
@ -93,6 +95,11 @@ func (m *Module) InstanceUpdatePATCHHandler(c *gin.Context) {
|
||||||
return
|
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
|
// only admins can update instance settings
|
||||||
if !authed.User.Admin {
|
if !authed.User.Admin {
|
||||||
l.Debug("user is not an admin so cannot update instance settings")
|
l.Debug("user is not an admin so cannot update instance settings")
|
||||||
|
|
|
@ -4,9 +4,15 @@ import (
|
||||||
"net/http"
|
"net/http"
|
||||||
|
|
||||||
"github.com/gin-gonic/gin"
|
"github.com/gin-gonic/gin"
|
||||||
|
"github.com/superseriousbusiness/gotosocial/internal/api"
|
||||||
)
|
)
|
||||||
|
|
||||||
// ListsGETHandler returns a list of lists created by/for the authed account
|
// ListsGETHandler returns a list of lists created by/for the authed account
|
||||||
func (m *Module) ListsGETHandler(c *gin.Context) {
|
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{})
|
c.JSON(http.StatusOK, []string{})
|
||||||
}
|
}
|
||||||
|
|
|
@ -27,6 +27,7 @@ import (
|
||||||
"github.com/spf13/viper"
|
"github.com/spf13/viper"
|
||||||
|
|
||||||
"github.com/gin-gonic/gin"
|
"github.com/gin-gonic/gin"
|
||||||
|
"github.com/superseriousbusiness/gotosocial/internal/api"
|
||||||
"github.com/superseriousbusiness/gotosocial/internal/api/model"
|
"github.com/superseriousbusiness/gotosocial/internal/api/model"
|
||||||
"github.com/superseriousbusiness/gotosocial/internal/config"
|
"github.com/superseriousbusiness/gotosocial/internal/config"
|
||||||
"github.com/superseriousbusiness/gotosocial/internal/oauth"
|
"github.com/superseriousbusiness/gotosocial/internal/oauth"
|
||||||
|
@ -93,6 +94,11 @@ func (m *Module) MediaCreatePOSTHandler(c *gin.Context) {
|
||||||
return
|
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
|
// extract the media create form from the request context
|
||||||
l.Tracef("parsing request form: %s", c.Request.Form)
|
l.Tracef("parsing request form: %s", c.Request.Form)
|
||||||
form := &model.AttachmentRequest{}
|
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 = 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("Content-Type", w.FormDataContentType())
|
||||||
|
ctx.Request.Header.Set("accept", "application/json")
|
||||||
|
|
||||||
// do the actual request
|
// do the actual request
|
||||||
suite.mediaModule.MediaCreatePOSTHandler(ctx)
|
suite.mediaModule.MediaCreatePOSTHandler(ctx)
|
||||||
|
|
|
@ -19,10 +19,12 @@
|
||||||
package media
|
package media
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"github.com/sirupsen/logrus"
|
|
||||||
"net/http"
|
"net/http"
|
||||||
|
|
||||||
|
"github.com/sirupsen/logrus"
|
||||||
|
|
||||||
"github.com/gin-gonic/gin"
|
"github.com/gin-gonic/gin"
|
||||||
|
"github.com/superseriousbusiness/gotosocial/internal/api"
|
||||||
"github.com/superseriousbusiness/gotosocial/internal/oauth"
|
"github.com/superseriousbusiness/gotosocial/internal/oauth"
|
||||||
)
|
)
|
||||||
|
|
||||||
|
@ -70,6 +72,11 @@ func (m *Module) MediaGETHandler(c *gin.Context) {
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if _, err := api.NegotiateAccept(c, api.JSONAcceptHeaders...); err != nil {
|
||||||
|
c.JSON(http.StatusNotAcceptable, gin.H{"error": err.Error()})
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
attachmentID := c.Param(IDKey)
|
attachmentID := c.Param(IDKey)
|
||||||
if attachmentID == "" {
|
if attachmentID == "" {
|
||||||
c.JSON(http.StatusBadRequest, gin.H{"error": "no attachment ID given in request"})
|
c.JSON(http.StatusBadRequest, gin.H{"error": "no attachment ID given in request"})
|
||||||
|
|
|
@ -27,6 +27,7 @@ import (
|
||||||
"github.com/spf13/viper"
|
"github.com/spf13/viper"
|
||||||
|
|
||||||
"github.com/gin-gonic/gin"
|
"github.com/gin-gonic/gin"
|
||||||
|
"github.com/superseriousbusiness/gotosocial/internal/api"
|
||||||
"github.com/superseriousbusiness/gotosocial/internal/api/model"
|
"github.com/superseriousbusiness/gotosocial/internal/api/model"
|
||||||
"github.com/superseriousbusiness/gotosocial/internal/config"
|
"github.com/superseriousbusiness/gotosocial/internal/config"
|
||||||
"github.com/superseriousbusiness/gotosocial/internal/oauth"
|
"github.com/superseriousbusiness/gotosocial/internal/oauth"
|
||||||
|
@ -102,6 +103,11 @@ func (m *Module) MediaPUTHandler(c *gin.Context) {
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if _, err := api.NegotiateAccept(c, api.JSONAcceptHeaders...); err != nil {
|
||||||
|
c.JSON(http.StatusNotAcceptable, gin.H{"error": err.Error()})
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
attachmentID := c.Param(IDKey)
|
attachmentID := c.Param(IDKey)
|
||||||
if attachmentID == "" {
|
if attachmentID == "" {
|
||||||
c.JSON(http.StatusBadRequest, gin.H{"error": "no attachment ID given in request"})
|
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/gin-gonic/gin"
|
||||||
"github.com/sirupsen/logrus"
|
"github.com/sirupsen/logrus"
|
||||||
|
"github.com/superseriousbusiness/gotosocial/internal/api"
|
||||||
"github.com/superseriousbusiness/gotosocial/internal/oauth"
|
"github.com/superseriousbusiness/gotosocial/internal/oauth"
|
||||||
)
|
)
|
||||||
|
|
||||||
|
@ -44,6 +45,11 @@ func (m *Module) NotificationsGETHandler(c *gin.Context) {
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if _, err := api.NegotiateAccept(c, api.JSONAcceptHeaders...); err != nil {
|
||||||
|
c.JSON(http.StatusNotAcceptable, gin.H{"error": err.Error()})
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
limit := 20
|
limit := 20
|
||||||
limitString := c.Query(LimitKey)
|
limitString := c.Query(LimitKey)
|
||||||
if limitString != "" {
|
if limitString != "" {
|
||||||
|
|
|
@ -25,6 +25,7 @@ import (
|
||||||
|
|
||||||
"github.com/gin-gonic/gin"
|
"github.com/gin-gonic/gin"
|
||||||
"github.com/sirupsen/logrus"
|
"github.com/sirupsen/logrus"
|
||||||
|
"github.com/superseriousbusiness/gotosocial/internal/api"
|
||||||
"github.com/superseriousbusiness/gotosocial/internal/api/model"
|
"github.com/superseriousbusiness/gotosocial/internal/api/model"
|
||||||
"github.com/superseriousbusiness/gotosocial/internal/oauth"
|
"github.com/superseriousbusiness/gotosocial/internal/oauth"
|
||||||
)
|
)
|
||||||
|
@ -71,6 +72,11 @@ func (m *Module) SearchGETHandler(c *gin.Context) {
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if _, err := api.NegotiateAccept(c, api.JSONAcceptHeaders...); err != nil {
|
||||||
|
c.JSON(http.StatusNotAcceptable, gin.H{"error": err.Error()})
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
accountID := c.Query(AccountIDKey)
|
accountID := c.Query(AccountIDKey)
|
||||||
maxID := c.Query(MaxIDKey)
|
maxID := c.Query(MaxIDKey)
|
||||||
minID := c.Query(MinIDKey)
|
minID := c.Query(MinIDKey)
|
||||||
|
|
|
@ -23,6 +23,7 @@ import (
|
||||||
|
|
||||||
"github.com/gin-gonic/gin"
|
"github.com/gin-gonic/gin"
|
||||||
"github.com/sirupsen/logrus"
|
"github.com/sirupsen/logrus"
|
||||||
|
"github.com/superseriousbusiness/gotosocial/internal/api"
|
||||||
"github.com/superseriousbusiness/gotosocial/internal/oauth"
|
"github.com/superseriousbusiness/gotosocial/internal/oauth"
|
||||||
)
|
)
|
||||||
|
|
||||||
|
@ -74,13 +75,18 @@ func (m *Module) StatusBoostPOSTHandler(c *gin.Context) {
|
||||||
})
|
})
|
||||||
l.Debugf("entering function")
|
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 {
|
if err != nil {
|
||||||
l.Debug("not authed so can't boost status")
|
l.Debug("not authed so can't boost status")
|
||||||
c.JSON(http.StatusUnauthorized, gin.H{"error": "not authorized"})
|
c.JSON(http.StatusUnauthorized, gin.H{"error": "not authorized"})
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if _, err := api.NegotiateAccept(c, api.JSONAcceptHeaders...); err != nil {
|
||||||
|
c.JSON(http.StatusNotAcceptable, gin.H{"error": err.Error()})
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
targetStatusID := c.Param(IDKey)
|
targetStatusID := c.Param(IDKey)
|
||||||
if targetStatusID == "" {
|
if targetStatusID == "" {
|
||||||
c.JSON(http.StatusBadRequest, gin.H{"error": "no status id provided"})
|
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.SessionAuthorizedUser, suite.testUsers["local_account_1"])
|
||||||
ctx.Set(oauth.SessionAuthorizedAccount, suite.testAccounts["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 = 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,
|
// 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.
|
// 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.SessionAuthorizedUser, suite.testUsers["local_account_1"])
|
||||||
ctx.Set(oauth.SessionAuthorizedAccount, suite.testAccounts["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 = 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,
|
// 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.
|
// 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.SessionAuthorizedUser, suite.testUsers["local_account_2"])
|
||||||
ctx.Set(oauth.SessionAuthorizedAccount, suite.testAccounts["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 = 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,
|
// 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.
|
// 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/gin-gonic/gin"
|
||||||
"github.com/sirupsen/logrus"
|
"github.com/sirupsen/logrus"
|
||||||
|
"github.com/superseriousbusiness/gotosocial/internal/api"
|
||||||
"github.com/superseriousbusiness/gotosocial/internal/oauth"
|
"github.com/superseriousbusiness/gotosocial/internal/oauth"
|
||||||
)
|
)
|
||||||
|
|
||||||
|
@ -80,6 +81,11 @@ func (m *Module) StatusContextGETHandler(c *gin.Context) {
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if _, err := api.NegotiateAccept(c, api.JSONAcceptHeaders...); err != nil {
|
||||||
|
c.JSON(http.StatusNotAcceptable, gin.H{"error": err.Error()})
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
targetStatusID := c.Param(IDKey)
|
targetStatusID := c.Param(IDKey)
|
||||||
if targetStatusID == "" {
|
if targetStatusID == "" {
|
||||||
c.JSON(http.StatusBadRequest, gin.H{"error": "no status id provided"})
|
c.JSON(http.StatusBadRequest, gin.H{"error": "no status id provided"})
|
||||||
|
|
|
@ -27,6 +27,7 @@ import (
|
||||||
"github.com/spf13/viper"
|
"github.com/spf13/viper"
|
||||||
|
|
||||||
"github.com/gin-gonic/gin"
|
"github.com/gin-gonic/gin"
|
||||||
|
"github.com/superseriousbusiness/gotosocial/internal/api"
|
||||||
"github.com/superseriousbusiness/gotosocial/internal/api/model"
|
"github.com/superseriousbusiness/gotosocial/internal/api/model"
|
||||||
"github.com/superseriousbusiness/gotosocial/internal/config"
|
"github.com/superseriousbusiness/gotosocial/internal/config"
|
||||||
"github.com/superseriousbusiness/gotosocial/internal/oauth"
|
"github.com/superseriousbusiness/gotosocial/internal/oauth"
|
||||||
|
@ -71,13 +72,18 @@ import (
|
||||||
// description: internal error
|
// description: internal error
|
||||||
func (m *Module) StatusCreatePOSTHandler(c *gin.Context) {
|
func (m *Module) StatusCreatePOSTHandler(c *gin.Context) {
|
||||||
l := logrus.WithField("func", "statusCreatePOSTHandler")
|
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 {
|
if err != nil {
|
||||||
l.Debugf("couldn't auth: %s", err)
|
l.Debugf("couldn't auth: %s", err)
|
||||||
c.JSON(http.StatusForbidden, gin.H{"error": err.Error()})
|
c.JSON(http.StatusForbidden, gin.H{"error": err.Error()})
|
||||||
return
|
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.
|
// First check this user/account is permitted to post new statuses.
|
||||||
// There's no point continuing otherwise.
|
// There's no point continuing otherwise.
|
||||||
if authed.User.Disabled || !authed.User.Approved || !authed.Account.SuspendedAt.IsZero() {
|
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.SessionAuthorizedUser, suite.testUsers["local_account_1"])
|
||||||
ctx.Set(oauth.SessionAuthorizedAccount, suite.testAccounts["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 = 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{
|
ctx.Request.Form = url.Values{
|
||||||
"status": {"this is a brand new status! #helloworld"},
|
"status": {"this is a brand new status! #helloworld"},
|
||||||
"spoiler_text": {"hello hello"},
|
"spoiler_text": {"hello hello"},
|
||||||
|
@ -119,6 +120,7 @@ func (suite *StatusCreateTestSuite) TestPostAnotherNewStatus() {
|
||||||
ctx.Set(oauth.SessionAuthorizedUser, suite.testUsers["local_account_1"])
|
ctx.Set(oauth.SessionAuthorizedUser, suite.testUsers["local_account_1"])
|
||||||
ctx.Set(oauth.SessionAuthorizedAccount, suite.testAccounts["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 = 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{
|
ctx.Request.Form = url.Values{
|
||||||
"status": {statusWithLinksAndTags},
|
"status": {statusWithLinksAndTags},
|
||||||
}
|
}
|
||||||
|
@ -154,6 +156,7 @@ func (suite *StatusCreateTestSuite) TestPostNewStatusWithEmoji() {
|
||||||
ctx.Set(oauth.SessionAuthorizedUser, suite.testUsers["local_account_1"])
|
ctx.Set(oauth.SessionAuthorizedUser, suite.testUsers["local_account_1"])
|
||||||
ctx.Set(oauth.SessionAuthorizedAccount, suite.testAccounts["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 = 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{
|
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: "},
|
"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.SessionAuthorizedUser, suite.testUsers["local_account_1"])
|
||||||
ctx.Set(oauth.SessionAuthorizedAccount, suite.testAccounts["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 = 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{
|
ctx.Request.Form = url.Values{
|
||||||
"status": {"this is a reply to a status that doesn't exist"},
|
"status": {"this is a reply to a status that doesn't exist"},
|
||||||
"spoiler_text": {"don't open cuz it won't work"},
|
"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.SessionAuthorizedUser, suite.testUsers["local_account_1"])
|
||||||
ctx.Set(oauth.SessionAuthorizedAccount, suite.testAccounts["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 = 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{
|
ctx.Request.Form = url.Values{
|
||||||
"status": {fmt.Sprintf("hello @%s this reply should work!", testrig.NewTestAccounts()["local_account_2"].Username)},
|
"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},
|
"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.SessionAuthorizedUser, suite.testUsers["local_account_1"])
|
||||||
ctx.Set(oauth.SessionAuthorizedAccount, suite.testAccounts["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 = 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{
|
ctx.Request.Form = url.Values{
|
||||||
"status": {"here's an image attachment"},
|
"status": {"here's an image attachment"},
|
||||||
"media_ids": {attachment.ID},
|
"media_ids": {attachment.ID},
|
||||||
|
|
|
@ -23,6 +23,7 @@ import (
|
||||||
|
|
||||||
"github.com/gin-gonic/gin"
|
"github.com/gin-gonic/gin"
|
||||||
"github.com/sirupsen/logrus"
|
"github.com/sirupsen/logrus"
|
||||||
|
"github.com/superseriousbusiness/gotosocial/internal/api"
|
||||||
"github.com/superseriousbusiness/gotosocial/internal/oauth"
|
"github.com/superseriousbusiness/gotosocial/internal/oauth"
|
||||||
)
|
)
|
||||||
|
|
||||||
|
@ -73,13 +74,18 @@ func (m *Module) StatusDELETEHandler(c *gin.Context) {
|
||||||
})
|
})
|
||||||
l.Debugf("entering function")
|
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 {
|
if err != nil {
|
||||||
l.Debug("not authed so can't delete status")
|
l.Debug("not authed so can't delete status")
|
||||||
c.JSON(http.StatusUnauthorized, gin.H{"error": "not authorized"})
|
c.JSON(http.StatusUnauthorized, gin.H{"error": "not authorized"})
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if _, err := api.NegotiateAccept(c, api.JSONAcceptHeaders...); err != nil {
|
||||||
|
c.JSON(http.StatusNotAcceptable, gin.H{"error": err.Error()})
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
targetStatusID := c.Param(IDKey)
|
targetStatusID := c.Param(IDKey)
|
||||||
if targetStatusID == "" {
|
if targetStatusID == "" {
|
||||||
c.JSON(http.StatusBadRequest, gin.H{"error": "no status id provided"})
|
c.JSON(http.StatusBadRequest, gin.H{"error": "no status id provided"})
|
||||||
|
|
|
@ -23,6 +23,7 @@ import (
|
||||||
|
|
||||||
"github.com/gin-gonic/gin"
|
"github.com/gin-gonic/gin"
|
||||||
"github.com/sirupsen/logrus"
|
"github.com/sirupsen/logrus"
|
||||||
|
"github.com/superseriousbusiness/gotosocial/internal/api"
|
||||||
"github.com/superseriousbusiness/gotosocial/internal/oauth"
|
"github.com/superseriousbusiness/gotosocial/internal/oauth"
|
||||||
)
|
)
|
||||||
|
|
||||||
|
@ -70,13 +71,18 @@ func (m *Module) StatusFavePOSTHandler(c *gin.Context) {
|
||||||
})
|
})
|
||||||
l.Debugf("entering function")
|
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 {
|
if err != nil {
|
||||||
l.Debug("not authed so can't fave status")
|
l.Debug("not authed so can't fave status")
|
||||||
c.JSON(http.StatusUnauthorized, gin.H{"error": "not authorized"})
|
c.JSON(http.StatusUnauthorized, gin.H{"error": "not authorized"})
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if _, err := api.NegotiateAccept(c, api.JSONAcceptHeaders...); err != nil {
|
||||||
|
c.JSON(http.StatusNotAcceptable, gin.H{"error": err.Error()})
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
targetStatusID := c.Param(IDKey)
|
targetStatusID := c.Param(IDKey)
|
||||||
if targetStatusID == "" {
|
if targetStatusID == "" {
|
||||||
c.JSON(http.StatusBadRequest, gin.H{"error": "no status id provided"})
|
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.SessionAuthorizedUser, suite.testUsers["local_account_1"])
|
||||||
ctx.Set(oauth.SessionAuthorizedAccount, suite.testAccounts["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 = 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,
|
// 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.
|
// 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.SessionAuthorizedUser, suite.testUsers["local_account_1"])
|
||||||
ctx.Set(oauth.SessionAuthorizedAccount, suite.testAccounts["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 = 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,
|
// 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.
|
// 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/gin-gonic/gin"
|
||||||
"github.com/sirupsen/logrus"
|
"github.com/sirupsen/logrus"
|
||||||
|
"github.com/superseriousbusiness/gotosocial/internal/api"
|
||||||
"github.com/superseriousbusiness/gotosocial/internal/oauth"
|
"github.com/superseriousbusiness/gotosocial/internal/oauth"
|
||||||
)
|
)
|
||||||
|
|
||||||
|
@ -71,13 +72,18 @@ func (m *Module) StatusFavedByGETHandler(c *gin.Context) {
|
||||||
})
|
})
|
||||||
l.Debugf("entering function")
|
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 {
|
if err != nil {
|
||||||
l.Errorf("error authing status faved by request: %s", err)
|
l.Errorf("error authing status faved by request: %s", err)
|
||||||
c.JSON(http.StatusBadRequest, gin.H{"error": "not authed"})
|
c.JSON(http.StatusBadRequest, gin.H{"error": "not authed"})
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if _, err := api.NegotiateAccept(c, api.JSONAcceptHeaders...); err != nil {
|
||||||
|
c.JSON(http.StatusNotAcceptable, gin.H{"error": err.Error()})
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
targetStatusID := c.Param(IDKey)
|
targetStatusID := c.Param(IDKey)
|
||||||
if targetStatusID == "" {
|
if targetStatusID == "" {
|
||||||
c.JSON(http.StatusBadRequest, gin.H{"error": "no status id provided"})
|
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.SessionAuthorizedUser, suite.testUsers["local_account_2"])
|
||||||
ctx.Set(oauth.SessionAuthorizedAccount, suite.testAccounts["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 = 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,
|
// 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.
|
// 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/gin-gonic/gin"
|
||||||
"github.com/sirupsen/logrus"
|
"github.com/sirupsen/logrus"
|
||||||
|
"github.com/superseriousbusiness/gotosocial/internal/api"
|
||||||
"github.com/superseriousbusiness/gotosocial/internal/oauth"
|
"github.com/superseriousbusiness/gotosocial/internal/oauth"
|
||||||
)
|
)
|
||||||
|
|
||||||
|
@ -70,13 +71,18 @@ func (m *Module) StatusGETHandler(c *gin.Context) {
|
||||||
})
|
})
|
||||||
l.Debugf("entering function")
|
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 {
|
if err != nil {
|
||||||
l.Errorf("error authing status faved by request: %s", err)
|
l.Errorf("error authing status faved by request: %s", err)
|
||||||
c.JSON(http.StatusBadRequest, gin.H{"error": "not authed"})
|
c.JSON(http.StatusBadRequest, gin.H{"error": "not authed"})
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if _, err := api.NegotiateAccept(c, api.JSONAcceptHeaders...); err != nil {
|
||||||
|
c.JSON(http.StatusNotAcceptable, gin.H{"error": err.Error()})
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
targetStatusID := c.Param(IDKey)
|
targetStatusID := c.Param(IDKey)
|
||||||
if targetStatusID == "" {
|
if targetStatusID == "" {
|
||||||
c.JSON(http.StatusBadRequest, gin.H{"error": "no status id provided"})
|
c.JSON(http.StatusBadRequest, gin.H{"error": "no status id provided"})
|
||||||
|
|
|
@ -23,6 +23,7 @@ import (
|
||||||
|
|
||||||
"github.com/gin-gonic/gin"
|
"github.com/gin-gonic/gin"
|
||||||
"github.com/sirupsen/logrus"
|
"github.com/sirupsen/logrus"
|
||||||
|
"github.com/superseriousbusiness/gotosocial/internal/api"
|
||||||
"github.com/superseriousbusiness/gotosocial/internal/oauth"
|
"github.com/superseriousbusiness/gotosocial/internal/oauth"
|
||||||
)
|
)
|
||||||
|
|
||||||
|
@ -71,13 +72,18 @@ func (m *Module) StatusUnboostPOSTHandler(c *gin.Context) {
|
||||||
})
|
})
|
||||||
l.Debugf("entering function")
|
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 {
|
if err != nil {
|
||||||
l.Debug("not authed so can't unboost status")
|
l.Debug("not authed so can't unboost status")
|
||||||
c.JSON(http.StatusUnauthorized, gin.H{"error": "not authorized"})
|
c.JSON(http.StatusUnauthorized, gin.H{"error": "not authorized"})
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if _, err := api.NegotiateAccept(c, api.JSONAcceptHeaders...); err != nil {
|
||||||
|
c.JSON(http.StatusNotAcceptable, gin.H{"error": err.Error()})
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
targetStatusID := c.Param(IDKey)
|
targetStatusID := c.Param(IDKey)
|
||||||
if targetStatusID == "" {
|
if targetStatusID == "" {
|
||||||
c.JSON(http.StatusBadRequest, gin.H{"error": "no status id provided"})
|
c.JSON(http.StatusBadRequest, gin.H{"error": "no status id provided"})
|
||||||
|
|
|
@ -23,6 +23,7 @@ import (
|
||||||
|
|
||||||
"github.com/gin-gonic/gin"
|
"github.com/gin-gonic/gin"
|
||||||
"github.com/sirupsen/logrus"
|
"github.com/sirupsen/logrus"
|
||||||
|
"github.com/superseriousbusiness/gotosocial/internal/api"
|
||||||
"github.com/superseriousbusiness/gotosocial/internal/oauth"
|
"github.com/superseriousbusiness/gotosocial/internal/oauth"
|
||||||
)
|
)
|
||||||
|
|
||||||
|
@ -70,13 +71,18 @@ func (m *Module) StatusUnfavePOSTHandler(c *gin.Context) {
|
||||||
})
|
})
|
||||||
l.Debugf("entering function")
|
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 {
|
if err != nil {
|
||||||
l.Debug("not authed so can't unfave status")
|
l.Debug("not authed so can't unfave status")
|
||||||
c.JSON(http.StatusUnauthorized, gin.H{"error": "not authorized"})
|
c.JSON(http.StatusUnauthorized, gin.H{"error": "not authorized"})
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if _, err := api.NegotiateAccept(c, api.JSONAcceptHeaders...); err != nil {
|
||||||
|
c.JSON(http.StatusNotAcceptable, gin.H{"error": err.Error()})
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
targetStatusID := c.Param(IDKey)
|
targetStatusID := c.Param(IDKey)
|
||||||
if targetStatusID == "" {
|
if targetStatusID == "" {
|
||||||
c.JSON(http.StatusBadRequest, gin.H{"error": "no status id provided"})
|
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.SessionAuthorizedUser, suite.testUsers["local_account_1"])
|
||||||
ctx.Set(oauth.SessionAuthorizedAccount, suite.testAccounts["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 = 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,
|
// 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.
|
// 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.SessionAuthorizedUser, suite.testUsers["local_account_1"])
|
||||||
ctx.Set(oauth.SessionAuthorizedAccount, suite.testAccounts["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 = 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,
|
// 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.
|
// but because we're calling the function directly, we need to set them manually.
|
||||||
|
|
|
@ -19,11 +19,13 @@
|
||||||
package timeline
|
package timeline
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"github.com/sirupsen/logrus"
|
|
||||||
"net/http"
|
"net/http"
|
||||||
"strconv"
|
"strconv"
|
||||||
|
|
||||||
|
"github.com/sirupsen/logrus"
|
||||||
|
|
||||||
"github.com/gin-gonic/gin"
|
"github.com/gin-gonic/gin"
|
||||||
|
"github.com/superseriousbusiness/gotosocial/internal/api"
|
||||||
"github.com/superseriousbusiness/gotosocial/internal/oauth"
|
"github.com/superseriousbusiness/gotosocial/internal/oauth"
|
||||||
)
|
)
|
||||||
|
|
||||||
|
@ -112,6 +114,11 @@ func (m *Module) HomeTimelineGETHandler(c *gin.Context) {
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if _, err := api.NegotiateAccept(c, api.JSONAcceptHeaders...); err != nil {
|
||||||
|
c.JSON(http.StatusNotAcceptable, gin.H{"error": err.Error()})
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
maxID := ""
|
maxID := ""
|
||||||
maxIDString := c.Query(MaxIDKey)
|
maxIDString := c.Query(MaxIDKey)
|
||||||
if maxIDString != "" {
|
if maxIDString != "" {
|
||||||
|
|
|
@ -19,11 +19,13 @@
|
||||||
package timeline
|
package timeline
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"github.com/sirupsen/logrus"
|
|
||||||
"net/http"
|
"net/http"
|
||||||
"strconv"
|
"strconv"
|
||||||
|
|
||||||
|
"github.com/sirupsen/logrus"
|
||||||
|
|
||||||
"github.com/gin-gonic/gin"
|
"github.com/gin-gonic/gin"
|
||||||
|
"github.com/superseriousbusiness/gotosocial/internal/api"
|
||||||
"github.com/superseriousbusiness/gotosocial/internal/oauth"
|
"github.com/superseriousbusiness/gotosocial/internal/oauth"
|
||||||
)
|
)
|
||||||
|
|
||||||
|
@ -112,6 +114,11 @@ func (m *Module) PublicTimelineGETHandler(c *gin.Context) {
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if _, err := api.NegotiateAccept(c, api.JSONAcceptHeaders...); err != nil {
|
||||||
|
c.JSON(http.StatusNotAcceptable, gin.H{"error": err.Error()})
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
maxID := ""
|
maxID := ""
|
||||||
maxIDString := c.Query(MaxIDKey)
|
maxIDString := c.Query(MaxIDKey)
|
||||||
if maxIDString != "" {
|
if maxIDString != "" {
|
||||||
|
|
|
@ -23,6 +23,7 @@ import (
|
||||||
|
|
||||||
"github.com/gin-gonic/gin"
|
"github.com/gin-gonic/gin"
|
||||||
"github.com/sirupsen/logrus"
|
"github.com/sirupsen/logrus"
|
||||||
|
"github.com/superseriousbusiness/gotosocial/internal/api"
|
||||||
"github.com/superseriousbusiness/gotosocial/internal/api/model"
|
"github.com/superseriousbusiness/gotosocial/internal/api/model"
|
||||||
"github.com/superseriousbusiness/gotosocial/internal/oauth"
|
"github.com/superseriousbusiness/gotosocial/internal/oauth"
|
||||||
)
|
)
|
||||||
|
@ -71,6 +72,11 @@ func (m *Module) PasswordChangePOSTHandler(c *gin.Context) {
|
||||||
return
|
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.
|
// First check this user/account is active.
|
||||||
if authed.User.Disabled || !authed.User.Approved || !authed.Account.SuspendedAt.IsZero() {
|
if authed.User.Disabled || !authed.User.Approved || !authed.Account.SuspendedAt.IsZero() {
|
||||||
l.Debugf("couldn't auth: %s", err)
|
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.SessionAuthorizedUser, suite.testUsers["local_account_1"])
|
||||||
ctx.Set(oauth.SessionAuthorizedAccount, suite.testAccounts["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 = 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{
|
ctx.Request.Form = url.Values{
|
||||||
"old_password": {"password"},
|
"old_password": {"password"},
|
||||||
"new_password": {"peepeepoopoopassword"},
|
"new_password": {"peepeepoopoopassword"},
|
||||||
|
@ -83,6 +84,7 @@ func (suite *PasswordChangeTestSuite) TestPasswordMissingOldPassword() {
|
||||||
ctx.Set(oauth.SessionAuthorizedUser, suite.testUsers["local_account_1"])
|
ctx.Set(oauth.SessionAuthorizedUser, suite.testUsers["local_account_1"])
|
||||||
ctx.Set(oauth.SessionAuthorizedAccount, suite.testAccounts["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 = 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{
|
ctx.Request.Form = url.Values{
|
||||||
"new_password": {"peepeepoopoopassword"},
|
"new_password": {"peepeepoopoopassword"},
|
||||||
}
|
}
|
||||||
|
@ -109,6 +111,7 @@ func (suite *PasswordChangeTestSuite) TestPasswordIncorrectOldPassword() {
|
||||||
ctx.Set(oauth.SessionAuthorizedUser, suite.testUsers["local_account_1"])
|
ctx.Set(oauth.SessionAuthorizedUser, suite.testUsers["local_account_1"])
|
||||||
ctx.Set(oauth.SessionAuthorizedAccount, suite.testAccounts["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 = 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{
|
ctx.Request.Form = url.Values{
|
||||||
"old_password": {"notright"},
|
"old_password": {"notright"},
|
||||||
"new_password": {"peepeepoopoopassword"},
|
"new_password": {"peepeepoopoopassword"},
|
||||||
|
@ -136,6 +139,7 @@ func (suite *PasswordChangeTestSuite) TestPasswordWeakNewPassword() {
|
||||||
ctx.Set(oauth.SessionAuthorizedUser, suite.testUsers["local_account_1"])
|
ctx.Set(oauth.SessionAuthorizedUser, suite.testUsers["local_account_1"])
|
||||||
ctx.Set(oauth.SessionAuthorizedAccount, suite.testAccounts["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 = 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{
|
ctx.Request.Form = url.Values{
|
||||||
"old_password": {"password"},
|
"old_password": {"password"},
|
||||||
"new_password": {"peepeepoopoo"},
|
"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
|
// For example, it would be returned from https://example.org/.well-known/webfinger?resource=acct:some_username@example.org
|
||||||
//
|
//
|
||||||
// See https://webfinger.net/
|
// See https://webfinger.net/
|
||||||
|
//
|
||||||
|
// swagger:model wellKnownResponse
|
||||||
type WellKnownResponse struct {
|
type WellKnownResponse struct {
|
||||||
Subject string `json:"subject,omitempty"`
|
Subject string `json:"subject,omitempty"`
|
||||||
Aliases []string `json:"aliases,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.
|
// Nodeinfo represents a version 2.1 or version 2.0 nodeinfo schema.
|
||||||
// See: https://nodeinfo.diaspora.software/schema.html
|
// See: https://nodeinfo.diaspora.software/schema.html
|
||||||
|
//
|
||||||
|
// swagger:model nodeinfo
|
||||||
type Nodeinfo struct {
|
type Nodeinfo struct {
|
||||||
// The schema version
|
// The schema version
|
||||||
|
// example: 2.0
|
||||||
Version string `json:"version"`
|
Version string `json:"version"`
|
||||||
// Metadata about server software in use.
|
// Metadata about server software in use.
|
||||||
Software NodeInfoSoftware `json:"software"`
|
Software NodeInfoSoftware `json:"software"`
|
||||||
|
@ -50,6 +55,7 @@ type Nodeinfo struct {
|
||||||
// The third party sites this server can connect to via their application API.
|
// The third party sites this server can connect to via their application API.
|
||||||
Services NodeInfoServices `json:"services"`
|
Services NodeInfoServices `json:"services"`
|
||||||
// Whether this server allows open self-registration.
|
// Whether this server allows open self-registration.
|
||||||
|
// example: false
|
||||||
OpenRegistrations bool `json:"openRegistrations"`
|
OpenRegistrations bool `json:"openRegistrations"`
|
||||||
// Usage statistics for this server.
|
// Usage statistics for this server.
|
||||||
Usage NodeInfoUsage `json:"usage"`
|
Usage NodeInfoUsage `json:"usage"`
|
||||||
|
@ -59,7 +65,9 @@ type Nodeinfo struct {
|
||||||
|
|
||||||
// NodeInfoSoftware represents the name and version number of the software of this node.
|
// NodeInfoSoftware represents the name and version number of the software of this node.
|
||||||
type NodeInfoSoftware struct {
|
type NodeInfoSoftware struct {
|
||||||
|
// example: gotosocial
|
||||||
Name string `json:"name"`
|
Name string `json:"name"`
|
||||||
|
// example: 0.1.2 1234567
|
||||||
Version string `json:"version"`
|
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
|
package nodeinfo
|
||||||
|
|
||||||
import (
|
import (
|
||||||
|
"encoding/json"
|
||||||
"net/http"
|
"net/http"
|
||||||
|
|
||||||
"github.com/gin-gonic/gin"
|
"github.com/gin-gonic/gin"
|
||||||
"github.com/sirupsen/logrus"
|
"github.com/sirupsen/logrus"
|
||||||
|
"github.com/superseriousbusiness/gotosocial/internal/api"
|
||||||
)
|
)
|
||||||
|
|
||||||
// NodeInfoGETHandler returns a compliant nodeinfo response to node info queries.
|
// NodeInfoGETHandler swagger:operation GET /nodeinfo/2.0 nodeInfoGet
|
||||||
// See: https://nodeinfo.diaspora.software/
|
//
|
||||||
|
// 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) {
|
func (m *Module) NodeInfoGETHandler(c *gin.Context) {
|
||||||
l := logrus.WithFields(logrus.Fields{
|
l := logrus.WithFields(logrus.Fields{
|
||||||
"func": "NodeInfoGETHandler",
|
"func": "NodeInfoGETHandler",
|
||||||
"user-agent": c.Request.UserAgent(),
|
"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)
|
ni, err := m.processor.GetNodeInfo(c.Request.Context(), c.Request)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
l.Debugf("error with get node info request: %s", err)
|
l.Debugf("error with get node info request: %s", err)
|
||||||
|
@ -40,5 +62,10 @@ func (m *Module) NodeInfoGETHandler(c *gin.Context) {
|
||||||
return
|
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/gin-gonic/gin"
|
||||||
"github.com/sirupsen/logrus"
|
"github.com/sirupsen/logrus"
|
||||||
|
"github.com/superseriousbusiness/gotosocial/internal/api"
|
||||||
)
|
)
|
||||||
|
|
||||||
// NodeInfoWellKnownGETHandler returns a well known response to a query to /.well-known/nodeinfo,
|
// NodeInfoWellKnownGETHandler swagger:operation GET /.well-known/nodeinfo nodeInfoWellKnownGet
|
||||||
// directing (but not redirecting...) callers to the NodeInfoGETHandler.
|
//
|
||||||
|
// 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) {
|
func (m *Module) NodeInfoWellKnownGETHandler(c *gin.Context) {
|
||||||
l := logrus.WithFields(logrus.Fields{
|
l := logrus.WithFields(logrus.Fields{
|
||||||
"func": "NodeInfoWellKnownGETHandler",
|
"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)
|
niRel, err := m.processor.GetNodeInfoRel(c.Request.Context(), c.Request)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
l.Debugf("error with get node info rel request: %s", err)
|
l.Debugf("error with get node info rel request: %s", err)
|
||||||
|
|
|
@ -20,19 +20,11 @@ package user
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"context"
|
"context"
|
||||||
"fmt"
|
|
||||||
|
|
||||||
"github.com/gin-gonic/gin"
|
"github.com/gin-gonic/gin"
|
||||||
"github.com/superseriousbusiness/gotosocial/internal/util"
|
"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
|
// transferContext transfers the signature verifier and signature from the gin context to the request context
|
||||||
func transferContext(c *gin.Context) context.Context {
|
func transferContext(c *gin.Context) context.Context {
|
||||||
ctx := c.Request.Context()
|
ctx := c.Request.Context()
|
||||||
|
@ -50,14 +42,6 @@ func transferContext(c *gin.Context) context.Context {
|
||||||
return ctx
|
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.
|
// SwaggerCollection represents an activitypub collection.
|
||||||
// swagger:model swaggerCollection
|
// swagger:model swaggerCollection
|
||||||
type SwaggerCollection struct {
|
type SwaggerCollection struct {
|
||||||
|
|
|
@ -25,6 +25,7 @@ import (
|
||||||
|
|
||||||
"github.com/gin-gonic/gin"
|
"github.com/gin-gonic/gin"
|
||||||
"github.com/sirupsen/logrus"
|
"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.
|
// 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
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
format, err := negotiateFormat(c)
|
format, err := api.NegotiateAccept(c, api.ActivityPubAcceptHeaders...)
|
||||||
if err != nil {
|
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
|
return
|
||||||
}
|
}
|
||||||
l.Tracef("negotiated format: %s", format)
|
l.Tracef("negotiated format: %s", format)
|
||||||
|
|
|
@ -25,6 +25,7 @@ import (
|
||||||
|
|
||||||
"github.com/gin-gonic/gin"
|
"github.com/gin-gonic/gin"
|
||||||
"github.com/sirupsen/logrus"
|
"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.
|
// 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
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
format, err := negotiateFormat(c)
|
format, err := api.NegotiateAccept(c, api.ActivityPubAcceptHeaders...)
|
||||||
if err != nil {
|
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
|
return
|
||||||
}
|
}
|
||||||
l.Tracef("negotiated format: %s", format)
|
l.Tracef("negotiated format: %s", format)
|
||||||
|
|
|
@ -26,6 +26,7 @@ import (
|
||||||
|
|
||||||
"github.com/gin-gonic/gin"
|
"github.com/gin-gonic/gin"
|
||||||
"github.com/sirupsen/logrus"
|
"github.com/sirupsen/logrus"
|
||||||
|
"github.com/superseriousbusiness/gotosocial/internal/api"
|
||||||
)
|
)
|
||||||
|
|
||||||
// OutboxGETHandler swagger:operation GET /users/{username}/outbox s2sOutboxGet
|
// OutboxGETHandler swagger:operation GET /users/{username}/outbox s2sOutboxGet
|
||||||
|
@ -113,9 +114,9 @@ func (m *Module) OutboxGETHandler(c *gin.Context) {
|
||||||
maxID = maxIDString
|
maxID = maxIDString
|
||||||
}
|
}
|
||||||
|
|
||||||
format, err := negotiateFormat(c)
|
format, err := api.NegotiateAccept(c, api.ActivityPubAcceptHeaders...)
|
||||||
if err != nil {
|
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
|
return
|
||||||
}
|
}
|
||||||
l.Tracef("negotiated format: %s", format)
|
l.Tracef("negotiated format: %s", format)
|
||||||
|
|
|
@ -54,6 +54,7 @@ func (suite *OutboxGetTestSuite) TestGetOutbox() {
|
||||||
recorder := httptest.NewRecorder()
|
recorder := httptest.NewRecorder()
|
||||||
ctx, _ := gin.CreateTestContext(recorder)
|
ctx, _ := gin.CreateTestContext(recorder)
|
||||||
ctx.Request = httptest.NewRequest(http.MethodGet, targetAccount.OutboxURI, nil) // the endpoint we're hitting
|
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("Signature", signedRequest.SignatureHeader)
|
||||||
ctx.Request.Header.Set("Date", signedRequest.DateHeader)
|
ctx.Request.Header.Set("Date", signedRequest.DateHeader)
|
||||||
|
|
||||||
|
@ -108,6 +109,7 @@ func (suite *OutboxGetTestSuite) TestGetOutboxFirstPage() {
|
||||||
recorder := httptest.NewRecorder()
|
recorder := httptest.NewRecorder()
|
||||||
ctx, _ := gin.CreateTestContext(recorder)
|
ctx, _ := gin.CreateTestContext(recorder)
|
||||||
ctx.Request = httptest.NewRequest(http.MethodGet, targetAccount.OutboxURI+"?page=true", nil) // the endpoint we're hitting
|
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("Signature", signedRequest.SignatureHeader)
|
||||||
ctx.Request.Header.Set("Date", signedRequest.DateHeader)
|
ctx.Request.Header.Set("Date", signedRequest.DateHeader)
|
||||||
|
|
||||||
|
@ -162,6 +164,7 @@ func (suite *OutboxGetTestSuite) TestGetOutboxNextPage() {
|
||||||
recorder := httptest.NewRecorder()
|
recorder := httptest.NewRecorder()
|
||||||
ctx, _ := gin.CreateTestContext(recorder)
|
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 = 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("Signature", signedRequest.SignatureHeader)
|
||||||
ctx.Request.Header.Set("Date", signedRequest.DateHeader)
|
ctx.Request.Header.Set("Date", signedRequest.DateHeader)
|
||||||
|
|
||||||
|
|
|
@ -25,6 +25,7 @@ import (
|
||||||
|
|
||||||
"github.com/gin-gonic/gin"
|
"github.com/gin-gonic/gin"
|
||||||
"github.com/sirupsen/logrus"
|
"github.com/sirupsen/logrus"
|
||||||
|
"github.com/superseriousbusiness/gotosocial/internal/api"
|
||||||
)
|
)
|
||||||
|
|
||||||
// PublicKeyGETHandler should be served at eg https://example.org/users/:username/main-key.
|
// 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
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
format, err := negotiateFormat(c)
|
format, err := api.NegotiateAccept(c, api.ActivityPubAcceptHeaders...)
|
||||||
if err != nil {
|
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
|
return
|
||||||
}
|
}
|
||||||
l.Tracef("negotiated format: %s", format)
|
l.Tracef("negotiated format: %s", format)
|
||||||
|
|
|
@ -26,6 +26,7 @@ import (
|
||||||
|
|
||||||
"github.com/gin-gonic/gin"
|
"github.com/gin-gonic/gin"
|
||||||
"github.com/sirupsen/logrus"
|
"github.com/sirupsen/logrus"
|
||||||
|
"github.com/superseriousbusiness/gotosocial/internal/api"
|
||||||
)
|
)
|
||||||
|
|
||||||
// StatusRepliesGETHandler swagger:operation GET /users/{username}/statuses/{status}/replies s2sRepliesGet
|
// StatusRepliesGETHandler swagger:operation GET /users/{username}/statuses/{status}/replies s2sRepliesGet
|
||||||
|
@ -131,9 +132,9 @@ func (m *Module) StatusRepliesGETHandler(c *gin.Context) {
|
||||||
minID = minIDString
|
minID = minIDString
|
||||||
}
|
}
|
||||||
|
|
||||||
format, err := negotiateFormat(c)
|
format, err := api.NegotiateAccept(c, api.ActivityPubAcceptHeaders...)
|
||||||
if err != nil {
|
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
|
return
|
||||||
}
|
}
|
||||||
l.Tracef("negotiated format: %s", format)
|
l.Tracef("negotiated format: %s", format)
|
||||||
|
|
|
@ -57,6 +57,7 @@ func (suite *RepliesGetTestSuite) TestGetReplies() {
|
||||||
recorder := httptest.NewRecorder()
|
recorder := httptest.NewRecorder()
|
||||||
ctx, _ := gin.CreateTestContext(recorder)
|
ctx, _ := gin.CreateTestContext(recorder)
|
||||||
ctx.Request = httptest.NewRequest(http.MethodGet, targetStatus.URI+"/replies", nil) // the endpoint we're hitting
|
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("Signature", signedRequest.SignatureHeader)
|
||||||
ctx.Request.Header.Set("Date", signedRequest.DateHeader)
|
ctx.Request.Header.Set("Date", signedRequest.DateHeader)
|
||||||
|
|
||||||
|
@ -117,6 +118,7 @@ func (suite *RepliesGetTestSuite) TestGetRepliesNext() {
|
||||||
recorder := httptest.NewRecorder()
|
recorder := httptest.NewRecorder()
|
||||||
ctx, _ := gin.CreateTestContext(recorder)
|
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 = 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("Signature", signedRequest.SignatureHeader)
|
||||||
ctx.Request.Header.Set("Date", signedRequest.DateHeader)
|
ctx.Request.Header.Set("Date", signedRequest.DateHeader)
|
||||||
|
|
||||||
|
@ -180,6 +182,7 @@ func (suite *RepliesGetTestSuite) TestGetRepliesLast() {
|
||||||
recorder := httptest.NewRecorder()
|
recorder := httptest.NewRecorder()
|
||||||
ctx, _ := gin.CreateTestContext(recorder)
|
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 = 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("Signature", signedRequest.SignatureHeader)
|
||||||
ctx.Request.Header.Set("Date", signedRequest.DateHeader)
|
ctx.Request.Header.Set("Date", signedRequest.DateHeader)
|
||||||
|
|
||||||
|
|
|
@ -25,6 +25,7 @@ import (
|
||||||
|
|
||||||
"github.com/gin-gonic/gin"
|
"github.com/gin-gonic/gin"
|
||||||
"github.com/sirupsen/logrus"
|
"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.
|
// 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
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
format, err := negotiateFormat(c)
|
format, err := api.NegotiateAccept(c, api.ActivityPubAcceptHeaders...)
|
||||||
if err != nil {
|
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
|
return
|
||||||
}
|
}
|
||||||
l.Tracef("negotiated format: %s", format)
|
l.Tracef("negotiated format: %s", format)
|
||||||
|
|
|
@ -25,6 +25,7 @@ import (
|
||||||
|
|
||||||
"github.com/gin-gonic/gin"
|
"github.com/gin-gonic/gin"
|
||||||
"github.com/sirupsen/logrus"
|
"github.com/sirupsen/logrus"
|
||||||
|
"github.com/superseriousbusiness/gotosocial/internal/api"
|
||||||
)
|
)
|
||||||
|
|
||||||
// UsersGETHandler should be served at https://example.org/users/:username.
|
// UsersGETHandler should be served at https://example.org/users/:username.
|
||||||
|
@ -48,9 +49,9 @@ func (m *Module) UsersGETHandler(c *gin.Context) {
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
format, err := negotiateFormat(c)
|
format, err := api.NegotiateAccept(c, api.ActivityPubAcceptHeaders...)
|
||||||
if err != nil {
|
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
|
return
|
||||||
}
|
}
|
||||||
l.Tracef("negotiated format: %s", format)
|
l.Tracef("negotiated format: %s", format)
|
||||||
|
|
|
@ -55,6 +55,7 @@ func (suite *UserGetTestSuite) TestGetUser() {
|
||||||
recorder := httptest.NewRecorder()
|
recorder := httptest.NewRecorder()
|
||||||
ctx, _ := gin.CreateTestContext(recorder)
|
ctx, _ := gin.CreateTestContext(recorder)
|
||||||
ctx.Request = httptest.NewRequest(http.MethodGet, targetAccount.URI, nil) // the endpoint we're hitting
|
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("Signature", signedRequest.SignatureHeader)
|
||||||
ctx.Request.Header.Set("Date", signedRequest.DateHeader)
|
ctx.Request.Header.Set("Date", signedRequest.DateHeader)
|
||||||
|
|
||||||
|
|
|
@ -27,26 +27,54 @@ import (
|
||||||
"github.com/gin-gonic/gin"
|
"github.com/gin-gonic/gin"
|
||||||
"github.com/sirupsen/logrus"
|
"github.com/sirupsen/logrus"
|
||||||
"github.com/spf13/viper"
|
"github.com/spf13/viper"
|
||||||
|
"github.com/superseriousbusiness/gotosocial/internal/api"
|
||||||
"github.com/superseriousbusiness/gotosocial/internal/config"
|
"github.com/superseriousbusiness/gotosocial/internal/config"
|
||||||
"github.com/superseriousbusiness/gotosocial/internal/util"
|
"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) {
|
func (m *Module) WebfingerGETRequest(c *gin.Context) {
|
||||||
l := logrus.WithFields(logrus.Fields{
|
l := logrus.WithFields(logrus.Fields{
|
||||||
"func": "WebfingerGETRequest",
|
"func": "WebfingerGETRequest",
|
||||||
"user-agent": c.Request.UserAgent(),
|
"user-agent": c.Request.UserAgent(),
|
||||||
})
|
})
|
||||||
|
|
||||||
q, set := c.GetQuery("resource")
|
resourceQuery, set := c.GetQuery("resource")
|
||||||
if !set || q == "" {
|
if !set || resourceQuery == "" {
|
||||||
l.Debug("aborting request because no resource was set in query")
|
l.Debug("aborting request because no resource was set in query")
|
||||||
c.JSON(http.StatusBadRequest, gin.H{"error": "no 'resource' in request query"})
|
c.JSON(http.StatusBadRequest, gin.H{"error": "no 'resource' in request query"})
|
||||||
return
|
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
|
// 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
|
// remove the first @ in @whatever@example.org if it's present
|
||||||
namestring := strings.TrimPrefix(trimAcct, "@")
|
namestring := strings.TrimPrefix(trimAcct, "@")
|
||||||
|
|
||||||
|
|
|
@ -50,6 +50,7 @@ func (suite *WebfingerGetTestSuite) TestFingerUser() {
|
||||||
recorder := httptest.NewRecorder()
|
recorder := httptest.NewRecorder()
|
||||||
ctx, _ := gin.CreateTestContext(recorder)
|
ctx, _ := gin.CreateTestContext(recorder)
|
||||||
ctx.Request = httptest.NewRequest(http.MethodGet, requestPath, nil) // the endpoint we're hitting
|
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
|
// trigger the function being tested
|
||||||
suite.webfingerModule.WebfingerGETRequest(ctx)
|
suite.webfingerModule.WebfingerGETRequest(ctx)
|
||||||
|
@ -83,6 +84,7 @@ func (suite *WebfingerGetTestSuite) TestFingerUserWithDifferentAccountDomainByHo
|
||||||
recorder := httptest.NewRecorder()
|
recorder := httptest.NewRecorder()
|
||||||
ctx, _ := gin.CreateTestContext(recorder)
|
ctx, _ := gin.CreateTestContext(recorder)
|
||||||
ctx.Request = httptest.NewRequest(http.MethodGet, requestPath, nil) // the endpoint we're hitting
|
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
|
// trigger the function being tested
|
||||||
suite.webfingerModule.WebfingerGETRequest(ctx)
|
suite.webfingerModule.WebfingerGETRequest(ctx)
|
||||||
|
@ -116,6 +118,7 @@ func (suite *WebfingerGetTestSuite) TestFingerUserWithDifferentAccountDomainByAc
|
||||||
recorder := httptest.NewRecorder()
|
recorder := httptest.NewRecorder()
|
||||||
ctx, _ := gin.CreateTestContext(recorder)
|
ctx, _ := gin.CreateTestContext(recorder)
|
||||||
ctx.Request = httptest.NewRequest(http.MethodGet, requestPath, nil) // the endpoint we're hitting
|
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
|
// trigger the function being tested
|
||||||
suite.webfingerModule.WebfingerGETRequest(ctx)
|
suite.webfingerModule.WebfingerGETRequest(ctx)
|
||||||
|
@ -141,6 +144,7 @@ func (suite *WebfingerGetTestSuite) TestFingerUserWithoutAcct() {
|
||||||
recorder := httptest.NewRecorder()
|
recorder := httptest.NewRecorder()
|
||||||
ctx, _ := gin.CreateTestContext(recorder)
|
ctx, _ := gin.CreateTestContext(recorder)
|
||||||
ctx.Request = httptest.NewRequest(http.MethodGet, requestPath, nil) // the endpoint we're hitting
|
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
|
// trigger the function being tested
|
||||||
suite.webfingerModule.WebfingerGETRequest(ctx)
|
suite.webfingerModule.WebfingerGETRequest(ctx)
|
||||||
|
|
|
@ -21,7 +21,7 @@ nav:
|
||||||
- "configuration/index.md"
|
- "configuration/index.md"
|
||||||
- "configuration/general.md"
|
- "configuration/general.md"
|
||||||
- "configuration/database.md"
|
- "configuration/database.md"
|
||||||
- "configuration/template.md"
|
- "configuration/web.md"
|
||||||
- "configuration/accounts.md"
|
- "configuration/accounts.md"
|
||||||
- "configuration/media.md"
|
- "configuration/media.md"
|
||||||
- "configuration/storage.md"
|
- "configuration/storage.md"
|
||||||
|
|
Loading…
Reference in New Issue