mirror of
https://github.com/superseriousbusiness/gotosocial.git
synced 2024-11-25 13:16:40 +00:00
[chore] The Big Middleware and API Refactor (tm) (#1250)
* interim commit: start refactoring middlewares into package under router * another interim commit, this is becoming a big job * another fucking massive interim commit * refactor bookmarks to new style * ambassador, wiz zeze commits you are spoiling uz * she compiles, we're getting there * we're just normal men; we're just innocent men * apiutil * whoopsie * i'm glad noone reads commit msgs haha :blob_sweat: * use that weirdo go-bytesize library for maxMultipartMemory * fix media module paths
This commit is contained in:
parent
560ff1209d
commit
941893a774
|
@ -251,7 +251,7 @@ Most of the crucial business logic of the application is found inside the variou
|
|||
|
||||
`internal/ap` - ActivityPub utility functions and interfaces.
|
||||
|
||||
`internal/api` - Models, routers, and utilities for the client and federated (s2s) APIs. This is where routes are attached to the router, and where you want to be if you're adding a route.
|
||||
`internal/api` - Models, routers, and utilities for the client and federated (ActivityPub) APIs. This is where routes are attached to the router, and where you want to be if you're adding a route.
|
||||
|
||||
`internal/concurrency` - Worker models used by the processor and other queues.
|
||||
|
||||
|
@ -283,6 +283,8 @@ Most of the crucial business logic of the application is found inside the variou
|
|||
|
||||
`internal/messages` - Models for wrapping worker messages.
|
||||
|
||||
`internal/middleware` - Gin Gonic router middlewares: http signature checking, cache control, token checks, etc.
|
||||
|
||||
`internal/netutil` - HTTP / net request validation code.
|
||||
|
||||
`internal/oauth` - Wrapper code/interfaces for OAuth server implementation.
|
||||
|
|
|
@ -20,37 +20,20 @@
|
|||
|
||||
import (
|
||||
"context"
|
||||
"errors"
|
||||
"fmt"
|
||||
"net/http"
|
||||
"os"
|
||||
"os/signal"
|
||||
"syscall"
|
||||
|
||||
"github.com/gin-gonic/gin"
|
||||
"github.com/superseriousbusiness/gotosocial/cmd/gotosocial/action"
|
||||
"github.com/superseriousbusiness/gotosocial/internal/api"
|
||||
"github.com/superseriousbusiness/gotosocial/internal/api/client/account"
|
||||
"github.com/superseriousbusiness/gotosocial/internal/api/client/admin"
|
||||
"github.com/superseriousbusiness/gotosocial/internal/api/client/app"
|
||||
"github.com/superseriousbusiness/gotosocial/internal/api/client/auth"
|
||||
"github.com/superseriousbusiness/gotosocial/internal/api/client/blocks"
|
||||
"github.com/superseriousbusiness/gotosocial/internal/api/client/bookmarks"
|
||||
"github.com/superseriousbusiness/gotosocial/internal/api/client/emoji"
|
||||
"github.com/superseriousbusiness/gotosocial/internal/api/client/favourites"
|
||||
"github.com/superseriousbusiness/gotosocial/internal/api/client/fileserver"
|
||||
"github.com/superseriousbusiness/gotosocial/internal/api/client/filter"
|
||||
"github.com/superseriousbusiness/gotosocial/internal/api/client/followrequest"
|
||||
"github.com/superseriousbusiness/gotosocial/internal/api/client/instance"
|
||||
"github.com/superseriousbusiness/gotosocial/internal/api/client/list"
|
||||
mediaModule "github.com/superseriousbusiness/gotosocial/internal/api/client/media"
|
||||
"github.com/superseriousbusiness/gotosocial/internal/api/client/notification"
|
||||
"github.com/superseriousbusiness/gotosocial/internal/api/client/search"
|
||||
"github.com/superseriousbusiness/gotosocial/internal/api/client/status"
|
||||
"github.com/superseriousbusiness/gotosocial/internal/api/client/streaming"
|
||||
"github.com/superseriousbusiness/gotosocial/internal/api/client/timeline"
|
||||
userClient "github.com/superseriousbusiness/gotosocial/internal/api/client/user"
|
||||
"github.com/superseriousbusiness/gotosocial/internal/api/s2s/nodeinfo"
|
||||
"github.com/superseriousbusiness/gotosocial/internal/api/s2s/user"
|
||||
"github.com/superseriousbusiness/gotosocial/internal/api/s2s/webfinger"
|
||||
"github.com/superseriousbusiness/gotosocial/internal/api/security"
|
||||
apiutil "github.com/superseriousbusiness/gotosocial/internal/api/util"
|
||||
"github.com/superseriousbusiness/gotosocial/internal/gtserror"
|
||||
"github.com/superseriousbusiness/gotosocial/internal/middleware"
|
||||
|
||||
"github.com/superseriousbusiness/gotosocial/internal/concurrency"
|
||||
"github.com/superseriousbusiness/gotosocial/internal/config"
|
||||
"github.com/superseriousbusiness/gotosocial/internal/db/bundb"
|
||||
|
@ -106,11 +89,6 @@
|
|||
|
||||
federatingDB := federatingdb.New(dbService, fedWorker)
|
||||
|
||||
router, err := router.New(ctx, dbService)
|
||||
if err != nil {
|
||||
return fmt.Errorf("error creating router: %s", err)
|
||||
}
|
||||
|
||||
// build converters and util
|
||||
typeConverter := typeutils.NewConverter(dbService)
|
||||
|
||||
|
@ -148,85 +126,72 @@
|
|||
}
|
||||
}
|
||||
|
||||
// create and start the message processor using the other services we've created so far
|
||||
// create the message processor using the other services we've created so far
|
||||
processor := processing.NewProcessor(typeConverter, federator, oauthServer, mediaManager, storage, dbService, emailSender, clientWorker, fedWorker)
|
||||
if err := processor.Start(); err != nil {
|
||||
return fmt.Errorf("error starting processor: %s", err)
|
||||
return fmt.Errorf("error creating processor: %s", err)
|
||||
}
|
||||
|
||||
idp, err := oidc.NewIDP(ctx)
|
||||
/*
|
||||
HTTP router initialization
|
||||
*/
|
||||
|
||||
router, err := router.New(ctx)
|
||||
if err != nil {
|
||||
return fmt.Errorf("error creating oidc idp: %s", err)
|
||||
return fmt.Errorf("error creating router: %s", err)
|
||||
}
|
||||
|
||||
// build web module
|
||||
webModule := web.New(processor)
|
||||
// attach global middlewares which are used for every request
|
||||
router.AttachGlobalMiddleware(
|
||||
middleware.Logger(),
|
||||
middleware.UserAgent(),
|
||||
middleware.CORS(),
|
||||
middleware.ExtraHeaders(),
|
||||
)
|
||||
|
||||
// build client api modules
|
||||
authModule := auth.New(dbService, idp, processor)
|
||||
accountModule := account.New(processor)
|
||||
instanceModule := instance.New(processor)
|
||||
appsModule := app.New(processor)
|
||||
followRequestsModule := followrequest.New(processor)
|
||||
webfingerModule := webfinger.New(processor)
|
||||
nodeInfoModule := nodeinfo.New(processor)
|
||||
usersModule := user.New(processor)
|
||||
timelineModule := timeline.New(processor)
|
||||
notificationModule := notification.New(processor)
|
||||
searchModule := search.New(processor)
|
||||
filtersModule := filter.New(processor)
|
||||
emojiModule := emoji.New(processor)
|
||||
listsModule := list.New(processor)
|
||||
mm := mediaModule.New(processor)
|
||||
fileServerModule := fileserver.New(processor)
|
||||
adminModule := admin.New(processor)
|
||||
statusModule := status.New(processor)
|
||||
bookmarksModule := bookmarks.New(processor)
|
||||
securityModule := security.New(dbService, oauthServer)
|
||||
streamingModule := streaming.New(processor)
|
||||
favouritesModule := favourites.New(processor)
|
||||
blocksModule := blocks.New(processor)
|
||||
userClientModule := userClient.New(processor)
|
||||
// attach global no route / 404 handler to the router
|
||||
router.AttachNoRouteHandler(func(c *gin.Context) {
|
||||
apiutil.ErrorHandler(c, gtserror.NewErrorNotFound(errors.New(http.StatusText(http.StatusNotFound))), processor.InstanceGet)
|
||||
})
|
||||
|
||||
apis := []api.ClientModule{
|
||||
// modules with middleware go first
|
||||
securityModule,
|
||||
authModule,
|
||||
|
||||
// now the web module
|
||||
webModule,
|
||||
|
||||
// now everything else
|
||||
accountModule,
|
||||
instanceModule,
|
||||
appsModule,
|
||||
followRequestsModule,
|
||||
mm,
|
||||
fileServerModule,
|
||||
adminModule,
|
||||
statusModule,
|
||||
bookmarksModule,
|
||||
webfingerModule,
|
||||
nodeInfoModule,
|
||||
usersModule,
|
||||
timelineModule,
|
||||
notificationModule,
|
||||
searchModule,
|
||||
filtersModule,
|
||||
emojiModule,
|
||||
listsModule,
|
||||
streamingModule,
|
||||
favouritesModule,
|
||||
blocksModule,
|
||||
userClientModule,
|
||||
}
|
||||
|
||||
for _, m := range apis {
|
||||
if err := m.Route(router); err != nil {
|
||||
return fmt.Errorf("routing error: %s", err)
|
||||
// build router modules
|
||||
var idp oidc.IDP
|
||||
if config.GetOIDCEnabled() {
|
||||
idp, err = oidc.NewIDP(ctx)
|
||||
if err != nil {
|
||||
return fmt.Errorf("error creating oidc idp: %w", err)
|
||||
}
|
||||
}
|
||||
|
||||
routerSession, err := dbService.GetSession(ctx)
|
||||
if err != nil {
|
||||
return fmt.Errorf("error retrieving router session for session middleware: %w", err)
|
||||
}
|
||||
|
||||
sessionName, err := middleware.SessionName()
|
||||
if err != nil {
|
||||
return fmt.Errorf("error generating session name for session middleware: %w", err)
|
||||
}
|
||||
|
||||
var (
|
||||
authModule = api.NewAuth(dbService, processor, idp, routerSession, sessionName) // auth/oauth paths
|
||||
clientModule = api.NewClient(dbService, processor) // api client endpoints
|
||||
fileserverModule = api.NewFileserver(processor) // fileserver endpoints
|
||||
wellKnownModule = api.NewWellKnown(processor) // .well-known endpoints
|
||||
nodeInfoModule = api.NewNodeInfo(processor) // nodeinfo endpoint
|
||||
activityPubModule = api.NewActivityPub(dbService, processor) // ActivityPub endpoints
|
||||
webModule = web.New(processor) // web pages + user profiles + settings panels etc
|
||||
)
|
||||
|
||||
// these should be routed in order
|
||||
authModule.Route(router)
|
||||
clientModule.Route(router)
|
||||
fileserverModule.Route(router)
|
||||
wellKnownModule.Route(router)
|
||||
nodeInfoModule.Route(router)
|
||||
activityPubModule.Route(router)
|
||||
webModule.Route(router)
|
||||
|
||||
gts, err := gotosocial.NewServer(dbService, router, federator, mediaManager)
|
||||
if err != nil {
|
||||
return fmt.Errorf("error creating gotosocial service: %s", err)
|
||||
|
|
|
@ -21,6 +21,7 @@
|
|||
import (
|
||||
"bytes"
|
||||
"context"
|
||||
"errors"
|
||||
"fmt"
|
||||
"io"
|
||||
"net/http"
|
||||
|
@ -28,35 +29,17 @@
|
|||
"os/signal"
|
||||
"syscall"
|
||||
|
||||
"github.com/gin-gonic/gin"
|
||||
"github.com/superseriousbusiness/gotosocial/cmd/gotosocial/action"
|
||||
"github.com/superseriousbusiness/gotosocial/internal/api"
|
||||
"github.com/superseriousbusiness/gotosocial/internal/api/client/account"
|
||||
"github.com/superseriousbusiness/gotosocial/internal/api/client/admin"
|
||||
"github.com/superseriousbusiness/gotosocial/internal/api/client/app"
|
||||
"github.com/superseriousbusiness/gotosocial/internal/api/client/auth"
|
||||
"github.com/superseriousbusiness/gotosocial/internal/api/client/blocks"
|
||||
"github.com/superseriousbusiness/gotosocial/internal/api/client/emoji"
|
||||
"github.com/superseriousbusiness/gotosocial/internal/api/client/favourites"
|
||||
"github.com/superseriousbusiness/gotosocial/internal/api/client/fileserver"
|
||||
"github.com/superseriousbusiness/gotosocial/internal/api/client/filter"
|
||||
"github.com/superseriousbusiness/gotosocial/internal/api/client/followrequest"
|
||||
"github.com/superseriousbusiness/gotosocial/internal/api/client/instance"
|
||||
"github.com/superseriousbusiness/gotosocial/internal/api/client/list"
|
||||
mediaModule "github.com/superseriousbusiness/gotosocial/internal/api/client/media"
|
||||
"github.com/superseriousbusiness/gotosocial/internal/api/client/notification"
|
||||
"github.com/superseriousbusiness/gotosocial/internal/api/client/search"
|
||||
"github.com/superseriousbusiness/gotosocial/internal/api/client/status"
|
||||
"github.com/superseriousbusiness/gotosocial/internal/api/client/streaming"
|
||||
"github.com/superseriousbusiness/gotosocial/internal/api/client/timeline"
|
||||
userClient "github.com/superseriousbusiness/gotosocial/internal/api/client/user"
|
||||
"github.com/superseriousbusiness/gotosocial/internal/api/s2s/nodeinfo"
|
||||
"github.com/superseriousbusiness/gotosocial/internal/api/s2s/user"
|
||||
"github.com/superseriousbusiness/gotosocial/internal/api/s2s/webfinger"
|
||||
"github.com/superseriousbusiness/gotosocial/internal/api/security"
|
||||
apiutil "github.com/superseriousbusiness/gotosocial/internal/api/util"
|
||||
"github.com/superseriousbusiness/gotosocial/internal/concurrency"
|
||||
"github.com/superseriousbusiness/gotosocial/internal/config"
|
||||
"github.com/superseriousbusiness/gotosocial/internal/gotosocial"
|
||||
"github.com/superseriousbusiness/gotosocial/internal/gtserror"
|
||||
"github.com/superseriousbusiness/gotosocial/internal/log"
|
||||
"github.com/superseriousbusiness/gotosocial/internal/messages"
|
||||
"github.com/superseriousbusiness/gotosocial/internal/middleware"
|
||||
"github.com/superseriousbusiness/gotosocial/internal/oidc"
|
||||
"github.com/superseriousbusiness/gotosocial/internal/storage"
|
||||
"github.com/superseriousbusiness/gotosocial/internal/web"
|
||||
|
@ -70,7 +53,6 @@
|
|||
|
||||
dbService := testrig.NewTestDB()
|
||||
testrig.StandardDBSetup(dbService, nil)
|
||||
router := testrig.NewTestRouter(dbService)
|
||||
var storageBackend *storage.Driver
|
||||
if os.Getenv("GTS_STORAGE_BACKEND") == "s3" {
|
||||
storageBackend, _ = storage.NewS3Storage()
|
||||
|
@ -84,7 +66,6 @@
|
|||
fedWorker := concurrency.NewWorkerPool[messages.FromFederator](-1, -1)
|
||||
|
||||
// build backend handlers
|
||||
oauthServer := testrig.NewTestOauthServer(dbService)
|
||||
transportController := testrig.NewTestTransportController(testrig.NewMockHTTPClient(func(req *http.Request) (*http.Response, error) {
|
||||
r := io.NopCloser(bytes.NewReader([]byte{}))
|
||||
return &http.Response{
|
||||
|
@ -102,77 +83,64 @@
|
|||
return fmt.Errorf("error starting processor: %s", err)
|
||||
}
|
||||
|
||||
idp, err := oidc.NewIDP(ctx)
|
||||
/*
|
||||
HTTP router initialization
|
||||
*/
|
||||
|
||||
router := testrig.NewTestRouter(dbService)
|
||||
|
||||
// attach global middlewares which are used for every request
|
||||
router.AttachGlobalMiddleware(
|
||||
middleware.Logger(),
|
||||
middleware.UserAgent(),
|
||||
middleware.CORS(),
|
||||
middleware.ExtraHeaders(),
|
||||
)
|
||||
|
||||
// attach global no route / 404 handler to the router
|
||||
router.AttachNoRouteHandler(func(c *gin.Context) {
|
||||
apiutil.ErrorHandler(c, gtserror.NewErrorNotFound(errors.New(http.StatusText(http.StatusNotFound))), processor.InstanceGet)
|
||||
})
|
||||
|
||||
// build router modules
|
||||
var idp oidc.IDP
|
||||
var err error
|
||||
if config.GetOIDCEnabled() {
|
||||
idp, err = oidc.NewIDP(ctx)
|
||||
if err != nil {
|
||||
return fmt.Errorf("error creating oidc idp: %s", err)
|
||||
}
|
||||
|
||||
// build web module
|
||||
webModule := web.New(processor)
|
||||
|
||||
// build client api modules
|
||||
authModule := auth.New(dbService, idp, processor)
|
||||
accountModule := account.New(processor)
|
||||
instanceModule := instance.New(processor)
|
||||
appsModule := app.New(processor)
|
||||
followRequestsModule := followrequest.New(processor)
|
||||
webfingerModule := webfinger.New(processor)
|
||||
nodeInfoModule := nodeinfo.New(processor)
|
||||
usersModule := user.New(processor)
|
||||
timelineModule := timeline.New(processor)
|
||||
notificationModule := notification.New(processor)
|
||||
searchModule := search.New(processor)
|
||||
filtersModule := filter.New(processor)
|
||||
emojiModule := emoji.New(processor)
|
||||
listsModule := list.New(processor)
|
||||
mm := mediaModule.New(processor)
|
||||
fileServerModule := fileserver.New(processor)
|
||||
adminModule := admin.New(processor)
|
||||
statusModule := status.New(processor)
|
||||
securityModule := security.New(dbService, oauthServer)
|
||||
streamingModule := streaming.New(processor)
|
||||
favouritesModule := favourites.New(processor)
|
||||
blocksModule := blocks.New(processor)
|
||||
userClientModule := userClient.New(processor)
|
||||
|
||||
apis := []api.ClientModule{
|
||||
// modules with middleware go first
|
||||
securityModule,
|
||||
authModule,
|
||||
|
||||
// now the web module
|
||||
webModule,
|
||||
|
||||
// now everything else
|
||||
accountModule,
|
||||
instanceModule,
|
||||
appsModule,
|
||||
followRequestsModule,
|
||||
mm,
|
||||
fileServerModule,
|
||||
adminModule,
|
||||
statusModule,
|
||||
webfingerModule,
|
||||
nodeInfoModule,
|
||||
usersModule,
|
||||
timelineModule,
|
||||
notificationModule,
|
||||
searchModule,
|
||||
filtersModule,
|
||||
emojiModule,
|
||||
listsModule,
|
||||
streamingModule,
|
||||
favouritesModule,
|
||||
blocksModule,
|
||||
userClientModule,
|
||||
}
|
||||
|
||||
for _, m := range apis {
|
||||
if err := m.Route(router); err != nil {
|
||||
return fmt.Errorf("routing error: %s", err)
|
||||
return fmt.Errorf("error creating oidc idp: %w", err)
|
||||
}
|
||||
}
|
||||
|
||||
routerSession, err := dbService.GetSession(ctx)
|
||||
if err != nil {
|
||||
return fmt.Errorf("error retrieving router session for session middleware: %w", err)
|
||||
}
|
||||
|
||||
sessionName, err := middleware.SessionName()
|
||||
if err != nil {
|
||||
return fmt.Errorf("error generating session name for session middleware: %w", err)
|
||||
}
|
||||
|
||||
var (
|
||||
authModule = api.NewAuth(dbService, processor, idp, routerSession, sessionName) // auth/oauth paths
|
||||
clientModule = api.NewClient(dbService, processor) // api client endpoints
|
||||
fileserverModule = api.NewFileserver(processor) // fileserver endpoints
|
||||
wellKnownModule = api.NewWellKnown(processor) // .well-known endpoints
|
||||
nodeInfoModule = api.NewNodeInfo(processor) // nodeinfo endpoint
|
||||
activityPubModule = api.NewActivityPub(dbService, processor) // ActivityPub endpoints
|
||||
webModule = web.New(processor) // web pages + user profiles + settings panels etc
|
||||
)
|
||||
|
||||
// these should be routed in order
|
||||
authModule.Route(router)
|
||||
clientModule.Route(router)
|
||||
fileserverModule.Route(router)
|
||||
wellKnownModule.Route(router)
|
||||
nodeInfoModule.Route(router)
|
||||
activityPubModule.Route(router)
|
||||
webModule.Route(router)
|
||||
|
||||
gts, err := gotosocial.NewServer(dbService, router, federator, mediaManager)
|
||||
if err != nil {
|
||||
return fmt.Errorf("error creating gotosocial service: %s", err)
|
||||
|
|
|
@ -1,10 +1,16 @@
|
|||
# Rate Limit
|
||||
|
||||
To mitigate abuse + scraping of your instance, an IP-based HTTP rate limit is in place.
|
||||
To mitigate abuse + scraping of your instance, IP-based HTTP rate limiting is in place.
|
||||
|
||||
This rate limit applies not just to the API, but to all requests (web, federation, etc).
|
||||
There are separate rate limiters configured for different groupings of endpoints. In other words, being rate limited for one part of the API doesn't necessarily mean you will be rate limited for other parts. Each entry in the following list has a separate rate limiter:
|
||||
|
||||
By default, a maximum of 1000 requests in a 5 minute time window are allowed.
|
||||
- `/users/*` and `/emoji/*` - ActivityPub (s2s) endpoints.
|
||||
- `/auth/*` and `/oauth/*` - Sign in + OAUTH token requests.
|
||||
- `/fileserver/*` - Media attachments, emojis, etc.
|
||||
- `/nodeinfo/*` - NodeInfo endpoint(s).
|
||||
- `/.well-known/*` - webfinger + nodeinfo requests.
|
||||
|
||||
By default, each rate limiter allows a maximum of 300 requests in a 5 minute time window: 1 request per second per client IP address.
|
||||
|
||||
Every response will include the current status of the rate limit with the following headers:
|
||||
|
||||
|
|
|
@ -1914,7 +1914,7 @@ definitions:
|
|||
title: SwaggerCollection represents an activitypub collection.
|
||||
type: object
|
||||
x-go-name: SwaggerCollection
|
||||
x-go-package: github.com/superseriousbusiness/gotosocial/internal/api/s2s/user
|
||||
x-go-package: github.com/superseriousbusiness/gotosocial/internal/api/activitypub/users
|
||||
swaggerCollectionPage:
|
||||
properties:
|
||||
id:
|
||||
|
@ -1949,7 +1949,7 @@ definitions:
|
|||
title: SwaggerCollectionPage represents one page of a collection.
|
||||
type: object
|
||||
x-go-name: SwaggerCollectionPage
|
||||
x-go-package: github.com/superseriousbusiness/gotosocial/internal/api/s2s/user
|
||||
x-go-package: github.com/superseriousbusiness/gotosocial/internal/api/activitypub/users
|
||||
tag:
|
||||
properties:
|
||||
name:
|
||||
|
@ -2049,9 +2049,9 @@ paths:
|
|||
description: ""
|
||||
schema:
|
||||
$ref: '#/definitions/wellKnownResponse'
|
||||
summary: Directs callers to /nodeinfo/2.0.
|
||||
summary: Returns a well-known response which redirects callers to `/nodeinfo/2.0`.
|
||||
tags:
|
||||
- nodeinfo
|
||||
- .well-known
|
||||
/.well-known/webfinger:
|
||||
get:
|
||||
description: |-
|
||||
|
@ -2074,7 +2074,7 @@ paths:
|
|||
$ref: '#/definitions/wellKnownResponse'
|
||||
summary: Handles webfinger account lookup requests.
|
||||
tags:
|
||||
- webfinger
|
||||
- .well-known
|
||||
/api/{api_version}/media:
|
||||
post:
|
||||
consumes:
|
||||
|
|
|
@ -641,9 +641,11 @@ syslog-address: "localhost:514"
|
|||
# Default: "lax"
|
||||
advanced-cookies-samesite: "lax"
|
||||
|
||||
# Int. Amount of requests to permit from a single IP address within a span of 5 minutes.
|
||||
# If this amount is exceeded, a 429 HTTP error code will be returned.
|
||||
# See https://docs.gotosocial.org/en/latest/api/swagger/#rate-limit.
|
||||
# Int. Amount of requests to permit per router grouping from a single IP address within
|
||||
# a span of 5 minutes. If this amount is exceeded, a 429 HTTP error code will be returned.
|
||||
#
|
||||
# Router groupings and rate limit headers are described here:
|
||||
# https://docs.gotosocial.org/en/latest/api/swagger/#rate-limit.
|
||||
#
|
||||
# If you find yourself adjusting this limit because it's regularly being exceeded,
|
||||
# you should first verify that your settings for `trusted-proxies` (above) are correct.
|
||||
|
@ -655,5 +657,5 @@ advanced-cookies-samesite: "lax"
|
|||
# If you set this to 0 or less, rate limiting will be disabled entirely.
|
||||
#
|
||||
# Examples: [1000, 500, 0]
|
||||
# Default: 1000
|
||||
advanced-rate-limit-requests: 1000
|
||||
# Default: 300
|
||||
advanced-rate-limit-requests: 300
|
||||
|
|
66
internal/api/activitypub.go
Normal file
66
internal/api/activitypub.go
Normal file
|
@ -0,0 +1,66 @@
|
|||
/*
|
||||
GoToSocial
|
||||
Copyright (C) 2021-2022 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 (
|
||||
"context"
|
||||
"net/url"
|
||||
|
||||
"github.com/superseriousbusiness/gotosocial/internal/api/activitypub/emoji"
|
||||
"github.com/superseriousbusiness/gotosocial/internal/api/activitypub/users"
|
||||
"github.com/superseriousbusiness/gotosocial/internal/db"
|
||||
"github.com/superseriousbusiness/gotosocial/internal/middleware"
|
||||
"github.com/superseriousbusiness/gotosocial/internal/processing"
|
||||
"github.com/superseriousbusiness/gotosocial/internal/router"
|
||||
)
|
||||
|
||||
type ActivityPub struct {
|
||||
emoji *emoji.Module
|
||||
users *users.Module
|
||||
|
||||
isURIBlocked func(context.Context, *url.URL) (bool, db.Error)
|
||||
}
|
||||
|
||||
func (a *ActivityPub) Route(r router.Router) {
|
||||
// create groupings for the 'emoji' and 'users' prefixes
|
||||
emojiGroup := r.AttachGroup("emoji")
|
||||
usersGroup := r.AttachGroup("users")
|
||||
|
||||
// instantiate + attach shared, non-global middlewares to both of these groups
|
||||
var (
|
||||
rateLimitMiddleware = middleware.RateLimit() // nolint:contextcheck
|
||||
signatureCheckMiddleware = middleware.SignatureCheck(a.isURIBlocked)
|
||||
gzipMiddleware = middleware.Gzip()
|
||||
cacheControlMiddleware = middleware.CacheControl("no-store")
|
||||
)
|
||||
emojiGroup.Use(rateLimitMiddleware, signatureCheckMiddleware, gzipMiddleware, cacheControlMiddleware)
|
||||
usersGroup.Use(rateLimitMiddleware, signatureCheckMiddleware, gzipMiddleware, cacheControlMiddleware)
|
||||
|
||||
a.emoji.Route(emojiGroup.Handle)
|
||||
a.users.Route(usersGroup.Handle)
|
||||
}
|
||||
|
||||
func NewActivityPub(db db.DB, p processing.Processor) *ActivityPub {
|
||||
return &ActivityPub{
|
||||
emoji: emoji.New(p),
|
||||
users: users.New(p),
|
||||
|
||||
isURIBlocked: db.IsURIBlocked,
|
||||
}
|
||||
}
|
|
@ -21,30 +21,27 @@
|
|||
import (
|
||||
"net/http"
|
||||
|
||||
"github.com/superseriousbusiness/gotosocial/internal/api"
|
||||
"github.com/gin-gonic/gin"
|
||||
"github.com/superseriousbusiness/gotosocial/internal/processing"
|
||||
"github.com/superseriousbusiness/gotosocial/internal/router"
|
||||
)
|
||||
|
||||
const (
|
||||
// BasePath is the base path for serving the emoji API
|
||||
BasePath = "/api/v1/custom_emojis"
|
||||
// EmojiIDKey is for emoji IDs
|
||||
EmojiIDKey = "id"
|
||||
// EmojiBasePath is the base path for serving AP Emojis, minus the "emoji" prefix
|
||||
EmojiWithIDPath = "/:" + EmojiIDKey
|
||||
)
|
||||
|
||||
// Module implements the ClientAPIModule interface for everything related to emoji
|
||||
type Module struct {
|
||||
processor processing.Processor
|
||||
}
|
||||
|
||||
// New returns a new emoji module
|
||||
func New(processor processing.Processor) api.ClientModule {
|
||||
func New(processor processing.Processor) *Module {
|
||||
return &Module{
|
||||
processor: processor,
|
||||
}
|
||||
}
|
||||
|
||||
// Route attaches all routes from this module to the given router
|
||||
func (m *Module) Route(r router.Router) error {
|
||||
r.AttachHandler(http.MethodGet, BasePath, m.EmojisGETHandler)
|
||||
return nil
|
||||
func (m *Module) Route(attachHandler func(method string, path string, f ...gin.HandlerFunc) gin.IRoutes) {
|
||||
attachHandler(http.MethodGet, EmojiWithIDPath, m.EmojiGetHandler)
|
||||
}
|
|
@ -19,54 +19,39 @@
|
|||
package emoji
|
||||
|
||||
import (
|
||||
"context"
|
||||
"encoding/json"
|
||||
"errors"
|
||||
"net/http"
|
||||
"strings"
|
||||
|
||||
"github.com/gin-gonic/gin"
|
||||
"github.com/superseriousbusiness/gotosocial/internal/ap"
|
||||
"github.com/superseriousbusiness/gotosocial/internal/api"
|
||||
apiutil "github.com/superseriousbusiness/gotosocial/internal/api/util"
|
||||
"github.com/superseriousbusiness/gotosocial/internal/gtserror"
|
||||
)
|
||||
|
||||
// EmojiGetHandler
|
||||
func (m *Module) EmojiGetHandler(c *gin.Context) {
|
||||
// usernames on our instance are always lowercase
|
||||
requestedEmojiID := strings.ToUpper(c.Param(EmojiIDKey))
|
||||
if requestedEmojiID == "" {
|
||||
err := errors.New("no emoji id specified in request")
|
||||
api.ErrorHandler(c, gtserror.NewErrorBadRequest(err, err.Error()), m.processor.InstanceGet)
|
||||
apiutil.ErrorHandler(c, gtserror.NewErrorBadRequest(err, err.Error()), m.processor.InstanceGet)
|
||||
return
|
||||
}
|
||||
|
||||
format, err := api.NegotiateAccept(c, api.ActivityPubAcceptHeaders...)
|
||||
format, err := apiutil.NegotiateAccept(c, apiutil.ActivityPubAcceptHeaders...)
|
||||
if err != nil {
|
||||
api.ErrorHandler(c, gtserror.NewErrorNotAcceptable(err, err.Error()), m.processor.InstanceGet)
|
||||
apiutil.ErrorHandler(c, gtserror.NewErrorNotAcceptable(err, err.Error()), m.processor.InstanceGet)
|
||||
return
|
||||
}
|
||||
|
||||
ctx := c.Request.Context()
|
||||
verifier, signed := c.Get(string(ap.ContextRequestingPublicKeyVerifier))
|
||||
if signed {
|
||||
ctx = context.WithValue(ctx, ap.ContextRequestingPublicKeyVerifier, verifier)
|
||||
}
|
||||
|
||||
signature, signed := c.Get(string(ap.ContextRequestingPublicKeySignature))
|
||||
if signed {
|
||||
ctx = context.WithValue(ctx, ap.ContextRequestingPublicKeySignature, signature)
|
||||
}
|
||||
|
||||
resp, errWithCode := m.processor.GetFediEmoji(ctx, requestedEmojiID, c.Request.URL)
|
||||
resp, errWithCode := m.processor.GetFediEmoji(apiutil.TransferSignatureContext(c), requestedEmojiID, c.Request.URL)
|
||||
if errWithCode != nil {
|
||||
api.ErrorHandler(c, errWithCode, m.processor.InstanceGet)
|
||||
apiutil.ErrorHandler(c, errWithCode, m.processor.InstanceGet)
|
||||
return
|
||||
}
|
||||
|
||||
b, err := json.Marshal(resp)
|
||||
if err != nil {
|
||||
api.ErrorHandler(c, gtserror.NewErrorInternalError(err), m.processor.InstanceGet)
|
||||
apiutil.ErrorHandler(c, gtserror.NewErrorInternalError(err), m.processor.InstanceGet)
|
||||
return
|
||||
}
|
||||
|
|
@ -26,8 +26,7 @@
|
|||
|
||||
"github.com/gin-gonic/gin"
|
||||
"github.com/stretchr/testify/suite"
|
||||
"github.com/superseriousbusiness/gotosocial/internal/api/s2s/emoji"
|
||||
"github.com/superseriousbusiness/gotosocial/internal/api/security"
|
||||
"github.com/superseriousbusiness/gotosocial/internal/api/activitypub/emoji"
|
||||
"github.com/superseriousbusiness/gotosocial/internal/concurrency"
|
||||
"github.com/superseriousbusiness/gotosocial/internal/db"
|
||||
"github.com/superseriousbusiness/gotosocial/internal/email"
|
||||
|
@ -35,7 +34,7 @@
|
|||
"github.com/superseriousbusiness/gotosocial/internal/gtsmodel"
|
||||
"github.com/superseriousbusiness/gotosocial/internal/media"
|
||||
"github.com/superseriousbusiness/gotosocial/internal/messages"
|
||||
"github.com/superseriousbusiness/gotosocial/internal/oauth"
|
||||
"github.com/superseriousbusiness/gotosocial/internal/middleware"
|
||||
"github.com/superseriousbusiness/gotosocial/internal/processing"
|
||||
"github.com/superseriousbusiness/gotosocial/internal/storage"
|
||||
"github.com/superseriousbusiness/gotosocial/internal/typeutils"
|
||||
|
@ -51,13 +50,13 @@ type EmojiGetTestSuite struct {
|
|||
emailSender email.Sender
|
||||
processor processing.Processor
|
||||
storage *storage.Driver
|
||||
oauthServer oauth.Server
|
||||
securityModule *security.Module
|
||||
|
||||
testEmojis map[string]*gtsmodel.Emoji
|
||||
testAccounts map[string]*gtsmodel.Account
|
||||
|
||||
emojiModule *emoji.Module
|
||||
|
||||
signatureCheck gin.HandlerFunc
|
||||
}
|
||||
|
||||
func (suite *EmojiGetTestSuite) SetupSuite() {
|
||||
|
@ -79,12 +78,12 @@ func (suite *EmojiGetTestSuite) SetupTest() {
|
|||
suite.federator = testrig.NewTestFederator(suite.db, testrig.NewTestTransportController(testrig.NewMockHTTPClient(nil, "../../../../testrig/media"), suite.db, fedWorker), suite.storage, suite.mediaManager, fedWorker)
|
||||
suite.emailSender = testrig.NewEmailSender("../../../../web/template/", nil)
|
||||
suite.processor = testrig.NewTestProcessor(suite.db, suite.storage, suite.federator, suite.emailSender, suite.mediaManager, clientWorker, fedWorker)
|
||||
suite.emojiModule = emoji.New(suite.processor).(*emoji.Module)
|
||||
suite.oauthServer = testrig.NewTestOauthServer(suite.db)
|
||||
suite.securityModule = security.New(suite.db, suite.oauthServer).(*security.Module)
|
||||
suite.emojiModule = emoji.New(suite.processor)
|
||||
testrig.StandardDBSetup(suite.db, suite.testAccounts)
|
||||
testrig.StandardStorageSetup(suite.storage, "../../../../testrig/media")
|
||||
|
||||
suite.signatureCheck = middleware.SignatureCheck(suite.db.IsURIBlocked)
|
||||
|
||||
suite.NoError(suite.processor.Start())
|
||||
}
|
||||
|
||||
|
@ -108,7 +107,7 @@ func (suite *EmojiGetTestSuite) TestGetEmoji() {
|
|||
ctx.Request.Header.Set("Date", signedRequest.DateHeader)
|
||||
|
||||
// we need to pass the context through signature check first to set appropriate values on it
|
||||
suite.securityModule.SignatureCheck(ctx)
|
||||
suite.signatureCheck(ctx)
|
||||
|
||||
// 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.
|
|
@ -16,31 +16,7 @@
|
|||
along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
package user
|
||||
|
||||
import (
|
||||
"context"
|
||||
|
||||
"github.com/gin-gonic/gin"
|
||||
"github.com/superseriousbusiness/gotosocial/internal/ap"
|
||||
)
|
||||
|
||||
// transferContext transfers the signature verifier and signature from the gin context to the request context
|
||||
func transferContext(c *gin.Context) context.Context {
|
||||
ctx := c.Request.Context()
|
||||
|
||||
verifier, signed := c.Get(string(ap.ContextRequestingPublicKeyVerifier))
|
||||
if signed {
|
||||
ctx = context.WithValue(ctx, ap.ContextRequestingPublicKeyVerifier, verifier)
|
||||
}
|
||||
|
||||
signature, signed := c.Get(string(ap.ContextRequestingPublicKeySignature))
|
||||
if signed {
|
||||
ctx = context.WithValue(ctx, ap.ContextRequestingPublicKeySignature, signature)
|
||||
}
|
||||
|
||||
return ctx
|
||||
}
|
||||
package users
|
||||
|
||||
// SwaggerCollection represents an activitypub collection.
|
||||
// swagger:model swaggerCollection
|
|
@ -16,7 +16,7 @@
|
|||
along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
package user
|
||||
package users
|
||||
|
||||
import (
|
||||
"encoding/json"
|
||||
|
@ -25,7 +25,7 @@
|
|||
"strings"
|
||||
|
||||
"github.com/gin-gonic/gin"
|
||||
"github.com/superseriousbusiness/gotosocial/internal/api"
|
||||
apiutil "github.com/superseriousbusiness/gotosocial/internal/api/util"
|
||||
"github.com/superseriousbusiness/gotosocial/internal/gtserror"
|
||||
)
|
||||
|
||||
|
@ -35,31 +35,31 @@ func (m *Module) FollowersGETHandler(c *gin.Context) {
|
|||
requestedUsername := strings.ToLower(c.Param(UsernameKey))
|
||||
if requestedUsername == "" {
|
||||
err := errors.New("no username specified in request")
|
||||
api.ErrorHandler(c, gtserror.NewErrorBadRequest(err, err.Error()), m.processor.InstanceGet)
|
||||
apiutil.ErrorHandler(c, gtserror.NewErrorBadRequest(err, err.Error()), m.processor.InstanceGet)
|
||||
return
|
||||
}
|
||||
|
||||
format, err := api.NegotiateAccept(c, api.HTMLOrActivityPubHeaders...)
|
||||
format, err := apiutil.NegotiateAccept(c, apiutil.HTMLOrActivityPubHeaders...)
|
||||
if err != nil {
|
||||
api.ErrorHandler(c, gtserror.NewErrorNotAcceptable(err, err.Error()), m.processor.InstanceGet)
|
||||
apiutil.ErrorHandler(c, gtserror.NewErrorNotAcceptable(err, err.Error()), m.processor.InstanceGet)
|
||||
return
|
||||
}
|
||||
|
||||
if format == string(api.TextHTML) {
|
||||
if format == string(apiutil.TextHTML) {
|
||||
// redirect to the user's profile
|
||||
c.Redirect(http.StatusSeeOther, "/@"+requestedUsername)
|
||||
return
|
||||
}
|
||||
|
||||
resp, errWithCode := m.processor.GetFediFollowers(transferContext(c), requestedUsername, c.Request.URL)
|
||||
resp, errWithCode := m.processor.GetFediFollowers(apiutil.TransferSignatureContext(c), requestedUsername, c.Request.URL)
|
||||
if errWithCode != nil {
|
||||
api.ErrorHandler(c, errWithCode, m.processor.InstanceGet)
|
||||
apiutil.ErrorHandler(c, errWithCode, m.processor.InstanceGet)
|
||||
return
|
||||
}
|
||||
|
||||
b, err := json.Marshal(resp)
|
||||
if err != nil {
|
||||
api.ErrorHandler(c, gtserror.NewErrorInternalError(err), m.processor.InstanceGet)
|
||||
apiutil.ErrorHandler(c, gtserror.NewErrorInternalError(err), m.processor.InstanceGet)
|
||||
return
|
||||
}
|
||||
|
|
@ -16,7 +16,7 @@
|
|||
along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
package user
|
||||
package users
|
||||
|
||||
import (
|
||||
"encoding/json"
|
||||
|
@ -25,7 +25,7 @@
|
|||
"strings"
|
||||
|
||||
"github.com/gin-gonic/gin"
|
||||
"github.com/superseriousbusiness/gotosocial/internal/api"
|
||||
apiutil "github.com/superseriousbusiness/gotosocial/internal/api/util"
|
||||
"github.com/superseriousbusiness/gotosocial/internal/gtserror"
|
||||
)
|
||||
|
||||
|
@ -35,31 +35,31 @@ func (m *Module) FollowingGETHandler(c *gin.Context) {
|
|||
requestedUsername := strings.ToLower(c.Param(UsernameKey))
|
||||
if requestedUsername == "" {
|
||||
err := errors.New("no username specified in request")
|
||||
api.ErrorHandler(c, gtserror.NewErrorBadRequest(err, err.Error()), m.processor.InstanceGet)
|
||||
apiutil.ErrorHandler(c, gtserror.NewErrorBadRequest(err, err.Error()), m.processor.InstanceGet)
|
||||
return
|
||||
}
|
||||
|
||||
format, err := api.NegotiateAccept(c, api.HTMLOrActivityPubHeaders...)
|
||||
format, err := apiutil.NegotiateAccept(c, apiutil.HTMLOrActivityPubHeaders...)
|
||||
if err != nil {
|
||||
api.ErrorHandler(c, gtserror.NewErrorNotAcceptable(err, err.Error()), m.processor.InstanceGet)
|
||||
apiutil.ErrorHandler(c, gtserror.NewErrorNotAcceptable(err, err.Error()), m.processor.InstanceGet)
|
||||
return
|
||||
}
|
||||
|
||||
if format == string(api.TextHTML) {
|
||||
if format == string(apiutil.TextHTML) {
|
||||
// redirect to the user's profile
|
||||
c.Redirect(http.StatusSeeOther, "/@"+requestedUsername)
|
||||
return
|
||||
}
|
||||
|
||||
resp, errWithCode := m.processor.GetFediFollowing(transferContext(c), requestedUsername, c.Request.URL)
|
||||
resp, errWithCode := m.processor.GetFediFollowing(apiutil.TransferSignatureContext(c), requestedUsername, c.Request.URL)
|
||||
if errWithCode != nil {
|
||||
api.ErrorHandler(c, errWithCode, m.processor.InstanceGet)
|
||||
apiutil.ErrorHandler(c, errWithCode, m.processor.InstanceGet)
|
||||
return
|
||||
}
|
||||
|
||||
b, err := json.Marshal(resp)
|
||||
if err != nil {
|
||||
api.ErrorHandler(c, gtserror.NewErrorInternalError(err), m.processor.InstanceGet)
|
||||
apiutil.ErrorHandler(c, gtserror.NewErrorInternalError(err), m.processor.InstanceGet)
|
||||
return
|
||||
}
|
||||
|
|
@ -16,14 +16,14 @@
|
|||
along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
package user
|
||||
package users
|
||||
|
||||
import (
|
||||
"errors"
|
||||
"strings"
|
||||
|
||||
"github.com/gin-gonic/gin"
|
||||
"github.com/superseriousbusiness/gotosocial/internal/api"
|
||||
apiutil "github.com/superseriousbusiness/gotosocial/internal/api/util"
|
||||
"github.com/superseriousbusiness/gotosocial/internal/gtserror" //nolint:typecheck
|
||||
)
|
||||
|
||||
|
@ -34,18 +34,18 @@ func (m *Module) InboxPOSTHandler(c *gin.Context) {
|
|||
requestedUsername := strings.ToLower(c.Param(UsernameKey))
|
||||
if requestedUsername == "" {
|
||||
err := errors.New("no username specified in request")
|
||||
api.ErrorHandler(c, gtserror.NewErrorBadRequest(err, err.Error()), m.processor.InstanceGet)
|
||||
apiutil.ErrorHandler(c, gtserror.NewErrorBadRequest(err, err.Error()), m.processor.InstanceGet)
|
||||
return
|
||||
}
|
||||
|
||||
if posted, err := m.processor.InboxPost(transferContext(c), c.Writer, c.Request); err != nil {
|
||||
if posted, err := m.processor.InboxPost(apiutil.TransferSignatureContext(c), c.Writer, c.Request); err != nil {
|
||||
if withCode, ok := err.(gtserror.WithCode); ok {
|
||||
api.ErrorHandler(c, withCode, m.processor.InstanceGet)
|
||||
apiutil.ErrorHandler(c, withCode, m.processor.InstanceGet)
|
||||
} else {
|
||||
api.ErrorHandler(c, gtserror.NewErrorBadRequest(err, err.Error()), m.processor.InstanceGet)
|
||||
apiutil.ErrorHandler(c, gtserror.NewErrorBadRequest(err, err.Error()), m.processor.InstanceGet)
|
||||
}
|
||||
} else if !posted {
|
||||
err := errors.New("unable to process request")
|
||||
api.ErrorHandler(c, gtserror.NewErrorBadRequest(err, err.Error()), m.processor.InstanceGet)
|
||||
apiutil.ErrorHandler(c, gtserror.NewErrorBadRequest(err, err.Error()), m.processor.InstanceGet)
|
||||
}
|
||||
}
|
|
@ -16,7 +16,7 @@
|
|||
along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
package user_test
|
||||
package users_test
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
|
@ -32,7 +32,7 @@
|
|||
"github.com/stretchr/testify/suite"
|
||||
"github.com/superseriousbusiness/activity/pub"
|
||||
"github.com/superseriousbusiness/activity/streams"
|
||||
"github.com/superseriousbusiness/gotosocial/internal/api/s2s/user"
|
||||
"github.com/superseriousbusiness/gotosocial/internal/api/activitypub/users"
|
||||
"github.com/superseriousbusiness/gotosocial/internal/concurrency"
|
||||
"github.com/superseriousbusiness/gotosocial/internal/db"
|
||||
"github.com/superseriousbusiness/gotosocial/internal/gtsmodel"
|
||||
|
@ -92,7 +92,7 @@ func (suite *InboxPostTestSuite) TestPostBlock() {
|
|||
federator := testrig.NewTestFederator(suite.db, tc, suite.storage, suite.mediaManager, fedWorker)
|
||||
emailSender := testrig.NewEmailSender("../../../../web/template/", nil)
|
||||
processor := testrig.NewTestProcessor(suite.db, suite.storage, federator, emailSender, suite.mediaManager, clientWorker, fedWorker)
|
||||
userModule := user.New(processor).(*user.Module)
|
||||
userModule := users.New(processor)
|
||||
suite.NoError(processor.Start())
|
||||
|
||||
// setup request
|
||||
|
@ -105,13 +105,13 @@ func (suite *InboxPostTestSuite) TestPostBlock() {
|
|||
ctx.Request.Header.Set("Content-Type", "application/activity+json")
|
||||
|
||||
// we need to pass the context through signature check first to set appropriate values on it
|
||||
suite.securityModule.SignatureCheck(ctx)
|
||||
suite.signatureCheck(ctx)
|
||||
|
||||
// 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.
|
||||
ctx.Params = gin.Params{
|
||||
gin.Param{
|
||||
Key: user.UsernameKey,
|
||||
Key: users.UsernameKey,
|
||||
Value: blockedAccount.Username,
|
||||
},
|
||||
}
|
||||
|
@ -196,7 +196,7 @@ func (suite *InboxPostTestSuite) TestPostUnblock() {
|
|||
federator := testrig.NewTestFederator(suite.db, tc, suite.storage, suite.mediaManager, fedWorker)
|
||||
emailSender := testrig.NewEmailSender("../../../../web/template/", nil)
|
||||
processor := testrig.NewTestProcessor(suite.db, suite.storage, federator, emailSender, suite.mediaManager, clientWorker, fedWorker)
|
||||
userModule := user.New(processor).(*user.Module)
|
||||
userModule := users.New(processor)
|
||||
suite.NoError(processor.Start())
|
||||
|
||||
// setup request
|
||||
|
@ -209,13 +209,13 @@ func (suite *InboxPostTestSuite) TestPostUnblock() {
|
|||
ctx.Request.Header.Set("Content-Type", "application/activity+json")
|
||||
|
||||
// we need to pass the context through signature check first to set appropriate values on it
|
||||
suite.securityModule.SignatureCheck(ctx)
|
||||
suite.signatureCheck(ctx)
|
||||
|
||||
// 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.
|
||||
ctx.Params = gin.Params{
|
||||
gin.Param{
|
||||
Key: user.UsernameKey,
|
||||
Key: users.UsernameKey,
|
||||
Value: blockedAccount.Username,
|
||||
},
|
||||
}
|
||||
|
@ -298,7 +298,7 @@ func (suite *InboxPostTestSuite) TestPostUpdate() {
|
|||
federator := testrig.NewTestFederator(suite.db, tc, suite.storage, suite.mediaManager, fedWorker)
|
||||
emailSender := testrig.NewEmailSender("../../../../web/template/", nil)
|
||||
processor := testrig.NewTestProcessor(suite.db, suite.storage, federator, emailSender, suite.mediaManager, clientWorker, fedWorker)
|
||||
userModule := user.New(processor).(*user.Module)
|
||||
userModule := users.New(processor)
|
||||
suite.NoError(processor.Start())
|
||||
|
||||
// setup request
|
||||
|
@ -311,13 +311,13 @@ func (suite *InboxPostTestSuite) TestPostUpdate() {
|
|||
ctx.Request.Header.Set("Content-Type", "application/activity+json")
|
||||
|
||||
// we need to pass the context through signature check first to set appropriate values on it
|
||||
suite.securityModule.SignatureCheck(ctx)
|
||||
suite.signatureCheck(ctx)
|
||||
|
||||
// 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.
|
||||
ctx.Params = gin.Params{
|
||||
gin.Param{
|
||||
Key: user.UsernameKey,
|
||||
Key: users.UsernameKey,
|
||||
Value: receivingAccount.Username,
|
||||
},
|
||||
}
|
||||
|
@ -431,7 +431,7 @@ func (suite *InboxPostTestSuite) TestPostDelete() {
|
|||
emailSender := testrig.NewEmailSender("../../../../web/template/", nil)
|
||||
processor := testrig.NewTestProcessor(suite.db, suite.storage, federator, emailSender, suite.mediaManager, clientWorker, fedWorker)
|
||||
suite.NoError(processor.Start())
|
||||
userModule := user.New(processor).(*user.Module)
|
||||
userModule := users.New(processor)
|
||||
|
||||
// setup request
|
||||
recorder := httptest.NewRecorder()
|
||||
|
@ -443,13 +443,13 @@ func (suite *InboxPostTestSuite) TestPostDelete() {
|
|||
ctx.Request.Header.Set("Content-Type", "application/activity+json")
|
||||
|
||||
// we need to pass the context through signature check first to set appropriate values on it
|
||||
suite.securityModule.SignatureCheck(ctx)
|
||||
suite.signatureCheck(ctx)
|
||||
|
||||
// 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.
|
||||
ctx.Params = gin.Params{
|
||||
gin.Param{
|
||||
Key: user.UsernameKey,
|
||||
Key: users.UsernameKey,
|
||||
Value: receivingAccount.Username,
|
||||
},
|
||||
}
|
|
@ -16,7 +16,7 @@
|
|||
along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
package user
|
||||
package users
|
||||
|
||||
import (
|
||||
"encoding/json"
|
||||
|
@ -27,7 +27,7 @@
|
|||
"strings"
|
||||
|
||||
"github.com/gin-gonic/gin"
|
||||
"github.com/superseriousbusiness/gotosocial/internal/api"
|
||||
apiutil "github.com/superseriousbusiness/gotosocial/internal/api/util"
|
||||
"github.com/superseriousbusiness/gotosocial/internal/gtserror"
|
||||
)
|
||||
|
||||
|
@ -90,17 +90,17 @@ func (m *Module) OutboxGETHandler(c *gin.Context) {
|
|||
requestedUsername := strings.ToLower(c.Param(UsernameKey))
|
||||
if requestedUsername == "" {
|
||||
err := errors.New("no username specified in request")
|
||||
api.ErrorHandler(c, gtserror.NewErrorBadRequest(err, err.Error()), m.processor.InstanceGet)
|
||||
apiutil.ErrorHandler(c, gtserror.NewErrorBadRequest(err, err.Error()), m.processor.InstanceGet)
|
||||
return
|
||||
}
|
||||
|
||||
format, err := api.NegotiateAccept(c, api.HTMLOrActivityPubHeaders...)
|
||||
format, err := apiutil.NegotiateAccept(c, apiutil.HTMLOrActivityPubHeaders...)
|
||||
if err != nil {
|
||||
api.ErrorHandler(c, gtserror.NewErrorNotAcceptable(err, err.Error()), m.processor.InstanceGet)
|
||||
apiutil.ErrorHandler(c, gtserror.NewErrorNotAcceptable(err, err.Error()), m.processor.InstanceGet)
|
||||
return
|
||||
}
|
||||
|
||||
if format == string(api.TextHTML) {
|
||||
if format == string(apiutil.TextHTML) {
|
||||
// redirect to the user's profile
|
||||
c.Redirect(http.StatusSeeOther, "/@"+requestedUsername)
|
||||
return
|
||||
|
@ -111,7 +111,7 @@ func (m *Module) OutboxGETHandler(c *gin.Context) {
|
|||
i, err := strconv.ParseBool(pageString)
|
||||
if err != nil {
|
||||
err := fmt.Errorf("error parsing %s: %s", PageKey, err)
|
||||
api.ErrorHandler(c, gtserror.NewErrorBadRequest(err, err.Error()), m.processor.InstanceGet)
|
||||
apiutil.ErrorHandler(c, gtserror.NewErrorBadRequest(err, err.Error()), m.processor.InstanceGet)
|
||||
return
|
||||
}
|
||||
page = i
|
||||
|
@ -129,15 +129,15 @@ func (m *Module) OutboxGETHandler(c *gin.Context) {
|
|||
maxID = maxIDString
|
||||
}
|
||||
|
||||
resp, errWithCode := m.processor.GetFediOutbox(transferContext(c), requestedUsername, page, maxID, minID, c.Request.URL)
|
||||
resp, errWithCode := m.processor.GetFediOutbox(apiutil.TransferSignatureContext(c), requestedUsername, page, maxID, minID, c.Request.URL)
|
||||
if errWithCode != nil {
|
||||
api.ErrorHandler(c, errWithCode, m.processor.InstanceGet)
|
||||
apiutil.ErrorHandler(c, errWithCode, m.processor.InstanceGet)
|
||||
return
|
||||
}
|
||||
|
||||
b, err := json.Marshal(resp)
|
||||
if err != nil {
|
||||
api.ErrorHandler(c, gtserror.NewErrorInternalError(err), m.processor.InstanceGet)
|
||||
apiutil.ErrorHandler(c, gtserror.NewErrorInternalError(err), m.processor.InstanceGet)
|
||||
return
|
||||
}
|
||||
|
|
@ -16,7 +16,7 @@
|
|||
along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
package user_test
|
||||
package users_test
|
||||
|
||||
import (
|
||||
"context"
|
||||
|
@ -30,7 +30,7 @@
|
|||
"github.com/stretchr/testify/suite"
|
||||
"github.com/superseriousbusiness/activity/streams"
|
||||
"github.com/superseriousbusiness/activity/streams/vocab"
|
||||
"github.com/superseriousbusiness/gotosocial/internal/api/s2s/user"
|
||||
"github.com/superseriousbusiness/gotosocial/internal/api/activitypub/users"
|
||||
"github.com/superseriousbusiness/gotosocial/internal/concurrency"
|
||||
"github.com/superseriousbusiness/gotosocial/internal/messages"
|
||||
"github.com/superseriousbusiness/gotosocial/testrig"
|
||||
|
@ -55,13 +55,13 @@ func (suite *OutboxGetTestSuite) TestGetOutbox() {
|
|||
ctx.Request.Header.Set("Date", signedRequest.DateHeader)
|
||||
|
||||
// we need to pass the context through signature check first to set appropriate values on it
|
||||
suite.securityModule.SignatureCheck(ctx)
|
||||
suite.signatureCheck(ctx)
|
||||
|
||||
// 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.
|
||||
ctx.Params = gin.Params{
|
||||
gin.Param{
|
||||
Key: user.UsernameKey,
|
||||
Key: users.UsernameKey,
|
||||
Value: targetAccount.Username,
|
||||
},
|
||||
}
|
||||
|
@ -102,7 +102,7 @@ func (suite *OutboxGetTestSuite) TestGetOutboxFirstPage() {
|
|||
federator := testrig.NewTestFederator(suite.db, tc, suite.storage, suite.mediaManager, fedWorker)
|
||||
emailSender := testrig.NewEmailSender("../../../../web/template/", nil)
|
||||
processor := testrig.NewTestProcessor(suite.db, suite.storage, federator, emailSender, suite.mediaManager, clientWorker, fedWorker)
|
||||
userModule := user.New(processor).(*user.Module)
|
||||
userModule := users.New(processor)
|
||||
suite.NoError(processor.Start())
|
||||
|
||||
// setup request
|
||||
|
@ -114,13 +114,13 @@ func (suite *OutboxGetTestSuite) TestGetOutboxFirstPage() {
|
|||
ctx.Request.Header.Set("Date", signedRequest.DateHeader)
|
||||
|
||||
// we need to pass the context through signature check first to set appropriate values on it
|
||||
suite.securityModule.SignatureCheck(ctx)
|
||||
suite.signatureCheck(ctx)
|
||||
|
||||
// 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.
|
||||
ctx.Params = gin.Params{
|
||||
gin.Param{
|
||||
Key: user.UsernameKey,
|
||||
Key: users.UsernameKey,
|
||||
Value: targetAccount.Username,
|
||||
},
|
||||
}
|
||||
|
@ -161,7 +161,7 @@ func (suite *OutboxGetTestSuite) TestGetOutboxNextPage() {
|
|||
federator := testrig.NewTestFederator(suite.db, tc, suite.storage, suite.mediaManager, fedWorker)
|
||||
emailSender := testrig.NewEmailSender("../../../../web/template/", nil)
|
||||
processor := testrig.NewTestProcessor(suite.db, suite.storage, federator, emailSender, suite.mediaManager, clientWorker, fedWorker)
|
||||
userModule := user.New(processor).(*user.Module)
|
||||
userModule := users.New(processor)
|
||||
suite.NoError(processor.Start())
|
||||
|
||||
// setup request
|
||||
|
@ -173,17 +173,17 @@ func (suite *OutboxGetTestSuite) TestGetOutboxNextPage() {
|
|||
ctx.Request.Header.Set("Date", signedRequest.DateHeader)
|
||||
|
||||
// we need to pass the context through signature check first to set appropriate values on it
|
||||
suite.securityModule.SignatureCheck(ctx)
|
||||
suite.signatureCheck(ctx)
|
||||
|
||||
// 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.
|
||||
ctx.Params = gin.Params{
|
||||
gin.Param{
|
||||
Key: user.UsernameKey,
|
||||
Key: users.UsernameKey,
|
||||
Value: targetAccount.Username,
|
||||
},
|
||||
gin.Param{
|
||||
Key: user.MaxIDKey,
|
||||
Key: users.MaxIDKey,
|
||||
Value: "01F8MHAMCHF6Y650WCRSCP4WMY",
|
||||
},
|
||||
}
|
|
@ -16,7 +16,7 @@
|
|||
along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
package user
|
||||
package users
|
||||
|
||||
import (
|
||||
"encoding/json"
|
||||
|
@ -25,7 +25,7 @@
|
|||
"strings"
|
||||
|
||||
"github.com/gin-gonic/gin"
|
||||
"github.com/superseriousbusiness/gotosocial/internal/api"
|
||||
apiutil "github.com/superseriousbusiness/gotosocial/internal/api/util"
|
||||
"github.com/superseriousbusiness/gotosocial/internal/gtserror"
|
||||
)
|
||||
|
||||
|
@ -39,31 +39,31 @@ func (m *Module) PublicKeyGETHandler(c *gin.Context) {
|
|||
requestedUsername := strings.ToLower(c.Param(UsernameKey))
|
||||
if requestedUsername == "" {
|
||||
err := errors.New("no username specified in request")
|
||||
api.ErrorHandler(c, gtserror.NewErrorBadRequest(err, err.Error()), m.processor.InstanceGet)
|
||||
apiutil.ErrorHandler(c, gtserror.NewErrorBadRequest(err, err.Error()), m.processor.InstanceGet)
|
||||
return
|
||||
}
|
||||
|
||||
format, err := api.NegotiateAccept(c, api.HTMLOrActivityPubHeaders...)
|
||||
format, err := apiutil.NegotiateAccept(c, apiutil.HTMLOrActivityPubHeaders...)
|
||||
if err != nil {
|
||||
api.ErrorHandler(c, gtserror.NewErrorNotAcceptable(err, err.Error()), m.processor.InstanceGet)
|
||||
apiutil.ErrorHandler(c, gtserror.NewErrorNotAcceptable(err, err.Error()), m.processor.InstanceGet)
|
||||
return
|
||||
}
|
||||
|
||||
if format == string(api.TextHTML) {
|
||||
if format == string(apiutil.TextHTML) {
|
||||
// redirect to the user's profile
|
||||
c.Redirect(http.StatusSeeOther, "/@"+requestedUsername)
|
||||
return
|
||||
}
|
||||
|
||||
resp, errWithCode := m.processor.GetFediUser(transferContext(c), requestedUsername, c.Request.URL)
|
||||
resp, errWithCode := m.processor.GetFediUser(apiutil.TransferSignatureContext(c), requestedUsername, c.Request.URL)
|
||||
if errWithCode != nil {
|
||||
api.ErrorHandler(c, errWithCode, m.processor.InstanceGet)
|
||||
apiutil.ErrorHandler(c, errWithCode, m.processor.InstanceGet)
|
||||
return
|
||||
}
|
||||
|
||||
b, err := json.Marshal(resp)
|
||||
if err != nil {
|
||||
api.ErrorHandler(c, gtserror.NewErrorInternalError(err), m.processor.InstanceGet)
|
||||
apiutil.ErrorHandler(c, gtserror.NewErrorInternalError(err), m.processor.InstanceGet)
|
||||
return
|
||||
}
|
||||
|
|
@ -16,7 +16,7 @@
|
|||
along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
package user
|
||||
package users
|
||||
|
||||
import (
|
||||
"encoding/json"
|
||||
|
@ -27,7 +27,7 @@
|
|||
"strings"
|
||||
|
||||
"github.com/gin-gonic/gin"
|
||||
"github.com/superseriousbusiness/gotosocial/internal/api"
|
||||
apiutil "github.com/superseriousbusiness/gotosocial/internal/api/util"
|
||||
"github.com/superseriousbusiness/gotosocial/internal/gtserror"
|
||||
)
|
||||
|
||||
|
@ -97,7 +97,7 @@ func (m *Module) StatusRepliesGETHandler(c *gin.Context) {
|
|||
requestedUsername := strings.ToLower(c.Param(UsernameKey))
|
||||
if requestedUsername == "" {
|
||||
err := errors.New("no username specified in request")
|
||||
api.ErrorHandler(c, gtserror.NewErrorBadRequest(err, err.Error()), m.processor.InstanceGet)
|
||||
apiutil.ErrorHandler(c, gtserror.NewErrorBadRequest(err, err.Error()), m.processor.InstanceGet)
|
||||
return
|
||||
}
|
||||
|
||||
|
@ -105,17 +105,17 @@ func (m *Module) StatusRepliesGETHandler(c *gin.Context) {
|
|||
requestedStatusID := strings.ToUpper(c.Param(StatusIDKey))
|
||||
if requestedStatusID == "" {
|
||||
err := errors.New("no status id specified in request")
|
||||
api.ErrorHandler(c, gtserror.NewErrorBadRequest(err, err.Error()), m.processor.InstanceGet)
|
||||
apiutil.ErrorHandler(c, gtserror.NewErrorBadRequest(err, err.Error()), m.processor.InstanceGet)
|
||||
return
|
||||
}
|
||||
|
||||
format, err := api.NegotiateAccept(c, api.HTMLOrActivityPubHeaders...)
|
||||
format, err := apiutil.NegotiateAccept(c, apiutil.HTMLOrActivityPubHeaders...)
|
||||
if err != nil {
|
||||
api.ErrorHandler(c, gtserror.NewErrorNotAcceptable(err, err.Error()), m.processor.InstanceGet)
|
||||
apiutil.ErrorHandler(c, gtserror.NewErrorNotAcceptable(err, err.Error()), m.processor.InstanceGet)
|
||||
return
|
||||
}
|
||||
|
||||
if format == string(api.TextHTML) {
|
||||
if format == string(apiutil.TextHTML) {
|
||||
// redirect to the status
|
||||
c.Redirect(http.StatusSeeOther, "/@"+requestedUsername+"/statuses/"+requestedStatusID)
|
||||
return
|
||||
|
@ -126,7 +126,7 @@ func (m *Module) StatusRepliesGETHandler(c *gin.Context) {
|
|||
i, err := strconv.ParseBool(pageString)
|
||||
if err != nil {
|
||||
err := fmt.Errorf("error parsing %s: %s", PageKey, err)
|
||||
api.ErrorHandler(c, gtserror.NewErrorBadRequest(err, err.Error()), m.processor.InstanceGet)
|
||||
apiutil.ErrorHandler(c, gtserror.NewErrorBadRequest(err, err.Error()), m.processor.InstanceGet)
|
||||
return
|
||||
}
|
||||
page = i
|
||||
|
@ -138,7 +138,7 @@ func (m *Module) StatusRepliesGETHandler(c *gin.Context) {
|
|||
i, err := strconv.ParseBool(onlyOtherAccountsString)
|
||||
if err != nil {
|
||||
err := fmt.Errorf("error parsing %s: %s", OnlyOtherAccountsKey, err)
|
||||
api.ErrorHandler(c, gtserror.NewErrorBadRequest(err, err.Error()), m.processor.InstanceGet)
|
||||
apiutil.ErrorHandler(c, gtserror.NewErrorBadRequest(err, err.Error()), m.processor.InstanceGet)
|
||||
return
|
||||
}
|
||||
onlyOtherAccounts = i
|
||||
|
@ -150,15 +150,15 @@ func (m *Module) StatusRepliesGETHandler(c *gin.Context) {
|
|||
minID = minIDString
|
||||
}
|
||||
|
||||
resp, errWithCode := m.processor.GetFediStatusReplies(transferContext(c), requestedUsername, requestedStatusID, page, onlyOtherAccounts, minID, c.Request.URL)
|
||||
resp, errWithCode := m.processor.GetFediStatusReplies(apiutil.TransferSignatureContext(c), requestedUsername, requestedStatusID, page, onlyOtherAccounts, minID, c.Request.URL)
|
||||
if errWithCode != nil {
|
||||
api.ErrorHandler(c, errWithCode, m.processor.InstanceGet)
|
||||
apiutil.ErrorHandler(c, errWithCode, m.processor.InstanceGet)
|
||||
return
|
||||
}
|
||||
|
||||
b, err := json.Marshal(resp)
|
||||
if err != nil {
|
||||
api.ErrorHandler(c, gtserror.NewErrorInternalError(err), m.processor.InstanceGet)
|
||||
apiutil.ErrorHandler(c, gtserror.NewErrorInternalError(err), m.processor.InstanceGet)
|
||||
return
|
||||
}
|
||||
|
|
@ -16,7 +16,7 @@
|
|||
along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
package user_test
|
||||
package users_test
|
||||
|
||||
import (
|
||||
"context"
|
||||
|
@ -32,7 +32,7 @@
|
|||
"github.com/stretchr/testify/suite"
|
||||
"github.com/superseriousbusiness/activity/streams"
|
||||
"github.com/superseriousbusiness/activity/streams/vocab"
|
||||
"github.com/superseriousbusiness/gotosocial/internal/api/s2s/user"
|
||||
"github.com/superseriousbusiness/gotosocial/internal/api/activitypub/users"
|
||||
"github.com/superseriousbusiness/gotosocial/internal/concurrency"
|
||||
"github.com/superseriousbusiness/gotosocial/internal/messages"
|
||||
"github.com/superseriousbusiness/gotosocial/testrig"
|
||||
|
@ -58,17 +58,17 @@ func (suite *RepliesGetTestSuite) TestGetReplies() {
|
|||
ctx.Request.Header.Set("Date", signedRequest.DateHeader)
|
||||
|
||||
// we need to pass the context through signature check first to set appropriate values on it
|
||||
suite.securityModule.SignatureCheck(ctx)
|
||||
suite.signatureCheck(ctx)
|
||||
|
||||
// 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.
|
||||
ctx.Params = gin.Params{
|
||||
gin.Param{
|
||||
Key: user.UsernameKey,
|
||||
Key: users.UsernameKey,
|
||||
Value: targetAccount.Username,
|
||||
},
|
||||
gin.Param{
|
||||
Key: user.StatusIDKey,
|
||||
Key: users.StatusIDKey,
|
||||
Value: targetStatus.ID,
|
||||
},
|
||||
}
|
||||
|
@ -111,7 +111,7 @@ func (suite *RepliesGetTestSuite) TestGetRepliesNext() {
|
|||
federator := testrig.NewTestFederator(suite.db, tc, suite.storage, suite.mediaManager, fedWorker)
|
||||
emailSender := testrig.NewEmailSender("../../../../web/template/", nil)
|
||||
processor := testrig.NewTestProcessor(suite.db, suite.storage, federator, emailSender, suite.mediaManager, clientWorker, fedWorker)
|
||||
userModule := user.New(processor).(*user.Module)
|
||||
userModule := users.New(processor)
|
||||
suite.NoError(processor.Start())
|
||||
|
||||
// setup request
|
||||
|
@ -123,17 +123,17 @@ func (suite *RepliesGetTestSuite) TestGetRepliesNext() {
|
|||
ctx.Request.Header.Set("Date", signedRequest.DateHeader)
|
||||
|
||||
// we need to pass the context through signature check first to set appropriate values on it
|
||||
suite.securityModule.SignatureCheck(ctx)
|
||||
suite.signatureCheck(ctx)
|
||||
|
||||
// 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.
|
||||
ctx.Params = gin.Params{
|
||||
gin.Param{
|
||||
Key: user.UsernameKey,
|
||||
Key: users.UsernameKey,
|
||||
Value: targetAccount.Username,
|
||||
},
|
||||
gin.Param{
|
||||
Key: user.StatusIDKey,
|
||||
Key: users.StatusIDKey,
|
||||
Value: targetStatus.ID,
|
||||
},
|
||||
}
|
||||
|
@ -179,7 +179,7 @@ func (suite *RepliesGetTestSuite) TestGetRepliesLast() {
|
|||
federator := testrig.NewTestFederator(suite.db, tc, suite.storage, suite.mediaManager, fedWorker)
|
||||
emailSender := testrig.NewEmailSender("../../../../web/template/", nil)
|
||||
processor := testrig.NewTestProcessor(suite.db, suite.storage, federator, emailSender, suite.mediaManager, clientWorker, fedWorker)
|
||||
userModule := user.New(processor).(*user.Module)
|
||||
userModule := users.New(processor)
|
||||
suite.NoError(processor.Start())
|
||||
|
||||
// setup request
|
||||
|
@ -191,17 +191,17 @@ func (suite *RepliesGetTestSuite) TestGetRepliesLast() {
|
|||
ctx.Request.Header.Set("Date", signedRequest.DateHeader)
|
||||
|
||||
// we need to pass the context through signature check first to set appropriate values on it
|
||||
suite.securityModule.SignatureCheck(ctx)
|
||||
suite.signatureCheck(ctx)
|
||||
|
||||
// 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.
|
||||
ctx.Params = gin.Params{
|
||||
gin.Param{
|
||||
Key: user.UsernameKey,
|
||||
Key: users.UsernameKey,
|
||||
Value: targetAccount.Username,
|
||||
},
|
||||
gin.Param{
|
||||
Key: user.StatusIDKey,
|
||||
Key: users.StatusIDKey,
|
||||
Value: targetStatus.ID,
|
||||
},
|
||||
}
|
|
@ -16,7 +16,7 @@
|
|||
along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
package user
|
||||
package users
|
||||
|
||||
import (
|
||||
"encoding/json"
|
||||
|
@ -25,7 +25,7 @@
|
|||
"strings"
|
||||
|
||||
"github.com/gin-gonic/gin"
|
||||
"github.com/superseriousbusiness/gotosocial/internal/api"
|
||||
apiutil "github.com/superseriousbusiness/gotosocial/internal/api/util"
|
||||
"github.com/superseriousbusiness/gotosocial/internal/gtserror"
|
||||
)
|
||||
|
||||
|
@ -35,7 +35,7 @@ func (m *Module) StatusGETHandler(c *gin.Context) {
|
|||
requestedUsername := strings.ToLower(c.Param(UsernameKey))
|
||||
if requestedUsername == "" {
|
||||
err := errors.New("no username specified in request")
|
||||
api.ErrorHandler(c, gtserror.NewErrorBadRequest(err, err.Error()), m.processor.InstanceGet)
|
||||
apiutil.ErrorHandler(c, gtserror.NewErrorBadRequest(err, err.Error()), m.processor.InstanceGet)
|
||||
return
|
||||
}
|
||||
|
||||
|
@ -43,31 +43,31 @@ func (m *Module) StatusGETHandler(c *gin.Context) {
|
|||
requestedStatusID := strings.ToUpper(c.Param(StatusIDKey))
|
||||
if requestedStatusID == "" {
|
||||
err := errors.New("no status id specified in request")
|
||||
api.ErrorHandler(c, gtserror.NewErrorBadRequest(err, err.Error()), m.processor.InstanceGet)
|
||||
apiutil.ErrorHandler(c, gtserror.NewErrorBadRequest(err, err.Error()), m.processor.InstanceGet)
|
||||
return
|
||||
}
|
||||
|
||||
format, err := api.NegotiateAccept(c, api.HTMLOrActivityPubHeaders...)
|
||||
format, err := apiutil.NegotiateAccept(c, apiutil.HTMLOrActivityPubHeaders...)
|
||||
if err != nil {
|
||||
api.ErrorHandler(c, gtserror.NewErrorNotAcceptable(err, err.Error()), m.processor.InstanceGet)
|
||||
apiutil.ErrorHandler(c, gtserror.NewErrorNotAcceptable(err, err.Error()), m.processor.InstanceGet)
|
||||
return
|
||||
}
|
||||
|
||||
if format == string(api.TextHTML) {
|
||||
if format == string(apiutil.TextHTML) {
|
||||
// redirect to the status
|
||||
c.Redirect(http.StatusSeeOther, "/@"+requestedUsername+"/statuses/"+requestedStatusID)
|
||||
return
|
||||
}
|
||||
|
||||
resp, errWithCode := m.processor.GetFediStatus(transferContext(c), requestedUsername, requestedStatusID, c.Request.URL)
|
||||
resp, errWithCode := m.processor.GetFediStatus(apiutil.TransferSignatureContext(c), requestedUsername, requestedStatusID, c.Request.URL)
|
||||
if errWithCode != nil {
|
||||
api.ErrorHandler(c, errWithCode, m.processor.InstanceGet)
|
||||
apiutil.ErrorHandler(c, errWithCode, m.processor.InstanceGet)
|
||||
return
|
||||
}
|
||||
|
||||
b, err := json.Marshal(resp)
|
||||
if err != nil {
|
||||
api.ErrorHandler(c, gtserror.NewErrorInternalError(err), m.processor.InstanceGet)
|
||||
apiutil.ErrorHandler(c, gtserror.NewErrorInternalError(err), m.processor.InstanceGet)
|
||||
return
|
||||
}
|
||||
|
|
@ -16,7 +16,7 @@
|
|||
along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
package user_test
|
||||
package users_test
|
||||
|
||||
import (
|
||||
"context"
|
||||
|
@ -31,7 +31,7 @@
|
|||
"github.com/stretchr/testify/suite"
|
||||
"github.com/superseriousbusiness/activity/streams"
|
||||
"github.com/superseriousbusiness/activity/streams/vocab"
|
||||
"github.com/superseriousbusiness/gotosocial/internal/api/s2s/user"
|
||||
"github.com/superseriousbusiness/gotosocial/internal/api/activitypub/users"
|
||||
"github.com/superseriousbusiness/gotosocial/testrig"
|
||||
)
|
||||
|
||||
|
@ -55,17 +55,17 @@ func (suite *StatusGetTestSuite) TestGetStatus() {
|
|||
ctx.Request.Header.Set("Date", signedRequest.DateHeader)
|
||||
|
||||
// we need to pass the context through signature check first to set appropriate values on it
|
||||
suite.securityModule.SignatureCheck(ctx)
|
||||
suite.signatureCheck(ctx)
|
||||
|
||||
// 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.
|
||||
ctx.Params = gin.Params{
|
||||
gin.Param{
|
||||
Key: user.UsernameKey,
|
||||
Key: users.UsernameKey,
|
||||
Value: targetAccount.Username,
|
||||
},
|
||||
gin.Param{
|
||||
Key: user.StatusIDKey,
|
||||
Key: users.StatusIDKey,
|
||||
Value: targetStatus.ID,
|
||||
},
|
||||
}
|
||||
|
@ -114,17 +114,17 @@ func (suite *StatusGetTestSuite) TestGetStatusLowercase() {
|
|||
ctx.Request.Header.Set("Date", signedRequest.DateHeader)
|
||||
|
||||
// we need to pass the context through signature check first to set appropriate values on it
|
||||
suite.securityModule.SignatureCheck(ctx)
|
||||
suite.signatureCheck(ctx)
|
||||
|
||||
// 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.
|
||||
ctx.Params = gin.Params{
|
||||
gin.Param{
|
||||
Key: user.UsernameKey,
|
||||
Key: users.UsernameKey,
|
||||
Value: strings.ToLower(targetAccount.Username),
|
||||
},
|
||||
gin.Param{
|
||||
Key: user.StatusIDKey,
|
||||
Key: users.StatusIDKey,
|
||||
Value: strings.ToLower(targetStatus.ID),
|
||||
},
|
||||
}
|
80
internal/api/activitypub/users/user.go
Normal file
80
internal/api/activitypub/users/user.go
Normal file
|
@ -0,0 +1,80 @@
|
|||
/*
|
||||
GoToSocial
|
||||
Copyright (C) 2021-2022 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 users
|
||||
|
||||
import (
|
||||
"net/http"
|
||||
|
||||
"github.com/gin-gonic/gin"
|
||||
"github.com/superseriousbusiness/gotosocial/internal/processing"
|
||||
"github.com/superseriousbusiness/gotosocial/internal/uris"
|
||||
)
|
||||
|
||||
const (
|
||||
// UsernameKey is for account usernames.
|
||||
UsernameKey = "username"
|
||||
// StatusIDKey is for status IDs
|
||||
StatusIDKey = "status"
|
||||
// OnlyOtherAccountsKey is for filtering status responses.
|
||||
OnlyOtherAccountsKey = "only_other_accounts"
|
||||
// MinIDKey is for filtering status responses.
|
||||
MinIDKey = "min_id"
|
||||
// MaxIDKey is for filtering status responses.
|
||||
MaxIDKey = "max_id"
|
||||
// PageKey is for filtering status responses.
|
||||
PageKey = "page"
|
||||
|
||||
// BasePath is the base path for serving AP 'users' requests, minus the 'users' prefix.
|
||||
BasePath = "/:" + UsernameKey
|
||||
// PublicKeyPath is a path to a user's public key, for serving bare minimum AP representations.
|
||||
PublicKeyPath = BasePath + "/" + uris.PublicKeyPath
|
||||
// InboxPath is for serving POST requests to a user's inbox with the given username key.
|
||||
InboxPath = BasePath + "/" + uris.InboxPath
|
||||
// OutboxPath is for serving GET requests to a user's outbox with the given username key.
|
||||
OutboxPath = BasePath + "/" + uris.OutboxPath
|
||||
// FollowersPath is for serving GET request's to a user's followers list, with the given username key.
|
||||
FollowersPath = BasePath + "/" + uris.FollowersPath
|
||||
// FollowingPath is for serving GET request's to a user's following list, with the given username key.
|
||||
FollowingPath = BasePath + "/" + uris.FollowingPath
|
||||
// StatusPath is for serving GET requests to a particular status by a user, with the given username key and status ID
|
||||
StatusPath = BasePath + "/" + uris.StatusesPath + "/:" + StatusIDKey
|
||||
// StatusRepliesPath is for serving the replies collection of a status.
|
||||
StatusRepliesPath = StatusPath + "/replies"
|
||||
)
|
||||
|
||||
type Module struct {
|
||||
processor processing.Processor
|
||||
}
|
||||
|
||||
func New(processor processing.Processor) *Module {
|
||||
return &Module{
|
||||
processor: processor,
|
||||
}
|
||||
}
|
||||
|
||||
func (m *Module) Route(attachHandler func(method string, path string, f ...gin.HandlerFunc) gin.IRoutes) {
|
||||
attachHandler(http.MethodGet, BasePath, m.UsersGETHandler)
|
||||
attachHandler(http.MethodPost, InboxPath, m.InboxPOSTHandler)
|
||||
attachHandler(http.MethodGet, FollowersPath, m.FollowersGETHandler)
|
||||
attachHandler(http.MethodGet, FollowingPath, m.FollowingGETHandler)
|
||||
attachHandler(http.MethodGet, StatusPath, m.StatusGETHandler)
|
||||
attachHandler(http.MethodGet, PublicKeyPath, m.PublicKeyGETHandler)
|
||||
attachHandler(http.MethodGet, StatusRepliesPath, m.StatusRepliesGETHandler)
|
||||
attachHandler(http.MethodGet, OutboxPath, m.OutboxGETHandler)
|
||||
}
|
|
@ -16,12 +16,12 @@
|
|||
along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
package user_test
|
||||
package users_test
|
||||
|
||||
import (
|
||||
"github.com/gin-gonic/gin"
|
||||
"github.com/stretchr/testify/suite"
|
||||
"github.com/superseriousbusiness/gotosocial/internal/api/s2s/user"
|
||||
"github.com/superseriousbusiness/gotosocial/internal/api/security"
|
||||
"github.com/superseriousbusiness/gotosocial/internal/api/activitypub/users"
|
||||
"github.com/superseriousbusiness/gotosocial/internal/concurrency"
|
||||
"github.com/superseriousbusiness/gotosocial/internal/db"
|
||||
"github.com/superseriousbusiness/gotosocial/internal/email"
|
||||
|
@ -29,7 +29,7 @@
|
|||
"github.com/superseriousbusiness/gotosocial/internal/gtsmodel"
|
||||
"github.com/superseriousbusiness/gotosocial/internal/media"
|
||||
"github.com/superseriousbusiness/gotosocial/internal/messages"
|
||||
"github.com/superseriousbusiness/gotosocial/internal/oauth"
|
||||
"github.com/superseriousbusiness/gotosocial/internal/middleware"
|
||||
"github.com/superseriousbusiness/gotosocial/internal/processing"
|
||||
"github.com/superseriousbusiness/gotosocial/internal/storage"
|
||||
"github.com/superseriousbusiness/gotosocial/internal/typeutils"
|
||||
|
@ -46,8 +46,6 @@ type UserStandardTestSuite struct {
|
|||
emailSender email.Sender
|
||||
processor processing.Processor
|
||||
storage *storage.Driver
|
||||
oauthServer oauth.Server
|
||||
securityModule *security.Module
|
||||
|
||||
// standard suite models
|
||||
testTokens map[string]*gtsmodel.Token
|
||||
|
@ -60,7 +58,9 @@ type UserStandardTestSuite struct {
|
|||
testBlocks map[string]*gtsmodel.Block
|
||||
|
||||
// module being tested
|
||||
userModule *user.Module
|
||||
userModule *users.Module
|
||||
|
||||
signatureCheck gin.HandlerFunc
|
||||
}
|
||||
|
||||
func (suite *UserStandardTestSuite) SetupSuite() {
|
||||
|
@ -88,12 +88,12 @@ func (suite *UserStandardTestSuite) SetupTest() {
|
|||
suite.federator = testrig.NewTestFederator(suite.db, testrig.NewTestTransportController(testrig.NewMockHTTPClient(nil, "../../../../testrig/media"), suite.db, fedWorker), suite.storage, suite.mediaManager, fedWorker)
|
||||
suite.emailSender = testrig.NewEmailSender("../../../../web/template/", nil)
|
||||
suite.processor = testrig.NewTestProcessor(suite.db, suite.storage, suite.federator, suite.emailSender, suite.mediaManager, clientWorker, fedWorker)
|
||||
suite.userModule = user.New(suite.processor).(*user.Module)
|
||||
suite.oauthServer = testrig.NewTestOauthServer(suite.db)
|
||||
suite.securityModule = security.New(suite.db, suite.oauthServer).(*security.Module)
|
||||
suite.userModule = users.New(suite.processor)
|
||||
testrig.StandardDBSetup(suite.db, suite.testAccounts)
|
||||
testrig.StandardStorageSetup(suite.storage, "../../../../testrig/media")
|
||||
|
||||
suite.signatureCheck = middleware.SignatureCheck(suite.db.IsURIBlocked)
|
||||
|
||||
suite.NoError(suite.processor.Start())
|
||||
}
|
||||
|
|
@ -16,7 +16,7 @@
|
|||
along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
package user
|
||||
package users
|
||||
|
||||
import (
|
||||
"encoding/json"
|
||||
|
@ -25,7 +25,7 @@
|
|||
"strings"
|
||||
|
||||
"github.com/gin-gonic/gin"
|
||||
"github.com/superseriousbusiness/gotosocial/internal/api"
|
||||
apiutil "github.com/superseriousbusiness/gotosocial/internal/api/util"
|
||||
"github.com/superseriousbusiness/gotosocial/internal/gtserror"
|
||||
)
|
||||
|
||||
|
@ -43,31 +43,31 @@ func (m *Module) UsersGETHandler(c *gin.Context) {
|
|||
requestedUsername := strings.ToLower(c.Param(UsernameKey))
|
||||
if requestedUsername == "" {
|
||||
err := errors.New("no username specified in request")
|
||||
api.ErrorHandler(c, gtserror.NewErrorBadRequest(err, err.Error()), m.processor.InstanceGet)
|
||||
apiutil.ErrorHandler(c, gtserror.NewErrorBadRequest(err, err.Error()), m.processor.InstanceGet)
|
||||
return
|
||||
}
|
||||
|
||||
format, err := api.NegotiateAccept(c, api.HTMLOrActivityPubHeaders...)
|
||||
format, err := apiutil.NegotiateAccept(c, apiutil.HTMLOrActivityPubHeaders...)
|
||||
if err != nil {
|
||||
api.ErrorHandler(c, gtserror.NewErrorNotAcceptable(err, err.Error()), m.processor.InstanceGet)
|
||||
apiutil.ErrorHandler(c, gtserror.NewErrorNotAcceptable(err, err.Error()), m.processor.InstanceGet)
|
||||
return
|
||||
}
|
||||
|
||||
if format == string(api.TextHTML) {
|
||||
if format == string(apiutil.TextHTML) {
|
||||
// redirect to the user's profile
|
||||
c.Redirect(http.StatusSeeOther, "/@"+requestedUsername)
|
||||
return
|
||||
}
|
||||
|
||||
resp, errWithCode := m.processor.GetFediUser(transferContext(c), requestedUsername, c.Request.URL)
|
||||
resp, errWithCode := m.processor.GetFediUser(apiutil.TransferSignatureContext(c), requestedUsername, c.Request.URL)
|
||||
if errWithCode != nil {
|
||||
api.ErrorHandler(c, errWithCode, m.processor.InstanceGet)
|
||||
apiutil.ErrorHandler(c, errWithCode, m.processor.InstanceGet)
|
||||
return
|
||||
}
|
||||
|
||||
b, err := json.Marshal(resp)
|
||||
if err != nil {
|
||||
api.ErrorHandler(c, gtserror.NewErrorInternalError(err), m.processor.InstanceGet)
|
||||
apiutil.ErrorHandler(c, gtserror.NewErrorInternalError(err), m.processor.InstanceGet)
|
||||
return
|
||||
}
|
||||
|
|
@ -16,7 +16,7 @@
|
|||
along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
package user_test
|
||||
package users_test
|
||||
|
||||
import (
|
||||
"context"
|
||||
|
@ -30,8 +30,8 @@
|
|||
"github.com/stretchr/testify/suite"
|
||||
"github.com/superseriousbusiness/activity/streams"
|
||||
"github.com/superseriousbusiness/activity/streams/vocab"
|
||||
"github.com/superseriousbusiness/gotosocial/internal/api/activitypub/users"
|
||||
apimodel "github.com/superseriousbusiness/gotosocial/internal/api/model"
|
||||
"github.com/superseriousbusiness/gotosocial/internal/api/s2s/user"
|
||||
"github.com/superseriousbusiness/gotosocial/internal/oauth"
|
||||
"github.com/superseriousbusiness/gotosocial/testrig"
|
||||
)
|
||||
|
@ -55,13 +55,13 @@ func (suite *UserGetTestSuite) TestGetUser() {
|
|||
ctx.Request.Header.Set("Date", signedRequest.DateHeader)
|
||||
|
||||
// we need to pass the context through signature check first to set appropriate values on it
|
||||
suite.securityModule.SignatureCheck(ctx)
|
||||
suite.signatureCheck(ctx)
|
||||
|
||||
// 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.
|
||||
ctx.Params = gin.Params{
|
||||
gin.Param{
|
||||
Key: user.UsernameKey,
|
||||
Key: users.UsernameKey,
|
||||
Value: targetAccount.Username,
|
||||
},
|
||||
}
|
||||
|
@ -98,7 +98,7 @@ func (suite *UserGetTestSuite) TestGetUser() {
|
|||
// TestGetUserPublicKeyDeleted checks whether the public key of a deleted account can still be dereferenced.
|
||||
// This is needed by remote instances for authenticating delete requests and stuff like that.
|
||||
func (suite *UserGetTestSuite) TestGetUserPublicKeyDeleted() {
|
||||
userModule := user.New(suite.processor).(*user.Module)
|
||||
userModule := users.New(suite.processor)
|
||||
targetAccount := suite.testAccounts["local_account_1"]
|
||||
|
||||
// first delete the account, as though zork had deleted himself
|
||||
|
@ -133,13 +133,13 @@ func (suite *UserGetTestSuite) TestGetUserPublicKeyDeleted() {
|
|||
ctx.Request.Header.Set("Date", signedRequest.DateHeader)
|
||||
|
||||
// we need to pass the context through signature check first to set appropriate values on it
|
||||
suite.securityModule.SignatureCheck(ctx)
|
||||
suite.signatureCheck(ctx)
|
||||
|
||||
// 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.
|
||||
ctx.Params = gin.Params{
|
||||
gin.Param{
|
||||
Key: user.UsernameKey,
|
||||
Key: users.UsernameKey,
|
||||
Value: targetAccount.Username,
|
||||
},
|
||||
}
|
|
@ -1,37 +0,0 @@
|
|||
/*
|
||||
GoToSocial
|
||||
Copyright (C) 2021-2022 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 (
|
||||
"github.com/superseriousbusiness/gotosocial/internal/router"
|
||||
)
|
||||
|
||||
// ClientModule represents a chunk of code (usually contained in a single package) that adds a set
|
||||
// of functionalities and/or side effects to a router, by mapping routes and/or middlewares onto it--in other words, a REST API ;)
|
||||
// A ClientAPIMpdule with routes corresponds roughly to one main path of the gotosocial REST api, for example /api/v1/accounts/ or /oauth/
|
||||
type ClientModule interface {
|
||||
Route(s router.Router) error
|
||||
}
|
||||
|
||||
// FederationModule represents a chunk of code (usually contained in a single package) that adds a set
|
||||
// of functionalities and/or side effects to a router, by mapping routes and/or middlewares onto it--in other words, a REST API ;)
|
||||
// Unlike ClientAPIModule, federation API module is not intended to be interacted with by clients directly -- it is primarily a server-to-server interface.
|
||||
type FederationModule interface {
|
||||
Route(s router.Router) error
|
||||
}
|
64
internal/api/auth.go
Normal file
64
internal/api/auth.go
Normal file
|
@ -0,0 +1,64 @@
|
|||
/*
|
||||
GoToSocial
|
||||
Copyright (C) 2021-2022 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 (
|
||||
"github.com/superseriousbusiness/gotosocial/internal/api/auth"
|
||||
"github.com/superseriousbusiness/gotosocial/internal/db"
|
||||
"github.com/superseriousbusiness/gotosocial/internal/gtsmodel"
|
||||
"github.com/superseriousbusiness/gotosocial/internal/middleware"
|
||||
"github.com/superseriousbusiness/gotosocial/internal/oidc"
|
||||
"github.com/superseriousbusiness/gotosocial/internal/processing"
|
||||
"github.com/superseriousbusiness/gotosocial/internal/router"
|
||||
)
|
||||
|
||||
type Auth struct {
|
||||
routerSession *gtsmodel.RouterSession
|
||||
sessionName string
|
||||
|
||||
auth *auth.Module
|
||||
}
|
||||
|
||||
// Route attaches 'auth' and 'oauth' groups to the given router.
|
||||
func (a *Auth) Route(r router.Router) {
|
||||
// create groupings for the 'auth' and 'oauth' prefixes
|
||||
authGroup := r.AttachGroup("auth")
|
||||
oauthGroup := r.AttachGroup("oauth")
|
||||
|
||||
// instantiate + attach shared, non-global middlewares to both of these groups
|
||||
var (
|
||||
rateLimitMiddleware = middleware.RateLimit() // nolint:contextcheck
|
||||
gzipMiddleware = middleware.Gzip()
|
||||
cacheControlMiddleware = middleware.CacheControl("private", "max-age=120")
|
||||
sessionMiddleware = middleware.Session(a.sessionName, a.routerSession.Auth, a.routerSession.Crypt)
|
||||
)
|
||||
authGroup.Use(rateLimitMiddleware, gzipMiddleware, cacheControlMiddleware, sessionMiddleware)
|
||||
oauthGroup.Use(rateLimitMiddleware, gzipMiddleware, cacheControlMiddleware, sessionMiddleware)
|
||||
|
||||
a.auth.RouteAuth(authGroup.Handle)
|
||||
a.auth.RouteOauth(oauthGroup.Handle)
|
||||
}
|
||||
|
||||
func NewAuth(db db.DB, p processing.Processor, idp oidc.IDP, routerSession *gtsmodel.RouterSession, sessionName string) *Auth {
|
||||
return &Auth{
|
||||
routerSession: routerSession,
|
||||
sessionName: sessionName,
|
||||
auth: auth.New(db, p, idp),
|
||||
}
|
||||
}
|
117
internal/api/auth/auth.go
Normal file
117
internal/api/auth/auth.go
Normal file
|
@ -0,0 +1,117 @@
|
|||
/*
|
||||
GoToSocial
|
||||
Copyright (C) 2021-2022 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 auth
|
||||
|
||||
import (
|
||||
"net/http"
|
||||
|
||||
"github.com/gin-contrib/sessions"
|
||||
"github.com/gin-gonic/gin"
|
||||
"github.com/superseriousbusiness/gotosocial/internal/db"
|
||||
"github.com/superseriousbusiness/gotosocial/internal/oidc"
|
||||
"github.com/superseriousbusiness/gotosocial/internal/processing"
|
||||
)
|
||||
|
||||
const (
|
||||
/*
|
||||
paths prefixed with 'auth'
|
||||
*/
|
||||
|
||||
// AuthSignInPath is the API path for users to sign in through
|
||||
AuthSignInPath = "/sign_in"
|
||||
// AuthCheckYourEmailPath users land here after registering a new account, instructs them to confirm their email
|
||||
AuthCheckYourEmailPath = "/check_your_email"
|
||||
// AuthWaitForApprovalPath users land here after confirming their email
|
||||
// but before an admin approves their account (if such is required)
|
||||
AuthWaitForApprovalPath = "/wait_for_approval"
|
||||
// AuthAccountDisabledPath users land here when their account is suspended by an admin
|
||||
AuthAccountDisabledPath = "/account_disabled"
|
||||
// AuthCallbackPath is the API path for receiving callback tokens from external OIDC providers
|
||||
AuthCallbackPath = "/callback"
|
||||
|
||||
/*
|
||||
paths prefixed with 'oauth'
|
||||
*/
|
||||
|
||||
// OauthTokenPath is the API path to use for granting token requests to users with valid credentials
|
||||
OauthTokenPath = "/token" // #nosec G101 else we get a hardcoded credentials warning
|
||||
// OauthAuthorizePath is the API path for authorization requests (eg., authorize this app to act on my behalf as a user)
|
||||
OauthAuthorizePath = "/authorize"
|
||||
// OauthFinalizePath is the API path for completing user registration with additional user details
|
||||
OauthFinalizePath = "/finalize"
|
||||
// OauthOobTokenPath is the path for serving an html representation of an oob token page.
|
||||
OauthOobTokenPath = "/oob" // #nosec G101 else we get a hardcoded credentials warning
|
||||
|
||||
/*
|
||||
params / session keys
|
||||
*/
|
||||
|
||||
callbackStateParam = "state"
|
||||
callbackCodeParam = "code"
|
||||
sessionUserID = "userid"
|
||||
sessionClientID = "client_id"
|
||||
sessionRedirectURI = "redirect_uri"
|
||||
sessionForceLogin = "force_login"
|
||||
sessionResponseType = "response_type"
|
||||
sessionScope = "scope"
|
||||
sessionInternalState = "internal_state"
|
||||
sessionClientState = "client_state"
|
||||
sessionClaims = "claims"
|
||||
sessionAppID = "app_id"
|
||||
)
|
||||
|
||||
type Module struct {
|
||||
db db.DB
|
||||
processor processing.Processor
|
||||
idp oidc.IDP
|
||||
}
|
||||
|
||||
// New returns an Auth module which provides both 'oauth' and 'auth' endpoints.
|
||||
//
|
||||
// It is safe to pass a nil idp if oidc is disabled.
|
||||
func New(db db.DB, processor processing.Processor, idp oidc.IDP) *Module {
|
||||
return &Module{
|
||||
db: db,
|
||||
processor: processor,
|
||||
idp: idp,
|
||||
}
|
||||
}
|
||||
|
||||
// RouteAuth routes all paths that should have an 'auth' prefix
|
||||
func (m *Module) RouteAuth(attachHandler func(method string, path string, f ...gin.HandlerFunc) gin.IRoutes) {
|
||||
attachHandler(http.MethodGet, AuthSignInPath, m.SignInGETHandler)
|
||||
attachHandler(http.MethodPost, AuthSignInPath, m.SignInPOSTHandler)
|
||||
attachHandler(http.MethodGet, AuthCallbackPath, m.CallbackGETHandler)
|
||||
}
|
||||
|
||||
// RouteOauth routes all paths that should have an 'oauth' prefix
|
||||
func (m *Module) RouteOauth(attachHandler func(method string, path string, f ...gin.HandlerFunc) gin.IRoutes) {
|
||||
attachHandler(http.MethodPost, OauthTokenPath, m.TokenPOSTHandler)
|
||||
attachHandler(http.MethodGet, OauthAuthorizePath, m.AuthorizeGETHandler)
|
||||
attachHandler(http.MethodPost, OauthAuthorizePath, m.AuthorizePOSTHandler)
|
||||
attachHandler(http.MethodPost, OauthFinalizePath, m.FinalizePOSTHandler)
|
||||
attachHandler(http.MethodGet, OauthOobTokenPath, m.OobHandler)
|
||||
}
|
||||
|
||||
func (m *Module) clearSession(s sessions.Session) {
|
||||
s.Clear()
|
||||
if err := s.Save(); err != nil {
|
||||
panic(err)
|
||||
}
|
||||
}
|
|
@ -20,7 +20,6 @@
|
|||
|
||||
import (
|
||||
"bytes"
|
||||
"context"
|
||||
"fmt"
|
||||
"net/http/httptest"
|
||||
|
||||
|
@ -28,7 +27,7 @@
|
|||
"github.com/gin-contrib/sessions/memstore"
|
||||
"github.com/gin-gonic/gin"
|
||||
"github.com/stretchr/testify/suite"
|
||||
"github.com/superseriousbusiness/gotosocial/internal/api/client/auth"
|
||||
"github.com/superseriousbusiness/gotosocial/internal/api/auth"
|
||||
"github.com/superseriousbusiness/gotosocial/internal/concurrency"
|
||||
"github.com/superseriousbusiness/gotosocial/internal/config"
|
||||
"github.com/superseriousbusiness/gotosocial/internal/db"
|
||||
|
@ -37,10 +36,9 @@
|
|||
"github.com/superseriousbusiness/gotosocial/internal/gtsmodel"
|
||||
"github.com/superseriousbusiness/gotosocial/internal/media"
|
||||
"github.com/superseriousbusiness/gotosocial/internal/messages"
|
||||
"github.com/superseriousbusiness/gotosocial/internal/oauth"
|
||||
"github.com/superseriousbusiness/gotosocial/internal/middleware"
|
||||
"github.com/superseriousbusiness/gotosocial/internal/oidc"
|
||||
"github.com/superseriousbusiness/gotosocial/internal/processing"
|
||||
"github.com/superseriousbusiness/gotosocial/internal/router"
|
||||
"github.com/superseriousbusiness/gotosocial/internal/storage"
|
||||
"github.com/superseriousbusiness/gotosocial/testrig"
|
||||
)
|
||||
|
@ -54,7 +52,6 @@ type AuthStandardTestSuite struct {
|
|||
processor processing.Processor
|
||||
emailSender email.Sender
|
||||
idp oidc.IDP
|
||||
oauthServer oauth.Server
|
||||
|
||||
// standard suite models
|
||||
testTokens map[string]*gtsmodel.Token
|
||||
|
@ -90,17 +87,10 @@ func (suite *AuthStandardTestSuite) SetupTest() {
|
|||
suite.db = testrig.NewTestDB()
|
||||
suite.storage = testrig.NewInMemoryStorage()
|
||||
suite.mediaManager = testrig.NewTestMediaManager(suite.db, suite.storage)
|
||||
suite.federator = testrig.NewTestFederator(suite.db, testrig.NewTestTransportController(testrig.NewMockHTTPClient(nil, "../../../../testrig/media"), suite.db, fedWorker), suite.storage, suite.mediaManager, fedWorker)
|
||||
suite.emailSender = testrig.NewEmailSender("../../../../web/template/", nil)
|
||||
suite.federator = testrig.NewTestFederator(suite.db, testrig.NewTestTransportController(testrig.NewMockHTTPClient(nil, "../../../testrig/media"), suite.db, fedWorker), suite.storage, suite.mediaManager, fedWorker)
|
||||
suite.emailSender = testrig.NewEmailSender("../../../web/template/", nil)
|
||||
suite.processor = testrig.NewTestProcessor(suite.db, suite.storage, suite.federator, suite.emailSender, suite.mediaManager, clientWorker, fedWorker)
|
||||
|
||||
suite.oauthServer = testrig.NewTestOauthServer(suite.db)
|
||||
var err error
|
||||
suite.idp, err = oidc.NewIDP(context.Background())
|
||||
if err != nil {
|
||||
panic(err)
|
||||
}
|
||||
suite.authModule = auth.New(suite.db, suite.idp, suite.processor).(*auth.Module)
|
||||
suite.authModule = auth.New(suite.db, suite.processor, suite.idp)
|
||||
testrig.StandardDBSetup(suite.db, suite.testAccounts)
|
||||
}
|
||||
|
||||
|
@ -114,7 +104,7 @@ func (suite *AuthStandardTestSuite) newContext(requestMethod string, requestPath
|
|||
ctx, engine := testrig.CreateGinTestContext(recorder, nil)
|
||||
|
||||
// load templates into the engine
|
||||
testrig.ConfigureTemplatesWithGin(engine, "../../../../web/template")
|
||||
testrig.ConfigureTemplatesWithGin(engine, "../../../web/template")
|
||||
|
||||
// create the request
|
||||
protocol := config.GetProtocol()
|
||||
|
@ -131,7 +121,7 @@ func (suite *AuthStandardTestSuite) newContext(requestMethod string, requestPath
|
|||
|
||||
// trigger the session middleware on the context
|
||||
store := memstore.NewStore(make([]byte, 32), make([]byte, 32))
|
||||
store.Options(router.SessionOptions())
|
||||
store.Options(middleware.SessionOptions())
|
||||
sessionMiddleware := sessions.Sessions("gotosocial-localhost", store)
|
||||
sessionMiddleware(ctx)
|
||||
|
|
@ -27,8 +27,8 @@
|
|||
"github.com/gin-contrib/sessions"
|
||||
"github.com/gin-gonic/gin"
|
||||
"github.com/google/uuid"
|
||||
"github.com/superseriousbusiness/gotosocial/internal/api"
|
||||
"github.com/superseriousbusiness/gotosocial/internal/api/model"
|
||||
apimodel "github.com/superseriousbusiness/gotosocial/internal/api/model"
|
||||
apiutil "github.com/superseriousbusiness/gotosocial/internal/api/util"
|
||||
"github.com/superseriousbusiness/gotosocial/internal/config"
|
||||
"github.com/superseriousbusiness/gotosocial/internal/db"
|
||||
"github.com/superseriousbusiness/gotosocial/internal/gtserror"
|
||||
|
@ -42,8 +42,8 @@
|
|||
func (m *Module) AuthorizeGETHandler(c *gin.Context) {
|
||||
s := sessions.Default(c)
|
||||
|
||||
if _, err := api.NegotiateAccept(c, api.HTMLAcceptHeaders...); err != nil {
|
||||
api.ErrorHandler(c, gtserror.NewErrorNotAcceptable(err, err.Error()), m.processor.InstanceGet)
|
||||
if _, err := apiutil.NegotiateAccept(c, apiutil.HTMLAcceptHeaders...); err != nil {
|
||||
apiutil.ErrorHandler(c, gtserror.NewErrorNotAcceptable(err, err.Error()), m.processor.InstanceGet)
|
||||
return
|
||||
}
|
||||
|
||||
|
@ -51,20 +51,20 @@ func (m *Module) AuthorizeGETHandler(c *gin.Context) {
|
|||
// 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)
|
||||
if !ok || userID == "" {
|
||||
form := &model.OAuthAuthorize{}
|
||||
form := &apimodel.OAuthAuthorize{}
|
||||
if err := c.ShouldBind(form); err != nil {
|
||||
m.clearSession(s)
|
||||
api.ErrorHandler(c, gtserror.NewErrorBadRequest(err, oauth.HelpfulAdvice), m.processor.InstanceGet)
|
||||
apiutil.ErrorHandler(c, gtserror.NewErrorBadRequest(err, oauth.HelpfulAdvice), m.processor.InstanceGet)
|
||||
return
|
||||
}
|
||||
|
||||
if errWithCode := saveAuthFormToSession(s, form); errWithCode != nil {
|
||||
m.clearSession(s)
|
||||
api.ErrorHandler(c, errWithCode, m.processor.InstanceGet)
|
||||
apiutil.ErrorHandler(c, errWithCode, m.processor.InstanceGet)
|
||||
return
|
||||
}
|
||||
|
||||
c.Redirect(http.StatusSeeOther, AuthSignInPath)
|
||||
c.Redirect(http.StatusSeeOther, "/auth"+AuthSignInPath)
|
||||
return
|
||||
}
|
||||
|
||||
|
@ -73,7 +73,7 @@ func (m *Module) AuthorizeGETHandler(c *gin.Context) {
|
|||
if !ok || clientID == "" {
|
||||
m.clearSession(s)
|
||||
err := fmt.Errorf("key %s was not found in session", sessionClientID)
|
||||
api.ErrorHandler(c, gtserror.NewErrorBadRequest(err, oauth.HelpfulAdvice), m.processor.InstanceGet)
|
||||
apiutil.ErrorHandler(c, gtserror.NewErrorBadRequest(err, oauth.HelpfulAdvice), m.processor.InstanceGet)
|
||||
return
|
||||
}
|
||||
|
||||
|
@ -87,7 +87,7 @@ func (m *Module) AuthorizeGETHandler(c *gin.Context) {
|
|||
} else {
|
||||
errWithCode = gtserror.NewErrorInternalError(err, safe, oauth.HelpfulAdvice)
|
||||
}
|
||||
api.ErrorHandler(c, errWithCode, m.processor.InstanceGet)
|
||||
apiutil.ErrorHandler(c, errWithCode, m.processor.InstanceGet)
|
||||
return
|
||||
}
|
||||
|
||||
|
@ -101,7 +101,7 @@ func (m *Module) AuthorizeGETHandler(c *gin.Context) {
|
|||
} else {
|
||||
errWithCode = gtserror.NewErrorInternalError(err, safe, oauth.HelpfulAdvice)
|
||||
}
|
||||
api.ErrorHandler(c, errWithCode, m.processor.InstanceGet)
|
||||
apiutil.ErrorHandler(c, errWithCode, m.processor.InstanceGet)
|
||||
return
|
||||
}
|
||||
|
||||
|
@ -115,7 +115,7 @@ func (m *Module) AuthorizeGETHandler(c *gin.Context) {
|
|||
} else {
|
||||
errWithCode = gtserror.NewErrorInternalError(err, safe, oauth.HelpfulAdvice)
|
||||
}
|
||||
api.ErrorHandler(c, errWithCode, m.processor.InstanceGet)
|
||||
apiutil.ErrorHandler(c, errWithCode, m.processor.InstanceGet)
|
||||
return
|
||||
}
|
||||
|
||||
|
@ -128,7 +128,7 @@ func (m *Module) AuthorizeGETHandler(c *gin.Context) {
|
|||
if !ok || redirect == "" {
|
||||
m.clearSession(s)
|
||||
err := fmt.Errorf("key %s was not found in session", sessionRedirectURI)
|
||||
api.ErrorHandler(c, gtserror.NewErrorBadRequest(err, oauth.HelpfulAdvice), m.processor.InstanceGet)
|
||||
apiutil.ErrorHandler(c, gtserror.NewErrorBadRequest(err, oauth.HelpfulAdvice), m.processor.InstanceGet)
|
||||
return
|
||||
}
|
||||
|
||||
|
@ -136,13 +136,13 @@ func (m *Module) AuthorizeGETHandler(c *gin.Context) {
|
|||
if !ok || scope == "" {
|
||||
m.clearSession(s)
|
||||
err := fmt.Errorf("key %s was not found in session", sessionScope)
|
||||
api.ErrorHandler(c, gtserror.NewErrorBadRequest(err, oauth.HelpfulAdvice), m.processor.InstanceGet)
|
||||
apiutil.ErrorHandler(c, gtserror.NewErrorBadRequest(err, oauth.HelpfulAdvice), m.processor.InstanceGet)
|
||||
return
|
||||
}
|
||||
|
||||
instance, errWithCode := m.processor.InstanceGet(c.Request.Context(), config.GetHost())
|
||||
if errWithCode != nil {
|
||||
api.ErrorHandler(c, errWithCode, m.processor.InstanceGet)
|
||||
apiutil.ErrorHandler(c, errWithCode, m.processor.InstanceGet)
|
||||
return
|
||||
}
|
||||
|
||||
|
@ -206,7 +206,7 @@ func (m *Module) AuthorizePOSTHandler(c *gin.Context) {
|
|||
|
||||
if len(errs) != 0 {
|
||||
errs = append(errs, oauth.HelpfulAdvice)
|
||||
api.ErrorHandler(c, gtserror.NewErrorBadRequest(errors.New("one or more missing keys on session during AuthorizePOSTHandler"), errs...), m.processor.InstanceGet)
|
||||
apiutil.ErrorHandler(c, gtserror.NewErrorBadRequest(errors.New("one or more missing keys on session during AuthorizePOSTHandler"), errs...), m.processor.InstanceGet)
|
||||
return
|
||||
}
|
||||
|
||||
|
@ -220,7 +220,7 @@ func (m *Module) AuthorizePOSTHandler(c *gin.Context) {
|
|||
} else {
|
||||
errWithCode = gtserror.NewErrorInternalError(err, safe, oauth.HelpfulAdvice)
|
||||
}
|
||||
api.ErrorHandler(c, errWithCode, m.processor.InstanceGet)
|
||||
apiutil.ErrorHandler(c, errWithCode, m.processor.InstanceGet)
|
||||
return
|
||||
}
|
||||
|
||||
|
@ -234,7 +234,7 @@ func (m *Module) AuthorizePOSTHandler(c *gin.Context) {
|
|||
} else {
|
||||
errWithCode = gtserror.NewErrorInternalError(err, safe, oauth.HelpfulAdvice)
|
||||
}
|
||||
api.ErrorHandler(c, errWithCode, m.processor.InstanceGet)
|
||||
apiutil.ErrorHandler(c, errWithCode, m.processor.InstanceGet)
|
||||
return
|
||||
}
|
||||
|
||||
|
@ -263,13 +263,13 @@ func (m *Module) AuthorizePOSTHandler(c *gin.Context) {
|
|||
}
|
||||
|
||||
if errWithCode := m.processor.OAuthHandleAuthorizeRequest(c.Writer, c.Request); errWithCode != nil {
|
||||
api.ErrorHandler(c, errWithCode, m.processor.InstanceGet)
|
||||
apiutil.ErrorHandler(c, errWithCode, m.processor.InstanceGet)
|
||||
}
|
||||
}
|
||||
|
||||
// saveAuthFormToSession checks the given OAuthAuthorize form,
|
||||
// and stores the values in the form into the session.
|
||||
func saveAuthFormToSession(s sessions.Session, form *model.OAuthAuthorize) gtserror.WithCode {
|
||||
func saveAuthFormToSession(s sessions.Session, form *apimodel.OAuthAuthorize) gtserror.WithCode {
|
||||
if form == nil {
|
||||
err := errors.New("OAuthAuthorize form was nil")
|
||||
return gtserror.NewErrorBadRequest(err, err.Error(), oauth.HelpfulAdvice)
|
||||
|
@ -314,19 +314,19 @@ func saveAuthFormToSession(s sessions.Session, form *model.OAuthAuthorize) gtser
|
|||
|
||||
func ensureUserIsAuthorizedOrRedirect(ctx *gin.Context, user *gtsmodel.User, account *gtsmodel.Account) (redirected bool) {
|
||||
if user.ConfirmedAt.IsZero() {
|
||||
ctx.Redirect(http.StatusSeeOther, CheckYourEmailPath)
|
||||
ctx.Redirect(http.StatusSeeOther, "/auth"+AuthCheckYourEmailPath)
|
||||
redirected = true
|
||||
return
|
||||
}
|
||||
|
||||
if !*user.Approved {
|
||||
ctx.Redirect(http.StatusSeeOther, WaitForApprovalPath)
|
||||
ctx.Redirect(http.StatusSeeOther, "/auth"+AuthWaitForApprovalPath)
|
||||
redirected = true
|
||||
return
|
||||
}
|
||||
|
||||
if *user.Disabled || !account.SuspendedAt.IsZero() {
|
||||
ctx.Redirect(http.StatusSeeOther, AccountDisabledPath)
|
||||
ctx.Redirect(http.StatusSeeOther, "/auth"+AuthAccountDisabledPath)
|
||||
redirected = true
|
||||
return
|
||||
}
|
|
@ -9,7 +9,7 @@
|
|||
|
||||
"github.com/gin-contrib/sessions"
|
||||
"github.com/stretchr/testify/suite"
|
||||
"github.com/superseriousbusiness/gotosocial/internal/api/client/auth"
|
||||
"github.com/superseriousbusiness/gotosocial/internal/api/auth"
|
||||
"github.com/superseriousbusiness/gotosocial/internal/gtsmodel"
|
||||
"github.com/superseriousbusiness/gotosocial/testrig"
|
||||
)
|
||||
|
@ -34,7 +34,7 @@ func (suite *AuthAuthorizeTestSuite) TestAccountAuthorizeHandler() {
|
|||
return []string{"confirmed_at"}
|
||||
},
|
||||
expectedStatusCode: http.StatusSeeOther,
|
||||
expectedLocationHeader: auth.CheckYourEmailPath,
|
||||
expectedLocationHeader: "/auth" + auth.AuthCheckYourEmailPath,
|
||||
},
|
||||
{
|
||||
description: "user has their email confirmed but is not approved",
|
||||
|
@ -44,7 +44,7 @@ func (suite *AuthAuthorizeTestSuite) TestAccountAuthorizeHandler() {
|
|||
return []string{"confirmed_at", "email"}
|
||||
},
|
||||
expectedStatusCode: http.StatusSeeOther,
|
||||
expectedLocationHeader: auth.WaitForApprovalPath,
|
||||
expectedLocationHeader: "/auth" + auth.AuthWaitForApprovalPath,
|
||||
},
|
||||
{
|
||||
description: "user has their email confirmed and is approved, but User entity has been disabled",
|
||||
|
@ -56,7 +56,7 @@ func (suite *AuthAuthorizeTestSuite) TestAccountAuthorizeHandler() {
|
|||
return []string{"confirmed_at", "email", "approved", "disabled"}
|
||||
},
|
||||
expectedStatusCode: http.StatusSeeOther,
|
||||
expectedLocationHeader: auth.AccountDisabledPath,
|
||||
expectedLocationHeader: "/auth" + auth.AuthAccountDisabledPath,
|
||||
},
|
||||
{
|
||||
description: "user has their email confirmed and is approved, but Account entity has been suspended",
|
||||
|
@ -69,7 +69,7 @@ func (suite *AuthAuthorizeTestSuite) TestAccountAuthorizeHandler() {
|
|||
return []string{"confirmed_at", "email", "approved", "disabled"}
|
||||
},
|
||||
expectedStatusCode: http.StatusSeeOther,
|
||||
expectedLocationHeader: auth.AccountDisabledPath,
|
||||
expectedLocationHeader: "/auth" + auth.AuthAccountDisabledPath,
|
||||
},
|
||||
}
|
||||
|
|
@ -29,7 +29,7 @@
|
|||
"github.com/gin-contrib/sessions"
|
||||
"github.com/gin-gonic/gin"
|
||||
"github.com/google/uuid"
|
||||
"github.com/superseriousbusiness/gotosocial/internal/api"
|
||||
apiutil "github.com/superseriousbusiness/gotosocial/internal/api/util"
|
||||
"github.com/superseriousbusiness/gotosocial/internal/config"
|
||||
"github.com/superseriousbusiness/gotosocial/internal/db"
|
||||
"github.com/superseriousbusiness/gotosocial/internal/gtserror"
|
||||
|
@ -47,6 +47,12 @@ type extraInfo struct {
|
|||
|
||||
// CallbackGETHandler parses a token from an external auth provider.
|
||||
func (m *Module) CallbackGETHandler(c *gin.Context) {
|
||||
if !config.GetOIDCEnabled() {
|
||||
err := errors.New("oidc is not enabled for this server")
|
||||
apiutil.ErrorHandler(c, gtserror.NewErrorNotFound(err, err.Error()), m.processor.InstanceGet)
|
||||
return
|
||||
}
|
||||
|
||||
s := sessions.Default(c)
|
||||
|
||||
// check the query vs session state parameter to mitigate csrf
|
||||
|
@ -56,7 +62,7 @@ func (m *Module) CallbackGETHandler(c *gin.Context) {
|
|||
if returnedInternalState == "" {
|
||||
m.clearSession(s)
|
||||
err := fmt.Errorf("%s parameter not found on callback query", callbackStateParam)
|
||||
api.ErrorHandler(c, gtserror.NewErrorBadRequest(err, err.Error()), m.processor.InstanceGet)
|
||||
apiutil.ErrorHandler(c, gtserror.NewErrorBadRequest(err, err.Error()), m.processor.InstanceGet)
|
||||
return
|
||||
}
|
||||
|
||||
|
@ -65,14 +71,14 @@ func (m *Module) CallbackGETHandler(c *gin.Context) {
|
|||
if !ok {
|
||||
m.clearSession(s)
|
||||
err := fmt.Errorf("key %s was not found in session", sessionInternalState)
|
||||
api.ErrorHandler(c, gtserror.NewErrorBadRequest(err, err.Error()), m.processor.InstanceGet)
|
||||
apiutil.ErrorHandler(c, gtserror.NewErrorBadRequest(err, err.Error()), m.processor.InstanceGet)
|
||||
return
|
||||
}
|
||||
|
||||
if returnedInternalState != savedInternalState {
|
||||
m.clearSession(s)
|
||||
err := errors.New("mismatch between callback state and saved state")
|
||||
api.ErrorHandler(c, gtserror.NewErrorUnauthorized(err, err.Error()), m.processor.InstanceGet)
|
||||
apiutil.ErrorHandler(c, gtserror.NewErrorUnauthorized(err, err.Error()), m.processor.InstanceGet)
|
||||
return
|
||||
}
|
||||
|
||||
|
@ -81,14 +87,14 @@ func (m *Module) CallbackGETHandler(c *gin.Context) {
|
|||
if code == "" {
|
||||
m.clearSession(s)
|
||||
err := fmt.Errorf("%s parameter not found on callback query", callbackCodeParam)
|
||||
api.ErrorHandler(c, gtserror.NewErrorBadRequest(err, err.Error()), m.processor.InstanceGet)
|
||||
apiutil.ErrorHandler(c, gtserror.NewErrorBadRequest(err, err.Error()), m.processor.InstanceGet)
|
||||
return
|
||||
}
|
||||
|
||||
claims, errWithCode := m.idp.HandleCallback(c.Request.Context(), code)
|
||||
if errWithCode != nil {
|
||||
m.clearSession(s)
|
||||
api.ErrorHandler(c, errWithCode, m.processor.InstanceGet)
|
||||
apiutil.ErrorHandler(c, errWithCode, m.processor.InstanceGet)
|
||||
return
|
||||
}
|
||||
|
||||
|
@ -98,7 +104,7 @@ func (m *Module) CallbackGETHandler(c *gin.Context) {
|
|||
if !ok || clientID == "" {
|
||||
m.clearSession(s)
|
||||
err := fmt.Errorf("key %s was not found in session", sessionClientID)
|
||||
api.ErrorHandler(c, gtserror.NewErrorBadRequest(err, oauth.HelpfulAdvice), m.processor.InstanceGet)
|
||||
apiutil.ErrorHandler(c, gtserror.NewErrorBadRequest(err, oauth.HelpfulAdvice), m.processor.InstanceGet)
|
||||
return
|
||||
}
|
||||
|
||||
|
@ -112,21 +118,21 @@ func (m *Module) CallbackGETHandler(c *gin.Context) {
|
|||
} else {
|
||||
errWithCode = gtserror.NewErrorInternalError(err, safe, oauth.HelpfulAdvice)
|
||||
}
|
||||
api.ErrorHandler(c, errWithCode, m.processor.InstanceGet)
|
||||
apiutil.ErrorHandler(c, errWithCode, m.processor.InstanceGet)
|
||||
return
|
||||
}
|
||||
|
||||
user, errWithCode := m.fetchUserForClaims(c.Request.Context(), claims, net.IP(c.ClientIP()), app.ID)
|
||||
if errWithCode != nil {
|
||||
m.clearSession(s)
|
||||
api.ErrorHandler(c, errWithCode, m.processor.InstanceGet)
|
||||
apiutil.ErrorHandler(c, errWithCode, m.processor.InstanceGet)
|
||||
return
|
||||
}
|
||||
if user == nil {
|
||||
// no user exists yet - let's ask them for their preferred username
|
||||
instance, errWithCode := m.processor.InstanceGet(c.Request.Context(), config.GetHost())
|
||||
if errWithCode != nil {
|
||||
api.ErrorHandler(c, errWithCode, m.processor.InstanceGet)
|
||||
apiutil.ErrorHandler(c, errWithCode, m.processor.InstanceGet)
|
||||
return
|
||||
}
|
||||
|
||||
|
@ -135,7 +141,7 @@ func (m *Module) CallbackGETHandler(c *gin.Context) {
|
|||
s.Set(sessionAppID, app.ID)
|
||||
if err := s.Save(); err != nil {
|
||||
m.clearSession(s)
|
||||
api.ErrorHandler(c, gtserror.NewErrorInternalError(err), m.processor.InstanceGet)
|
||||
apiutil.ErrorHandler(c, gtserror.NewErrorInternalError(err), m.processor.InstanceGet)
|
||||
return
|
||||
}
|
||||
c.HTML(http.StatusOK, "finalize.tmpl", gin.H{
|
||||
|
@ -148,10 +154,10 @@ func (m *Module) CallbackGETHandler(c *gin.Context) {
|
|||
s.Set(sessionUserID, user.ID)
|
||||
if err := s.Save(); err != nil {
|
||||
m.clearSession(s)
|
||||
api.ErrorHandler(c, gtserror.NewErrorInternalError(err), m.processor.InstanceGet)
|
||||
apiutil.ErrorHandler(c, gtserror.NewErrorInternalError(err), m.processor.InstanceGet)
|
||||
return
|
||||
}
|
||||
c.Redirect(http.StatusFound, OauthAuthorizePath)
|
||||
c.Redirect(http.StatusFound, "/oauth"+OauthAuthorizePath)
|
||||
}
|
||||
|
||||
// FinalizePOSTHandler registers the user after additional data has been provided
|
||||
|
@ -161,7 +167,7 @@ func (m *Module) FinalizePOSTHandler(c *gin.Context) {
|
|||
form := &extraInfo{}
|
||||
if err := c.ShouldBind(form); err != nil {
|
||||
m.clearSession(s)
|
||||
api.ErrorHandler(c, gtserror.NewErrorBadRequest(err, oauth.HelpfulAdvice), m.processor.InstanceGet)
|
||||
apiutil.ErrorHandler(c, gtserror.NewErrorBadRequest(err, oauth.HelpfulAdvice), m.processor.InstanceGet)
|
||||
return
|
||||
}
|
||||
|
||||
|
@ -169,7 +175,7 @@ func (m *Module) FinalizePOSTHandler(c *gin.Context) {
|
|||
validationError := func(err error) {
|
||||
instance, errWithCode := m.processor.InstanceGet(c.Request.Context(), config.GetHost())
|
||||
if errWithCode != nil {
|
||||
api.ErrorHandler(c, errWithCode, m.processor.InstanceGet)
|
||||
apiutil.ErrorHandler(c, errWithCode, m.processor.InstanceGet)
|
||||
return
|
||||
}
|
||||
c.HTML(http.StatusOK, "finalize.tmpl", gin.H{
|
||||
|
@ -189,7 +195,7 @@ func (m *Module) FinalizePOSTHandler(c *gin.Context) {
|
|||
// see if the username is still available
|
||||
usernameAvailable, err := m.db.IsUsernameAvailable(c.Request.Context(), form.Username)
|
||||
if err != nil {
|
||||
api.ErrorHandler(c, gtserror.NewErrorBadRequest(err, oauth.HelpfulAdvice), m.processor.InstanceGet)
|
||||
apiutil.ErrorHandler(c, gtserror.NewErrorBadRequest(err, oauth.HelpfulAdvice), m.processor.InstanceGet)
|
||||
return
|
||||
}
|
||||
if !usernameAvailable {
|
||||
|
@ -201,7 +207,7 @@ func (m *Module) FinalizePOSTHandler(c *gin.Context) {
|
|||
appID, ok := s.Get(sessionAppID).(string)
|
||||
if !ok {
|
||||
err := fmt.Errorf("key %s was not found in session", sessionAppID)
|
||||
api.ErrorHandler(c, gtserror.NewErrorBadRequest(err, oauth.HelpfulAdvice), m.processor.InstanceGet)
|
||||
apiutil.ErrorHandler(c, gtserror.NewErrorBadRequest(err, oauth.HelpfulAdvice), m.processor.InstanceGet)
|
||||
return
|
||||
}
|
||||
|
||||
|
@ -209,7 +215,7 @@ func (m *Module) FinalizePOSTHandler(c *gin.Context) {
|
|||
claims, ok := s.Get(sessionClaims).(*oidc.Claims)
|
||||
if !ok {
|
||||
err := fmt.Errorf("key %s was not found in session", sessionClaims)
|
||||
api.ErrorHandler(c, gtserror.NewErrorBadRequest(err, oauth.HelpfulAdvice), m.processor.InstanceGet)
|
||||
apiutil.ErrorHandler(c, gtserror.NewErrorBadRequest(err, oauth.HelpfulAdvice), m.processor.InstanceGet)
|
||||
return
|
||||
}
|
||||
|
||||
|
@ -217,7 +223,7 @@ func (m *Module) FinalizePOSTHandler(c *gin.Context) {
|
|||
user, errWithCode := m.createUserFromOIDC(c.Request.Context(), claims, form, net.IP(c.ClientIP()), appID)
|
||||
if errWithCode != nil {
|
||||
m.clearSession(s)
|
||||
api.ErrorHandler(c, errWithCode, m.processor.InstanceGet)
|
||||
apiutil.ErrorHandler(c, errWithCode, m.processor.InstanceGet)
|
||||
return
|
||||
}
|
||||
s.Delete(sessionClaims)
|
||||
|
@ -225,10 +231,10 @@ func (m *Module) FinalizePOSTHandler(c *gin.Context) {
|
|||
s.Set(sessionUserID, user.ID)
|
||||
if err := s.Save(); err != nil {
|
||||
m.clearSession(s)
|
||||
api.ErrorHandler(c, gtserror.NewErrorInternalError(err), m.processor.InstanceGet)
|
||||
apiutil.ErrorHandler(c, gtserror.NewErrorInternalError(err), m.processor.InstanceGet)
|
||||
return
|
||||
}
|
||||
c.Redirect(http.StatusFound, OauthAuthorizePath)
|
||||
c.Redirect(http.StatusFound, "/oauth"+OauthAuthorizePath)
|
||||
}
|
||||
|
||||
func (m *Module) fetchUserForClaims(ctx context.Context, claims *oidc.Claims, ip net.IP, appID string) (*gtsmodel.User, gtserror.WithCode) {
|
|
@ -26,8 +26,8 @@
|
|||
|
||||
"github.com/gin-contrib/sessions"
|
||||
"github.com/gin-gonic/gin"
|
||||
"github.com/superseriousbusiness/gotosocial/internal/api"
|
||||
"github.com/superseriousbusiness/gotosocial/internal/api/model"
|
||||
apimodel "github.com/superseriousbusiness/gotosocial/internal/api/model"
|
||||
apiutil "github.com/superseriousbusiness/gotosocial/internal/api/util"
|
||||
"github.com/superseriousbusiness/gotosocial/internal/config"
|
||||
"github.com/superseriousbusiness/gotosocial/internal/db"
|
||||
"github.com/superseriousbusiness/gotosocial/internal/gtserror"
|
||||
|
@ -38,16 +38,18 @@ func (m *Module) OobHandler(c *gin.Context) {
|
|||
host := config.GetHost()
|
||||
instance, errWithCode := m.processor.InstanceGet(c.Request.Context(), host)
|
||||
if errWithCode != nil {
|
||||
api.ErrorHandler(c, errWithCode, m.processor.InstanceGet)
|
||||
apiutil.ErrorHandler(c, errWithCode, m.processor.InstanceGet)
|
||||
return
|
||||
}
|
||||
|
||||
instanceGet := func(ctx context.Context, domain string) (*model.Instance, gtserror.WithCode) { return instance, nil }
|
||||
instanceGet := func(ctx context.Context, domain string) (*apimodel.Instance, gtserror.WithCode) {
|
||||
return instance, nil
|
||||
}
|
||||
|
||||
oobToken := c.Query("code")
|
||||
if oobToken == "" {
|
||||
err := errors.New("no 'code' query value provided in callback redirect")
|
||||
api.ErrorHandler(c, gtserror.NewErrorBadRequest(err, err.Error(), oauth.HelpfulAdvice), instanceGet)
|
||||
apiutil.ErrorHandler(c, gtserror.NewErrorBadRequest(err, err.Error(), oauth.HelpfulAdvice), instanceGet)
|
||||
return
|
||||
}
|
||||
|
||||
|
@ -67,7 +69,7 @@ func (m *Module) OobHandler(c *gin.Context) {
|
|||
|
||||
if len(errs) != 0 {
|
||||
errs = append(errs, oauth.HelpfulAdvice)
|
||||
api.ErrorHandler(c, gtserror.NewErrorBadRequest(errors.New("one or more missing keys on session during OobHandler"), errs...), m.processor.InstanceGet)
|
||||
apiutil.ErrorHandler(c, gtserror.NewErrorBadRequest(errors.New("one or more missing keys on session during OobHandler"), errs...), m.processor.InstanceGet)
|
||||
return
|
||||
}
|
||||
|
||||
|
@ -81,7 +83,7 @@ func (m *Module) OobHandler(c *gin.Context) {
|
|||
} else {
|
||||
errWithCode = gtserror.NewErrorInternalError(err, safe, oauth.HelpfulAdvice)
|
||||
}
|
||||
api.ErrorHandler(c, errWithCode, instanceGet)
|
||||
apiutil.ErrorHandler(c, errWithCode, instanceGet)
|
||||
return
|
||||
}
|
||||
|
||||
|
@ -95,7 +97,7 @@ func (m *Module) OobHandler(c *gin.Context) {
|
|||
} else {
|
||||
errWithCode = gtserror.NewErrorInternalError(err, safe, oauth.HelpfulAdvice)
|
||||
}
|
||||
api.ErrorHandler(c, errWithCode, instanceGet)
|
||||
apiutil.ErrorHandler(c, errWithCode, instanceGet)
|
||||
return
|
||||
}
|
||||
|
|
@ -26,7 +26,7 @@
|
|||
|
||||
"github.com/gin-contrib/sessions"
|
||||
"github.com/gin-gonic/gin"
|
||||
"github.com/superseriousbusiness/gotosocial/internal/api"
|
||||
apiutil "github.com/superseriousbusiness/gotosocial/internal/api/util"
|
||||
"github.com/superseriousbusiness/gotosocial/internal/config"
|
||||
"github.com/superseriousbusiness/gotosocial/internal/gtserror"
|
||||
"github.com/superseriousbusiness/gotosocial/internal/oauth"
|
||||
|
@ -44,15 +44,15 @@ type login struct {
|
|||
// The form will then POST to the sign in page, which will be handled by SignInPOSTHandler.
|
||||
// If an idp provider is set, then the user will be redirected to that to do their sign in.
|
||||
func (m *Module) SignInGETHandler(c *gin.Context) {
|
||||
if _, err := api.NegotiateAccept(c, api.HTMLAcceptHeaders...); err != nil {
|
||||
api.ErrorHandler(c, gtserror.NewErrorNotAcceptable(err, err.Error()), m.processor.InstanceGet)
|
||||
if _, err := apiutil.NegotiateAccept(c, apiutil.HTMLAcceptHeaders...); err != nil {
|
||||
apiutil.ErrorHandler(c, gtserror.NewErrorNotAcceptable(err, err.Error()), m.processor.InstanceGet)
|
||||
return
|
||||
}
|
||||
|
||||
if m.idp == nil {
|
||||
if !config.GetOIDCEnabled() {
|
||||
instance, errWithCode := m.processor.InstanceGet(c.Request.Context(), config.GetHost())
|
||||
if errWithCode != nil {
|
||||
api.ErrorHandler(c, errWithCode, m.processor.InstanceGet)
|
||||
apiutil.ErrorHandler(c, errWithCode, m.processor.InstanceGet)
|
||||
return
|
||||
}
|
||||
|
||||
|
@ -71,7 +71,7 @@ func (m *Module) SignInGETHandler(c *gin.Context) {
|
|||
if !ok {
|
||||
m.clearSession(s)
|
||||
err := fmt.Errorf("key %s was not found in session", sessionInternalState)
|
||||
api.ErrorHandler(c, gtserror.NewErrorBadRequest(err, err.Error()), m.processor.InstanceGet)
|
||||
apiutil.ErrorHandler(c, gtserror.NewErrorBadRequest(err, err.Error()), m.processor.InstanceGet)
|
||||
return
|
||||
}
|
||||
|
||||
|
@ -87,7 +87,7 @@ func (m *Module) SignInPOSTHandler(c *gin.Context) {
|
|||
form := &login{}
|
||||
if err := c.ShouldBind(form); err != nil {
|
||||
m.clearSession(s)
|
||||
api.ErrorHandler(c, gtserror.NewErrorBadRequest(err, oauth.HelpfulAdvice), m.processor.InstanceGet)
|
||||
apiutil.ErrorHandler(c, gtserror.NewErrorBadRequest(err, oauth.HelpfulAdvice), m.processor.InstanceGet)
|
||||
return
|
||||
}
|
||||
|
||||
|
@ -95,17 +95,17 @@ func (m *Module) SignInPOSTHandler(c *gin.Context) {
|
|||
if errWithCode != nil {
|
||||
// don't clear session here, so the user can just press back and try again
|
||||
// if they accidentally gave the wrong password or something
|
||||
api.ErrorHandler(c, errWithCode, m.processor.InstanceGet)
|
||||
apiutil.ErrorHandler(c, errWithCode, m.processor.InstanceGet)
|
||||
return
|
||||
}
|
||||
|
||||
s.Set(sessionUserID, userid)
|
||||
if err := s.Save(); err != nil {
|
||||
err := fmt.Errorf("error saving user id onto session: %s", err)
|
||||
api.ErrorHandler(c, gtserror.NewErrorInternalError(err, oauth.HelpfulAdvice), m.processor.InstanceGet)
|
||||
apiutil.ErrorHandler(c, gtserror.NewErrorInternalError(err, oauth.HelpfulAdvice), m.processor.InstanceGet)
|
||||
}
|
||||
|
||||
c.Redirect(http.StatusFound, OauthAuthorizePath)
|
||||
c.Redirect(http.StatusFound, "/oauth"+OauthAuthorizePath)
|
||||
}
|
||||
|
||||
// ValidatePassword takes an email address and a password.
|
|
@ -22,7 +22,7 @@
|
|||
"net/http"
|
||||
"net/url"
|
||||
|
||||
"github.com/superseriousbusiness/gotosocial/internal/api"
|
||||
apiutil "github.com/superseriousbusiness/gotosocial/internal/api/util"
|
||||
"github.com/superseriousbusiness/gotosocial/internal/gtserror"
|
||||
"github.com/superseriousbusiness/gotosocial/internal/oauth"
|
||||
|
||||
|
@ -41,8 +41,8 @@ type tokenRequestForm struct {
|
|||
// TokenPOSTHandler should be served as a POST at https://example.org/oauth/token
|
||||
// The idea here is to serve an oauth access token to a user, which can be used for authorizing against non-public APIs.
|
||||
func (m *Module) TokenPOSTHandler(c *gin.Context) {
|
||||
if _, err := api.NegotiateAccept(c, api.JSONAcceptHeaders...); err != nil {
|
||||
api.ErrorHandler(c, gtserror.NewErrorNotAcceptable(err, err.Error()), m.processor.InstanceGet)
|
||||
if _, err := apiutil.NegotiateAccept(c, apiutil.JSONAcceptHeaders...); err != nil {
|
||||
apiutil.ErrorHandler(c, gtserror.NewErrorNotAcceptable(err, err.Error()), m.processor.InstanceGet)
|
||||
return
|
||||
}
|
||||
|
||||
|
@ -50,7 +50,7 @@ func (m *Module) TokenPOSTHandler(c *gin.Context) {
|
|||
|
||||
form := &tokenRequestForm{}
|
||||
if err := c.ShouldBind(form); err != nil {
|
||||
api.OAuthErrorHandler(c, gtserror.NewErrorBadRequest(oauth.InvalidRequest(), err.Error()))
|
||||
apiutil.OAuthErrorHandler(c, gtserror.NewErrorBadRequest(oauth.InvalidRequest(), err.Error()))
|
||||
return
|
||||
}
|
||||
|
||||
|
@ -99,13 +99,13 @@ func (m *Module) TokenPOSTHandler(c *gin.Context) {
|
|||
}
|
||||
|
||||
if len(help) != 0 {
|
||||
api.OAuthErrorHandler(c, gtserror.NewErrorBadRequest(oauth.InvalidRequest(), help...))
|
||||
apiutil.OAuthErrorHandler(c, gtserror.NewErrorBadRequest(oauth.InvalidRequest(), help...))
|
||||
return
|
||||
}
|
||||
|
||||
token, errWithCode := m.processor.OAuthHandleTokenRequest(c.Request)
|
||||
if errWithCode != nil {
|
||||
api.OAuthErrorHandler(c, errWithCode)
|
||||
apiutil.OAuthErrorHandler(c, errWithCode)
|
||||
return
|
||||
}
|
||||
|
129
internal/api/client.go
Normal file
129
internal/api/client.go
Normal file
|
@ -0,0 +1,129 @@
|
|||
/*
|
||||
GoToSocial
|
||||
Copyright (C) 2021-2022 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 (
|
||||
"github.com/superseriousbusiness/gotosocial/internal/api/client/accounts"
|
||||
"github.com/superseriousbusiness/gotosocial/internal/api/client/admin"
|
||||
"github.com/superseriousbusiness/gotosocial/internal/api/client/apps"
|
||||
"github.com/superseriousbusiness/gotosocial/internal/api/client/blocks"
|
||||
"github.com/superseriousbusiness/gotosocial/internal/api/client/bookmarks"
|
||||
"github.com/superseriousbusiness/gotosocial/internal/api/client/customemojis"
|
||||
"github.com/superseriousbusiness/gotosocial/internal/api/client/favourites"
|
||||
filter "github.com/superseriousbusiness/gotosocial/internal/api/client/filters"
|
||||
"github.com/superseriousbusiness/gotosocial/internal/api/client/followrequests"
|
||||
"github.com/superseriousbusiness/gotosocial/internal/api/client/instance"
|
||||
"github.com/superseriousbusiness/gotosocial/internal/api/client/lists"
|
||||
"github.com/superseriousbusiness/gotosocial/internal/api/client/media"
|
||||
"github.com/superseriousbusiness/gotosocial/internal/api/client/notifications"
|
||||
"github.com/superseriousbusiness/gotosocial/internal/api/client/search"
|
||||
"github.com/superseriousbusiness/gotosocial/internal/api/client/statuses"
|
||||
"github.com/superseriousbusiness/gotosocial/internal/api/client/streaming"
|
||||
"github.com/superseriousbusiness/gotosocial/internal/api/client/timelines"
|
||||
"github.com/superseriousbusiness/gotosocial/internal/api/client/user"
|
||||
"github.com/superseriousbusiness/gotosocial/internal/db"
|
||||
"github.com/superseriousbusiness/gotosocial/internal/middleware"
|
||||
"github.com/superseriousbusiness/gotosocial/internal/processing"
|
||||
"github.com/superseriousbusiness/gotosocial/internal/router"
|
||||
)
|
||||
|
||||
type Client struct {
|
||||
processor processing.Processor
|
||||
db db.DB
|
||||
|
||||
accounts *accounts.Module // api/v1/accounts
|
||||
admin *admin.Module // api/v1/admin
|
||||
apps *apps.Module // api/v1/apps
|
||||
blocks *blocks.Module // api/v1/blocks
|
||||
bookmarks *bookmarks.Module // api/v1/bookmarks
|
||||
customEmojis *customemojis.Module // api/v1/custom_emojis
|
||||
favourites *favourites.Module // api/v1/favourites
|
||||
filters *filter.Module // api/v1/filters
|
||||
followRequests *followrequests.Module // api/v1/follow_requests
|
||||
instance *instance.Module // api/v1/instance
|
||||
lists *lists.Module // api/v1/lists
|
||||
media *media.Module // api/v1/media, api/v2/media
|
||||
notifications *notifications.Module // api/v1/notifications
|
||||
search *search.Module // api/v1/search, api/v2/search
|
||||
statuses *statuses.Module // api/v1/statuses
|
||||
streaming *streaming.Module // api/v1/streaming
|
||||
timelines *timelines.Module // api/v1/timelines
|
||||
user *user.Module // api/v1/user
|
||||
}
|
||||
|
||||
func (c *Client) Route(r router.Router) {
|
||||
// create a new group on the top level client 'api' prefix
|
||||
apiGroup := r.AttachGroup("api")
|
||||
|
||||
// attach non-global middlewares appropriate to the client api
|
||||
apiGroup.Use(
|
||||
middleware.TokenCheck(c.db, c.processor.OAuthValidateBearerToken),
|
||||
middleware.RateLimit(),
|
||||
middleware.Gzip(),
|
||||
middleware.CacheControl("no-store"), // never cache api responses
|
||||
)
|
||||
|
||||
// for each client api module, pass it the Handle function
|
||||
// so that the module can attach its routes to this group
|
||||
h := apiGroup.Handle
|
||||
c.accounts.Route(h)
|
||||
c.admin.Route(h)
|
||||
c.apps.Route(h)
|
||||
c.blocks.Route(h)
|
||||
c.bookmarks.Route(h)
|
||||
c.customEmojis.Route(h)
|
||||
c.favourites.Route(h)
|
||||
c.filters.Route(h)
|
||||
c.followRequests.Route(h)
|
||||
c.instance.Route(h)
|
||||
c.lists.Route(h)
|
||||
c.media.Route(h)
|
||||
c.notifications.Route(h)
|
||||
c.search.Route(h)
|
||||
c.statuses.Route(h)
|
||||
c.streaming.Route(h)
|
||||
c.timelines.Route(h)
|
||||
c.user.Route(h)
|
||||
}
|
||||
|
||||
func NewClient(db db.DB, p processing.Processor) *Client {
|
||||
return &Client{
|
||||
processor: p,
|
||||
db: db,
|
||||
|
||||
accounts: accounts.New(p),
|
||||
admin: admin.New(p),
|
||||
apps: apps.New(p),
|
||||
blocks: blocks.New(p),
|
||||
bookmarks: bookmarks.New(p),
|
||||
customEmojis: customemojis.New(p),
|
||||
favourites: favourites.New(p),
|
||||
filters: filter.New(p),
|
||||
followRequests: followrequests.New(p),
|
||||
instance: instance.New(p),
|
||||
lists: lists.New(p),
|
||||
media: media.New(p),
|
||||
notifications: notifications.New(p),
|
||||
search: search.New(p),
|
||||
statuses: statuses.New(p),
|
||||
streaming: streaming.New(p),
|
||||
timelines: timelines.New(p),
|
||||
user: user.New(p),
|
||||
}
|
||||
}
|
|
@ -16,7 +16,7 @@
|
|||
along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
package account_test
|
||||
package accounts_test
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
|
@ -26,7 +26,7 @@
|
|||
|
||||
"github.com/gin-gonic/gin"
|
||||
"github.com/stretchr/testify/suite"
|
||||
"github.com/superseriousbusiness/gotosocial/internal/api/client/account"
|
||||
"github.com/superseriousbusiness/gotosocial/internal/api/client/accounts"
|
||||
"github.com/superseriousbusiness/gotosocial/internal/concurrency"
|
||||
"github.com/superseriousbusiness/gotosocial/internal/config"
|
||||
"github.com/superseriousbusiness/gotosocial/internal/db"
|
||||
|
@ -62,7 +62,7 @@ type AccountStandardTestSuite struct {
|
|||
testStatuses map[string]*gtsmodel.Status
|
||||
|
||||
// module being tested
|
||||
accountModule *account.Module
|
||||
accountsModule *accounts.Module
|
||||
}
|
||||
|
||||
func (suite *AccountStandardTestSuite) SetupSuite() {
|
||||
|
@ -89,7 +89,7 @@ func (suite *AccountStandardTestSuite) SetupTest() {
|
|||
suite.sentEmails = make(map[string]string)
|
||||
suite.emailSender = testrig.NewEmailSender("../../../../web/template/", suite.sentEmails)
|
||||
suite.processor = testrig.NewTestProcessor(suite.db, suite.storage, suite.federator, suite.emailSender, suite.mediaManager, clientWorker, fedWorker)
|
||||
suite.accountModule = account.New(suite.processor).(*account.Module)
|
||||
suite.accountsModule = accounts.New(suite.processor)
|
||||
testrig.StandardDBSetup(suite.db, nil)
|
||||
testrig.StandardStorageSetup(suite.storage, "../../../../testrig/media")
|
||||
|
|
@ -16,7 +16,7 @@
|
|||
along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
package account
|
||||
package accounts
|
||||
|
||||
import (
|
||||
"errors"
|
||||
|
@ -24,8 +24,8 @@
|
|||
"net/http"
|
||||
|
||||
"github.com/gin-gonic/gin"
|
||||
"github.com/superseriousbusiness/gotosocial/internal/api"
|
||||
"github.com/superseriousbusiness/gotosocial/internal/api/model"
|
||||
apimodel "github.com/superseriousbusiness/gotosocial/internal/api/model"
|
||||
apiutil "github.com/superseriousbusiness/gotosocial/internal/api/util"
|
||||
"github.com/superseriousbusiness/gotosocial/internal/config"
|
||||
"github.com/superseriousbusiness/gotosocial/internal/gtserror"
|
||||
"github.com/superseriousbusiness/gotosocial/internal/oauth"
|
||||
|
@ -73,23 +73,23 @@
|
|||
func (m *Module) AccountCreatePOSTHandler(c *gin.Context) {
|
||||
authed, err := oauth.Authed(c, true, true, false, false)
|
||||
if err != nil {
|
||||
api.ErrorHandler(c, gtserror.NewErrorUnauthorized(err, err.Error()), m.processor.InstanceGet)
|
||||
apiutil.ErrorHandler(c, gtserror.NewErrorUnauthorized(err, err.Error()), m.processor.InstanceGet)
|
||||
return
|
||||
}
|
||||
|
||||
if _, err := api.NegotiateAccept(c, api.JSONAcceptHeaders...); err != nil {
|
||||
api.ErrorHandler(c, gtserror.NewErrorNotAcceptable(err, err.Error()), m.processor.InstanceGet)
|
||||
if _, err := apiutil.NegotiateAccept(c, apiutil.JSONAcceptHeaders...); err != nil {
|
||||
apiutil.ErrorHandler(c, gtserror.NewErrorNotAcceptable(err, err.Error()), m.processor.InstanceGet)
|
||||
return
|
||||
}
|
||||
|
||||
form := &model.AccountCreateRequest{}
|
||||
form := &apimodel.AccountCreateRequest{}
|
||||
if err := c.ShouldBind(form); err != nil {
|
||||
api.ErrorHandler(c, gtserror.NewErrorBadRequest(err, err.Error()), m.processor.InstanceGet)
|
||||
apiutil.ErrorHandler(c, gtserror.NewErrorBadRequest(err, err.Error()), m.processor.InstanceGet)
|
||||
return
|
||||
}
|
||||
|
||||
if err := validateCreateAccount(form); err != nil {
|
||||
api.ErrorHandler(c, gtserror.NewErrorBadRequest(err, err.Error()), m.processor.InstanceGet)
|
||||
apiutil.ErrorHandler(c, gtserror.NewErrorBadRequest(err, err.Error()), m.processor.InstanceGet)
|
||||
return
|
||||
}
|
||||
|
||||
|
@ -97,14 +97,14 @@ func (m *Module) AccountCreatePOSTHandler(c *gin.Context) {
|
|||
signUpIP := net.ParseIP(clientIP)
|
||||
if signUpIP == nil {
|
||||
err := errors.New("ip address could not be parsed from request")
|
||||
api.ErrorHandler(c, gtserror.NewErrorBadRequest(err, err.Error()), m.processor.InstanceGet)
|
||||
apiutil.ErrorHandler(c, gtserror.NewErrorBadRequest(err, err.Error()), m.processor.InstanceGet)
|
||||
return
|
||||
}
|
||||
form.IP = signUpIP
|
||||
|
||||
ti, errWithCode := m.processor.AccountCreate(c.Request.Context(), authed, form)
|
||||
if errWithCode != nil {
|
||||
api.ErrorHandler(c, errWithCode, m.processor.InstanceGet)
|
||||
apiutil.ErrorHandler(c, errWithCode, m.processor.InstanceGet)
|
||||
return
|
||||
}
|
||||
|
||||
|
@ -113,7 +113,7 @@ func (m *Module) AccountCreatePOSTHandler(c *gin.Context) {
|
|||
|
||||
// validateCreateAccount checks through all the necessary prerequisites for creating a new account,
|
||||
// according to the provided account create request. If the account isn't eligible, an error will be returned.
|
||||
func validateCreateAccount(form *model.AccountCreateRequest) error {
|
||||
func validateCreateAccount(form *apimodel.AccountCreateRequest) error {
|
||||
if form == nil {
|
||||
return errors.New("form was nil")
|
||||
}
|
|
@ -16,4 +16,4 @@
|
|||
// along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
// */
|
||||
|
||||
package account_test
|
||||
package accounts_test
|
|
@ -16,15 +16,15 @@
|
|||
along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
package account
|
||||
package accounts
|
||||
|
||||
import (
|
||||
"errors"
|
||||
"net/http"
|
||||
|
||||
"github.com/gin-gonic/gin"
|
||||
"github.com/superseriousbusiness/gotosocial/internal/api"
|
||||
"github.com/superseriousbusiness/gotosocial/internal/api/model"
|
||||
apimodel "github.com/superseriousbusiness/gotosocial/internal/api/model"
|
||||
apiutil "github.com/superseriousbusiness/gotosocial/internal/api/util"
|
||||
"github.com/superseriousbusiness/gotosocial/internal/gtserror"
|
||||
"github.com/superseriousbusiness/gotosocial/internal/oauth"
|
||||
)
|
||||
|
@ -68,26 +68,26 @@
|
|||
func (m *Module) AccountDeletePOSTHandler(c *gin.Context) {
|
||||
authed, err := oauth.Authed(c, true, true, true, true)
|
||||
if err != nil {
|
||||
api.ErrorHandler(c, gtserror.NewErrorUnauthorized(err, err.Error()), m.processor.InstanceGet)
|
||||
apiutil.ErrorHandler(c, gtserror.NewErrorUnauthorized(err, err.Error()), m.processor.InstanceGet)
|
||||
return
|
||||
}
|
||||
|
||||
form := &model.AccountDeleteRequest{}
|
||||
form := &apimodel.AccountDeleteRequest{}
|
||||
if err := c.ShouldBind(&form); err != nil {
|
||||
api.ErrorHandler(c, gtserror.NewErrorBadRequest(err, err.Error()), m.processor.InstanceGet)
|
||||
apiutil.ErrorHandler(c, gtserror.NewErrorBadRequest(err, err.Error()), m.processor.InstanceGet)
|
||||
return
|
||||
}
|
||||
|
||||
if form.Password == "" {
|
||||
err = errors.New("no password provided in account delete request")
|
||||
api.ErrorHandler(c, gtserror.NewErrorBadRequest(err, err.Error()), m.processor.InstanceGet)
|
||||
apiutil.ErrorHandler(c, gtserror.NewErrorBadRequest(err, err.Error()), m.processor.InstanceGet)
|
||||
return
|
||||
}
|
||||
|
||||
form.DeleteOriginID = authed.Account.ID
|
||||
|
||||
if errWithCode := m.processor.AccountDeleteLocal(c.Request.Context(), authed, form); errWithCode != nil {
|
||||
api.ErrorHandler(c, errWithCode, m.processor.InstanceGet)
|
||||
apiutil.ErrorHandler(c, errWithCode, m.processor.InstanceGet)
|
||||
return
|
||||
}
|
||||
|
|
@ -16,7 +16,7 @@
|
|||
along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
package account_test
|
||||
package accounts_test
|
||||
|
||||
import (
|
||||
"net/http"
|
||||
|
@ -24,7 +24,7 @@
|
|||
"testing"
|
||||
|
||||
"github.com/stretchr/testify/suite"
|
||||
"github.com/superseriousbusiness/gotosocial/internal/api/client/account"
|
||||
"github.com/superseriousbusiness/gotosocial/internal/api/client/accounts"
|
||||
"github.com/superseriousbusiness/gotosocial/testrig"
|
||||
)
|
||||
|
||||
|
@ -45,10 +45,10 @@ func (suite *AccountDeleteTestSuite) TestAccountDeletePOSTHandler() {
|
|||
}
|
||||
bodyBytes := requestBody.Bytes()
|
||||
recorder := httptest.NewRecorder()
|
||||
ctx := suite.newContext(recorder, http.MethodPost, bodyBytes, account.DeleteAccountPath, w.FormDataContentType())
|
||||
ctx := suite.newContext(recorder, http.MethodPost, bodyBytes, accounts.DeleteAccountPath, w.FormDataContentType())
|
||||
|
||||
// call the handler
|
||||
suite.accountModule.AccountDeletePOSTHandler(ctx)
|
||||
suite.accountsModule.AccountDeletePOSTHandler(ctx)
|
||||
|
||||
// 1. we should have Accepted because our request was valid
|
||||
suite.Equal(http.StatusAccepted, recorder.Code)
|
||||
|
@ -67,10 +67,10 @@ func (suite *AccountDeleteTestSuite) TestAccountDeletePOSTHandlerWrongPassword()
|
|||
}
|
||||
bodyBytes := requestBody.Bytes()
|
||||
recorder := httptest.NewRecorder()
|
||||
ctx := suite.newContext(recorder, http.MethodPost, bodyBytes, account.DeleteAccountPath, w.FormDataContentType())
|
||||
ctx := suite.newContext(recorder, http.MethodPost, bodyBytes, accounts.DeleteAccountPath, w.FormDataContentType())
|
||||
|
||||
// call the handler
|
||||
suite.accountModule.AccountDeletePOSTHandler(ctx)
|
||||
suite.accountsModule.AccountDeletePOSTHandler(ctx)
|
||||
|
||||
// 1. we should have Forbidden because we supplied the wrong password
|
||||
suite.Equal(http.StatusForbidden, recorder.Code)
|
||||
|
@ -87,10 +87,10 @@ func (suite *AccountDeleteTestSuite) TestAccountDeletePOSTHandlerNoPassword() {
|
|||
}
|
||||
bodyBytes := requestBody.Bytes()
|
||||
recorder := httptest.NewRecorder()
|
||||
ctx := suite.newContext(recorder, http.MethodPost, bodyBytes, account.DeleteAccountPath, w.FormDataContentType())
|
||||
ctx := suite.newContext(recorder, http.MethodPost, bodyBytes, accounts.DeleteAccountPath, w.FormDataContentType())
|
||||
|
||||
// call the handler
|
||||
suite.accountModule.AccountDeletePOSTHandler(ctx)
|
||||
suite.accountsModule.AccountDeletePOSTHandler(ctx)
|
||||
|
||||
// 1. we should have StatusBadRequest because our request was invalid
|
||||
suite.Equal(http.StatusBadRequest, recorder.Code)
|
|
@ -16,14 +16,14 @@
|
|||
along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
package account
|
||||
package accounts
|
||||
|
||||
import (
|
||||
"errors"
|
||||
"net/http"
|
||||
|
||||
"github.com/gin-gonic/gin"
|
||||
"github.com/superseriousbusiness/gotosocial/internal/api"
|
||||
apiutil "github.com/superseriousbusiness/gotosocial/internal/api/util"
|
||||
"github.com/superseriousbusiness/gotosocial/internal/gtserror"
|
||||
"github.com/superseriousbusiness/gotosocial/internal/oauth"
|
||||
)
|
||||
|
@ -69,25 +69,25 @@
|
|||
func (m *Module) AccountGETHandler(c *gin.Context) {
|
||||
authed, err := oauth.Authed(c, true, true, true, true)
|
||||
if err != nil {
|
||||
api.ErrorHandler(c, gtserror.NewErrorUnauthorized(err, err.Error()), m.processor.InstanceGet)
|
||||
apiutil.ErrorHandler(c, gtserror.NewErrorUnauthorized(err, err.Error()), m.processor.InstanceGet)
|
||||
return
|
||||
}
|
||||
|
||||
if _, err := api.NegotiateAccept(c, api.JSONAcceptHeaders...); err != nil {
|
||||
api.ErrorHandler(c, gtserror.NewErrorNotAcceptable(err, err.Error()), m.processor.InstanceGet)
|
||||
if _, err := apiutil.NegotiateAccept(c, apiutil.JSONAcceptHeaders...); err != nil {
|
||||
apiutil.ErrorHandler(c, gtserror.NewErrorNotAcceptable(err, err.Error()), m.processor.InstanceGet)
|
||||
return
|
||||
}
|
||||
|
||||
targetAcctID := c.Param(IDKey)
|
||||
if targetAcctID == "" {
|
||||
err := errors.New("no account id specified")
|
||||
api.ErrorHandler(c, gtserror.NewErrorBadRequest(err, err.Error()), m.processor.InstanceGet)
|
||||
apiutil.ErrorHandler(c, gtserror.NewErrorBadRequest(err, err.Error()), m.processor.InstanceGet)
|
||||
return
|
||||
}
|
||||
|
||||
acctInfo, errWithCode := m.processor.AccountGet(c.Request.Context(), authed, targetAcctID)
|
||||
if errWithCode != nil {
|
||||
api.ErrorHandler(c, errWithCode, m.processor.InstanceGet)
|
||||
apiutil.ErrorHandler(c, errWithCode, m.processor.InstanceGet)
|
||||
return
|
||||
}
|
||||
|
|
@ -16,17 +16,13 @@
|
|||
along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
package account
|
||||
package accounts
|
||||
|
||||
import (
|
||||
"net/http"
|
||||
"strings"
|
||||
|
||||
"github.com/gin-gonic/gin"
|
||||
"github.com/superseriousbusiness/gotosocial/internal/api"
|
||||
"github.com/superseriousbusiness/gotosocial/internal/processing"
|
||||
|
||||
"github.com/superseriousbusiness/gotosocial/internal/router"
|
||||
)
|
||||
|
||||
const (
|
||||
|
@ -49,8 +45,8 @@
|
|||
|
||||
// IDKey is the key to use for retrieving account ID in requests
|
||||
IDKey = "id"
|
||||
// BasePath is the base API path for this module
|
||||
BasePath = "/api/v1/accounts"
|
||||
// BasePath is the base API path for this module, excluding the 'api' prefix
|
||||
BasePath = "/v1/accounts"
|
||||
// BasePathWithID is the base path for this module with the ID key
|
||||
BasePathWithID = BasePath + "/:" + IDKey
|
||||
// VerifyPath is for verifying account credentials
|
||||
|
@ -77,65 +73,47 @@
|
|||
DeleteAccountPath = BasePath + "/delete"
|
||||
)
|
||||
|
||||
// Module implements the ClientAPIModule interface for account-related actions
|
||||
type Module struct {
|
||||
processor processing.Processor
|
||||
}
|
||||
|
||||
// New returns a new account module
|
||||
func New(processor processing.Processor) api.ClientModule {
|
||||
func New(processor processing.Processor) *Module {
|
||||
return &Module{
|
||||
processor: processor,
|
||||
}
|
||||
}
|
||||
|
||||
// Route attaches all routes from this module to the given router
|
||||
func (m *Module) Route(r router.Router) error {
|
||||
func (m *Module) Route(attachHandler func(method string, path string, f ...gin.HandlerFunc) gin.IRoutes) {
|
||||
// create account
|
||||
r.AttachHandler(http.MethodPost, BasePath, m.AccountCreatePOSTHandler)
|
||||
|
||||
// delete account
|
||||
r.AttachHandler(http.MethodPost, DeleteAccountPath, m.AccountDeletePOSTHandler)
|
||||
attachHandler(http.MethodPost, BasePath, m.AccountCreatePOSTHandler)
|
||||
|
||||
// get account
|
||||
r.AttachHandler(http.MethodGet, BasePathWithID, m.muxHandler)
|
||||
attachHandler(http.MethodGet, BasePathWithID, m.AccountGETHandler)
|
||||
|
||||
// delete account
|
||||
attachHandler(http.MethodPost, DeleteAccountPath, m.AccountDeletePOSTHandler)
|
||||
|
||||
// verify account
|
||||
attachHandler(http.MethodGet, VerifyPath, m.AccountVerifyGETHandler)
|
||||
|
||||
// modify account
|
||||
r.AttachHandler(http.MethodPatch, BasePathWithID, m.muxHandler)
|
||||
attachHandler(http.MethodPatch, UpdateCredentialsPath, m.AccountUpdateCredentialsPATCHHandler)
|
||||
|
||||
// get account's statuses
|
||||
r.AttachHandler(http.MethodGet, GetStatusesPath, m.AccountStatusesGETHandler)
|
||||
attachHandler(http.MethodGet, GetStatusesPath, m.AccountStatusesGETHandler)
|
||||
|
||||
// get following or followers
|
||||
r.AttachHandler(http.MethodGet, GetFollowersPath, m.AccountFollowersGETHandler)
|
||||
r.AttachHandler(http.MethodGet, GetFollowingPath, m.AccountFollowingGETHandler)
|
||||
attachHandler(http.MethodGet, GetFollowersPath, m.AccountFollowersGETHandler)
|
||||
attachHandler(http.MethodGet, GetFollowingPath, m.AccountFollowingGETHandler)
|
||||
|
||||
// get relationship with account
|
||||
r.AttachHandler(http.MethodGet, GetRelationshipsPath, m.AccountRelationshipsGETHandler)
|
||||
attachHandler(http.MethodGet, GetRelationshipsPath, m.AccountRelationshipsGETHandler)
|
||||
|
||||
// follow or unfollow account
|
||||
r.AttachHandler(http.MethodPost, FollowPath, m.AccountFollowPOSTHandler)
|
||||
r.AttachHandler(http.MethodPost, UnfollowPath, m.AccountUnfollowPOSTHandler)
|
||||
attachHandler(http.MethodPost, FollowPath, m.AccountFollowPOSTHandler)
|
||||
attachHandler(http.MethodPost, UnfollowPath, m.AccountUnfollowPOSTHandler)
|
||||
|
||||
// block or unblock account
|
||||
r.AttachHandler(http.MethodPost, BlockPath, m.AccountBlockPOSTHandler)
|
||||
r.AttachHandler(http.MethodPost, UnblockPath, m.AccountUnblockPOSTHandler)
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
func (m *Module) muxHandler(c *gin.Context) {
|
||||
ru := c.Request.RequestURI
|
||||
switch c.Request.Method {
|
||||
case http.MethodGet:
|
||||
if strings.HasPrefix(ru, VerifyPath) {
|
||||
m.AccountVerifyGETHandler(c)
|
||||
} else {
|
||||
m.AccountGETHandler(c)
|
||||
}
|
||||
case http.MethodPatch:
|
||||
if strings.HasPrefix(ru, UpdateCredentialsPath) {
|
||||
m.AccountUpdateCredentialsPATCHHandler(c)
|
||||
}
|
||||
}
|
||||
attachHandler(http.MethodPost, BlockPath, m.AccountBlockPOSTHandler)
|
||||
attachHandler(http.MethodPost, UnblockPath, m.AccountUnblockPOSTHandler)
|
||||
}
|
|
@ -16,7 +16,7 @@
|
|||
along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
package account
|
||||
package accounts
|
||||
|
||||
import (
|
||||
"errors"
|
||||
|
@ -25,8 +25,8 @@
|
|||
"strconv"
|
||||
|
||||
"github.com/gin-gonic/gin"
|
||||
"github.com/superseriousbusiness/gotosocial/internal/api"
|
||||
"github.com/superseriousbusiness/gotosocial/internal/api/model"
|
||||
apimodel "github.com/superseriousbusiness/gotosocial/internal/api/model"
|
||||
apiutil "github.com/superseriousbusiness/gotosocial/internal/api/util"
|
||||
"github.com/superseriousbusiness/gotosocial/internal/gtserror"
|
||||
"github.com/superseriousbusiness/gotosocial/internal/oauth"
|
||||
)
|
||||
|
@ -138,33 +138,33 @@
|
|||
func (m *Module) AccountUpdateCredentialsPATCHHandler(c *gin.Context) {
|
||||
authed, err := oauth.Authed(c, true, true, true, true)
|
||||
if err != nil {
|
||||
api.ErrorHandler(c, gtserror.NewErrorUnauthorized(err, err.Error()), m.processor.InstanceGet)
|
||||
apiutil.ErrorHandler(c, gtserror.NewErrorUnauthorized(err, err.Error()), m.processor.InstanceGet)
|
||||
return
|
||||
}
|
||||
|
||||
if _, err := api.NegotiateAccept(c, api.JSONAcceptHeaders...); err != nil {
|
||||
api.ErrorHandler(c, gtserror.NewErrorNotAcceptable(err, err.Error()), m.processor.InstanceGet)
|
||||
if _, err := apiutil.NegotiateAccept(c, apiutil.JSONAcceptHeaders...); err != nil {
|
||||
apiutil.ErrorHandler(c, gtserror.NewErrorNotAcceptable(err, err.Error()), m.processor.InstanceGet)
|
||||
return
|
||||
}
|
||||
|
||||
form, err := parseUpdateAccountForm(c)
|
||||
if err != nil {
|
||||
api.ErrorHandler(c, gtserror.NewErrorBadRequest(err, err.Error()), m.processor.InstanceGet)
|
||||
apiutil.ErrorHandler(c, gtserror.NewErrorBadRequest(err, err.Error()), m.processor.InstanceGet)
|
||||
return
|
||||
}
|
||||
|
||||
acctSensitive, errWithCode := m.processor.AccountUpdate(c.Request.Context(), authed, form)
|
||||
if errWithCode != nil {
|
||||
api.ErrorHandler(c, errWithCode, m.processor.InstanceGet)
|
||||
apiutil.ErrorHandler(c, errWithCode, m.processor.InstanceGet)
|
||||
return
|
||||
}
|
||||
|
||||
c.JSON(http.StatusOK, acctSensitive)
|
||||
}
|
||||
|
||||
func parseUpdateAccountForm(c *gin.Context) (*model.UpdateCredentialsRequest, error) {
|
||||
form := &model.UpdateCredentialsRequest{
|
||||
Source: &model.UpdateSource{},
|
||||
func parseUpdateAccountForm(c *gin.Context) (*apimodel.UpdateCredentialsRequest, error) {
|
||||
form := &apimodel.UpdateCredentialsRequest{
|
||||
Source: &apimodel.UpdateSource{},
|
||||
}
|
||||
|
||||
if err := c.ShouldBind(&form); err != nil {
|
|
@ -16,7 +16,7 @@
|
|||
along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
package account_test
|
||||
package accounts_test
|
||||
|
||||
import (
|
||||
"context"
|
||||
|
@ -27,7 +27,7 @@
|
|||
"testing"
|
||||
|
||||
"github.com/stretchr/testify/suite"
|
||||
"github.com/superseriousbusiness/gotosocial/internal/api/client/account"
|
||||
"github.com/superseriousbusiness/gotosocial/internal/api/client/accounts"
|
||||
apimodel "github.com/superseriousbusiness/gotosocial/internal/api/model"
|
||||
"github.com/superseriousbusiness/gotosocial/testrig"
|
||||
)
|
||||
|
@ -50,10 +50,10 @@ func (suite *AccountUpdateTestSuite) TestAccountUpdateCredentialsPATCHHandler()
|
|||
}
|
||||
bodyBytes := requestBody.Bytes()
|
||||
recorder := httptest.NewRecorder()
|
||||
ctx := suite.newContext(recorder, http.MethodPatch, bodyBytes, account.UpdateCredentialsPath, w.FormDataContentType())
|
||||
ctx := suite.newContext(recorder, http.MethodPatch, bodyBytes, accounts.UpdateCredentialsPath, w.FormDataContentType())
|
||||
|
||||
// call the handler
|
||||
suite.accountModule.AccountUpdateCredentialsPATCHHandler(ctx)
|
||||
suite.accountsModule.AccountUpdateCredentialsPATCHHandler(ctx)
|
||||
|
||||
// 1. we should have OK because our request was valid
|
||||
suite.Equal(http.StatusOK, recorder.Code)
|
||||
|
@ -89,10 +89,10 @@ func (suite *AccountUpdateTestSuite) TestAccountUpdateCredentialsPATCHHandlerUnl
|
|||
}
|
||||
bodyBytes1 := requestBody1.Bytes()
|
||||
recorder1 := httptest.NewRecorder()
|
||||
ctx1 := suite.newContext(recorder1, http.MethodPatch, bodyBytes1, account.UpdateCredentialsPath, w1.FormDataContentType())
|
||||
ctx1 := suite.newContext(recorder1, http.MethodPatch, bodyBytes1, accounts.UpdateCredentialsPath, w1.FormDataContentType())
|
||||
|
||||
// call the handler
|
||||
suite.accountModule.AccountUpdateCredentialsPATCHHandler(ctx1)
|
||||
suite.accountsModule.AccountUpdateCredentialsPATCHHandler(ctx1)
|
||||
|
||||
// 1. we should have OK because our request was valid
|
||||
suite.Equal(http.StatusOK, recorder1.Code)
|
||||
|
@ -125,10 +125,10 @@ func (suite *AccountUpdateTestSuite) TestAccountUpdateCredentialsPATCHHandlerUnl
|
|||
}
|
||||
bodyBytes2 := requestBody2.Bytes()
|
||||
recorder2 := httptest.NewRecorder()
|
||||
ctx2 := suite.newContext(recorder2, http.MethodPatch, bodyBytes2, account.UpdateCredentialsPath, w2.FormDataContentType())
|
||||
ctx2 := suite.newContext(recorder2, http.MethodPatch, bodyBytes2, accounts.UpdateCredentialsPath, w2.FormDataContentType())
|
||||
|
||||
// call the handler
|
||||
suite.accountModule.AccountUpdateCredentialsPATCHHandler(ctx2)
|
||||
suite.accountsModule.AccountUpdateCredentialsPATCHHandler(ctx2)
|
||||
|
||||
// 1. we should have OK because our request was valid
|
||||
suite.Equal(http.StatusOK, recorder1.Code)
|
||||
|
@ -170,10 +170,10 @@ func (suite *AccountUpdateTestSuite) TestAccountUpdateCredentialsPATCHHandlerGet
|
|||
}
|
||||
bodyBytes := requestBody.Bytes()
|
||||
recorder := httptest.NewRecorder()
|
||||
ctx := suite.newContext(recorder, http.MethodPatch, bodyBytes, account.UpdateCredentialsPath, w.FormDataContentType())
|
||||
ctx := suite.newContext(recorder, http.MethodPatch, bodyBytes, accounts.UpdateCredentialsPath, w.FormDataContentType())
|
||||
|
||||
// call the handler
|
||||
suite.accountModule.AccountUpdateCredentialsPATCHHandler(ctx)
|
||||
suite.accountsModule.AccountUpdateCredentialsPATCHHandler(ctx)
|
||||
|
||||
// 1. we should have OK because our request was valid
|
||||
suite.Equal(http.StatusOK, recorder.Code)
|
||||
|
@ -212,10 +212,10 @@ func (suite *AccountUpdateTestSuite) TestAccountUpdateCredentialsPATCHHandlerTwo
|
|||
}
|
||||
bodyBytes := requestBody.Bytes()
|
||||
recorder := httptest.NewRecorder()
|
||||
ctx := suite.newContext(recorder, http.MethodPatch, bodyBytes, account.UpdateCredentialsPath, w.FormDataContentType())
|
||||
ctx := suite.newContext(recorder, http.MethodPatch, bodyBytes, accounts.UpdateCredentialsPath, w.FormDataContentType())
|
||||
|
||||
// call the handler
|
||||
suite.accountModule.AccountUpdateCredentialsPATCHHandler(ctx)
|
||||
suite.accountsModule.AccountUpdateCredentialsPATCHHandler(ctx)
|
||||
|
||||
// 1. we should have OK because our request was valid
|
||||
suite.Equal(http.StatusOK, recorder.Code)
|
||||
|
@ -266,10 +266,10 @@ func (suite *AccountUpdateTestSuite) TestAccountUpdateCredentialsPATCHHandlerWit
|
|||
}
|
||||
bodyBytes := requestBody.Bytes()
|
||||
recorder := httptest.NewRecorder()
|
||||
ctx := suite.newContext(recorder, http.MethodPatch, bodyBytes, account.UpdateCredentialsPath, w.FormDataContentType())
|
||||
ctx := suite.newContext(recorder, http.MethodPatch, bodyBytes, accounts.UpdateCredentialsPath, w.FormDataContentType())
|
||||
|
||||
// call the handler
|
||||
suite.accountModule.AccountUpdateCredentialsPATCHHandler(ctx)
|
||||
suite.accountsModule.AccountUpdateCredentialsPATCHHandler(ctx)
|
||||
|
||||
// 1. we should have OK because our request was valid
|
||||
suite.Equal(http.StatusOK, recorder.Code)
|
||||
|
@ -308,10 +308,10 @@ func (suite *AccountUpdateTestSuite) TestAccountUpdateCredentialsPATCHHandlerEmp
|
|||
// set up the request
|
||||
bodyBytes := []byte{}
|
||||
recorder := httptest.NewRecorder()
|
||||
ctx := suite.newContext(recorder, http.MethodPatch, bodyBytes, account.UpdateCredentialsPath, "")
|
||||
ctx := suite.newContext(recorder, http.MethodPatch, bodyBytes, accounts.UpdateCredentialsPath, "")
|
||||
|
||||
// call the handler
|
||||
suite.accountModule.AccountUpdateCredentialsPATCHHandler(ctx)
|
||||
suite.accountsModule.AccountUpdateCredentialsPATCHHandler(ctx)
|
||||
|
||||
// 1. we should have OK because our request was valid
|
||||
suite.Equal(http.StatusBadRequest, recorder.Code)
|
||||
|
@ -343,10 +343,10 @@ func (suite *AccountUpdateTestSuite) TestAccountUpdateCredentialsPATCHHandlerUpd
|
|||
}
|
||||
bodyBytes := requestBody.Bytes()
|
||||
recorder := httptest.NewRecorder()
|
||||
ctx := suite.newContext(recorder, http.MethodPatch, bodyBytes, account.UpdateCredentialsPath, w.FormDataContentType())
|
||||
ctx := suite.newContext(recorder, http.MethodPatch, bodyBytes, accounts.UpdateCredentialsPath, w.FormDataContentType())
|
||||
|
||||
// call the handler
|
||||
suite.accountModule.AccountUpdateCredentialsPATCHHandler(ctx)
|
||||
suite.accountsModule.AccountUpdateCredentialsPATCHHandler(ctx)
|
||||
|
||||
// 1. we should have OK because our request was valid
|
||||
suite.Equal(http.StatusOK, recorder.Code)
|
||||
|
@ -385,10 +385,10 @@ func (suite *AccountUpdateTestSuite) TestAccountUpdateCredentialsPATCHHandlerUpd
|
|||
}
|
||||
bodyBytes := requestBody.Bytes()
|
||||
recorder := httptest.NewRecorder()
|
||||
ctx := suite.newContext(recorder, http.MethodPatch, bodyBytes, account.UpdateCredentialsPath, w.FormDataContentType())
|
||||
ctx := suite.newContext(recorder, http.MethodPatch, bodyBytes, accounts.UpdateCredentialsPath, w.FormDataContentType())
|
||||
|
||||
// call the handler
|
||||
suite.accountModule.AccountUpdateCredentialsPATCHHandler(ctx)
|
||||
suite.accountsModule.AccountUpdateCredentialsPATCHHandler(ctx)
|
||||
|
||||
// 1. we should have OK because our request was valid
|
||||
suite.Equal(http.StatusOK, recorder.Code)
|
||||
|
@ -430,10 +430,10 @@ func (suite *AccountUpdateTestSuite) TestAccountUpdateCredentialsPATCHHandlerUpd
|
|||
}
|
||||
bodyBytes := requestBody.Bytes()
|
||||
recorder := httptest.NewRecorder()
|
||||
ctx := suite.newContext(recorder, http.MethodPatch, bodyBytes, account.UpdateCredentialsPath, w.FormDataContentType())
|
||||
ctx := suite.newContext(recorder, http.MethodPatch, bodyBytes, accounts.UpdateCredentialsPath, w.FormDataContentType())
|
||||
|
||||
// call the handler
|
||||
suite.accountModule.AccountUpdateCredentialsPATCHHandler(ctx)
|
||||
suite.accountsModule.AccountUpdateCredentialsPATCHHandler(ctx)
|
||||
|
||||
suite.Equal(http.StatusBadRequest, recorder.Code)
|
||||
|
|
@ -16,13 +16,13 @@
|
|||
along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
package account
|
||||
package accounts
|
||||
|
||||
import (
|
||||
"net/http"
|
||||
|
||||
"github.com/gin-gonic/gin"
|
||||
"github.com/superseriousbusiness/gotosocial/internal/api"
|
||||
apiutil "github.com/superseriousbusiness/gotosocial/internal/api/util"
|
||||
"github.com/superseriousbusiness/gotosocial/internal/gtserror"
|
||||
"github.com/superseriousbusiness/gotosocial/internal/oauth"
|
||||
)
|
||||
|
@ -59,18 +59,18 @@
|
|||
func (m *Module) AccountVerifyGETHandler(c *gin.Context) {
|
||||
authed, err := oauth.Authed(c, true, true, true, true)
|
||||
if err != nil {
|
||||
api.ErrorHandler(c, gtserror.NewErrorUnauthorized(err, err.Error()), m.processor.InstanceGet)
|
||||
apiutil.ErrorHandler(c, gtserror.NewErrorUnauthorized(err, err.Error()), m.processor.InstanceGet)
|
||||
return
|
||||
}
|
||||
|
||||
if _, err := api.NegotiateAccept(c, api.JSONAcceptHeaders...); err != nil {
|
||||
api.ErrorHandler(c, gtserror.NewErrorNotAcceptable(err, err.Error()), m.processor.InstanceGet)
|
||||
if _, err := apiutil.NegotiateAccept(c, apiutil.JSONAcceptHeaders...); err != nil {
|
||||
apiutil.ErrorHandler(c, gtserror.NewErrorNotAcceptable(err, err.Error()), m.processor.InstanceGet)
|
||||
return
|
||||
}
|
||||
|
||||
acctSensitive, errWithCode := m.processor.AccountGet(c.Request.Context(), authed, authed.Account.ID)
|
||||
if errWithCode != nil {
|
||||
api.ErrorHandler(c, errWithCode, m.processor.InstanceGet)
|
||||
apiutil.ErrorHandler(c, errWithCode, m.processor.InstanceGet)
|
||||
return
|
||||
}
|
||||
|
|
@ -16,7 +16,7 @@
|
|||
along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
package account_test
|
||||
package accounts_test
|
||||
|
||||
import (
|
||||
"encoding/json"
|
||||
|
@ -28,7 +28,7 @@
|
|||
|
||||
"github.com/stretchr/testify/assert"
|
||||
"github.com/stretchr/testify/suite"
|
||||
"github.com/superseriousbusiness/gotosocial/internal/api/client/account"
|
||||
"github.com/superseriousbusiness/gotosocial/internal/api/client/accounts"
|
||||
apimodel "github.com/superseriousbusiness/gotosocial/internal/api/model"
|
||||
"github.com/superseriousbusiness/gotosocial/internal/gtsmodel"
|
||||
)
|
||||
|
@ -42,10 +42,10 @@ func (suite *AccountVerifyTestSuite) TestAccountVerifyGet() {
|
|||
|
||||
// set up the request
|
||||
recorder := httptest.NewRecorder()
|
||||
ctx := suite.newContext(recorder, http.MethodGet, nil, account.VerifyPath, "")
|
||||
ctx := suite.newContext(recorder, http.MethodGet, nil, accounts.VerifyPath, "")
|
||||
|
||||
// call the handler
|
||||
suite.accountModule.AccountVerifyGETHandler(ctx)
|
||||
suite.accountsModule.AccountVerifyGETHandler(ctx)
|
||||
|
||||
// 1. we should have OK because our request was valid
|
||||
suite.Equal(http.StatusOK, recorder.Code)
|
|
@ -16,14 +16,14 @@
|
|||
along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
package account
|
||||
package accounts
|
||||
|
||||
import (
|
||||
"errors"
|
||||
"net/http"
|
||||
|
||||
"github.com/gin-gonic/gin"
|
||||
"github.com/superseriousbusiness/gotosocial/internal/api"
|
||||
apiutil "github.com/superseriousbusiness/gotosocial/internal/api/util"
|
||||
"github.com/superseriousbusiness/gotosocial/internal/gtserror"
|
||||
"github.com/superseriousbusiness/gotosocial/internal/oauth"
|
||||
)
|
||||
|
@ -69,25 +69,25 @@
|
|||
func (m *Module) AccountBlockPOSTHandler(c *gin.Context) {
|
||||
authed, err := oauth.Authed(c, true, true, true, true)
|
||||
if err != nil {
|
||||
api.ErrorHandler(c, gtserror.NewErrorUnauthorized(err, err.Error()), m.processor.InstanceGet)
|
||||
apiutil.ErrorHandler(c, gtserror.NewErrorUnauthorized(err, err.Error()), m.processor.InstanceGet)
|
||||
return
|
||||
}
|
||||
|
||||
if _, err := api.NegotiateAccept(c, api.JSONAcceptHeaders...); err != nil {
|
||||
api.ErrorHandler(c, gtserror.NewErrorNotAcceptable(err, err.Error()), m.processor.InstanceGet)
|
||||
if _, err := apiutil.NegotiateAccept(c, apiutil.JSONAcceptHeaders...); err != nil {
|
||||
apiutil.ErrorHandler(c, gtserror.NewErrorNotAcceptable(err, err.Error()), m.processor.InstanceGet)
|
||||
return
|
||||
}
|
||||
|
||||
targetAcctID := c.Param(IDKey)
|
||||
if targetAcctID == "" {
|
||||
err := errors.New("no account id specified")
|
||||
api.ErrorHandler(c, gtserror.NewErrorBadRequest(err, err.Error()), m.processor.InstanceGet)
|
||||
apiutil.ErrorHandler(c, gtserror.NewErrorBadRequest(err, err.Error()), m.processor.InstanceGet)
|
||||
return
|
||||
}
|
||||
|
||||
relationship, errWithCode := m.processor.AccountBlockCreate(c.Request.Context(), authed, targetAcctID)
|
||||
if errWithCode != nil {
|
||||
api.ErrorHandler(c, errWithCode, m.processor.InstanceGet)
|
||||
apiutil.ErrorHandler(c, errWithCode, m.processor.InstanceGet)
|
||||
return
|
||||
}
|
||||
|
|
@ -16,7 +16,7 @@
|
|||
along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
package account_test
|
||||
package accounts_test
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
|
@ -29,7 +29,7 @@
|
|||
"github.com/gin-gonic/gin"
|
||||
"github.com/stretchr/testify/assert"
|
||||
"github.com/stretchr/testify/suite"
|
||||
"github.com/superseriousbusiness/gotosocial/internal/api/client/account"
|
||||
"github.com/superseriousbusiness/gotosocial/internal/api/client/accounts"
|
||||
"github.com/superseriousbusiness/gotosocial/internal/oauth"
|
||||
"github.com/superseriousbusiness/gotosocial/testrig"
|
||||
)
|
||||
|
@ -46,16 +46,16 @@ func (suite *BlockTestSuite) TestBlockSelf() {
|
|||
ctx.Set(oauth.SessionAuthorizedToken, oauth.DBTokenToToken(suite.testTokens["local_account_1"]))
|
||||
ctx.Set(oauth.SessionAuthorizedApplication, suite.testApplications["application_1"])
|
||||
ctx.Set(oauth.SessionAuthorizedUser, suite.testUsers["local_account_1"])
|
||||
ctx.Request = httptest.NewRequest(http.MethodPost, fmt.Sprintf("http://localhost:8080%s", strings.Replace(account.BlockPath, ":id", testAcct.ID, 1)), nil)
|
||||
ctx.Request = httptest.NewRequest(http.MethodPost, fmt.Sprintf("http://localhost:8080%s", strings.Replace(accounts.BlockPath, ":id", testAcct.ID, 1)), nil)
|
||||
|
||||
ctx.Params = gin.Params{
|
||||
gin.Param{
|
||||
Key: account.IDKey,
|
||||
Key: accounts.IDKey,
|
||||
Value: testAcct.ID,
|
||||
},
|
||||
}
|
||||
|
||||
suite.accountModule.AccountBlockPOSTHandler(ctx)
|
||||
suite.accountsModule.AccountBlockPOSTHandler(ctx)
|
||||
|
||||
// 1. status should be Not Acceptable due to attempted self-block
|
||||
suite.Equal(http.StatusNotAcceptable, recorder.Code)
|
|
@ -16,15 +16,15 @@
|
|||
along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
package account
|
||||
package accounts
|
||||
|
||||
import (
|
||||
"errors"
|
||||
"net/http"
|
||||
|
||||
"github.com/gin-gonic/gin"
|
||||
"github.com/superseriousbusiness/gotosocial/internal/api"
|
||||
"github.com/superseriousbusiness/gotosocial/internal/api/model"
|
||||
apimodel "github.com/superseriousbusiness/gotosocial/internal/api/model"
|
||||
apiutil "github.com/superseriousbusiness/gotosocial/internal/api/util"
|
||||
"github.com/superseriousbusiness/gotosocial/internal/gtserror"
|
||||
"github.com/superseriousbusiness/gotosocial/internal/oauth"
|
||||
)
|
||||
|
@ -91,32 +91,32 @@
|
|||
func (m *Module) AccountFollowPOSTHandler(c *gin.Context) {
|
||||
authed, err := oauth.Authed(c, true, true, true, true)
|
||||
if err != nil {
|
||||
api.ErrorHandler(c, gtserror.NewErrorUnauthorized(err, err.Error()), m.processor.InstanceGet)
|
||||
apiutil.ErrorHandler(c, gtserror.NewErrorUnauthorized(err, err.Error()), m.processor.InstanceGet)
|
||||
return
|
||||
}
|
||||
|
||||
if _, err := api.NegotiateAccept(c, api.JSONAcceptHeaders...); err != nil {
|
||||
api.ErrorHandler(c, gtserror.NewErrorNotAcceptable(err, err.Error()), m.processor.InstanceGet)
|
||||
if _, err := apiutil.NegotiateAccept(c, apiutil.JSONAcceptHeaders...); err != nil {
|
||||
apiutil.ErrorHandler(c, gtserror.NewErrorNotAcceptable(err, err.Error()), m.processor.InstanceGet)
|
||||
return
|
||||
}
|
||||
|
||||
targetAcctID := c.Param(IDKey)
|
||||
if targetAcctID == "" {
|
||||
err := errors.New("no account id specified")
|
||||
api.ErrorHandler(c, gtserror.NewErrorBadRequest(err, err.Error()), m.processor.InstanceGet)
|
||||
apiutil.ErrorHandler(c, gtserror.NewErrorBadRequest(err, err.Error()), m.processor.InstanceGet)
|
||||
return
|
||||
}
|
||||
|
||||
form := &model.AccountFollowRequest{}
|
||||
form := &apimodel.AccountFollowRequest{}
|
||||
if err := c.ShouldBind(form); err != nil {
|
||||
api.ErrorHandler(c, gtserror.NewErrorBadRequest(err, err.Error()), m.processor.InstanceGet)
|
||||
apiutil.ErrorHandler(c, gtserror.NewErrorBadRequest(err, err.Error()), m.processor.InstanceGet)
|
||||
return
|
||||
}
|
||||
form.ID = targetAcctID
|
||||
|
||||
relationship, errWithCode := m.processor.AccountFollowCreate(c.Request.Context(), authed, form)
|
||||
if errWithCode != nil {
|
||||
api.ErrorHandler(c, errWithCode, m.processor.InstanceGet)
|
||||
apiutil.ErrorHandler(c, errWithCode, m.processor.InstanceGet)
|
||||
return
|
||||
}
|
||||
|
|
@ -16,7 +16,7 @@
|
|||
along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
package account_test
|
||||
package accounts_test
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
|
@ -29,7 +29,7 @@
|
|||
"github.com/gin-gonic/gin"
|
||||
"github.com/stretchr/testify/assert"
|
||||
"github.com/stretchr/testify/suite"
|
||||
"github.com/superseriousbusiness/gotosocial/internal/api/client/account"
|
||||
"github.com/superseriousbusiness/gotosocial/internal/api/client/accounts"
|
||||
"github.com/superseriousbusiness/gotosocial/internal/oauth"
|
||||
"github.com/superseriousbusiness/gotosocial/testrig"
|
||||
)
|
||||
|
@ -46,17 +46,17 @@ func (suite *FollowTestSuite) TestFollowSelf() {
|
|||
ctx.Set(oauth.SessionAuthorizedToken, oauth.DBTokenToToken(suite.testTokens["local_account_1"]))
|
||||
ctx.Set(oauth.SessionAuthorizedApplication, suite.testApplications["application_1"])
|
||||
ctx.Set(oauth.SessionAuthorizedUser, suite.testUsers["local_account_1"])
|
||||
ctx.Request = httptest.NewRequest(http.MethodPost, fmt.Sprintf("http://localhost:8080%s", strings.Replace(account.FollowPath, ":id", testAcct.ID, 1)), nil)
|
||||
ctx.Request = httptest.NewRequest(http.MethodPost, fmt.Sprintf("http://localhost:8080%s", strings.Replace(accounts.FollowPath, ":id", testAcct.ID, 1)), nil)
|
||||
|
||||
ctx.Params = gin.Params{
|
||||
gin.Param{
|
||||
Key: account.IDKey,
|
||||
Key: accounts.IDKey,
|
||||
Value: testAcct.ID,
|
||||
},
|
||||
}
|
||||
|
||||
// call the handler
|
||||
suite.accountModule.AccountFollowPOSTHandler(ctx)
|
||||
suite.accountsModule.AccountFollowPOSTHandler(ctx)
|
||||
|
||||
// 1. status should be Not Acceptable due to self-follow attempt
|
||||
suite.Equal(http.StatusNotAcceptable, recorder.Code)
|
|
@ -16,14 +16,14 @@
|
|||
along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
package account
|
||||
package accounts
|
||||
|
||||
import (
|
||||
"errors"
|
||||
"net/http"
|
||||
|
||||
"github.com/gin-gonic/gin"
|
||||
"github.com/superseriousbusiness/gotosocial/internal/api"
|
||||
apiutil "github.com/superseriousbusiness/gotosocial/internal/api/util"
|
||||
"github.com/superseriousbusiness/gotosocial/internal/gtserror"
|
||||
"github.com/superseriousbusiness/gotosocial/internal/oauth"
|
||||
)
|
||||
|
@ -72,25 +72,25 @@
|
|||
func (m *Module) AccountFollowersGETHandler(c *gin.Context) {
|
||||
authed, err := oauth.Authed(c, true, true, true, true)
|
||||
if err != nil {
|
||||
api.ErrorHandler(c, gtserror.NewErrorUnauthorized(err, err.Error()), m.processor.InstanceGet)
|
||||
apiutil.ErrorHandler(c, gtserror.NewErrorUnauthorized(err, err.Error()), m.processor.InstanceGet)
|
||||
return
|
||||
}
|
||||
|
||||
if _, err := api.NegotiateAccept(c, api.JSONAcceptHeaders...); err != nil {
|
||||
api.ErrorHandler(c, gtserror.NewErrorNotAcceptable(err, err.Error()), m.processor.InstanceGet)
|
||||
if _, err := apiutil.NegotiateAccept(c, apiutil.JSONAcceptHeaders...); err != nil {
|
||||
apiutil.ErrorHandler(c, gtserror.NewErrorNotAcceptable(err, err.Error()), m.processor.InstanceGet)
|
||||
return
|
||||
}
|
||||
|
||||
targetAcctID := c.Param(IDKey)
|
||||
if targetAcctID == "" {
|
||||
err := errors.New("no account id specified")
|
||||
api.ErrorHandler(c, gtserror.NewErrorBadRequest(err, err.Error()), m.processor.InstanceGet)
|
||||
apiutil.ErrorHandler(c, gtserror.NewErrorBadRequest(err, err.Error()), m.processor.InstanceGet)
|
||||
return
|
||||
}
|
||||
|
||||
followers, errWithCode := m.processor.AccountFollowersGet(c.Request.Context(), authed, targetAcctID)
|
||||
if errWithCode != nil {
|
||||
api.ErrorHandler(c, errWithCode, m.processor.InstanceGet)
|
||||
apiutil.ErrorHandler(c, errWithCode, m.processor.InstanceGet)
|
||||
return
|
||||
}
|
||||
|
|
@ -16,14 +16,14 @@
|
|||
along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
package account
|
||||
package accounts
|
||||
|
||||
import (
|
||||
"errors"
|
||||
"net/http"
|
||||
|
||||
"github.com/gin-gonic/gin"
|
||||
"github.com/superseriousbusiness/gotosocial/internal/api"
|
||||
apiutil "github.com/superseriousbusiness/gotosocial/internal/api/util"
|
||||
"github.com/superseriousbusiness/gotosocial/internal/gtserror"
|
||||
"github.com/superseriousbusiness/gotosocial/internal/oauth"
|
||||
)
|
||||
|
@ -72,25 +72,25 @@
|
|||
func (m *Module) AccountFollowingGETHandler(c *gin.Context) {
|
||||
authed, err := oauth.Authed(c, true, true, true, true)
|
||||
if err != nil {
|
||||
api.ErrorHandler(c, gtserror.NewErrorUnauthorized(err, err.Error()), m.processor.InstanceGet)
|
||||
apiutil.ErrorHandler(c, gtserror.NewErrorUnauthorized(err, err.Error()), m.processor.InstanceGet)
|
||||
return
|
||||
}
|
||||
|
||||
if _, err := api.NegotiateAccept(c, api.JSONAcceptHeaders...); err != nil {
|
||||
api.ErrorHandler(c, gtserror.NewErrorNotAcceptable(err, err.Error()), m.processor.InstanceGet)
|
||||
if _, err := apiutil.NegotiateAccept(c, apiutil.JSONAcceptHeaders...); err != nil {
|
||||
apiutil.ErrorHandler(c, gtserror.NewErrorNotAcceptable(err, err.Error()), m.processor.InstanceGet)
|
||||
return
|
||||
}
|
||||
|
||||
targetAcctID := c.Param(IDKey)
|
||||
if targetAcctID == "" {
|
||||
err := errors.New("no account id specified")
|
||||
api.ErrorHandler(c, gtserror.NewErrorBadRequest(err, err.Error()), m.processor.InstanceGet)
|
||||
apiutil.ErrorHandler(c, gtserror.NewErrorBadRequest(err, err.Error()), m.processor.InstanceGet)
|
||||
return
|
||||
}
|
||||
|
||||
following, errWithCode := m.processor.AccountFollowingGet(c.Request.Context(), authed, targetAcctID)
|
||||
if errWithCode != nil {
|
||||
api.ErrorHandler(c, errWithCode, m.processor.InstanceGet)
|
||||
apiutil.ErrorHandler(c, errWithCode, m.processor.InstanceGet)
|
||||
return
|
||||
}
|
||||
|
|
@ -1,12 +1,12 @@
|
|||
package account
|
||||
package accounts
|
||||
|
||||
import (
|
||||
"errors"
|
||||
"net/http"
|
||||
|
||||
"github.com/gin-gonic/gin"
|
||||
"github.com/superseriousbusiness/gotosocial/internal/api"
|
||||
"github.com/superseriousbusiness/gotosocial/internal/api/model"
|
||||
apimodel "github.com/superseriousbusiness/gotosocial/internal/api/model"
|
||||
apiutil "github.com/superseriousbusiness/gotosocial/internal/api/util"
|
||||
"github.com/superseriousbusiness/gotosocial/internal/gtserror"
|
||||
"github.com/superseriousbusiness/gotosocial/internal/oauth"
|
||||
)
|
||||
|
@ -57,12 +57,12 @@
|
|||
func (m *Module) AccountRelationshipsGETHandler(c *gin.Context) {
|
||||
authed, err := oauth.Authed(c, true, true, true, true)
|
||||
if err != nil {
|
||||
api.ErrorHandler(c, gtserror.NewErrorUnauthorized(err, err.Error()), m.processor.InstanceGet)
|
||||
apiutil.ErrorHandler(c, gtserror.NewErrorUnauthorized(err, err.Error()), m.processor.InstanceGet)
|
||||
return
|
||||
}
|
||||
|
||||
if _, err := api.NegotiateAccept(c, api.JSONAcceptHeaders...); err != nil {
|
||||
api.ErrorHandler(c, gtserror.NewErrorNotAcceptable(err, err.Error()), m.processor.InstanceGet)
|
||||
if _, err := apiutil.NegotiateAccept(c, apiutil.JSONAcceptHeaders...); err != nil {
|
||||
apiutil.ErrorHandler(c, gtserror.NewErrorNotAcceptable(err, err.Error()), m.processor.InstanceGet)
|
||||
return
|
||||
}
|
||||
|
||||
|
@ -72,18 +72,18 @@ func (m *Module) AccountRelationshipsGETHandler(c *gin.Context) {
|
|||
id := c.Query("id")
|
||||
if id == "" {
|
||||
err = errors.New("no account id(s) specified in query")
|
||||
api.ErrorHandler(c, gtserror.NewErrorBadRequest(err, err.Error()), m.processor.InstanceGet)
|
||||
apiutil.ErrorHandler(c, gtserror.NewErrorBadRequest(err, err.Error()), m.processor.InstanceGet)
|
||||
return
|
||||
}
|
||||
targetAccountIDs = append(targetAccountIDs, id)
|
||||
}
|
||||
|
||||
relationships := []model.Relationship{}
|
||||
relationships := []apimodel.Relationship{}
|
||||
|
||||
for _, targetAccountID := range targetAccountIDs {
|
||||
r, errWithCode := m.processor.AccountRelationshipGet(c.Request.Context(), authed, targetAccountID)
|
||||
if errWithCode != nil {
|
||||
api.ErrorHandler(c, errWithCode, m.processor.InstanceGet)
|
||||
apiutil.ErrorHandler(c, errWithCode, m.processor.InstanceGet)
|
||||
return
|
||||
}
|
||||
relationships = append(relationships, *r)
|
|
@ -16,7 +16,7 @@
|
|||
along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
package account
|
||||
package accounts
|
||||
|
||||
import (
|
||||
"errors"
|
||||
|
@ -25,7 +25,7 @@
|
|||
"strconv"
|
||||
|
||||
"github.com/gin-gonic/gin"
|
||||
"github.com/superseriousbusiness/gotosocial/internal/api"
|
||||
apiutil "github.com/superseriousbusiness/gotosocial/internal/api/util"
|
||||
"github.com/superseriousbusiness/gotosocial/internal/gtserror"
|
||||
"github.com/superseriousbusiness/gotosocial/internal/oauth"
|
||||
)
|
||||
|
@ -133,19 +133,19 @@
|
|||
func (m *Module) AccountStatusesGETHandler(c *gin.Context) {
|
||||
authed, err := oauth.Authed(c, false, false, false, false)
|
||||
if err != nil {
|
||||
api.ErrorHandler(c, gtserror.NewErrorUnauthorized(err, err.Error()), m.processor.InstanceGet)
|
||||
apiutil.ErrorHandler(c, gtserror.NewErrorUnauthorized(err, err.Error()), m.processor.InstanceGet)
|
||||
return
|
||||
}
|
||||
|
||||
if _, err := api.NegotiateAccept(c, api.JSONAcceptHeaders...); err != nil {
|
||||
api.ErrorHandler(c, gtserror.NewErrorNotAcceptable(err, err.Error()), m.processor.InstanceGet)
|
||||
if _, err := apiutil.NegotiateAccept(c, apiutil.JSONAcceptHeaders...); err != nil {
|
||||
apiutil.ErrorHandler(c, gtserror.NewErrorNotAcceptable(err, err.Error()), m.processor.InstanceGet)
|
||||
return
|
||||
}
|
||||
|
||||
targetAcctID := c.Param(IDKey)
|
||||
if targetAcctID == "" {
|
||||
err := errors.New("no account id specified")
|
||||
api.ErrorHandler(c, gtserror.NewErrorBadRequest(err, err.Error()), m.processor.InstanceGet)
|
||||
apiutil.ErrorHandler(c, gtserror.NewErrorBadRequest(err, err.Error()), m.processor.InstanceGet)
|
||||
return
|
||||
}
|
||||
|
||||
|
@ -155,7 +155,7 @@ func (m *Module) AccountStatusesGETHandler(c *gin.Context) {
|
|||
i, err := strconv.ParseInt(limitString, 10, 32)
|
||||
if err != nil {
|
||||
err := fmt.Errorf("error parsing %s: %s", LimitKey, err)
|
||||
api.ErrorHandler(c, gtserror.NewErrorBadRequest(err, err.Error()), m.processor.InstanceGet)
|
||||
apiutil.ErrorHandler(c, gtserror.NewErrorBadRequest(err, err.Error()), m.processor.InstanceGet)
|
||||
return
|
||||
}
|
||||
limit = int(i)
|
||||
|
@ -167,7 +167,7 @@ func (m *Module) AccountStatusesGETHandler(c *gin.Context) {
|
|||
i, err := strconv.ParseBool(excludeRepliesString)
|
||||
if err != nil {
|
||||
err := fmt.Errorf("error parsing %s: %s", ExcludeRepliesKey, err)
|
||||
api.ErrorHandler(c, gtserror.NewErrorBadRequest(err, err.Error()), m.processor.InstanceGet)
|
||||
apiutil.ErrorHandler(c, gtserror.NewErrorBadRequest(err, err.Error()), m.processor.InstanceGet)
|
||||
return
|
||||
}
|
||||
excludeReplies = i
|
||||
|
@ -179,7 +179,7 @@ func (m *Module) AccountStatusesGETHandler(c *gin.Context) {
|
|||
i, err := strconv.ParseBool(excludeReblogsString)
|
||||
if err != nil {
|
||||
err := fmt.Errorf("error parsing %s: %s", ExcludeReblogsKey, err)
|
||||
api.ErrorHandler(c, gtserror.NewErrorBadRequest(err, err.Error()), m.processor.InstanceGet)
|
||||
apiutil.ErrorHandler(c, gtserror.NewErrorBadRequest(err, err.Error()), m.processor.InstanceGet)
|
||||
return
|
||||
}
|
||||
excludeReblogs = i
|
||||
|
@ -203,7 +203,7 @@ func (m *Module) AccountStatusesGETHandler(c *gin.Context) {
|
|||
i, err := strconv.ParseBool(pinnedString)
|
||||
if err != nil {
|
||||
err := fmt.Errorf("error parsing %s: %s", PinnedKey, err)
|
||||
api.ErrorHandler(c, gtserror.NewErrorBadRequest(err, err.Error()), m.processor.InstanceGet)
|
||||
apiutil.ErrorHandler(c, gtserror.NewErrorBadRequest(err, err.Error()), m.processor.InstanceGet)
|
||||
return
|
||||
}
|
||||
pinnedOnly = i
|
||||
|
@ -215,7 +215,7 @@ func (m *Module) AccountStatusesGETHandler(c *gin.Context) {
|
|||
i, err := strconv.ParseBool(mediaOnlyString)
|
||||
if err != nil {
|
||||
err := fmt.Errorf("error parsing %s: %s", OnlyMediaKey, err)
|
||||
api.ErrorHandler(c, gtserror.NewErrorBadRequest(err, err.Error()), m.processor.InstanceGet)
|
||||
apiutil.ErrorHandler(c, gtserror.NewErrorBadRequest(err, err.Error()), m.processor.InstanceGet)
|
||||
return
|
||||
}
|
||||
mediaOnly = i
|
||||
|
@ -227,7 +227,7 @@ func (m *Module) AccountStatusesGETHandler(c *gin.Context) {
|
|||
i, err := strconv.ParseBool(publicOnlyString)
|
||||
if err != nil {
|
||||
err := fmt.Errorf("error parsing %s: %s", OnlyPublicKey, err)
|
||||
api.ErrorHandler(c, gtserror.NewErrorBadRequest(err, err.Error()), m.processor.InstanceGet)
|
||||
apiutil.ErrorHandler(c, gtserror.NewErrorBadRequest(err, err.Error()), m.processor.InstanceGet)
|
||||
return
|
||||
}
|
||||
publicOnly = i
|
||||
|
@ -235,7 +235,7 @@ func (m *Module) AccountStatusesGETHandler(c *gin.Context) {
|
|||
|
||||
resp, errWithCode := m.processor.AccountStatusesGet(c.Request.Context(), authed, targetAcctID, limit, excludeReplies, excludeReblogs, maxID, minID, pinnedOnly, mediaOnly, publicOnly)
|
||||
if errWithCode != nil {
|
||||
api.ErrorHandler(c, errWithCode, m.processor.InstanceGet)
|
||||
apiutil.ErrorHandler(c, errWithCode, m.processor.InstanceGet)
|
||||
return
|
||||
}
|
||||
|
|
@ -16,7 +16,7 @@
|
|||
along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
package account_test
|
||||
package accounts_test
|
||||
|
||||
import (
|
||||
"encoding/json"
|
||||
|
@ -29,7 +29,7 @@
|
|||
"github.com/gin-gonic/gin"
|
||||
"github.com/stretchr/testify/assert"
|
||||
"github.com/stretchr/testify/suite"
|
||||
"github.com/superseriousbusiness/gotosocial/internal/api/client/account"
|
||||
"github.com/superseriousbusiness/gotosocial/internal/api/client/accounts"
|
||||
apimodel "github.com/superseriousbusiness/gotosocial/internal/api/model"
|
||||
)
|
||||
|
||||
|
@ -45,13 +45,13 @@ func (suite *AccountStatusesTestSuite) TestGetStatusesPublicOnly() {
|
|||
ctx := suite.newContext(recorder, http.MethodGet, nil, fmt.Sprintf("/api/v1/accounts/%s/statuses?limit=20&only_media=false&only_public=true", targetAccount.ID), "")
|
||||
ctx.Params = gin.Params{
|
||||
gin.Param{
|
||||
Key: account.IDKey,
|
||||
Key: accounts.IDKey,
|
||||
Value: targetAccount.ID,
|
||||
},
|
||||
}
|
||||
|
||||
// call the handler
|
||||
suite.accountModule.AccountStatusesGETHandler(ctx)
|
||||
suite.accountsModule.AccountStatusesGETHandler(ctx)
|
||||
|
||||
// 1. we should have OK because our request was valid
|
||||
suite.Equal(http.StatusOK, recorder.Code)
|
||||
|
@ -85,13 +85,13 @@ func (suite *AccountStatusesTestSuite) TestGetStatusesPublicOnlyMediaOnly() {
|
|||
ctx := suite.newContext(recorder, http.MethodGet, nil, fmt.Sprintf("/api/v1/accounts/%s/statuses?limit=20&only_media=true&only_public=true", targetAccount.ID), "")
|
||||
ctx.Params = gin.Params{
|
||||
gin.Param{
|
||||
Key: account.IDKey,
|
||||
Key: accounts.IDKey,
|
||||
Value: targetAccount.ID,
|
||||
},
|
||||
}
|
||||
|
||||
// call the handler
|
||||
suite.accountModule.AccountStatusesGETHandler(ctx)
|
||||
suite.accountsModule.AccountStatusesGETHandler(ctx)
|
||||
|
||||
// 1. we should have OK because our request was valid
|
||||
suite.Equal(http.StatusOK, recorder.Code)
|
|
@ -16,14 +16,14 @@
|
|||
along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
package account
|
||||
package accounts
|
||||
|
||||
import (
|
||||
"errors"
|
||||
"net/http"
|
||||
|
||||
"github.com/gin-gonic/gin"
|
||||
"github.com/superseriousbusiness/gotosocial/internal/api"
|
||||
apiutil "github.com/superseriousbusiness/gotosocial/internal/api/util"
|
||||
"github.com/superseriousbusiness/gotosocial/internal/gtserror"
|
||||
"github.com/superseriousbusiness/gotosocial/internal/oauth"
|
||||
)
|
||||
|
@ -70,25 +70,25 @@
|
|||
func (m *Module) AccountUnblockPOSTHandler(c *gin.Context) {
|
||||
authed, err := oauth.Authed(c, true, true, true, true)
|
||||
if err != nil {
|
||||
api.ErrorHandler(c, gtserror.NewErrorUnauthorized(err, err.Error()), m.processor.InstanceGet)
|
||||
apiutil.ErrorHandler(c, gtserror.NewErrorUnauthorized(err, err.Error()), m.processor.InstanceGet)
|
||||
return
|
||||
}
|
||||
|
||||
if _, err := api.NegotiateAccept(c, api.JSONAcceptHeaders...); err != nil {
|
||||
api.ErrorHandler(c, gtserror.NewErrorNotAcceptable(err, err.Error()), m.processor.InstanceGet)
|
||||
if _, err := apiutil.NegotiateAccept(c, apiutil.JSONAcceptHeaders...); err != nil {
|
||||
apiutil.ErrorHandler(c, gtserror.NewErrorNotAcceptable(err, err.Error()), m.processor.InstanceGet)
|
||||
return
|
||||
}
|
||||
|
||||
targetAcctID := c.Param(IDKey)
|
||||
if targetAcctID == "" {
|
||||
err := errors.New("no account id specified")
|
||||
api.ErrorHandler(c, gtserror.NewErrorBadRequest(err, err.Error()), m.processor.InstanceGet)
|
||||
apiutil.ErrorHandler(c, gtserror.NewErrorBadRequest(err, err.Error()), m.processor.InstanceGet)
|
||||
return
|
||||
}
|
||||
|
||||
relationship, errWithCode := m.processor.AccountBlockRemove(c.Request.Context(), authed, targetAcctID)
|
||||
if errWithCode != nil {
|
||||
api.ErrorHandler(c, errWithCode, m.processor.InstanceGet)
|
||||
apiutil.ErrorHandler(c, errWithCode, m.processor.InstanceGet)
|
||||
return
|
||||
}
|
||||
|
|
@ -16,14 +16,14 @@
|
|||
along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
package account
|
||||
package accounts
|
||||
|
||||
import (
|
||||
"errors"
|
||||
"net/http"
|
||||
|
||||
"github.com/gin-gonic/gin"
|
||||
"github.com/superseriousbusiness/gotosocial/internal/api"
|
||||
apiutil "github.com/superseriousbusiness/gotosocial/internal/api/util"
|
||||
"github.com/superseriousbusiness/gotosocial/internal/gtserror"
|
||||
"github.com/superseriousbusiness/gotosocial/internal/oauth"
|
||||
)
|
||||
|
@ -70,25 +70,25 @@
|
|||
func (m *Module) AccountUnfollowPOSTHandler(c *gin.Context) {
|
||||
authed, err := oauth.Authed(c, true, true, true, true)
|
||||
if err != nil {
|
||||
api.ErrorHandler(c, gtserror.NewErrorUnauthorized(err, err.Error()), m.processor.InstanceGet)
|
||||
apiutil.ErrorHandler(c, gtserror.NewErrorUnauthorized(err, err.Error()), m.processor.InstanceGet)
|
||||
return
|
||||
}
|
||||
|
||||
if _, err := api.NegotiateAccept(c, api.JSONAcceptHeaders...); err != nil {
|
||||
api.ErrorHandler(c, gtserror.NewErrorNotAcceptable(err, err.Error()), m.processor.InstanceGet)
|
||||
if _, err := apiutil.NegotiateAccept(c, apiutil.JSONAcceptHeaders...); err != nil {
|
||||
apiutil.ErrorHandler(c, gtserror.NewErrorNotAcceptable(err, err.Error()), m.processor.InstanceGet)
|
||||
return
|
||||
}
|
||||
|
||||
targetAcctID := c.Param(IDKey)
|
||||
if targetAcctID == "" {
|
||||
err := errors.New("no account id specified")
|
||||
api.ErrorHandler(c, gtserror.NewErrorBadRequest(err, err.Error()), m.processor.InstanceGet)
|
||||
apiutil.ErrorHandler(c, gtserror.NewErrorBadRequest(err, err.Error()), m.processor.InstanceGet)
|
||||
return
|
||||
}
|
||||
|
||||
relationship, errWithCode := m.processor.AccountFollowRemove(c.Request.Context(), authed, targetAcctID)
|
||||
if errWithCode != nil {
|
||||
api.ErrorHandler(c, errWithCode, m.processor.InstanceGet)
|
||||
apiutil.ErrorHandler(c, errWithCode, m.processor.InstanceGet)
|
||||
return
|
||||
}
|
||||
|
|
@ -24,8 +24,8 @@
|
|||
"net/http"
|
||||
|
||||
"github.com/gin-gonic/gin"
|
||||
"github.com/superseriousbusiness/gotosocial/internal/api"
|
||||
"github.com/superseriousbusiness/gotosocial/internal/api/model"
|
||||
apimodel "github.com/superseriousbusiness/gotosocial/internal/api/model"
|
||||
apiutil "github.com/superseriousbusiness/gotosocial/internal/api/util"
|
||||
"github.com/superseriousbusiness/gotosocial/internal/gtserror"
|
||||
"github.com/superseriousbusiness/gotosocial/internal/oauth"
|
||||
)
|
||||
|
@ -85,38 +85,38 @@
|
|||
func (m *Module) AccountActionPOSTHandler(c *gin.Context) {
|
||||
authed, err := oauth.Authed(c, true, true, true, true)
|
||||
if err != nil {
|
||||
api.ErrorHandler(c, gtserror.NewErrorUnauthorized(err, err.Error()), m.processor.InstanceGet)
|
||||
apiutil.ErrorHandler(c, gtserror.NewErrorUnauthorized(err, err.Error()), m.processor.InstanceGet)
|
||||
return
|
||||
}
|
||||
|
||||
if !*authed.User.Admin {
|
||||
err := fmt.Errorf("user %s not an admin", authed.User.ID)
|
||||
api.ErrorHandler(c, gtserror.NewErrorForbidden(err, err.Error()), m.processor.InstanceGet)
|
||||
apiutil.ErrorHandler(c, gtserror.NewErrorForbidden(err, err.Error()), m.processor.InstanceGet)
|
||||
return
|
||||
}
|
||||
|
||||
form := &model.AdminAccountActionRequest{}
|
||||
form := &apimodel.AdminAccountActionRequest{}
|
||||
if err := c.ShouldBind(form); err != nil {
|
||||
api.ErrorHandler(c, gtserror.NewErrorBadRequest(err, err.Error()), m.processor.InstanceGet)
|
||||
apiutil.ErrorHandler(c, gtserror.NewErrorBadRequest(err, err.Error()), m.processor.InstanceGet)
|
||||
return
|
||||
}
|
||||
|
||||
if form.Type == "" {
|
||||
err := errors.New("no type specified")
|
||||
api.ErrorHandler(c, gtserror.NewErrorBadRequest(err, err.Error()), m.processor.InstanceGet)
|
||||
apiutil.ErrorHandler(c, gtserror.NewErrorBadRequest(err, err.Error()), m.processor.InstanceGet)
|
||||
return
|
||||
}
|
||||
|
||||
targetAcctID := c.Param(IDKey)
|
||||
if targetAcctID == "" {
|
||||
err := errors.New("no account id specified")
|
||||
api.ErrorHandler(c, gtserror.NewErrorBadRequest(err, err.Error()), m.processor.InstanceGet)
|
||||
apiutil.ErrorHandler(c, gtserror.NewErrorBadRequest(err, err.Error()), m.processor.InstanceGet)
|
||||
return
|
||||
}
|
||||
form.TargetAccountID = targetAcctID
|
||||
|
||||
if errWithCode := m.processor.AdminAccountAction(c.Request.Context(), authed, form); errWithCode != nil {
|
||||
api.ErrorHandler(c, errWithCode, m.processor.InstanceGet)
|
||||
apiutil.ErrorHandler(c, errWithCode, m.processor.InstanceGet)
|
||||
return
|
||||
}
|
||||
|
||||
|
|
|
@ -21,14 +21,13 @@
|
|||
import (
|
||||
"net/http"
|
||||
|
||||
"github.com/superseriousbusiness/gotosocial/internal/api"
|
||||
"github.com/gin-gonic/gin"
|
||||
"github.com/superseriousbusiness/gotosocial/internal/processing"
|
||||
"github.com/superseriousbusiness/gotosocial/internal/router"
|
||||
)
|
||||
|
||||
const (
|
||||
// BasePath is the base API path for this module.
|
||||
BasePath = "/api/v1/admin"
|
||||
// BasePath is the base API path for this module, excluding the api prefix
|
||||
BasePath = "/v1/admin"
|
||||
// EmojiPath is used for posting/deleting custom emojis.
|
||||
EmojiPath = BasePath + "/custom_emojis"
|
||||
// EmojiPathWithID is used for interacting with a single emoji.
|
||||
|
@ -68,32 +67,28 @@
|
|||
DomainQueryKey = "domain"
|
||||
)
|
||||
|
||||
// Module implements the ClientAPIModule interface for admin-related actions (reports, emojis, etc)
|
||||
type Module struct {
|
||||
processor processing.Processor
|
||||
}
|
||||
|
||||
// New returns a new admin module
|
||||
func New(processor processing.Processor) api.ClientModule {
|
||||
func New(processor processing.Processor) *Module {
|
||||
return &Module{
|
||||
processor: processor,
|
||||
}
|
||||
}
|
||||
|
||||
// Route attaches all routes from this module to the given router
|
||||
func (m *Module) Route(r router.Router) error {
|
||||
r.AttachHandler(http.MethodPost, EmojiPath, m.EmojiCreatePOSTHandler)
|
||||
r.AttachHandler(http.MethodGet, EmojiPath, m.EmojisGETHandler)
|
||||
r.AttachHandler(http.MethodDelete, EmojiPathWithID, m.EmojiDELETEHandler)
|
||||
r.AttachHandler(http.MethodGet, EmojiPathWithID, m.EmojiGETHandler)
|
||||
r.AttachHandler(http.MethodPatch, EmojiPathWithID, m.EmojiPATCHHandler)
|
||||
r.AttachHandler(http.MethodPost, DomainBlocksPath, m.DomainBlocksPOSTHandler)
|
||||
r.AttachHandler(http.MethodGet, DomainBlocksPath, m.DomainBlocksGETHandler)
|
||||
r.AttachHandler(http.MethodGet, DomainBlocksPathWithID, m.DomainBlockGETHandler)
|
||||
r.AttachHandler(http.MethodDelete, DomainBlocksPathWithID, m.DomainBlockDELETEHandler)
|
||||
r.AttachHandler(http.MethodPost, AccountsActionPath, m.AccountActionPOSTHandler)
|
||||
r.AttachHandler(http.MethodPost, MediaCleanupPath, m.MediaCleanupPOSTHandler)
|
||||
r.AttachHandler(http.MethodPost, MediaRefetchPath, m.MediaRefetchPOSTHandler)
|
||||
r.AttachHandler(http.MethodGet, EmojiCategoriesPath, m.EmojiCategoriesGETHandler)
|
||||
return nil
|
||||
func (m *Module) Route(attachHandler func(method string, path string, f ...gin.HandlerFunc) gin.IRoutes) {
|
||||
attachHandler(http.MethodPost, EmojiPath, m.EmojiCreatePOSTHandler)
|
||||
attachHandler(http.MethodGet, EmojiPath, m.EmojisGETHandler)
|
||||
attachHandler(http.MethodDelete, EmojiPathWithID, m.EmojiDELETEHandler)
|
||||
attachHandler(http.MethodGet, EmojiPathWithID, m.EmojiGETHandler)
|
||||
attachHandler(http.MethodPatch, EmojiPathWithID, m.EmojiPATCHHandler)
|
||||
attachHandler(http.MethodPost, DomainBlocksPath, m.DomainBlocksPOSTHandler)
|
||||
attachHandler(http.MethodGet, DomainBlocksPath, m.DomainBlocksGETHandler)
|
||||
attachHandler(http.MethodGet, DomainBlocksPathWithID, m.DomainBlockGETHandler)
|
||||
attachHandler(http.MethodDelete, DomainBlocksPathWithID, m.DomainBlockDELETEHandler)
|
||||
attachHandler(http.MethodPost, AccountsActionPath, m.AccountActionPOSTHandler)
|
||||
attachHandler(http.MethodPost, MediaCleanupPath, m.MediaCleanupPOSTHandler)
|
||||
attachHandler(http.MethodPost, MediaRefetchPath, m.MediaRefetchPOSTHandler)
|
||||
attachHandler(http.MethodGet, EmojiCategoriesPath, m.EmojiCategoriesGETHandler)
|
||||
}
|
||||
|
|
|
@ -93,7 +93,7 @@ func (suite *AdminStandardTestSuite) SetupTest() {
|
|||
suite.sentEmails = make(map[string]string)
|
||||
suite.emailSender = testrig.NewEmailSender("../../../../web/template/", suite.sentEmails)
|
||||
suite.processor = testrig.NewTestProcessor(suite.db, suite.storage, suite.federator, suite.emailSender, suite.mediaManager, clientWorker, fedWorker)
|
||||
suite.adminModule = admin.New(suite.processor).(*admin.Module)
|
||||
suite.adminModule = admin.New(suite.processor)
|
||||
testrig.StandardDBSetup(suite.db, nil)
|
||||
testrig.StandardStorageSetup(suite.storage, "../../../../testrig/media")
|
||||
}
|
||||
|
|
|
@ -25,8 +25,8 @@
|
|||
"strconv"
|
||||
|
||||
"github.com/gin-gonic/gin"
|
||||
"github.com/superseriousbusiness/gotosocial/internal/api"
|
||||
"github.com/superseriousbusiness/gotosocial/internal/api/model"
|
||||
apimodel "github.com/superseriousbusiness/gotosocial/internal/api/model"
|
||||
apiutil "github.com/superseriousbusiness/gotosocial/internal/api/util"
|
||||
"github.com/superseriousbusiness/gotosocial/internal/gtserror"
|
||||
"github.com/superseriousbusiness/gotosocial/internal/oauth"
|
||||
)
|
||||
|
@ -126,18 +126,18 @@
|
|||
func (m *Module) DomainBlocksPOSTHandler(c *gin.Context) {
|
||||
authed, err := oauth.Authed(c, true, true, true, true)
|
||||
if err != nil {
|
||||
api.ErrorHandler(c, gtserror.NewErrorUnauthorized(err, err.Error()), m.processor.InstanceGet)
|
||||
apiutil.ErrorHandler(c, gtserror.NewErrorUnauthorized(err, err.Error()), m.processor.InstanceGet)
|
||||
return
|
||||
}
|
||||
|
||||
if !*authed.User.Admin {
|
||||
err := fmt.Errorf("user %s not an admin", authed.User.ID)
|
||||
api.ErrorHandler(c, gtserror.NewErrorForbidden(err, err.Error()), m.processor.InstanceGet)
|
||||
apiutil.ErrorHandler(c, gtserror.NewErrorForbidden(err, err.Error()), m.processor.InstanceGet)
|
||||
return
|
||||
}
|
||||
|
||||
if _, err := api.NegotiateAccept(c, api.JSONAcceptHeaders...); err != nil {
|
||||
api.ErrorHandler(c, gtserror.NewErrorNotAcceptable(err, err.Error()), m.processor.InstanceGet)
|
||||
if _, err := apiutil.NegotiateAccept(c, apiutil.JSONAcceptHeaders...); err != nil {
|
||||
apiutil.ErrorHandler(c, gtserror.NewErrorNotAcceptable(err, err.Error()), m.processor.InstanceGet)
|
||||
return
|
||||
}
|
||||
|
||||
|
@ -147,21 +147,21 @@ func (m *Module) DomainBlocksPOSTHandler(c *gin.Context) {
|
|||
i, err := strconv.ParseBool(importString)
|
||||
if err != nil {
|
||||
err := fmt.Errorf("error parsing %s: %s", ImportQueryKey, err)
|
||||
api.ErrorHandler(c, gtserror.NewErrorBadRequest(err, err.Error()), m.processor.InstanceGet)
|
||||
apiutil.ErrorHandler(c, gtserror.NewErrorBadRequest(err, err.Error()), m.processor.InstanceGet)
|
||||
return
|
||||
}
|
||||
imp = i
|
||||
}
|
||||
|
||||
form := &model.DomainBlockCreateRequest{}
|
||||
form := &apimodel.DomainBlockCreateRequest{}
|
||||
if err := c.ShouldBind(form); err != nil {
|
||||
api.ErrorHandler(c, gtserror.NewErrorBadRequest(err, err.Error()), m.processor.InstanceGet)
|
||||
apiutil.ErrorHandler(c, gtserror.NewErrorBadRequest(err, err.Error()), m.processor.InstanceGet)
|
||||
return
|
||||
}
|
||||
|
||||
if err := validateCreateDomainBlock(form, imp); err != nil {
|
||||
err := fmt.Errorf("error validating form: %s", err)
|
||||
api.ErrorHandler(c, gtserror.NewErrorBadRequest(err, err.Error()), m.processor.InstanceGet)
|
||||
apiutil.ErrorHandler(c, gtserror.NewErrorBadRequest(err, err.Error()), m.processor.InstanceGet)
|
||||
return
|
||||
}
|
||||
|
||||
|
@ -169,7 +169,7 @@ func (m *Module) DomainBlocksPOSTHandler(c *gin.Context) {
|
|||
// we're importing multiple blocks
|
||||
domainBlocks, errWithCode := m.processor.AdminDomainBlocksImport(c.Request.Context(), authed, form)
|
||||
if errWithCode != nil {
|
||||
api.ErrorHandler(c, errWithCode, m.processor.InstanceGet)
|
||||
apiutil.ErrorHandler(c, errWithCode, m.processor.InstanceGet)
|
||||
return
|
||||
}
|
||||
c.JSON(http.StatusOK, domainBlocks)
|
||||
|
@ -179,13 +179,13 @@ func (m *Module) DomainBlocksPOSTHandler(c *gin.Context) {
|
|||
// we're just creating one block
|
||||
domainBlock, errWithCode := m.processor.AdminDomainBlockCreate(c.Request.Context(), authed, form)
|
||||
if errWithCode != nil {
|
||||
api.ErrorHandler(c, errWithCode, m.processor.InstanceGet)
|
||||
apiutil.ErrorHandler(c, errWithCode, m.processor.InstanceGet)
|
||||
return
|
||||
}
|
||||
c.JSON(http.StatusOK, domainBlock)
|
||||
}
|
||||
|
||||
func validateCreateDomainBlock(form *model.DomainBlockCreateRequest, imp bool) error {
|
||||
func validateCreateDomainBlock(form *apimodel.DomainBlockCreateRequest, imp bool) error {
|
||||
if imp {
|
||||
if form.Domains.Size == 0 {
|
||||
return errors.New("import was specified but list of domains is empty")
|
||||
|
|
|
@ -24,7 +24,7 @@
|
|||
"net/http"
|
||||
|
||||
"github.com/gin-gonic/gin"
|
||||
"github.com/superseriousbusiness/gotosocial/internal/api"
|
||||
apiutil "github.com/superseriousbusiness/gotosocial/internal/api/util"
|
||||
"github.com/superseriousbusiness/gotosocial/internal/gtserror"
|
||||
"github.com/superseriousbusiness/gotosocial/internal/oauth"
|
||||
)
|
||||
|
@ -72,31 +72,31 @@
|
|||
func (m *Module) DomainBlockDELETEHandler(c *gin.Context) {
|
||||
authed, err := oauth.Authed(c, true, true, true, true)
|
||||
if err != nil {
|
||||
api.ErrorHandler(c, gtserror.NewErrorUnauthorized(err, err.Error()), m.processor.InstanceGet)
|
||||
apiutil.ErrorHandler(c, gtserror.NewErrorUnauthorized(err, err.Error()), m.processor.InstanceGet)
|
||||
return
|
||||
}
|
||||
|
||||
if !*authed.User.Admin {
|
||||
err := fmt.Errorf("user %s not an admin", authed.User.ID)
|
||||
api.ErrorHandler(c, gtserror.NewErrorForbidden(err, err.Error()), m.processor.InstanceGet)
|
||||
apiutil.ErrorHandler(c, gtserror.NewErrorForbidden(err, err.Error()), m.processor.InstanceGet)
|
||||
return
|
||||
}
|
||||
|
||||
if _, err := api.NegotiateAccept(c, api.JSONAcceptHeaders...); err != nil {
|
||||
api.ErrorHandler(c, gtserror.NewErrorNotAcceptable(err, err.Error()), m.processor.InstanceGet)
|
||||
if _, err := apiutil.NegotiateAccept(c, apiutil.JSONAcceptHeaders...); err != nil {
|
||||
apiutil.ErrorHandler(c, gtserror.NewErrorNotAcceptable(err, err.Error()), m.processor.InstanceGet)
|
||||
return
|
||||
}
|
||||
|
||||
domainBlockID := c.Param(IDKey)
|
||||
if domainBlockID == "" {
|
||||
err := errors.New("no domain block id specified")
|
||||
api.ErrorHandler(c, gtserror.NewErrorBadRequest(err, err.Error()), m.processor.InstanceGet)
|
||||
apiutil.ErrorHandler(c, gtserror.NewErrorBadRequest(err, err.Error()), m.processor.InstanceGet)
|
||||
return
|
||||
}
|
||||
|
||||
domainBlock, errWithCode := m.processor.AdminDomainBlockDelete(c.Request.Context(), authed, domainBlockID)
|
||||
if errWithCode != nil {
|
||||
api.ErrorHandler(c, errWithCode, m.processor.InstanceGet)
|
||||
apiutil.ErrorHandler(c, errWithCode, m.processor.InstanceGet)
|
||||
return
|
||||
}
|
||||
|
||||
|
|
|
@ -25,7 +25,7 @@
|
|||
"strconv"
|
||||
|
||||
"github.com/gin-gonic/gin"
|
||||
"github.com/superseriousbusiness/gotosocial/internal/api"
|
||||
apiutil "github.com/superseriousbusiness/gotosocial/internal/api/util"
|
||||
"github.com/superseriousbusiness/gotosocial/internal/gtserror"
|
||||
"github.com/superseriousbusiness/gotosocial/internal/oauth"
|
||||
)
|
||||
|
@ -73,25 +73,25 @@
|
|||
func (m *Module) DomainBlockGETHandler(c *gin.Context) {
|
||||
authed, err := oauth.Authed(c, true, true, true, true)
|
||||
if err != nil {
|
||||
api.ErrorHandler(c, gtserror.NewErrorUnauthorized(err, err.Error()), m.processor.InstanceGet)
|
||||
apiutil.ErrorHandler(c, gtserror.NewErrorUnauthorized(err, err.Error()), m.processor.InstanceGet)
|
||||
return
|
||||
}
|
||||
|
||||
if !*authed.User.Admin {
|
||||
err := fmt.Errorf("user %s not an admin", authed.User.ID)
|
||||
api.ErrorHandler(c, gtserror.NewErrorForbidden(err, err.Error()), m.processor.InstanceGet)
|
||||
apiutil.ErrorHandler(c, gtserror.NewErrorForbidden(err, err.Error()), m.processor.InstanceGet)
|
||||
return
|
||||
}
|
||||
|
||||
if _, err := api.NegotiateAccept(c, api.JSONAcceptHeaders...); err != nil {
|
||||
api.ErrorHandler(c, gtserror.NewErrorNotAcceptable(err, err.Error()), m.processor.InstanceGet)
|
||||
if _, err := apiutil.NegotiateAccept(c, apiutil.JSONAcceptHeaders...); err != nil {
|
||||
apiutil.ErrorHandler(c, gtserror.NewErrorNotAcceptable(err, err.Error()), m.processor.InstanceGet)
|
||||
return
|
||||
}
|
||||
|
||||
domainBlockID := c.Param(IDKey)
|
||||
if domainBlockID == "" {
|
||||
err := errors.New("no domain block id specified")
|
||||
api.ErrorHandler(c, gtserror.NewErrorBadRequest(err, err.Error()), m.processor.InstanceGet)
|
||||
apiutil.ErrorHandler(c, gtserror.NewErrorBadRequest(err, err.Error()), m.processor.InstanceGet)
|
||||
return
|
||||
}
|
||||
|
||||
|
@ -101,7 +101,7 @@ func (m *Module) DomainBlockGETHandler(c *gin.Context) {
|
|||
i, err := strconv.ParseBool(exportString)
|
||||
if err != nil {
|
||||
err := fmt.Errorf("error parsing %s: %s", ExportQueryKey, err)
|
||||
api.ErrorHandler(c, gtserror.NewErrorBadRequest(err, err.Error()), m.processor.InstanceGet)
|
||||
apiutil.ErrorHandler(c, gtserror.NewErrorBadRequest(err, err.Error()), m.processor.InstanceGet)
|
||||
return
|
||||
}
|
||||
export = i
|
||||
|
@ -109,7 +109,7 @@ func (m *Module) DomainBlockGETHandler(c *gin.Context) {
|
|||
|
||||
domainBlock, errWithCode := m.processor.AdminDomainBlockGet(c.Request.Context(), authed, domainBlockID, export)
|
||||
if errWithCode != nil {
|
||||
api.ErrorHandler(c, errWithCode, m.processor.InstanceGet)
|
||||
apiutil.ErrorHandler(c, errWithCode, m.processor.InstanceGet)
|
||||
return
|
||||
}
|
||||
|
||||
|
|
|
@ -24,7 +24,7 @@
|
|||
"strconv"
|
||||
|
||||
"github.com/gin-gonic/gin"
|
||||
"github.com/superseriousbusiness/gotosocial/internal/api"
|
||||
apiutil "github.com/superseriousbusiness/gotosocial/internal/api/util"
|
||||
"github.com/superseriousbusiness/gotosocial/internal/gtserror"
|
||||
"github.com/superseriousbusiness/gotosocial/internal/oauth"
|
||||
)
|
||||
|
@ -78,18 +78,18 @@
|
|||
func (m *Module) DomainBlocksGETHandler(c *gin.Context) {
|
||||
authed, err := oauth.Authed(c, true, true, true, true)
|
||||
if err != nil {
|
||||
api.ErrorHandler(c, gtserror.NewErrorUnauthorized(err, err.Error()), m.processor.InstanceGet)
|
||||
apiutil.ErrorHandler(c, gtserror.NewErrorUnauthorized(err, err.Error()), m.processor.InstanceGet)
|
||||
return
|
||||
}
|
||||
|
||||
if !*authed.User.Admin {
|
||||
err := fmt.Errorf("user %s not an admin", authed.User.ID)
|
||||
api.ErrorHandler(c, gtserror.NewErrorForbidden(err, err.Error()), m.processor.InstanceGet)
|
||||
apiutil.ErrorHandler(c, gtserror.NewErrorForbidden(err, err.Error()), m.processor.InstanceGet)
|
||||
return
|
||||
}
|
||||
|
||||
if _, err := api.NegotiateAccept(c, api.JSONAcceptHeaders...); err != nil {
|
||||
api.ErrorHandler(c, gtserror.NewErrorNotAcceptable(err, err.Error()), m.processor.InstanceGet)
|
||||
if _, err := apiutil.NegotiateAccept(c, apiutil.JSONAcceptHeaders...); err != nil {
|
||||
apiutil.ErrorHandler(c, gtserror.NewErrorNotAcceptable(err, err.Error()), m.processor.InstanceGet)
|
||||
return
|
||||
}
|
||||
|
||||
|
@ -99,7 +99,7 @@ func (m *Module) DomainBlocksGETHandler(c *gin.Context) {
|
|||
i, err := strconv.ParseBool(exportString)
|
||||
if err != nil {
|
||||
err := fmt.Errorf("error parsing %s: %s", ExportQueryKey, err)
|
||||
api.ErrorHandler(c, gtserror.NewErrorBadRequest(err, err.Error()), m.processor.InstanceGet)
|
||||
apiutil.ErrorHandler(c, gtserror.NewErrorBadRequest(err, err.Error()), m.processor.InstanceGet)
|
||||
return
|
||||
}
|
||||
export = i
|
||||
|
@ -107,7 +107,7 @@ func (m *Module) DomainBlocksGETHandler(c *gin.Context) {
|
|||
|
||||
domainBlocks, errWithCode := m.processor.AdminDomainBlocksGet(c.Request.Context(), authed, export)
|
||||
if errWithCode != nil {
|
||||
api.ErrorHandler(c, errWithCode, m.processor.InstanceGet)
|
||||
apiutil.ErrorHandler(c, errWithCode, m.processor.InstanceGet)
|
||||
return
|
||||
}
|
||||
|
||||
|
|
|
@ -23,7 +23,7 @@
|
|||
"net/http"
|
||||
|
||||
"github.com/gin-gonic/gin"
|
||||
"github.com/superseriousbusiness/gotosocial/internal/api"
|
||||
apiutil "github.com/superseriousbusiness/gotosocial/internal/api/util"
|
||||
"github.com/superseriousbusiness/gotosocial/internal/gtserror"
|
||||
"github.com/superseriousbusiness/gotosocial/internal/oauth"
|
||||
)
|
||||
|
@ -69,24 +69,24 @@
|
|||
func (m *Module) EmojiCategoriesGETHandler(c *gin.Context) {
|
||||
authed, err := oauth.Authed(c, true, true, true, true)
|
||||
if err != nil {
|
||||
api.ErrorHandler(c, gtserror.NewErrorUnauthorized(err, err.Error()), m.processor.InstanceGet)
|
||||
apiutil.ErrorHandler(c, gtserror.NewErrorUnauthorized(err, err.Error()), m.processor.InstanceGet)
|
||||
return
|
||||
}
|
||||
|
||||
if !*authed.User.Admin {
|
||||
err := fmt.Errorf("user %s not an admin", authed.User.ID)
|
||||
api.ErrorHandler(c, gtserror.NewErrorForbidden(err, err.Error()), m.processor.InstanceGet)
|
||||
apiutil.ErrorHandler(c, gtserror.NewErrorForbidden(err, err.Error()), m.processor.InstanceGet)
|
||||
return
|
||||
}
|
||||
|
||||
if _, err := api.NegotiateAccept(c, api.JSONAcceptHeaders...); err != nil {
|
||||
api.ErrorHandler(c, gtserror.NewErrorNotAcceptable(err, err.Error()), m.processor.InstanceGet)
|
||||
if _, err := apiutil.NegotiateAccept(c, apiutil.JSONAcceptHeaders...); err != nil {
|
||||
apiutil.ErrorHandler(c, gtserror.NewErrorNotAcceptable(err, err.Error()), m.processor.InstanceGet)
|
||||
return
|
||||
}
|
||||
|
||||
categories, errWithCode := m.processor.AdminEmojiCategoriesGet(c.Request.Context())
|
||||
if errWithCode != nil {
|
||||
api.ErrorHandler(c, errWithCode, m.processor.InstanceGet)
|
||||
apiutil.ErrorHandler(c, errWithCode, m.processor.InstanceGet)
|
||||
return
|
||||
}
|
||||
|
||||
|
|
|
@ -24,8 +24,8 @@
|
|||
"net/http"
|
||||
|
||||
"github.com/gin-gonic/gin"
|
||||
"github.com/superseriousbusiness/gotosocial/internal/api"
|
||||
"github.com/superseriousbusiness/gotosocial/internal/api/model"
|
||||
apimodel "github.com/superseriousbusiness/gotosocial/internal/api/model"
|
||||
apiutil "github.com/superseriousbusiness/gotosocial/internal/api/util"
|
||||
"github.com/superseriousbusiness/gotosocial/internal/config"
|
||||
"github.com/superseriousbusiness/gotosocial/internal/gtserror"
|
||||
"github.com/superseriousbusiness/gotosocial/internal/oauth"
|
||||
|
@ -100,42 +100,42 @@
|
|||
func (m *Module) EmojiCreatePOSTHandler(c *gin.Context) {
|
||||
authed, err := oauth.Authed(c, true, true, true, true)
|
||||
if err != nil {
|
||||
api.ErrorHandler(c, gtserror.NewErrorUnauthorized(err, err.Error()), m.processor.InstanceGet)
|
||||
apiutil.ErrorHandler(c, gtserror.NewErrorUnauthorized(err, err.Error()), m.processor.InstanceGet)
|
||||
return
|
||||
}
|
||||
|
||||
if !*authed.User.Admin {
|
||||
err := fmt.Errorf("user %s not an admin", authed.User.ID)
|
||||
api.ErrorHandler(c, gtserror.NewErrorForbidden(err, err.Error()), m.processor.InstanceGet)
|
||||
apiutil.ErrorHandler(c, gtserror.NewErrorForbidden(err, err.Error()), m.processor.InstanceGet)
|
||||
return
|
||||
}
|
||||
|
||||
if _, err := api.NegotiateAccept(c, api.JSONAcceptHeaders...); err != nil {
|
||||
api.ErrorHandler(c, gtserror.NewErrorNotAcceptable(err, err.Error()), m.processor.InstanceGet)
|
||||
if _, err := apiutil.NegotiateAccept(c, apiutil.JSONAcceptHeaders...); err != nil {
|
||||
apiutil.ErrorHandler(c, gtserror.NewErrorNotAcceptable(err, err.Error()), m.processor.InstanceGet)
|
||||
return
|
||||
}
|
||||
|
||||
form := &model.EmojiCreateRequest{}
|
||||
form := &apimodel.EmojiCreateRequest{}
|
||||
if err := c.ShouldBind(form); err != nil {
|
||||
api.ErrorHandler(c, gtserror.NewErrorBadRequest(err, err.Error()), m.processor.InstanceGet)
|
||||
apiutil.ErrorHandler(c, gtserror.NewErrorBadRequest(err, err.Error()), m.processor.InstanceGet)
|
||||
return
|
||||
}
|
||||
|
||||
if err := validateCreateEmoji(form); err != nil {
|
||||
api.ErrorHandler(c, gtserror.NewErrorBadRequest(err, err.Error()), m.processor.InstanceGet)
|
||||
apiutil.ErrorHandler(c, gtserror.NewErrorBadRequest(err, err.Error()), m.processor.InstanceGet)
|
||||
return
|
||||
}
|
||||
|
||||
apiEmoji, errWithCode := m.processor.AdminEmojiCreate(c.Request.Context(), authed, form)
|
||||
if errWithCode != nil {
|
||||
api.ErrorHandler(c, errWithCode, m.processor.InstanceGet)
|
||||
apiutil.ErrorHandler(c, errWithCode, m.processor.InstanceGet)
|
||||
return
|
||||
}
|
||||
|
||||
c.JSON(http.StatusOK, apiEmoji)
|
||||
}
|
||||
|
||||
func validateCreateEmoji(form *model.EmojiCreateRequest) error {
|
||||
func validateCreateEmoji(form *apimodel.EmojiCreateRequest) error {
|
||||
if form.Image == nil || form.Image.Size == 0 {
|
||||
return errors.New("no emoji given")
|
||||
}
|
||||
|
|
|
@ -24,7 +24,7 @@
|
|||
"net/http"
|
||||
|
||||
"github.com/gin-gonic/gin"
|
||||
"github.com/superseriousbusiness/gotosocial/internal/api"
|
||||
apiutil "github.com/superseriousbusiness/gotosocial/internal/api/util"
|
||||
"github.com/superseriousbusiness/gotosocial/internal/gtserror"
|
||||
"github.com/superseriousbusiness/gotosocial/internal/oauth"
|
||||
)
|
||||
|
@ -78,31 +78,31 @@
|
|||
func (m *Module) EmojiDELETEHandler(c *gin.Context) {
|
||||
authed, err := oauth.Authed(c, true, true, true, true)
|
||||
if err != nil {
|
||||
api.ErrorHandler(c, gtserror.NewErrorUnauthorized(err, err.Error()), m.processor.InstanceGet)
|
||||
apiutil.ErrorHandler(c, gtserror.NewErrorUnauthorized(err, err.Error()), m.processor.InstanceGet)
|
||||
return
|
||||
}
|
||||
|
||||
if !*authed.User.Admin {
|
||||
err := fmt.Errorf("user %s not an admin", authed.User.ID)
|
||||
api.ErrorHandler(c, gtserror.NewErrorForbidden(err, err.Error()), m.processor.InstanceGet)
|
||||
apiutil.ErrorHandler(c, gtserror.NewErrorForbidden(err, err.Error()), m.processor.InstanceGet)
|
||||
return
|
||||
}
|
||||
|
||||
if _, err := api.NegotiateAccept(c, api.JSONAcceptHeaders...); err != nil {
|
||||
api.ErrorHandler(c, gtserror.NewErrorNotAcceptable(err, err.Error()), m.processor.InstanceGet)
|
||||
if _, err := apiutil.NegotiateAccept(c, apiutil.JSONAcceptHeaders...); err != nil {
|
||||
apiutil.ErrorHandler(c, gtserror.NewErrorNotAcceptable(err, err.Error()), m.processor.InstanceGet)
|
||||
return
|
||||
}
|
||||
|
||||
emojiID := c.Param(IDKey)
|
||||
if emojiID == "" {
|
||||
err := errors.New("no emoji id specified")
|
||||
api.ErrorHandler(c, gtserror.NewErrorBadRequest(err, err.Error()), m.processor.InstanceGet)
|
||||
apiutil.ErrorHandler(c, gtserror.NewErrorBadRequest(err, err.Error()), m.processor.InstanceGet)
|
||||
return
|
||||
}
|
||||
|
||||
emoji, errWithCode := m.processor.AdminEmojiDelete(c.Request.Context(), authed, emojiID)
|
||||
if errWithCode != nil {
|
||||
api.ErrorHandler(c, errWithCode, m.processor.InstanceGet)
|
||||
apiutil.ErrorHandler(c, errWithCode, m.processor.InstanceGet)
|
||||
return
|
||||
}
|
||||
|
||||
|
|
|
@ -24,7 +24,7 @@
|
|||
"net/http"
|
||||
|
||||
"github.com/gin-gonic/gin"
|
||||
"github.com/superseriousbusiness/gotosocial/internal/api"
|
||||
apiutil "github.com/superseriousbusiness/gotosocial/internal/api/util"
|
||||
"github.com/superseriousbusiness/gotosocial/internal/gtserror"
|
||||
"github.com/superseriousbusiness/gotosocial/internal/oauth"
|
||||
)
|
||||
|
@ -68,31 +68,31 @@
|
|||
func (m *Module) EmojiGETHandler(c *gin.Context) {
|
||||
authed, err := oauth.Authed(c, true, true, true, true)
|
||||
if err != nil {
|
||||
api.ErrorHandler(c, gtserror.NewErrorUnauthorized(err, err.Error()), m.processor.InstanceGet)
|
||||
apiutil.ErrorHandler(c, gtserror.NewErrorUnauthorized(err, err.Error()), m.processor.InstanceGet)
|
||||
return
|
||||
}
|
||||
|
||||
if !*authed.User.Admin {
|
||||
err := fmt.Errorf("user %s not an admin", authed.User.ID)
|
||||
api.ErrorHandler(c, gtserror.NewErrorForbidden(err, err.Error()), m.processor.InstanceGet)
|
||||
apiutil.ErrorHandler(c, gtserror.NewErrorForbidden(err, err.Error()), m.processor.InstanceGet)
|
||||
return
|
||||
}
|
||||
|
||||
if _, err := api.NegotiateAccept(c, api.JSONAcceptHeaders...); err != nil {
|
||||
api.ErrorHandler(c, gtserror.NewErrorNotAcceptable(err, err.Error()), m.processor.InstanceGet)
|
||||
if _, err := apiutil.NegotiateAccept(c, apiutil.JSONAcceptHeaders...); err != nil {
|
||||
apiutil.ErrorHandler(c, gtserror.NewErrorNotAcceptable(err, err.Error()), m.processor.InstanceGet)
|
||||
return
|
||||
}
|
||||
|
||||
emojiID := c.Param(IDKey)
|
||||
if emojiID == "" {
|
||||
err := errors.New("no emoji id specified")
|
||||
api.ErrorHandler(c, gtserror.NewErrorBadRequest(err, err.Error()), m.processor.InstanceGet)
|
||||
apiutil.ErrorHandler(c, gtserror.NewErrorBadRequest(err, err.Error()), m.processor.InstanceGet)
|
||||
return
|
||||
}
|
||||
|
||||
emoji, errWithCode := m.processor.AdminEmojiGet(c.Request.Context(), authed, emojiID)
|
||||
if errWithCode != nil {
|
||||
api.ErrorHandler(c, errWithCode, m.processor.InstanceGet)
|
||||
apiutil.ErrorHandler(c, errWithCode, m.processor.InstanceGet)
|
||||
return
|
||||
}
|
||||
|
||||
|
|
|
@ -25,7 +25,7 @@
|
|||
"strings"
|
||||
|
||||
"github.com/gin-gonic/gin"
|
||||
"github.com/superseriousbusiness/gotosocial/internal/api"
|
||||
apiutil "github.com/superseriousbusiness/gotosocial/internal/api/util"
|
||||
"github.com/superseriousbusiness/gotosocial/internal/config"
|
||||
"github.com/superseriousbusiness/gotosocial/internal/db"
|
||||
"github.com/superseriousbusiness/gotosocial/internal/gtserror"
|
||||
|
@ -125,18 +125,18 @@
|
|||
func (m *Module) EmojisGETHandler(c *gin.Context) {
|
||||
authed, err := oauth.Authed(c, true, true, true, true)
|
||||
if err != nil {
|
||||
api.ErrorHandler(c, gtserror.NewErrorUnauthorized(err, err.Error()), m.processor.InstanceGet)
|
||||
apiutil.ErrorHandler(c, gtserror.NewErrorUnauthorized(err, err.Error()), m.processor.InstanceGet)
|
||||
return
|
||||
}
|
||||
|
||||
if !*authed.User.Admin {
|
||||
err := fmt.Errorf("user %s not an admin", authed.User.ID)
|
||||
api.ErrorHandler(c, gtserror.NewErrorForbidden(err, err.Error()), m.processor.InstanceGet)
|
||||
apiutil.ErrorHandler(c, gtserror.NewErrorForbidden(err, err.Error()), m.processor.InstanceGet)
|
||||
return
|
||||
}
|
||||
|
||||
if _, err := api.NegotiateAccept(c, api.JSONAcceptHeaders...); err != nil {
|
||||
api.ErrorHandler(c, gtserror.NewErrorNotAcceptable(err, err.Error()), m.processor.InstanceGet)
|
||||
if _, err := apiutil.NegotiateAccept(c, apiutil.JSONAcceptHeaders...); err != nil {
|
||||
apiutil.ErrorHandler(c, gtserror.NewErrorNotAcceptable(err, err.Error()), m.processor.InstanceGet)
|
||||
return
|
||||
}
|
||||
|
||||
|
@ -149,7 +149,7 @@ func (m *Module) EmojisGETHandler(c *gin.Context) {
|
|||
i, err := strconv.ParseInt(limitString, 10, 32)
|
||||
if err != nil {
|
||||
err := fmt.Errorf("error parsing %s: %s", LimitKey, err)
|
||||
api.ErrorHandler(c, gtserror.NewErrorBadRequest(err, err.Error()), m.processor.InstanceGet)
|
||||
apiutil.ErrorHandler(c, gtserror.NewErrorBadRequest(err, err.Error()), m.processor.InstanceGet)
|
||||
return
|
||||
}
|
||||
limit = int(i)
|
||||
|
@ -177,7 +177,7 @@ func (m *Module) EmojisGETHandler(c *gin.Context) {
|
|||
shortcode = strings.Trim(filter[10:], ":") // remove any errant ":"
|
||||
default:
|
||||
err := fmt.Errorf("filter %s not recognized; accepted values are 'domain:[domain]', 'disabled', 'enabled', 'shortcode:[shortcode]'", filter)
|
||||
api.ErrorHandler(c, gtserror.NewErrorBadRequest(err, err.Error()), m.processor.InstanceGet)
|
||||
apiutil.ErrorHandler(c, gtserror.NewErrorBadRequest(err, err.Error()), m.processor.InstanceGet)
|
||||
return
|
||||
}
|
||||
}
|
||||
|
@ -200,7 +200,7 @@ func (m *Module) EmojisGETHandler(c *gin.Context) {
|
|||
|
||||
resp, errWithCode := m.processor.AdminEmojisGet(c.Request.Context(), authed, domain, includeDisabled, includeEnabled, shortcode, maxShortcodeDomain, minShortcodeDomain, limit)
|
||||
if errWithCode != nil {
|
||||
api.ErrorHandler(c, errWithCode, m.processor.InstanceGet)
|
||||
apiutil.ErrorHandler(c, errWithCode, m.processor.InstanceGet)
|
||||
return
|
||||
}
|
||||
|
||||
|
|
|
@ -25,8 +25,8 @@
|
|||
"strings"
|
||||
|
||||
"github.com/gin-gonic/gin"
|
||||
"github.com/superseriousbusiness/gotosocial/internal/api"
|
||||
"github.com/superseriousbusiness/gotosocial/internal/api/model"
|
||||
apimodel "github.com/superseriousbusiness/gotosocial/internal/api/model"
|
||||
apiutil "github.com/superseriousbusiness/gotosocial/internal/api/util"
|
||||
"github.com/superseriousbusiness/gotosocial/internal/config"
|
||||
"github.com/superseriousbusiness/gotosocial/internal/gtserror"
|
||||
"github.com/superseriousbusiness/gotosocial/internal/oauth"
|
||||
|
@ -123,42 +123,42 @@
|
|||
func (m *Module) EmojiPATCHHandler(c *gin.Context) {
|
||||
authed, err := oauth.Authed(c, true, true, true, true)
|
||||
if err != nil {
|
||||
api.ErrorHandler(c, gtserror.NewErrorUnauthorized(err, err.Error()), m.processor.InstanceGet)
|
||||
apiutil.ErrorHandler(c, gtserror.NewErrorUnauthorized(err, err.Error()), m.processor.InstanceGet)
|
||||
return
|
||||
}
|
||||
|
||||
if !*authed.User.Admin {
|
||||
err := fmt.Errorf("user %s not an admin", authed.User.ID)
|
||||
api.ErrorHandler(c, gtserror.NewErrorForbidden(err, err.Error()), m.processor.InstanceGet)
|
||||
apiutil.ErrorHandler(c, gtserror.NewErrorForbidden(err, err.Error()), m.processor.InstanceGet)
|
||||
return
|
||||
}
|
||||
|
||||
if _, err := api.NegotiateAccept(c, api.JSONAcceptHeaders...); err != nil {
|
||||
api.ErrorHandler(c, gtserror.NewErrorNotAcceptable(err, err.Error()), m.processor.InstanceGet)
|
||||
if _, err := apiutil.NegotiateAccept(c, apiutil.JSONAcceptHeaders...); err != nil {
|
||||
apiutil.ErrorHandler(c, gtserror.NewErrorNotAcceptable(err, err.Error()), m.processor.InstanceGet)
|
||||
return
|
||||
}
|
||||
|
||||
emojiID := c.Param(IDKey)
|
||||
if emojiID == "" {
|
||||
err := errors.New("no emoji id specified")
|
||||
api.ErrorHandler(c, gtserror.NewErrorBadRequest(err, err.Error()), m.processor.InstanceGet)
|
||||
apiutil.ErrorHandler(c, gtserror.NewErrorBadRequest(err, err.Error()), m.processor.InstanceGet)
|
||||
return
|
||||
}
|
||||
|
||||
form := &model.EmojiUpdateRequest{}
|
||||
form := &apimodel.EmojiUpdateRequest{}
|
||||
if err := c.ShouldBind(form); err != nil {
|
||||
api.ErrorHandler(c, gtserror.NewErrorBadRequest(err, err.Error()), m.processor.InstanceGet)
|
||||
apiutil.ErrorHandler(c, gtserror.NewErrorBadRequest(err, err.Error()), m.processor.InstanceGet)
|
||||
return
|
||||
}
|
||||
|
||||
if err := validateUpdateEmoji(form); err != nil {
|
||||
api.ErrorHandler(c, gtserror.NewErrorBadRequest(err, err.Error()), m.processor.InstanceGet)
|
||||
apiutil.ErrorHandler(c, gtserror.NewErrorBadRequest(err, err.Error()), m.processor.InstanceGet)
|
||||
return
|
||||
}
|
||||
|
||||
emoji, errWithCode := m.processor.AdminEmojiUpdate(c.Request.Context(), emojiID, form)
|
||||
if errWithCode != nil {
|
||||
api.ErrorHandler(c, errWithCode, m.processor.InstanceGet)
|
||||
apiutil.ErrorHandler(c, errWithCode, m.processor.InstanceGet)
|
||||
return
|
||||
}
|
||||
|
||||
|
@ -166,14 +166,14 @@ func (m *Module) EmojiPATCHHandler(c *gin.Context) {
|
|||
}
|
||||
|
||||
// do a first pass on the form here
|
||||
func validateUpdateEmoji(form *model.EmojiUpdateRequest) error {
|
||||
func validateUpdateEmoji(form *apimodel.EmojiUpdateRequest) error {
|
||||
// check + normalize update type so we don't need
|
||||
// to do this trimming + lowercasing again later
|
||||
switch strings.TrimSpace(strings.ToLower(string(form.Type))) {
|
||||
case string(model.EmojiUpdateDisable):
|
||||
case string(apimodel.EmojiUpdateDisable):
|
||||
// no params required for this one, so don't bother checking
|
||||
form.Type = model.EmojiUpdateDisable
|
||||
case string(model.EmojiUpdateCopy):
|
||||
form.Type = apimodel.EmojiUpdateDisable
|
||||
case string(apimodel.EmojiUpdateCopy):
|
||||
// need at least a valid shortcode when doing a copy
|
||||
if form.Shortcode == nil {
|
||||
return errors.New("emoji action type was 'copy' but no shortcode was provided")
|
||||
|
@ -190,8 +190,8 @@ func validateUpdateEmoji(form *model.EmojiUpdateRequest) error {
|
|||
}
|
||||
}
|
||||
|
||||
form.Type = model.EmojiUpdateCopy
|
||||
case string(model.EmojiUpdateModify):
|
||||
form.Type = apimodel.EmojiUpdateCopy
|
||||
case string(apimodel.EmojiUpdateModify):
|
||||
// need either image or category name for modify
|
||||
hasImage := form.Image != nil && form.Image.Size != 0
|
||||
hasCategoryName := form.CategoryName != nil
|
||||
|
@ -212,7 +212,7 @@ func validateUpdateEmoji(form *model.EmojiUpdateRequest) error {
|
|||
}
|
||||
}
|
||||
|
||||
form.Type = model.EmojiUpdateModify
|
||||
form.Type = apimodel.EmojiUpdateModify
|
||||
default:
|
||||
return errors.New("emoji action type must be one of 'disable', 'copy', 'modify'")
|
||||
}
|
||||
|
|
|
@ -23,8 +23,8 @@
|
|||
"net/http"
|
||||
|
||||
"github.com/gin-gonic/gin"
|
||||
"github.com/superseriousbusiness/gotosocial/internal/api"
|
||||
"github.com/superseriousbusiness/gotosocial/internal/api/model"
|
||||
apimodel "github.com/superseriousbusiness/gotosocial/internal/api/model"
|
||||
apiutil "github.com/superseriousbusiness/gotosocial/internal/api/util"
|
||||
"github.com/superseriousbusiness/gotosocial/internal/config"
|
||||
"github.com/superseriousbusiness/gotosocial/internal/gtserror"
|
||||
"github.com/superseriousbusiness/gotosocial/internal/oauth"
|
||||
|
@ -71,19 +71,19 @@
|
|||
func (m *Module) MediaCleanupPOSTHandler(c *gin.Context) {
|
||||
authed, err := oauth.Authed(c, true, true, true, true)
|
||||
if err != nil {
|
||||
api.ErrorHandler(c, gtserror.NewErrorUnauthorized(err, err.Error()), m.processor.InstanceGet)
|
||||
apiutil.ErrorHandler(c, gtserror.NewErrorUnauthorized(err, err.Error()), m.processor.InstanceGet)
|
||||
return
|
||||
}
|
||||
|
||||
if !*authed.User.Admin {
|
||||
err := fmt.Errorf("user %s not an admin", authed.User.ID)
|
||||
api.ErrorHandler(c, gtserror.NewErrorForbidden(err, err.Error()), m.processor.InstanceGet)
|
||||
apiutil.ErrorHandler(c, gtserror.NewErrorForbidden(err, err.Error()), m.processor.InstanceGet)
|
||||
return
|
||||
}
|
||||
|
||||
form := &model.MediaCleanupRequest{}
|
||||
form := &apimodel.MediaCleanupRequest{}
|
||||
if err := c.ShouldBind(form); err != nil {
|
||||
api.ErrorHandler(c, gtserror.NewErrorBadRequest(err, err.Error()), m.processor.InstanceGet)
|
||||
apiutil.ErrorHandler(c, gtserror.NewErrorBadRequest(err, err.Error()), m.processor.InstanceGet)
|
||||
return
|
||||
}
|
||||
|
||||
|
@ -98,7 +98,7 @@ func (m *Module) MediaCleanupPOSTHandler(c *gin.Context) {
|
|||
}
|
||||
|
||||
if errWithCode := m.processor.AdminMediaPrune(c.Request.Context(), remoteCacheDays); errWithCode != nil {
|
||||
api.ErrorHandler(c, errWithCode, m.processor.InstanceGet)
|
||||
apiutil.ErrorHandler(c, errWithCode, m.processor.InstanceGet)
|
||||
return
|
||||
}
|
||||
|
||||
|
|
|
@ -23,7 +23,7 @@
|
|||
"net/http"
|
||||
|
||||
"github.com/gin-gonic/gin"
|
||||
"github.com/superseriousbusiness/gotosocial/internal/api"
|
||||
apiutil "github.com/superseriousbusiness/gotosocial/internal/api/util"
|
||||
"github.com/superseriousbusiness/gotosocial/internal/gtserror"
|
||||
"github.com/superseriousbusiness/gotosocial/internal/oauth"
|
||||
)
|
||||
|
@ -74,18 +74,18 @@
|
|||
func (m *Module) MediaRefetchPOSTHandler(c *gin.Context) {
|
||||
authed, err := oauth.Authed(c, true, true, true, true)
|
||||
if err != nil {
|
||||
api.ErrorHandler(c, gtserror.NewErrorUnauthorized(err, err.Error()), m.processor.InstanceGet)
|
||||
apiutil.ErrorHandler(c, gtserror.NewErrorUnauthorized(err, err.Error()), m.processor.InstanceGet)
|
||||
return
|
||||
}
|
||||
|
||||
if !*authed.User.Admin {
|
||||
err := fmt.Errorf("user %s not an admin", authed.User.ID)
|
||||
api.ErrorHandler(c, gtserror.NewErrorForbidden(err, err.Error()), m.processor.InstanceGet)
|
||||
apiutil.ErrorHandler(c, gtserror.NewErrorForbidden(err, err.Error()), m.processor.InstanceGet)
|
||||
return
|
||||
}
|
||||
|
||||
if errWithCode := m.processor.AdminMediaRefetch(c.Request.Context(), authed, c.Query(DomainQueryKey)); errWithCode != nil {
|
||||
api.ErrorHandler(c, errWithCode, m.processor.InstanceGet)
|
||||
apiutil.ErrorHandler(c, errWithCode, m.processor.InstanceGet)
|
||||
return
|
||||
}
|
||||
|
||||
|
|
|
@ -1,21 +0,0 @@
|
|||
/*
|
||||
GoToSocial
|
||||
Copyright (C) 2021-2022 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 app_test
|
||||
|
||||
// TODO: write tests
|
|
@ -16,15 +16,15 @@
|
|||
along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
package app
|
||||
package apps
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"net/http"
|
||||
|
||||
"github.com/gin-gonic/gin"
|
||||
"github.com/superseriousbusiness/gotosocial/internal/api"
|
||||
"github.com/superseriousbusiness/gotosocial/internal/api/model"
|
||||
apimodel "github.com/superseriousbusiness/gotosocial/internal/api/model"
|
||||
apiutil "github.com/superseriousbusiness/gotosocial/internal/api/util"
|
||||
"github.com/superseriousbusiness/gotosocial/internal/gtserror"
|
||||
"github.com/superseriousbusiness/gotosocial/internal/oauth"
|
||||
)
|
||||
|
@ -77,48 +77,48 @@
|
|||
func (m *Module) AppsPOSTHandler(c *gin.Context) {
|
||||
authed, err := oauth.Authed(c, false, false, false, false)
|
||||
if err != nil {
|
||||
api.ErrorHandler(c, gtserror.NewErrorUnauthorized(err, err.Error()), m.processor.InstanceGet)
|
||||
apiutil.ErrorHandler(c, gtserror.NewErrorUnauthorized(err, err.Error()), m.processor.InstanceGet)
|
||||
return
|
||||
}
|
||||
|
||||
if _, err := api.NegotiateAccept(c, api.JSONAcceptHeaders...); err != nil {
|
||||
api.ErrorHandler(c, gtserror.NewErrorNotAcceptable(err, err.Error()), m.processor.InstanceGet)
|
||||
if _, err := apiutil.NegotiateAccept(c, apiutil.JSONAcceptHeaders...); err != nil {
|
||||
apiutil.ErrorHandler(c, gtserror.NewErrorNotAcceptable(err, err.Error()), m.processor.InstanceGet)
|
||||
return
|
||||
}
|
||||
|
||||
form := &model.ApplicationCreateRequest{}
|
||||
form := &apimodel.ApplicationCreateRequest{}
|
||||
if err := c.ShouldBind(form); err != nil {
|
||||
api.ErrorHandler(c, gtserror.NewErrorBadRequest(err, err.Error()), m.processor.InstanceGet)
|
||||
apiutil.ErrorHandler(c, gtserror.NewErrorBadRequest(err, err.Error()), m.processor.InstanceGet)
|
||||
return
|
||||
}
|
||||
|
||||
if len([]rune(form.ClientName)) > formFieldLen {
|
||||
err := fmt.Errorf("client_name must be less than %d characters", formFieldLen)
|
||||
api.ErrorHandler(c, gtserror.NewErrorBadRequest(err, err.Error()), m.processor.InstanceGet)
|
||||
apiutil.ErrorHandler(c, gtserror.NewErrorBadRequest(err, err.Error()), m.processor.InstanceGet)
|
||||
return
|
||||
}
|
||||
|
||||
if len([]rune(form.RedirectURIs)) > formRedirectLen {
|
||||
err := fmt.Errorf("redirect_uris must be less than %d characters", formRedirectLen)
|
||||
api.ErrorHandler(c, gtserror.NewErrorBadRequest(err, err.Error()), m.processor.InstanceGet)
|
||||
apiutil.ErrorHandler(c, gtserror.NewErrorBadRequest(err, err.Error()), m.processor.InstanceGet)
|
||||
return
|
||||
}
|
||||
|
||||
if len([]rune(form.Scopes)) > formFieldLen {
|
||||
err := fmt.Errorf("scopes must be less than %d characters", formFieldLen)
|
||||
api.ErrorHandler(c, gtserror.NewErrorBadRequest(err, err.Error()), m.processor.InstanceGet)
|
||||
apiutil.ErrorHandler(c, gtserror.NewErrorBadRequest(err, err.Error()), m.processor.InstanceGet)
|
||||
return
|
||||
}
|
||||
|
||||
if len([]rune(form.Website)) > formFieldLen {
|
||||
err := fmt.Errorf("website must be less than %d characters", formFieldLen)
|
||||
api.ErrorHandler(c, gtserror.NewErrorBadRequest(err, err.Error()), m.processor.InstanceGet)
|
||||
apiutil.ErrorHandler(c, gtserror.NewErrorBadRequest(err, err.Error()), m.processor.InstanceGet)
|
||||
return
|
||||
}
|
||||
|
||||
apiApp, errWithCode := m.processor.AppCreate(c.Request.Context(), authed, form)
|
||||
if errWithCode != nil {
|
||||
api.ErrorHandler(c, errWithCode, m.processor.InstanceGet)
|
||||
apiutil.ErrorHandler(c, errWithCode, m.processor.InstanceGet)
|
||||
return
|
||||
}
|
||||
|
|
@ -16,33 +16,28 @@
|
|||
along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
package app
|
||||
package apps
|
||||
|
||||
import (
|
||||
"net/http"
|
||||
|
||||
"github.com/superseriousbusiness/gotosocial/internal/api"
|
||||
"github.com/gin-gonic/gin"
|
||||
"github.com/superseriousbusiness/gotosocial/internal/processing"
|
||||
"github.com/superseriousbusiness/gotosocial/internal/router"
|
||||
)
|
||||
|
||||
// BasePath is the base path for this api module
|
||||
const BasePath = "/api/v1/apps"
|
||||
// BasePath is the base path for this api module, excluding the api prefix
|
||||
const BasePath = "/v1/apps"
|
||||
|
||||
// Module implements the ClientAPIModule interface for requests relating to registering/removing applications
|
||||
type Module struct {
|
||||
processor processing.Processor
|
||||
}
|
||||
|
||||
// New returns a new auth module
|
||||
func New(processor processing.Processor) api.ClientModule {
|
||||
func New(processor processing.Processor) *Module {
|
||||
return &Module{
|
||||
processor: processor,
|
||||
}
|
||||
}
|
||||
|
||||
// Route satisfies the RESTAPIModule interface
|
||||
func (m *Module) Route(s router.Router) error {
|
||||
s.AttachHandler(http.MethodPost, BasePath, m.AppsPOSTHandler)
|
||||
return nil
|
||||
func (m *Module) Route(attachHandler func(method string, path string, f ...gin.HandlerFunc) gin.IRoutes) {
|
||||
attachHandler(http.MethodPost, BasePath, m.AppsPOSTHandler)
|
||||
}
|
|
@ -1,105 +0,0 @@
|
|||
/*
|
||||
GoToSocial
|
||||
Copyright (C) 2021-2022 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 auth
|
||||
|
||||
import (
|
||||
"net/http"
|
||||
|
||||
"github.com/superseriousbusiness/gotosocial/internal/api"
|
||||
"github.com/superseriousbusiness/gotosocial/internal/db"
|
||||
"github.com/superseriousbusiness/gotosocial/internal/oauth"
|
||||
"github.com/superseriousbusiness/gotosocial/internal/oidc"
|
||||
"github.com/superseriousbusiness/gotosocial/internal/processing"
|
||||
"github.com/superseriousbusiness/gotosocial/internal/router"
|
||||
)
|
||||
|
||||
/* #nosec G101 */
|
||||
const (
|
||||
// AuthSignInPath is the API path for users to sign in through
|
||||
AuthSignInPath = "/auth/sign_in"
|
||||
|
||||
// CheckYourEmailPath users land here after registering a new account, instructs them to confirm thier email
|
||||
CheckYourEmailPath = "/check_your_email"
|
||||
|
||||
// WaitForApprovalPath users land here after confirming thier email but before an admin approves thier account
|
||||
// (if such is required)
|
||||
WaitForApprovalPath = "/wait_for_approval"
|
||||
|
||||
// AccountDisabledPath users land here when thier account is suspended by an admin
|
||||
AccountDisabledPath = "/account_disabled"
|
||||
|
||||
// OauthTokenPath is the API path to use for granting token requests to users with valid credentials
|
||||
OauthTokenPath = "/oauth/token"
|
||||
|
||||
// OauthAuthorizePath is the API path for authorization requests (eg., authorize this app to act on my behalf as a user)
|
||||
OauthAuthorizePath = "/oauth/authorize"
|
||||
|
||||
// OauthFinalizePath is the API path for completing user registration with additional user details
|
||||
OauthFinalizePath = "/oauth/finalize"
|
||||
|
||||
// CallbackPath is the API path for receiving callback tokens from external OIDC providers
|
||||
CallbackPath = oidc.CallbackPath
|
||||
|
||||
callbackStateParam = "state"
|
||||
callbackCodeParam = "code"
|
||||
|
||||
sessionUserID = "userid"
|
||||
sessionClientID = "client_id"
|
||||
sessionRedirectURI = "redirect_uri"
|
||||
sessionForceLogin = "force_login"
|
||||
sessionResponseType = "response_type"
|
||||
sessionScope = "scope"
|
||||
sessionInternalState = "internal_state"
|
||||
sessionClientState = "client_state"
|
||||
sessionClaims = "claims"
|
||||
sessionAppID = "app_id"
|
||||
)
|
||||
|
||||
// Module implements the ClientAPIModule interface for
|
||||
type Module struct {
|
||||
db db.DB
|
||||
idp oidc.IDP
|
||||
processor processing.Processor
|
||||
}
|
||||
|
||||
// New returns a new auth module
|
||||
func New(db db.DB, idp oidc.IDP, processor processing.Processor) api.ClientModule {
|
||||
return &Module{
|
||||
db: db,
|
||||
idp: idp,
|
||||
processor: processor,
|
||||
}
|
||||
}
|
||||
|
||||
// Route satisfies the RESTAPIModule interface
|
||||
func (m *Module) Route(s router.Router) error {
|
||||
s.AttachHandler(http.MethodGet, AuthSignInPath, m.SignInGETHandler)
|
||||
s.AttachHandler(http.MethodPost, AuthSignInPath, m.SignInPOSTHandler)
|
||||
|
||||
s.AttachHandler(http.MethodPost, OauthTokenPath, m.TokenPOSTHandler)
|
||||
|
||||
s.AttachHandler(http.MethodGet, OauthAuthorizePath, m.AuthorizeGETHandler)
|
||||
s.AttachHandler(http.MethodPost, OauthAuthorizePath, m.AuthorizePOSTHandler)
|
||||
|
||||
s.AttachHandler(http.MethodGet, CallbackPath, m.CallbackGETHandler)
|
||||
s.AttachHandler(http.MethodPost, OauthFinalizePath, m.FinalizePOSTHandler)
|
||||
|
||||
s.AttachHandler(http.MethodGet, oauth.OOBTokenPath, m.OobHandler)
|
||||
return nil
|
||||
}
|
|
@ -21,14 +21,13 @@
|
|||
import (
|
||||
"net/http"
|
||||
|
||||
"github.com/superseriousbusiness/gotosocial/internal/api"
|
||||
"github.com/gin-gonic/gin"
|
||||
"github.com/superseriousbusiness/gotosocial/internal/processing"
|
||||
"github.com/superseriousbusiness/gotosocial/internal/router"
|
||||
)
|
||||
|
||||
const (
|
||||
// BasePath is the base URI path for serving favourites
|
||||
BasePath = "/api/v1/blocks"
|
||||
// BasePath is the base URI path for serving blocks, minus the api prefix.
|
||||
BasePath = "/v1/blocks"
|
||||
|
||||
// MaxIDKey is the url query for setting a max ID to return
|
||||
MaxIDKey = "max_id"
|
||||
|
@ -38,20 +37,16 @@
|
|||
LimitKey = "limit"
|
||||
)
|
||||
|
||||
// Module implements the ClientAPIModule interface for everything relating to viewing blocks
|
||||
type Module struct {
|
||||
processor processing.Processor
|
||||
}
|
||||
|
||||
// New returns a new blocks module
|
||||
func New(processor processing.Processor) api.ClientModule {
|
||||
func New(processor processing.Processor) *Module {
|
||||
return &Module{
|
||||
processor: processor,
|
||||
}
|
||||
}
|
||||
|
||||
// Route attaches all routes from this module to the given router
|
||||
func (m *Module) Route(r router.Router) error {
|
||||
r.AttachHandler(http.MethodGet, BasePath, m.BlocksGETHandler)
|
||||
return nil
|
||||
func (m *Module) Route(attachHandler func(method string, path string, f ...gin.HandlerFunc) gin.IRoutes) {
|
||||
attachHandler(http.MethodGet, BasePath, m.BlocksGETHandler)
|
||||
}
|
||||
|
|
|
@ -24,7 +24,7 @@
|
|||
"strconv"
|
||||
|
||||
"github.com/gin-gonic/gin"
|
||||
"github.com/superseriousbusiness/gotosocial/internal/api"
|
||||
apiutil "github.com/superseriousbusiness/gotosocial/internal/api/util"
|
||||
"github.com/superseriousbusiness/gotosocial/internal/gtserror"
|
||||
"github.com/superseriousbusiness/gotosocial/internal/oauth"
|
||||
)
|
||||
|
@ -96,12 +96,12 @@
|
|||
func (m *Module) BlocksGETHandler(c *gin.Context) {
|
||||
authed, err := oauth.Authed(c, true, true, true, true)
|
||||
if err != nil {
|
||||
api.ErrorHandler(c, gtserror.NewErrorUnauthorized(err, err.Error()), m.processor.InstanceGet)
|
||||
apiutil.ErrorHandler(c, gtserror.NewErrorUnauthorized(err, err.Error()), m.processor.InstanceGet)
|
||||
return
|
||||
}
|
||||
|
||||
if _, err := api.NegotiateAccept(c, api.JSONAcceptHeaders...); err != nil {
|
||||
api.ErrorHandler(c, gtserror.NewErrorNotAcceptable(err, err.Error()), m.processor.InstanceGet)
|
||||
if _, err := apiutil.NegotiateAccept(c, apiutil.JSONAcceptHeaders...); err != nil {
|
||||
apiutil.ErrorHandler(c, gtserror.NewErrorNotAcceptable(err, err.Error()), m.processor.InstanceGet)
|
||||
return
|
||||
}
|
||||
|
||||
|
@ -123,7 +123,7 @@ func (m *Module) BlocksGETHandler(c *gin.Context) {
|
|||
i, err := strconv.ParseInt(limitString, 10, 32)
|
||||
if err != nil {
|
||||
err := fmt.Errorf("error parsing %s: %s", LimitKey, err)
|
||||
api.ErrorHandler(c, gtserror.NewErrorBadRequest(err, err.Error()), m.processor.InstanceGet)
|
||||
apiutil.ErrorHandler(c, gtserror.NewErrorBadRequest(err, err.Error()), m.processor.InstanceGet)
|
||||
return
|
||||
}
|
||||
limit = int(i)
|
||||
|
@ -131,7 +131,7 @@ func (m *Module) BlocksGETHandler(c *gin.Context) {
|
|||
|
||||
resp, errWithCode := m.processor.BlocksGet(c.Request.Context(), authed, maxID, sinceID, limit)
|
||||
if errWithCode != nil {
|
||||
api.ErrorHandler(c, errWithCode, m.processor.InstanceGet)
|
||||
apiutil.ErrorHandler(c, errWithCode, m.processor.InstanceGet)
|
||||
return
|
||||
}
|
||||
|
||||
|
|
|
@ -21,9 +21,8 @@
|
|||
import (
|
||||
"net/http"
|
||||
|
||||
"github.com/superseriousbusiness/gotosocial/internal/api"
|
||||
"github.com/gin-gonic/gin"
|
||||
"github.com/superseriousbusiness/gotosocial/internal/processing"
|
||||
"github.com/superseriousbusiness/gotosocial/internal/router"
|
||||
)
|
||||
|
||||
const (
|
||||
|
@ -31,20 +30,16 @@
|
|||
BasePath = "/api/v1/bookmarks"
|
||||
)
|
||||
|
||||
// Module implements the ClientAPIModule interface for everything related to bookmarks
|
||||
type Module struct {
|
||||
processor processing.Processor
|
||||
}
|
||||
|
||||
// New returns a new emoji module
|
||||
func New(processor processing.Processor) api.ClientModule {
|
||||
func New(processor processing.Processor) *Module {
|
||||
return &Module{
|
||||
processor: processor,
|
||||
}
|
||||
}
|
||||
|
||||
// Route attaches all routes from this module to the given router
|
||||
func (m *Module) Route(r router.Router) error {
|
||||
r.AttachHandler(http.MethodGet, BasePath, m.BookmarksGETHandler)
|
||||
return nil
|
||||
func (m *Module) Route(attachHandler func(method string, path string, f ...gin.HandlerFunc) gin.IRoutes) {
|
||||
attachHandler(http.MethodGet, BasePath, m.BookmarksGETHandler)
|
||||
}
|
||||
|
|
|
@ -29,7 +29,7 @@
|
|||
|
||||
"github.com/stretchr/testify/suite"
|
||||
"github.com/superseriousbusiness/gotosocial/internal/api/client/bookmarks"
|
||||
"github.com/superseriousbusiness/gotosocial/internal/api/client/status"
|
||||
"github.com/superseriousbusiness/gotosocial/internal/api/client/statuses"
|
||||
"github.com/superseriousbusiness/gotosocial/internal/api/model"
|
||||
"github.com/superseriousbusiness/gotosocial/internal/concurrency"
|
||||
"github.com/superseriousbusiness/gotosocial/internal/db"
|
||||
|
@ -67,7 +67,7 @@ type BookmarkTestSuite struct {
|
|||
testFollows map[string]*gtsmodel.Follow
|
||||
|
||||
// module being tested
|
||||
statusModule *status.Module
|
||||
statusModule *statuses.Module
|
||||
bookmarkModule *bookmarks.Module
|
||||
}
|
||||
|
||||
|
@ -99,8 +99,8 @@ func (suite *BookmarkTestSuite) SetupTest() {
|
|||
suite.federator = testrig.NewTestFederator(suite.db, testrig.NewTestTransportController(testrig.NewMockHTTPClient(nil, "../../../../testrig/media"), suite.db, fedWorker), suite.storage, suite.mediaManager, fedWorker)
|
||||
suite.emailSender = testrig.NewEmailSender("../../../../web/template/", nil)
|
||||
suite.processor = testrig.NewTestProcessor(suite.db, suite.storage, suite.federator, suite.emailSender, suite.mediaManager, clientWorker, fedWorker)
|
||||
suite.statusModule = status.New(suite.processor).(*status.Module)
|
||||
suite.bookmarkModule = bookmarks.New(suite.processor).(*bookmarks.Module)
|
||||
suite.statusModule = statuses.New(suite.processor)
|
||||
suite.bookmarkModule = bookmarks.New(suite.processor)
|
||||
|
||||
suite.NoError(suite.processor.Start())
|
||||
}
|
||||
|
@ -123,7 +123,7 @@ func (suite *BookmarkTestSuite) TestGetBookmark() {
|
|||
ctx.Set(oauth.SessionAuthorizedToken, oauthToken)
|
||||
ctx.Set(oauth.SessionAuthorizedUser, suite.testUsers["local_account_1"])
|
||||
ctx.Set(oauth.SessionAuthorizedAccount, suite.testAccounts["local_account_1"])
|
||||
ctx.Request = httptest.NewRequest(http.MethodPost, fmt.Sprintf("http://localhost:8080%s", strings.Replace(status.BookmarkPath, ":id", targetStatus.ID, 1)), nil) // the endpoint we're hitting
|
||||
ctx.Request = httptest.NewRequest(http.MethodPost, fmt.Sprintf("http://localhost:8080%s", strings.Replace(statuses.BookmarkPath, ":id", targetStatus.ID, 1)), nil) // the endpoint we're hitting
|
||||
ctx.Request.Header.Set("accept", "application/json")
|
||||
|
||||
suite.bookmarkModule.BookmarksGETHandler(ctx)
|
||||
|
|
|
@ -6,7 +6,7 @@
|
|||
"strconv"
|
||||
|
||||
"github.com/gin-gonic/gin"
|
||||
"github.com/superseriousbusiness/gotosocial/internal/api"
|
||||
apiutil "github.com/superseriousbusiness/gotosocial/internal/api/util"
|
||||
"github.com/superseriousbusiness/gotosocial/internal/gtserror"
|
||||
"github.com/superseriousbusiness/gotosocial/internal/oauth"
|
||||
)
|
||||
|
@ -56,12 +56,12 @@
|
|||
func (m *Module) BookmarksGETHandler(c *gin.Context) {
|
||||
authed, err := oauth.Authed(c, true, true, true, true)
|
||||
if err != nil {
|
||||
api.ErrorHandler(c, gtserror.NewErrorUnauthorized(err, err.Error()), m.processor.InstanceGet)
|
||||
apiutil.ErrorHandler(c, gtserror.NewErrorUnauthorized(err, err.Error()), m.processor.InstanceGet)
|
||||
return
|
||||
}
|
||||
|
||||
if _, err := api.NegotiateAccept(c, api.JSONAcceptHeaders...); err != nil {
|
||||
api.ErrorHandler(c, gtserror.NewErrorNotAcceptable(err, err.Error()), m.processor.InstanceGet)
|
||||
if _, err := apiutil.NegotiateAccept(c, apiutil.JSONAcceptHeaders...); err != nil {
|
||||
apiutil.ErrorHandler(c, gtserror.NewErrorNotAcceptable(err, err.Error()), m.processor.InstanceGet)
|
||||
return
|
||||
}
|
||||
|
||||
|
@ -71,7 +71,7 @@ func (m *Module) BookmarksGETHandler(c *gin.Context) {
|
|||
i, err := strconv.ParseInt(limitString, 10, 64)
|
||||
if err != nil {
|
||||
err := fmt.Errorf("error parsing %s: %s", LimitKey, err)
|
||||
api.ErrorHandler(c, gtserror.NewErrorBadRequest(err, err.Error()), m.processor.InstanceGet)
|
||||
apiutil.ErrorHandler(c, gtserror.NewErrorBadRequest(err, err.Error()), m.processor.InstanceGet)
|
||||
return
|
||||
}
|
||||
limit = int(i)
|
||||
|
@ -91,12 +91,12 @@ func (m *Module) BookmarksGETHandler(c *gin.Context) {
|
|||
|
||||
resp, errWithCode := m.processor.BookmarksGet(c.Request.Context(), authed, maxID, minID, limit)
|
||||
if errWithCode != nil {
|
||||
api.ErrorHandler(c, errWithCode, m.processor.InstanceGet)
|
||||
apiutil.ErrorHandler(c, errWithCode, m.processor.InstanceGet)
|
||||
return
|
||||
}
|
||||
|
||||
if errWithCode != nil {
|
||||
api.ErrorHandler(c, errWithCode, m.processor.InstanceGet)
|
||||
apiutil.ErrorHandler(c, errWithCode, m.processor.InstanceGet)
|
||||
return
|
||||
}
|
||||
|
||||
|
|
45
internal/api/client/customemojis/customemojis.go
Normal file
45
internal/api/client/customemojis/customemojis.go
Normal file
|
@ -0,0 +1,45 @@
|
|||
/*
|
||||
GoToSocial
|
||||
Copyright (C) 2021-2022 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 customemojis
|
||||
|
||||
import (
|
||||
"net/http"
|
||||
|
||||
"github.com/gin-gonic/gin"
|
||||
"github.com/superseriousbusiness/gotosocial/internal/processing"
|
||||
)
|
||||
|
||||
const (
|
||||
// BasePath is the base path for serving custom emojis, minus the 'api' prefix
|
||||
BasePath = "/v1/custom_emojis"
|
||||
)
|
||||
|
||||
type Module struct {
|
||||
processor processing.Processor
|
||||
}
|
||||
|
||||
func New(processor processing.Processor) *Module {
|
||||
return &Module{
|
||||
processor: processor,
|
||||
}
|
||||
}
|
||||
|
||||
func (m *Module) Route(attachHandler func(method string, path string, f ...gin.HandlerFunc) gin.IRoutes) {
|
||||
attachHandler(http.MethodGet, BasePath, m.CustomEmojisGETHandler)
|
||||
}
|
76
internal/api/client/customemojis/customemojisget.go
Normal file
76
internal/api/client/customemojis/customemojisget.go
Normal file
|
@ -0,0 +1,76 @@
|
|||
/*
|
||||
GoToSocial
|
||||
Copyright (C) 2021-2022 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 customemojis
|
||||
|
||||
import (
|
||||
"net/http"
|
||||
|
||||
"github.com/gin-gonic/gin"
|
||||
apiutil "github.com/superseriousbusiness/gotosocial/internal/api/util"
|
||||
"github.com/superseriousbusiness/gotosocial/internal/gtserror"
|
||||
"github.com/superseriousbusiness/gotosocial/internal/oauth"
|
||||
)
|
||||
|
||||
// CustomEmojisGETHandler swagger:operation GET /api/v1/custom_emojis customEmojisGet
|
||||
//
|
||||
// Get an array of custom emojis available on the instance.
|
||||
//
|
||||
// ---
|
||||
// tags:
|
||||
// - custom_emojis
|
||||
//
|
||||
// produces:
|
||||
// - application/json
|
||||
//
|
||||
// security:
|
||||
// - OAuth2 Bearer:
|
||||
// - read:custom_emojis
|
||||
//
|
||||
// responses:
|
||||
// '200':
|
||||
// description: Array of custom emojis.
|
||||
// schema:
|
||||
// type: array
|
||||
// items:
|
||||
// "$ref": "#/definitions/emoji"
|
||||
// '401':
|
||||
// description: unauthorized
|
||||
// '406':
|
||||
// description: not acceptable
|
||||
// '500':
|
||||
// description: internal server error
|
||||
func (m *Module) CustomEmojisGETHandler(c *gin.Context) {
|
||||
if _, err := oauth.Authed(c, true, true, true, true); err != nil {
|
||||
apiutil.ErrorHandler(c, gtserror.NewErrorUnauthorized(err, err.Error()), m.processor.InstanceGet)
|
||||
return
|
||||
}
|
||||
|
||||
if _, err := apiutil.NegotiateAccept(c, apiutil.JSONAcceptHeaders...); err != nil {
|
||||
apiutil.ErrorHandler(c, gtserror.NewErrorNotAcceptable(err, err.Error()), m.processor.InstanceGet)
|
||||
return
|
||||
}
|
||||
|
||||
emojis, errWithCode := m.processor.CustomEmojisGet(c)
|
||||
if errWithCode != nil {
|
||||
apiutil.ErrorHandler(c, errWithCode, m.processor.InstanceGet)
|
||||
return
|
||||
}
|
||||
|
||||
c.JSON(http.StatusOK, emojis)
|
||||
}
|
|
@ -1,58 +0,0 @@
|
|||
package emoji
|
||||
|
||||
import (
|
||||
"net/http"
|
||||
|
||||
"github.com/gin-gonic/gin"
|
||||
"github.com/superseriousbusiness/gotosocial/internal/api"
|
||||
"github.com/superseriousbusiness/gotosocial/internal/gtserror"
|
||||
"github.com/superseriousbusiness/gotosocial/internal/oauth"
|
||||
)
|
||||
|
||||
// EmojisGETHandler swagger:operation GET /api/v1/custom_emojis customEmojisGet
|
||||
//
|
||||
// Get an array of custom emojis available on the instance.
|
||||
//
|
||||
// ---
|
||||
// tags:
|
||||
// - custom_emojis
|
||||
//
|
||||
// produces:
|
||||
// - application/json
|
||||
//
|
||||
// security:
|
||||
// - OAuth2 Bearer:
|
||||
// - read:custom_emojis
|
||||
//
|
||||
// responses:
|
||||
// '200':
|
||||
// description: Array of custom emojis.
|
||||
// schema:
|
||||
// type: array
|
||||
// items:
|
||||
// "$ref": "#/definitions/emoji"
|
||||
// '401':
|
||||
// description: unauthorized
|
||||
// '406':
|
||||
// description: not acceptable
|
||||
// '500':
|
||||
// description: internal server error
|
||||
func (m *Module) EmojisGETHandler(c *gin.Context) {
|
||||
if _, err := oauth.Authed(c, true, true, true, true); err != nil {
|
||||
api.ErrorHandler(c, gtserror.NewErrorUnauthorized(err, err.Error()), m.processor.InstanceGet)
|
||||
return
|
||||
}
|
||||
|
||||
if _, err := api.NegotiateAccept(c, api.JSONAcceptHeaders...); err != nil {
|
||||
api.ErrorHandler(c, gtserror.NewErrorNotAcceptable(err, err.Error()), m.processor.InstanceGet)
|
||||
return
|
||||
}
|
||||
|
||||
emojis, errWithCode := m.processor.CustomEmojisGet(c)
|
||||
if errWithCode != nil {
|
||||
api.ErrorHandler(c, errWithCode, m.processor.InstanceGet)
|
||||
return
|
||||
}
|
||||
|
||||
c.JSON(http.StatusOK, emojis)
|
||||
}
|
|
@ -21,14 +21,13 @@
|
|||
import (
|
||||
"net/http"
|
||||
|
||||
"github.com/superseriousbusiness/gotosocial/internal/api"
|
||||
"github.com/gin-gonic/gin"
|
||||
"github.com/superseriousbusiness/gotosocial/internal/processing"
|
||||
"github.com/superseriousbusiness/gotosocial/internal/router"
|
||||
)
|
||||
|
||||
const (
|
||||
// BasePath is the base URI path for serving favourites
|
||||
BasePath = "/api/v1/favourites"
|
||||
// BasePath is the base URI path for serving favourites, minus the 'api' prefix
|
||||
BasePath = "/v1/favourites"
|
||||
|
||||
// MaxIDKey is the url query for setting a max status ID to return
|
||||
MaxIDKey = "max_id"
|
||||
|
@ -42,20 +41,16 @@
|
|||
LocalKey = "local"
|
||||
)
|
||||
|
||||
// Module implements the ClientAPIModule interface for everything relating to viewing favourites
|
||||
type Module struct {
|
||||
processor processing.Processor
|
||||
}
|
||||
|
||||
// New returns a new favourites module
|
||||
func New(processor processing.Processor) api.ClientModule {
|
||||
func New(processor processing.Processor) *Module {
|
||||
return &Module{
|
||||
processor: processor,
|
||||
}
|
||||
}
|
||||
|
||||
// Route attaches all routes from this module to the given router
|
||||
func (m *Module) Route(r router.Router) error {
|
||||
r.AttachHandler(http.MethodGet, BasePath, m.FavouritesGETHandler)
|
||||
return nil
|
||||
func (m *Module) Route(attachHandler func(method string, path string, f ...gin.HandlerFunc) gin.IRoutes) {
|
||||
attachHandler(http.MethodGet, BasePath, m.FavouritesGETHandler)
|
||||
}
|
||||
|
|
|
@ -87,7 +87,7 @@ func (suite *FavouritesStandardTestSuite) SetupTest() {
|
|||
suite.federator = testrig.NewTestFederator(suite.db, testrig.NewTestTransportController(testrig.NewMockHTTPClient(nil, "../../../../testrig/media"), suite.db, fedWorker), suite.storage, suite.mediaManager, fedWorker)
|
||||
suite.emailSender = testrig.NewEmailSender("../../../../web/template/", nil)
|
||||
suite.processor = testrig.NewTestProcessor(suite.db, suite.storage, suite.federator, suite.emailSender, suite.mediaManager, clientWorker, fedWorker)
|
||||
suite.favModule = favourites.New(suite.processor).(*favourites.Module)
|
||||
suite.favModule = favourites.New(suite.processor)
|
||||
|
||||
suite.NoError(suite.processor.Start())
|
||||
}
|
||||
|
|
|
@ -6,7 +6,7 @@
|
|||
"strconv"
|
||||
|
||||
"github.com/gin-gonic/gin"
|
||||
"github.com/superseriousbusiness/gotosocial/internal/api"
|
||||
apiutil "github.com/superseriousbusiness/gotosocial/internal/api/util"
|
||||
"github.com/superseriousbusiness/gotosocial/internal/gtserror"
|
||||
"github.com/superseriousbusiness/gotosocial/internal/oauth"
|
||||
)
|
||||
|
@ -78,12 +78,12 @@
|
|||
func (m *Module) FavouritesGETHandler(c *gin.Context) {
|
||||
authed, err := oauth.Authed(c, true, true, true, true)
|
||||
if err != nil {
|
||||
api.ErrorHandler(c, gtserror.NewErrorUnauthorized(err, err.Error()), m.processor.InstanceGet)
|
||||
apiutil.ErrorHandler(c, gtserror.NewErrorUnauthorized(err, err.Error()), m.processor.InstanceGet)
|
||||
return
|
||||
}
|
||||
|
||||
if _, err := api.NegotiateAccept(c, api.JSONAcceptHeaders...); err != nil {
|
||||
api.ErrorHandler(c, gtserror.NewErrorNotAcceptable(err, err.Error()), m.processor.InstanceGet)
|
||||
if _, err := apiutil.NegotiateAccept(c, apiutil.JSONAcceptHeaders...); err != nil {
|
||||
apiutil.ErrorHandler(c, gtserror.NewErrorNotAcceptable(err, err.Error()), m.processor.InstanceGet)
|
||||
return
|
||||
}
|
||||
|
||||
|
@ -105,7 +105,7 @@ func (m *Module) FavouritesGETHandler(c *gin.Context) {
|
|||
i, err := strconv.ParseInt(limitString, 10, 32)
|
||||
if err != nil {
|
||||
err := fmt.Errorf("error parsing %s: %s", LimitKey, err)
|
||||
api.ErrorHandler(c, gtserror.NewErrorBadRequest(err, err.Error()), m.processor.InstanceGet)
|
||||
apiutil.ErrorHandler(c, gtserror.NewErrorBadRequest(err, err.Error()), m.processor.InstanceGet)
|
||||
return
|
||||
}
|
||||
limit = int(i)
|
||||
|
@ -113,7 +113,7 @@ func (m *Module) FavouritesGETHandler(c *gin.Context) {
|
|||
|
||||
resp, errWithCode := m.processor.FavedTimelineGet(c.Request.Context(), authed, maxID, minID, limit)
|
||||
if errWithCode != nil {
|
||||
api.ErrorHandler(c, errWithCode, m.processor.InstanceGet)
|
||||
apiutil.ErrorHandler(c, errWithCode, m.processor.InstanceGet)
|
||||
return
|
||||
}
|
||||
|
||||
|
|
|
@ -21,30 +21,25 @@
|
|||
import (
|
||||
"net/http"
|
||||
|
||||
"github.com/superseriousbusiness/gotosocial/internal/api"
|
||||
"github.com/gin-gonic/gin"
|
||||
"github.com/superseriousbusiness/gotosocial/internal/processing"
|
||||
"github.com/superseriousbusiness/gotosocial/internal/router"
|
||||
)
|
||||
|
||||
const (
|
||||
// BasePath is the base path for serving the filter API
|
||||
BasePath = "/api/v1/filters"
|
||||
// BasePath is the base path for serving the filters API, minus the 'api' prefix
|
||||
BasePath = "/v1/filters"
|
||||
)
|
||||
|
||||
// Module implements the ClientAPIModule interface for every related to filters
|
||||
type Module struct {
|
||||
processor processing.Processor
|
||||
}
|
||||
|
||||
// New returns a new filter module
|
||||
func New(processor processing.Processor) api.ClientModule {
|
||||
func New(processor processing.Processor) *Module {
|
||||
return &Module{
|
||||
processor: processor,
|
||||
}
|
||||
}
|
||||
|
||||
// Route attaches all routes from this module to the given router
|
||||
func (m *Module) Route(r router.Router) error {
|
||||
r.AttachHandler(http.MethodGet, BasePath, m.FiltersGETHandler)
|
||||
return nil
|
||||
func (m *Module) Route(attachHandler func(method string, path string, f ...gin.HandlerFunc) gin.IRoutes) {
|
||||
attachHandler(http.MethodGet, BasePath, m.FiltersGETHandler)
|
||||
}
|
|
@ -4,7 +4,7 @@
|
|||
"net/http"
|
||||
|
||||
"github.com/gin-gonic/gin"
|
||||
"github.com/superseriousbusiness/gotosocial/internal/api"
|
||||
apiutil "github.com/superseriousbusiness/gotosocial/internal/api/util"
|
||||
"github.com/superseriousbusiness/gotosocial/internal/gtserror"
|
||||
"github.com/superseriousbusiness/gotosocial/internal/oauth"
|
||||
)
|
||||
|
@ -12,12 +12,12 @@
|
|||
// FiltersGETHandler returns a list of filters set by/for the authed account
|
||||
func (m *Module) FiltersGETHandler(c *gin.Context) {
|
||||
if _, err := oauth.Authed(c, true, true, true, true); err != nil {
|
||||
api.ErrorHandler(c, gtserror.NewErrorUnauthorized(err, err.Error()), m.processor.InstanceGet)
|
||||
apiutil.ErrorHandler(c, gtserror.NewErrorUnauthorized(err, err.Error()), m.processor.InstanceGet)
|
||||
return
|
||||
}
|
||||
|
||||
if _, err := api.NegotiateAccept(c, api.JSONAcceptHeaders...); err != nil {
|
||||
api.ErrorHandler(c, gtserror.NewErrorNotAcceptable(err, err.Error()), m.processor.InstanceGet)
|
||||
if _, err := apiutil.NegotiateAccept(c, apiutil.JSONAcceptHeaders...); err != nil {
|
||||
apiutil.ErrorHandler(c, gtserror.NewErrorNotAcceptable(err, err.Error()), m.processor.InstanceGet)
|
||||
return
|
||||
}
|
||||
|
|
@ -16,14 +16,14 @@
|
|||
along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
package followrequest
|
||||
package followrequests
|
||||
|
||||
import (
|
||||
"errors"
|
||||
"net/http"
|
||||
|
||||
"github.com/gin-gonic/gin"
|
||||
"github.com/superseriousbusiness/gotosocial/internal/api"
|
||||
apiutil "github.com/superseriousbusiness/gotosocial/internal/api/util"
|
||||
"github.com/superseriousbusiness/gotosocial/internal/gtserror"
|
||||
"github.com/superseriousbusiness/gotosocial/internal/oauth"
|
||||
)
|
||||
|
@ -72,25 +72,25 @@
|
|||
func (m *Module) FollowRequestAuthorizePOSTHandler(c *gin.Context) {
|
||||
authed, err := oauth.Authed(c, true, true, true, true)
|
||||
if err != nil {
|
||||
api.ErrorHandler(c, gtserror.NewErrorUnauthorized(err, err.Error()), m.processor.InstanceGet)
|
||||
apiutil.ErrorHandler(c, gtserror.NewErrorUnauthorized(err, err.Error()), m.processor.InstanceGet)
|
||||
return
|
||||
}
|
||||
|
||||
if _, err := api.NegotiateAccept(c, api.JSONAcceptHeaders...); err != nil {
|
||||
api.ErrorHandler(c, gtserror.NewErrorNotAcceptable(err, err.Error()), m.processor.InstanceGet)
|
||||
if _, err := apiutil.NegotiateAccept(c, apiutil.JSONAcceptHeaders...); err != nil {
|
||||
apiutil.ErrorHandler(c, gtserror.NewErrorNotAcceptable(err, err.Error()), m.processor.InstanceGet)
|
||||
return
|
||||
}
|
||||
|
||||
originAccountID := c.Param(IDKey)
|
||||
if originAccountID == "" {
|
||||
err := errors.New("no account id specified")
|
||||
api.ErrorHandler(c, gtserror.NewErrorBadRequest(err, err.Error()), m.processor.InstanceGet)
|
||||
apiutil.ErrorHandler(c, gtserror.NewErrorBadRequest(err, err.Error()), m.processor.InstanceGet)
|
||||
return
|
||||
}
|
||||
|
||||
relationship, errWithCode := m.processor.FollowRequestAccept(c.Request.Context(), authed, originAccountID)
|
||||
if errWithCode != nil {
|
||||
api.ErrorHandler(c, errWithCode, m.processor.InstanceGet)
|
||||
apiutil.ErrorHandler(c, errWithCode, m.processor.InstanceGet)
|
||||
return
|
||||
}
|
||||
|
|
@ -16,7 +16,7 @@
|
|||
along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
package followrequest_test
|
||||
package followrequests_test
|
||||
|
||||
import (
|
||||
"context"
|
||||
|
@ -30,7 +30,7 @@
|
|||
"github.com/gin-gonic/gin"
|
||||
"github.com/stretchr/testify/assert"
|
||||
"github.com/stretchr/testify/suite"
|
||||
"github.com/superseriousbusiness/gotosocial/internal/api/client/followrequest"
|
||||
"github.com/superseriousbusiness/gotosocial/internal/api/client/followrequests"
|
||||
"github.com/superseriousbusiness/gotosocial/internal/gtsmodel"
|
||||
)
|
||||
|
||||
|
@ -60,7 +60,7 @@ func (suite *AuthorizeTestSuite) TestAuthorize() {
|
|||
|
||||
ctx.Params = gin.Params{
|
||||
gin.Param{
|
||||
Key: followrequest.IDKey,
|
||||
Key: followrequests.IDKey,
|
||||
Value: requestingAccount.ID,
|
||||
},
|
||||
}
|
||||
|
@ -90,7 +90,7 @@ func (suite *AuthorizeTestSuite) TestAuthorizeNoFR() {
|
|||
|
||||
ctx.Params = gin.Params{
|
||||
gin.Param{
|
||||
Key: followrequest.IDKey,
|
||||
Key: followrequests.IDKey,
|
||||
Value: requestingAccount.ID,
|
||||
},
|
||||
}
|
|
@ -16,21 +16,20 @@
|
|||
along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
package followrequest
|
||||
package followrequests
|
||||
|
||||
import (
|
||||
"net/http"
|
||||
|
||||
"github.com/superseriousbusiness/gotosocial/internal/api"
|
||||
"github.com/gin-gonic/gin"
|
||||
"github.com/superseriousbusiness/gotosocial/internal/processing"
|
||||
"github.com/superseriousbusiness/gotosocial/internal/router"
|
||||
)
|
||||
|
||||
const (
|
||||
// IDKey is for account IDs
|
||||
IDKey = "id"
|
||||
// BasePath is the base path for serving the follow request API
|
||||
BasePath = "/api/v1/follow_requests"
|
||||
// BasePath is the base path for serving the follow request API, minus the 'api' prefix
|
||||
BasePath = "/v1/follow_requests"
|
||||
// BasePathWithID is just the base path with the ID key in it.
|
||||
// Use this anywhere you need to know the ID of the account that owns the follow request being queried.
|
||||
BasePathWithID = BasePath + "/:" + IDKey
|
||||
|
@ -40,22 +39,18 @@
|
|||
RejectPath = BasePathWithID + "/reject"
|
||||
)
|
||||
|
||||
// Module implements the ClientAPIModule interface
|
||||
type Module struct {
|
||||
processor processing.Processor
|
||||
}
|
||||
|
||||
// New returns a new follow request module
|
||||
func New(processor processing.Processor) api.ClientModule {
|
||||
func New(processor processing.Processor) *Module {
|
||||
return &Module{
|
||||
processor: processor,
|
||||
}
|
||||
}
|
||||
|
||||
// Route attaches all routes from this module to the given router
|
||||
func (m *Module) Route(r router.Router) error {
|
||||
r.AttachHandler(http.MethodGet, BasePath, m.FollowRequestGETHandler)
|
||||
r.AttachHandler(http.MethodPost, AuthorizePath, m.FollowRequestAuthorizePOSTHandler)
|
||||
r.AttachHandler(http.MethodPost, RejectPath, m.FollowRequestRejectPOSTHandler)
|
||||
return nil
|
||||
func (m *Module) Route(attachHandler func(method string, path string, f ...gin.HandlerFunc) gin.IRoutes) {
|
||||
attachHandler(http.MethodGet, BasePath, m.FollowRequestGETHandler)
|
||||
attachHandler(http.MethodPost, AuthorizePath, m.FollowRequestAuthorizePOSTHandler)
|
||||
attachHandler(http.MethodPost, RejectPath, m.FollowRequestRejectPOSTHandler)
|
||||
}
|
|
@ -16,7 +16,7 @@
|
|||
along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
package followrequest_test
|
||||
package followrequests_test
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
|
@ -25,7 +25,7 @@
|
|||
|
||||
"github.com/gin-gonic/gin"
|
||||
"github.com/stretchr/testify/suite"
|
||||
"github.com/superseriousbusiness/gotosocial/internal/api/client/followrequest"
|
||||
"github.com/superseriousbusiness/gotosocial/internal/api/client/followrequests"
|
||||
"github.com/superseriousbusiness/gotosocial/internal/concurrency"
|
||||
"github.com/superseriousbusiness/gotosocial/internal/config"
|
||||
"github.com/superseriousbusiness/gotosocial/internal/db"
|
||||
|
@ -59,7 +59,7 @@ type FollowRequestStandardTestSuite struct {
|
|||
testStatuses map[string]*gtsmodel.Status
|
||||
|
||||
// module being tested
|
||||
followRequestModule *followrequest.Module
|
||||
followRequestModule *followrequests.Module
|
||||
}
|
||||
|
||||
func (suite *FollowRequestStandardTestSuite) SetupSuite() {
|
||||
|
@ -85,7 +85,7 @@ func (suite *FollowRequestStandardTestSuite) SetupTest() {
|
|||
suite.federator = testrig.NewTestFederator(suite.db, testrig.NewTestTransportController(testrig.NewMockHTTPClient(nil, "../../../../testrig/media"), suite.db, fedWorker), suite.storage, suite.mediaManager, fedWorker)
|
||||
suite.emailSender = testrig.NewEmailSender("../../../../web/template/", nil)
|
||||
suite.processor = testrig.NewTestProcessor(suite.db, suite.storage, suite.federator, suite.emailSender, suite.mediaManager, clientWorker, fedWorker)
|
||||
suite.followRequestModule = followrequest.New(suite.processor).(*followrequest.Module)
|
||||
suite.followRequestModule = followrequests.New(suite.processor)
|
||||
testrig.StandardDBSetup(suite.db, nil)
|
||||
testrig.StandardStorageSetup(suite.storage, "../../../../testrig/media")
|
||||
|
|
@ -16,13 +16,13 @@
|
|||
along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
package followrequest
|
||||
package followrequests
|
||||
|
||||
import (
|
||||
"net/http"
|
||||
|
||||
"github.com/gin-gonic/gin"
|
||||
"github.com/superseriousbusiness/gotosocial/internal/api"
|
||||
apiutil "github.com/superseriousbusiness/gotosocial/internal/api/util"
|
||||
"github.com/superseriousbusiness/gotosocial/internal/gtserror"
|
||||
"github.com/superseriousbusiness/gotosocial/internal/oauth"
|
||||
)
|
||||
|
@ -74,18 +74,18 @@
|
|||
func (m *Module) FollowRequestGETHandler(c *gin.Context) {
|
||||
authed, err := oauth.Authed(c, true, true, true, true)
|
||||
if err != nil {
|
||||
api.ErrorHandler(c, gtserror.NewErrorUnauthorized(err, err.Error()), m.processor.InstanceGet)
|
||||
apiutil.ErrorHandler(c, gtserror.NewErrorUnauthorized(err, err.Error()), m.processor.InstanceGet)
|
||||
return
|
||||
}
|
||||
|
||||
if _, err := api.NegotiateAccept(c, api.JSONAcceptHeaders...); err != nil {
|
||||
api.ErrorHandler(c, gtserror.NewErrorNotAcceptable(err, err.Error()), m.processor.InstanceGet)
|
||||
if _, err := apiutil.NegotiateAccept(c, apiutil.JSONAcceptHeaders...); err != nil {
|
||||
apiutil.ErrorHandler(c, gtserror.NewErrorNotAcceptable(err, err.Error()), m.processor.InstanceGet)
|
||||
return
|
||||
}
|
||||
|
||||
accts, errWithCode := m.processor.FollowRequestsGet(c.Request.Context(), authed)
|
||||
if errWithCode != nil {
|
||||
api.ErrorHandler(c, errWithCode, m.processor.InstanceGet)
|
||||
apiutil.ErrorHandler(c, errWithCode, m.processor.InstanceGet)
|
||||
return
|
||||
}
|
||||
|
|
@ -16,7 +16,7 @@
|
|||
along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
package followrequest_test
|
||||
package followrequests_test
|
||||
|
||||
import (
|
||||
"context"
|
|
@ -16,14 +16,14 @@
|
|||
along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
package followrequest
|
||||
package followrequests
|
||||
|
||||
import (
|
||||
"errors"
|
||||
"net/http"
|
||||
|
||||
"github.com/gin-gonic/gin"
|
||||
"github.com/superseriousbusiness/gotosocial/internal/api"
|
||||
apiutil "github.com/superseriousbusiness/gotosocial/internal/api/util"
|
||||
"github.com/superseriousbusiness/gotosocial/internal/gtserror"
|
||||
"github.com/superseriousbusiness/gotosocial/internal/oauth"
|
||||
)
|
||||
|
@ -70,25 +70,25 @@
|
|||
func (m *Module) FollowRequestRejectPOSTHandler(c *gin.Context) {
|
||||
authed, err := oauth.Authed(c, true, true, true, true)
|
||||
if err != nil {
|
||||
api.ErrorHandler(c, gtserror.NewErrorUnauthorized(err, err.Error()), m.processor.InstanceGet)
|
||||
apiutil.ErrorHandler(c, gtserror.NewErrorUnauthorized(err, err.Error()), m.processor.InstanceGet)
|
||||
return
|
||||
}
|
||||
|
||||
if _, err := api.NegotiateAccept(c, api.JSONAcceptHeaders...); err != nil {
|
||||
api.ErrorHandler(c, gtserror.NewErrorNotAcceptable(err, err.Error()), m.processor.InstanceGet)
|
||||
if _, err := apiutil.NegotiateAccept(c, apiutil.JSONAcceptHeaders...); err != nil {
|
||||
apiutil.ErrorHandler(c, gtserror.NewErrorNotAcceptable(err, err.Error()), m.processor.InstanceGet)
|
||||
return
|
||||
}
|
||||
|
||||
originAccountID := c.Param(IDKey)
|
||||
if originAccountID == "" {
|
||||
err := errors.New("no account id specified")
|
||||
api.ErrorHandler(c, gtserror.NewErrorBadRequest(err, err.Error()), m.processor.InstanceGet)
|
||||
apiutil.ErrorHandler(c, gtserror.NewErrorBadRequest(err, err.Error()), m.processor.InstanceGet)
|
||||
return
|
||||
}
|
||||
|
||||
relationship, errWithCode := m.processor.FollowRequestReject(c.Request.Context(), authed, originAccountID)
|
||||
if errWithCode != nil {
|
||||
api.ErrorHandler(c, errWithCode, m.processor.InstanceGet)
|
||||
apiutil.ErrorHandler(c, errWithCode, m.processor.InstanceGet)
|
||||
return
|
||||
}
|
||||
|
|
@ -16,7 +16,7 @@
|
|||
along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
package followrequest_test
|
||||
package followrequests_test
|
||||
|
||||
import (
|
||||
"context"
|
||||
|
@ -30,7 +30,7 @@
|
|||
"github.com/gin-gonic/gin"
|
||||
"github.com/stretchr/testify/assert"
|
||||
"github.com/stretchr/testify/suite"
|
||||
"github.com/superseriousbusiness/gotosocial/internal/api/client/followrequest"
|
||||
"github.com/superseriousbusiness/gotosocial/internal/api/client/followrequests"
|
||||
"github.com/superseriousbusiness/gotosocial/internal/gtsmodel"
|
||||
)
|
||||
|
||||
|
@ -60,7 +60,7 @@ func (suite *RejectTestSuite) TestReject() {
|
|||
|
||||
ctx.Params = gin.Params{
|
||||
gin.Param{
|
||||
Key: followrequest.IDKey,
|
||||
Key: followrequests.IDKey,
|
||||
Value: requestingAccount.ID,
|
||||
},
|
||||
}
|
Some files were not shown because too many files have changed in this diff Show more
Loading…
Reference in a new issue