[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/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.
|
`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/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/netutil` - HTTP / net request validation code.
|
||||||
|
|
||||||
`internal/oauth` - Wrapper code/interfaces for OAuth server implementation.
|
`internal/oauth` - Wrapper code/interfaces for OAuth server implementation.
|
||||||
|
|
|
@ -20,37 +20,20 @@ package server
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"context"
|
"context"
|
||||||
|
"errors"
|
||||||
"fmt"
|
"fmt"
|
||||||
|
"net/http"
|
||||||
"os"
|
"os"
|
||||||
"os/signal"
|
"os/signal"
|
||||||
"syscall"
|
"syscall"
|
||||||
|
|
||||||
|
"github.com/gin-gonic/gin"
|
||||||
"github.com/superseriousbusiness/gotosocial/cmd/gotosocial/action"
|
"github.com/superseriousbusiness/gotosocial/cmd/gotosocial/action"
|
||||||
"github.com/superseriousbusiness/gotosocial/internal/api"
|
"github.com/superseriousbusiness/gotosocial/internal/api"
|
||||||
"github.com/superseriousbusiness/gotosocial/internal/api/client/account"
|
apiutil "github.com/superseriousbusiness/gotosocial/internal/api/util"
|
||||||
"github.com/superseriousbusiness/gotosocial/internal/api/client/admin"
|
"github.com/superseriousbusiness/gotosocial/internal/gtserror"
|
||||||
"github.com/superseriousbusiness/gotosocial/internal/api/client/app"
|
"github.com/superseriousbusiness/gotosocial/internal/middleware"
|
||||||
"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"
|
|
||||||
"github.com/superseriousbusiness/gotosocial/internal/concurrency"
|
"github.com/superseriousbusiness/gotosocial/internal/concurrency"
|
||||||
"github.com/superseriousbusiness/gotosocial/internal/config"
|
"github.com/superseriousbusiness/gotosocial/internal/config"
|
||||||
"github.com/superseriousbusiness/gotosocial/internal/db/bundb"
|
"github.com/superseriousbusiness/gotosocial/internal/db/bundb"
|
||||||
|
@ -106,11 +89,6 @@ var Start action.GTSAction = func(ctx context.Context) error {
|
||||||
|
|
||||||
federatingDB := federatingdb.New(dbService, fedWorker)
|
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
|
// build converters and util
|
||||||
typeConverter := typeutils.NewConverter(dbService)
|
typeConverter := typeutils.NewConverter(dbService)
|
||||||
|
|
||||||
|
@ -148,85 +126,72 @@ var Start action.GTSAction = func(ctx context.Context) error {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// 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)
|
processor := processing.NewProcessor(typeConverter, federator, oauthServer, mediaManager, storage, dbService, emailSender, clientWorker, fedWorker)
|
||||||
if err := processor.Start(); err != nil {
|
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 {
|
if err != nil {
|
||||||
return fmt.Errorf("error creating oidc idp: %s", err)
|
return fmt.Errorf("error creating router: %s", err)
|
||||||
}
|
}
|
||||||
|
|
||||||
// build web module
|
// attach global middlewares which are used for every request
|
||||||
webModule := web.New(processor)
|
router.AttachGlobalMiddleware(
|
||||||
|
middleware.Logger(),
|
||||||
|
middleware.UserAgent(),
|
||||||
|
middleware.CORS(),
|
||||||
|
middleware.ExtraHeaders(),
|
||||||
|
)
|
||||||
|
|
||||||
// build client api modules
|
// attach global no route / 404 handler to the router
|
||||||
authModule := auth.New(dbService, idp, processor)
|
router.AttachNoRouteHandler(func(c *gin.Context) {
|
||||||
accountModule := account.New(processor)
|
apiutil.ErrorHandler(c, gtserror.NewErrorNotFound(errors.New(http.StatusText(http.StatusNotFound))), processor.InstanceGet)
|
||||||
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)
|
|
||||||
|
|
||||||
apis := []api.ClientModule{
|
// build router modules
|
||||||
// modules with middleware go first
|
var idp oidc.IDP
|
||||||
securityModule,
|
if config.GetOIDCEnabled() {
|
||||||
authModule,
|
idp, err = oidc.NewIDP(ctx)
|
||||||
|
if err != nil {
|
||||||
// now the web module
|
return fmt.Errorf("error creating oidc idp: %w", err)
|
||||||
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)
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
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)
|
gts, err := gotosocial.NewServer(dbService, router, federator, mediaManager)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return fmt.Errorf("error creating gotosocial service: %s", err)
|
return fmt.Errorf("error creating gotosocial service: %s", err)
|
||||||
|
|
|
@ -21,6 +21,7 @@ package testrig
|
||||||
import (
|
import (
|
||||||
"bytes"
|
"bytes"
|
||||||
"context"
|
"context"
|
||||||
|
"errors"
|
||||||
"fmt"
|
"fmt"
|
||||||
"io"
|
"io"
|
||||||
"net/http"
|
"net/http"
|
||||||
|
@ -28,35 +29,17 @@ import (
|
||||||
"os/signal"
|
"os/signal"
|
||||||
"syscall"
|
"syscall"
|
||||||
|
|
||||||
|
"github.com/gin-gonic/gin"
|
||||||
"github.com/superseriousbusiness/gotosocial/cmd/gotosocial/action"
|
"github.com/superseriousbusiness/gotosocial/cmd/gotosocial/action"
|
||||||
"github.com/superseriousbusiness/gotosocial/internal/api"
|
"github.com/superseriousbusiness/gotosocial/internal/api"
|
||||||
"github.com/superseriousbusiness/gotosocial/internal/api/client/account"
|
apiutil "github.com/superseriousbusiness/gotosocial/internal/api/util"
|
||||||
"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"
|
|
||||||
"github.com/superseriousbusiness/gotosocial/internal/concurrency"
|
"github.com/superseriousbusiness/gotosocial/internal/concurrency"
|
||||||
|
"github.com/superseriousbusiness/gotosocial/internal/config"
|
||||||
"github.com/superseriousbusiness/gotosocial/internal/gotosocial"
|
"github.com/superseriousbusiness/gotosocial/internal/gotosocial"
|
||||||
|
"github.com/superseriousbusiness/gotosocial/internal/gtserror"
|
||||||
"github.com/superseriousbusiness/gotosocial/internal/log"
|
"github.com/superseriousbusiness/gotosocial/internal/log"
|
||||||
"github.com/superseriousbusiness/gotosocial/internal/messages"
|
"github.com/superseriousbusiness/gotosocial/internal/messages"
|
||||||
|
"github.com/superseriousbusiness/gotosocial/internal/middleware"
|
||||||
"github.com/superseriousbusiness/gotosocial/internal/oidc"
|
"github.com/superseriousbusiness/gotosocial/internal/oidc"
|
||||||
"github.com/superseriousbusiness/gotosocial/internal/storage"
|
"github.com/superseriousbusiness/gotosocial/internal/storage"
|
||||||
"github.com/superseriousbusiness/gotosocial/internal/web"
|
"github.com/superseriousbusiness/gotosocial/internal/web"
|
||||||
|
@ -70,7 +53,6 @@ var Start action.GTSAction = func(ctx context.Context) error {
|
||||||
|
|
||||||
dbService := testrig.NewTestDB()
|
dbService := testrig.NewTestDB()
|
||||||
testrig.StandardDBSetup(dbService, nil)
|
testrig.StandardDBSetup(dbService, nil)
|
||||||
router := testrig.NewTestRouter(dbService)
|
|
||||||
var storageBackend *storage.Driver
|
var storageBackend *storage.Driver
|
||||||
if os.Getenv("GTS_STORAGE_BACKEND") == "s3" {
|
if os.Getenv("GTS_STORAGE_BACKEND") == "s3" {
|
||||||
storageBackend, _ = storage.NewS3Storage()
|
storageBackend, _ = storage.NewS3Storage()
|
||||||
|
@ -84,7 +66,6 @@ var Start action.GTSAction = func(ctx context.Context) error {
|
||||||
fedWorker := concurrency.NewWorkerPool[messages.FromFederator](-1, -1)
|
fedWorker := concurrency.NewWorkerPool[messages.FromFederator](-1, -1)
|
||||||
|
|
||||||
// build backend handlers
|
// build backend handlers
|
||||||
oauthServer := testrig.NewTestOauthServer(dbService)
|
|
||||||
transportController := testrig.NewTestTransportController(testrig.NewMockHTTPClient(func(req *http.Request) (*http.Response, error) {
|
transportController := testrig.NewTestTransportController(testrig.NewMockHTTPClient(func(req *http.Request) (*http.Response, error) {
|
||||||
r := io.NopCloser(bytes.NewReader([]byte{}))
|
r := io.NopCloser(bytes.NewReader([]byte{}))
|
||||||
return &http.Response{
|
return &http.Response{
|
||||||
|
@ -102,77 +83,64 @@ var Start action.GTSAction = func(ctx context.Context) error {
|
||||||
return fmt.Errorf("error starting processor: %s", err)
|
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 {
|
if err != nil {
|
||||||
return fmt.Errorf("error creating oidc idp: %s", err)
|
return fmt.Errorf("error creating oidc idp: %w", 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)
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
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)
|
gts, err := gotosocial.NewServer(dbService, router, federator, mediaManager)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return fmt.Errorf("error creating gotosocial service: %s", err)
|
return fmt.Errorf("error creating gotosocial service: %s", err)
|
||||||
|
|
|
@ -1,10 +1,16 @@
|
||||||
# Rate Limit
|
# 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:
|
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.
|
title: SwaggerCollection represents an activitypub collection.
|
||||||
type: object
|
type: object
|
||||||
x-go-name: SwaggerCollection
|
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:
|
swaggerCollectionPage:
|
||||||
properties:
|
properties:
|
||||||
id:
|
id:
|
||||||
|
@ -1949,7 +1949,7 @@ definitions:
|
||||||
title: SwaggerCollectionPage represents one page of a collection.
|
title: SwaggerCollectionPage represents one page of a collection.
|
||||||
type: object
|
type: object
|
||||||
x-go-name: SwaggerCollectionPage
|
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:
|
tag:
|
||||||
properties:
|
properties:
|
||||||
name:
|
name:
|
||||||
|
@ -2049,9 +2049,9 @@ paths:
|
||||||
description: ""
|
description: ""
|
||||||
schema:
|
schema:
|
||||||
$ref: '#/definitions/wellKnownResponse'
|
$ref: '#/definitions/wellKnownResponse'
|
||||||
summary: Directs callers to /nodeinfo/2.0.
|
summary: Returns a well-known response which redirects callers to `/nodeinfo/2.0`.
|
||||||
tags:
|
tags:
|
||||||
- nodeinfo
|
- .well-known
|
||||||
/.well-known/webfinger:
|
/.well-known/webfinger:
|
||||||
get:
|
get:
|
||||||
description: |-
|
description: |-
|
||||||
|
@ -2074,7 +2074,7 @@ paths:
|
||||||
$ref: '#/definitions/wellKnownResponse'
|
$ref: '#/definitions/wellKnownResponse'
|
||||||
summary: Handles webfinger account lookup requests.
|
summary: Handles webfinger account lookup requests.
|
||||||
tags:
|
tags:
|
||||||
- webfinger
|
- .well-known
|
||||||
/api/{api_version}/media:
|
/api/{api_version}/media:
|
||||||
post:
|
post:
|
||||||
consumes:
|
consumes:
|
||||||
|
|
|
@ -641,9 +641,11 @@ syslog-address: "localhost:514"
|
||||||
# Default: "lax"
|
# Default: "lax"
|
||||||
advanced-cookies-samesite: "lax"
|
advanced-cookies-samesite: "lax"
|
||||||
|
|
||||||
# Int. Amount of requests to permit from a single IP address within a span of 5 minutes.
|
# Int. Amount of requests to permit per router grouping from a single IP address within
|
||||||
# If this amount is exceeded, a 429 HTTP error code will be returned.
|
# 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.
|
#
|
||||||
|
# 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,
|
# 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.
|
# 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.
|
# If you set this to 0 or less, rate limiting will be disabled entirely.
|
||||||
#
|
#
|
||||||
# Examples: [1000, 500, 0]
|
# Examples: [1000, 500, 0]
|
||||||
# Default: 1000
|
# Default: 300
|
||||||
advanced-rate-limit-requests: 1000
|
advanced-rate-limit-requests: 300
|
||||||
|
|
|
@ -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 @@ package emoji
|
||||||
import (
|
import (
|
||||||
"net/http"
|
"net/http"
|
||||||
|
|
||||||
"github.com/superseriousbusiness/gotosocial/internal/api"
|
"github.com/gin-gonic/gin"
|
||||||
"github.com/superseriousbusiness/gotosocial/internal/processing"
|
"github.com/superseriousbusiness/gotosocial/internal/processing"
|
||||||
"github.com/superseriousbusiness/gotosocial/internal/router"
|
|
||||||
)
|
)
|
||||||
|
|
||||||
const (
|
const (
|
||||||
// BasePath is the base path for serving the emoji API
|
// EmojiIDKey is for emoji IDs
|
||||||
BasePath = "/api/v1/custom_emojis"
|
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 {
|
type Module struct {
|
||||||
processor processing.Processor
|
processor processing.Processor
|
||||||
}
|
}
|
||||||
|
|
||||||
// New returns a new emoji module
|
func New(processor processing.Processor) *Module {
|
||||||
func New(processor processing.Processor) api.ClientModule {
|
|
||||||
return &Module{
|
return &Module{
|
||||||
processor: processor,
|
processor: processor,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// Route attaches all routes from this module to the given router
|
func (m *Module) Route(attachHandler func(method string, path string, f ...gin.HandlerFunc) gin.IRoutes) {
|
||||||
func (m *Module) Route(r router.Router) error {
|
attachHandler(http.MethodGet, EmojiWithIDPath, m.EmojiGetHandler)
|
||||||
r.AttachHandler(http.MethodGet, BasePath, m.EmojisGETHandler)
|
|
||||||
return nil
|
|
||||||
}
|
}
|
|
@ -19,54 +19,39 @@
|
||||||
package emoji
|
package emoji
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"context"
|
|
||||||
"encoding/json"
|
"encoding/json"
|
||||||
"errors"
|
"errors"
|
||||||
"net/http"
|
"net/http"
|
||||||
"strings"
|
"strings"
|
||||||
|
|
||||||
"github.com/gin-gonic/gin"
|
"github.com/gin-gonic/gin"
|
||||||
"github.com/superseriousbusiness/gotosocial/internal/ap"
|
apiutil "github.com/superseriousbusiness/gotosocial/internal/api/util"
|
||||||
"github.com/superseriousbusiness/gotosocial/internal/api"
|
|
||||||
"github.com/superseriousbusiness/gotosocial/internal/gtserror"
|
"github.com/superseriousbusiness/gotosocial/internal/gtserror"
|
||||||
)
|
)
|
||||||
|
|
||||||
// EmojiGetHandler
|
|
||||||
func (m *Module) EmojiGetHandler(c *gin.Context) {
|
func (m *Module) EmojiGetHandler(c *gin.Context) {
|
||||||
// usernames on our instance are always lowercase
|
|
||||||
requestedEmojiID := strings.ToUpper(c.Param(EmojiIDKey))
|
requestedEmojiID := strings.ToUpper(c.Param(EmojiIDKey))
|
||||||
if requestedEmojiID == "" {
|
if requestedEmojiID == "" {
|
||||||
err := errors.New("no emoji id specified in request")
|
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
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
format, err := api.NegotiateAccept(c, api.ActivityPubAcceptHeaders...)
|
format, err := apiutil.NegotiateAccept(c, apiutil.ActivityPubAcceptHeaders...)
|
||||||
if err != nil {
|
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
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
ctx := c.Request.Context()
|
resp, errWithCode := m.processor.GetFediEmoji(apiutil.TransferSignatureContext(c), requestedEmojiID, c.Request.URL)
|
||||||
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)
|
|
||||||
if errWithCode != nil {
|
if errWithCode != nil {
|
||||||
api.ErrorHandler(c, errWithCode, m.processor.InstanceGet)
|
apiutil.ErrorHandler(c, errWithCode, m.processor.InstanceGet)
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
b, err := json.Marshal(resp)
|
b, err := json.Marshal(resp)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
api.ErrorHandler(c, gtserror.NewErrorInternalError(err), m.processor.InstanceGet)
|
apiutil.ErrorHandler(c, gtserror.NewErrorInternalError(err), m.processor.InstanceGet)
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
|
@ -26,8 +26,7 @@ import (
|
||||||
|
|
||||||
"github.com/gin-gonic/gin"
|
"github.com/gin-gonic/gin"
|
||||||
"github.com/stretchr/testify/suite"
|
"github.com/stretchr/testify/suite"
|
||||||
"github.com/superseriousbusiness/gotosocial/internal/api/s2s/emoji"
|
"github.com/superseriousbusiness/gotosocial/internal/api/activitypub/emoji"
|
||||||
"github.com/superseriousbusiness/gotosocial/internal/api/security"
|
|
||||||
"github.com/superseriousbusiness/gotosocial/internal/concurrency"
|
"github.com/superseriousbusiness/gotosocial/internal/concurrency"
|
||||||
"github.com/superseriousbusiness/gotosocial/internal/db"
|
"github.com/superseriousbusiness/gotosocial/internal/db"
|
||||||
"github.com/superseriousbusiness/gotosocial/internal/email"
|
"github.com/superseriousbusiness/gotosocial/internal/email"
|
||||||
|
@ -35,7 +34,7 @@ import (
|
||||||
"github.com/superseriousbusiness/gotosocial/internal/gtsmodel"
|
"github.com/superseriousbusiness/gotosocial/internal/gtsmodel"
|
||||||
"github.com/superseriousbusiness/gotosocial/internal/media"
|
"github.com/superseriousbusiness/gotosocial/internal/media"
|
||||||
"github.com/superseriousbusiness/gotosocial/internal/messages"
|
"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/processing"
|
||||||
"github.com/superseriousbusiness/gotosocial/internal/storage"
|
"github.com/superseriousbusiness/gotosocial/internal/storage"
|
||||||
"github.com/superseriousbusiness/gotosocial/internal/typeutils"
|
"github.com/superseriousbusiness/gotosocial/internal/typeutils"
|
||||||
|
@ -51,13 +50,13 @@ type EmojiGetTestSuite struct {
|
||||||
emailSender email.Sender
|
emailSender email.Sender
|
||||||
processor processing.Processor
|
processor processing.Processor
|
||||||
storage *storage.Driver
|
storage *storage.Driver
|
||||||
oauthServer oauth.Server
|
|
||||||
securityModule *security.Module
|
|
||||||
|
|
||||||
testEmojis map[string]*gtsmodel.Emoji
|
testEmojis map[string]*gtsmodel.Emoji
|
||||||
testAccounts map[string]*gtsmodel.Account
|
testAccounts map[string]*gtsmodel.Account
|
||||||
|
|
||||||
emojiModule *emoji.Module
|
emojiModule *emoji.Module
|
||||||
|
|
||||||
|
signatureCheck gin.HandlerFunc
|
||||||
}
|
}
|
||||||
|
|
||||||
func (suite *EmojiGetTestSuite) SetupSuite() {
|
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.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.emailSender = testrig.NewEmailSender("../../../../web/template/", nil)
|
||||||
suite.processor = testrig.NewTestProcessor(suite.db, suite.storage, suite.federator, suite.emailSender, suite.mediaManager, clientWorker, fedWorker)
|
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.emojiModule = emoji.New(suite.processor)
|
||||||
suite.oauthServer = testrig.NewTestOauthServer(suite.db)
|
|
||||||
suite.securityModule = security.New(suite.db, suite.oauthServer).(*security.Module)
|
|
||||||
testrig.StandardDBSetup(suite.db, suite.testAccounts)
|
testrig.StandardDBSetup(suite.db, suite.testAccounts)
|
||||||
testrig.StandardStorageSetup(suite.storage, "../../../../testrig/media")
|
testrig.StandardStorageSetup(suite.storage, "../../../../testrig/media")
|
||||||
|
|
||||||
|
suite.signatureCheck = middleware.SignatureCheck(suite.db.IsURIBlocked)
|
||||||
|
|
||||||
suite.NoError(suite.processor.Start())
|
suite.NoError(suite.processor.Start())
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -108,7 +107,7 @@ func (suite *EmojiGetTestSuite) TestGetEmoji() {
|
||||||
ctx.Request.Header.Set("Date", signedRequest.DateHeader)
|
ctx.Request.Header.Set("Date", signedRequest.DateHeader)
|
||||||
|
|
||||||
// we need to pass the context through signature check first to set appropriate values on it
|
// 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,
|
// normally the router would populate these params from the path values,
|
||||||
// but because we're calling the function directly, we need to set them manually.
|
// but because we're calling the function directly, we need to set them manually.
|
|
@ -16,31 +16,7 @@
|
||||||
along with this program. If not, see <http://www.gnu.org/licenses/>.
|
along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||||
*/
|
*/
|
||||||
|
|
||||||
package user
|
package users
|
||||||
|
|
||||||
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
|
|
||||||
}
|
|
||||||
|
|
||||||
// SwaggerCollection represents an activitypub collection.
|
// SwaggerCollection represents an activitypub collection.
|
||||||
// swagger:model swaggerCollection
|
// swagger:model swaggerCollection
|
|
@ -16,7 +16,7 @@
|
||||||
along with this program. If not, see <http://www.gnu.org/licenses/>.
|
along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||||
*/
|
*/
|
||||||
|
|
||||||
package user
|
package users
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"encoding/json"
|
"encoding/json"
|
||||||
|
@ -25,7 +25,7 @@ import (
|
||||||
"strings"
|
"strings"
|
||||||
|
|
||||||
"github.com/gin-gonic/gin"
|
"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/gtserror"
|
||||||
)
|
)
|
||||||
|
|
||||||
|
@ -35,31 +35,31 @@ func (m *Module) FollowersGETHandler(c *gin.Context) {
|
||||||
requestedUsername := strings.ToLower(c.Param(UsernameKey))
|
requestedUsername := strings.ToLower(c.Param(UsernameKey))
|
||||||
if requestedUsername == "" {
|
if requestedUsername == "" {
|
||||||
err := errors.New("no username specified in request")
|
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
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
format, err := api.NegotiateAccept(c, api.HTMLOrActivityPubHeaders...)
|
format, err := apiutil.NegotiateAccept(c, apiutil.HTMLOrActivityPubHeaders...)
|
||||||
if err != nil {
|
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
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
if format == string(api.TextHTML) {
|
if format == string(apiutil.TextHTML) {
|
||||||
// redirect to the user's profile
|
// redirect to the user's profile
|
||||||
c.Redirect(http.StatusSeeOther, "/@"+requestedUsername)
|
c.Redirect(http.StatusSeeOther, "/@"+requestedUsername)
|
||||||
return
|
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 {
|
if errWithCode != nil {
|
||||||
api.ErrorHandler(c, errWithCode, m.processor.InstanceGet)
|
apiutil.ErrorHandler(c, errWithCode, m.processor.InstanceGet)
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
b, err := json.Marshal(resp)
|
b, err := json.Marshal(resp)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
api.ErrorHandler(c, gtserror.NewErrorInternalError(err), m.processor.InstanceGet)
|
apiutil.ErrorHandler(c, gtserror.NewErrorInternalError(err), m.processor.InstanceGet)
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
|
@ -16,7 +16,7 @@
|
||||||
along with this program. If not, see <http://www.gnu.org/licenses/>.
|
along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||||
*/
|
*/
|
||||||
|
|
||||||
package user
|
package users
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"encoding/json"
|
"encoding/json"
|
||||||
|
@ -25,7 +25,7 @@ import (
|
||||||
"strings"
|
"strings"
|
||||||
|
|
||||||
"github.com/gin-gonic/gin"
|
"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/gtserror"
|
||||||
)
|
)
|
||||||
|
|
||||||
|
@ -35,31 +35,31 @@ func (m *Module) FollowingGETHandler(c *gin.Context) {
|
||||||
requestedUsername := strings.ToLower(c.Param(UsernameKey))
|
requestedUsername := strings.ToLower(c.Param(UsernameKey))
|
||||||
if requestedUsername == "" {
|
if requestedUsername == "" {
|
||||||
err := errors.New("no username specified in request")
|
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
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
format, err := api.NegotiateAccept(c, api.HTMLOrActivityPubHeaders...)
|
format, err := apiutil.NegotiateAccept(c, apiutil.HTMLOrActivityPubHeaders...)
|
||||||
if err != nil {
|
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
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
if format == string(api.TextHTML) {
|
if format == string(apiutil.TextHTML) {
|
||||||
// redirect to the user's profile
|
// redirect to the user's profile
|
||||||
c.Redirect(http.StatusSeeOther, "/@"+requestedUsername)
|
c.Redirect(http.StatusSeeOther, "/@"+requestedUsername)
|
||||||
return
|
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 {
|
if errWithCode != nil {
|
||||||
api.ErrorHandler(c, errWithCode, m.processor.InstanceGet)
|
apiutil.ErrorHandler(c, errWithCode, m.processor.InstanceGet)
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
b, err := json.Marshal(resp)
|
b, err := json.Marshal(resp)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
api.ErrorHandler(c, gtserror.NewErrorInternalError(err), m.processor.InstanceGet)
|
apiutil.ErrorHandler(c, gtserror.NewErrorInternalError(err), m.processor.InstanceGet)
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
|
@ -16,14 +16,14 @@
|
||||||
along with this program. If not, see <http://www.gnu.org/licenses/>.
|
along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||||
*/
|
*/
|
||||||
|
|
||||||
package user
|
package users
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"errors"
|
"errors"
|
||||||
"strings"
|
"strings"
|
||||||
|
|
||||||
"github.com/gin-gonic/gin"
|
"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
|
"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))
|
requestedUsername := strings.ToLower(c.Param(UsernameKey))
|
||||||
if requestedUsername == "" {
|
if requestedUsername == "" {
|
||||||
err := errors.New("no username specified in request")
|
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
|
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 {
|
if withCode, ok := err.(gtserror.WithCode); ok {
|
||||||
api.ErrorHandler(c, withCode, m.processor.InstanceGet)
|
apiutil.ErrorHandler(c, withCode, m.processor.InstanceGet)
|
||||||
} else {
|
} 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 {
|
} else if !posted {
|
||||||
err := errors.New("unable to process request")
|
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/>.
|
along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||||
*/
|
*/
|
||||||
|
|
||||||
package user_test
|
package users_test
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"bytes"
|
"bytes"
|
||||||
|
@ -32,7 +32,7 @@ import (
|
||||||
"github.com/stretchr/testify/suite"
|
"github.com/stretchr/testify/suite"
|
||||||
"github.com/superseriousbusiness/activity/pub"
|
"github.com/superseriousbusiness/activity/pub"
|
||||||
"github.com/superseriousbusiness/activity/streams"
|
"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/concurrency"
|
||||||
"github.com/superseriousbusiness/gotosocial/internal/db"
|
"github.com/superseriousbusiness/gotosocial/internal/db"
|
||||||
"github.com/superseriousbusiness/gotosocial/internal/gtsmodel"
|
"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)
|
federator := testrig.NewTestFederator(suite.db, tc, suite.storage, suite.mediaManager, fedWorker)
|
||||||
emailSender := testrig.NewEmailSender("../../../../web/template/", nil)
|
emailSender := testrig.NewEmailSender("../../../../web/template/", nil)
|
||||||
processor := testrig.NewTestProcessor(suite.db, suite.storage, federator, emailSender, suite.mediaManager, clientWorker, fedWorker)
|
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())
|
suite.NoError(processor.Start())
|
||||||
|
|
||||||
// setup request
|
// setup request
|
||||||
|
@ -105,13 +105,13 @@ func (suite *InboxPostTestSuite) TestPostBlock() {
|
||||||
ctx.Request.Header.Set("Content-Type", "application/activity+json")
|
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
|
// 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,
|
// normally the router would populate these params from the path values,
|
||||||
// but because we're calling the function directly, we need to set them manually.
|
// but because we're calling the function directly, we need to set them manually.
|
||||||
ctx.Params = gin.Params{
|
ctx.Params = gin.Params{
|
||||||
gin.Param{
|
gin.Param{
|
||||||
Key: user.UsernameKey,
|
Key: users.UsernameKey,
|
||||||
Value: blockedAccount.Username,
|
Value: blockedAccount.Username,
|
||||||
},
|
},
|
||||||
}
|
}
|
||||||
|
@ -196,7 +196,7 @@ func (suite *InboxPostTestSuite) TestPostUnblock() {
|
||||||
federator := testrig.NewTestFederator(suite.db, tc, suite.storage, suite.mediaManager, fedWorker)
|
federator := testrig.NewTestFederator(suite.db, tc, suite.storage, suite.mediaManager, fedWorker)
|
||||||
emailSender := testrig.NewEmailSender("../../../../web/template/", nil)
|
emailSender := testrig.NewEmailSender("../../../../web/template/", nil)
|
||||||
processor := testrig.NewTestProcessor(suite.db, suite.storage, federator, emailSender, suite.mediaManager, clientWorker, fedWorker)
|
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())
|
suite.NoError(processor.Start())
|
||||||
|
|
||||||
// setup request
|
// setup request
|
||||||
|
@ -209,13 +209,13 @@ func (suite *InboxPostTestSuite) TestPostUnblock() {
|
||||||
ctx.Request.Header.Set("Content-Type", "application/activity+json")
|
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
|
// 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,
|
// normally the router would populate these params from the path values,
|
||||||
// but because we're calling the function directly, we need to set them manually.
|
// but because we're calling the function directly, we need to set them manually.
|
||||||
ctx.Params = gin.Params{
|
ctx.Params = gin.Params{
|
||||||
gin.Param{
|
gin.Param{
|
||||||
Key: user.UsernameKey,
|
Key: users.UsernameKey,
|
||||||
Value: blockedAccount.Username,
|
Value: blockedAccount.Username,
|
||||||
},
|
},
|
||||||
}
|
}
|
||||||
|
@ -298,7 +298,7 @@ func (suite *InboxPostTestSuite) TestPostUpdate() {
|
||||||
federator := testrig.NewTestFederator(suite.db, tc, suite.storage, suite.mediaManager, fedWorker)
|
federator := testrig.NewTestFederator(suite.db, tc, suite.storage, suite.mediaManager, fedWorker)
|
||||||
emailSender := testrig.NewEmailSender("../../../../web/template/", nil)
|
emailSender := testrig.NewEmailSender("../../../../web/template/", nil)
|
||||||
processor := testrig.NewTestProcessor(suite.db, suite.storage, federator, emailSender, suite.mediaManager, clientWorker, fedWorker)
|
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())
|
suite.NoError(processor.Start())
|
||||||
|
|
||||||
// setup request
|
// setup request
|
||||||
|
@ -311,13 +311,13 @@ func (suite *InboxPostTestSuite) TestPostUpdate() {
|
||||||
ctx.Request.Header.Set("Content-Type", "application/activity+json")
|
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
|
// 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,
|
// normally the router would populate these params from the path values,
|
||||||
// but because we're calling the function directly, we need to set them manually.
|
// but because we're calling the function directly, we need to set them manually.
|
||||||
ctx.Params = gin.Params{
|
ctx.Params = gin.Params{
|
||||||
gin.Param{
|
gin.Param{
|
||||||
Key: user.UsernameKey,
|
Key: users.UsernameKey,
|
||||||
Value: receivingAccount.Username,
|
Value: receivingAccount.Username,
|
||||||
},
|
},
|
||||||
}
|
}
|
||||||
|
@ -431,7 +431,7 @@ func (suite *InboxPostTestSuite) TestPostDelete() {
|
||||||
emailSender := testrig.NewEmailSender("../../../../web/template/", nil)
|
emailSender := testrig.NewEmailSender("../../../../web/template/", nil)
|
||||||
processor := testrig.NewTestProcessor(suite.db, suite.storage, federator, emailSender, suite.mediaManager, clientWorker, fedWorker)
|
processor := testrig.NewTestProcessor(suite.db, suite.storage, federator, emailSender, suite.mediaManager, clientWorker, fedWorker)
|
||||||
suite.NoError(processor.Start())
|
suite.NoError(processor.Start())
|
||||||
userModule := user.New(processor).(*user.Module)
|
userModule := users.New(processor)
|
||||||
|
|
||||||
// setup request
|
// setup request
|
||||||
recorder := httptest.NewRecorder()
|
recorder := httptest.NewRecorder()
|
||||||
|
@ -443,13 +443,13 @@ func (suite *InboxPostTestSuite) TestPostDelete() {
|
||||||
ctx.Request.Header.Set("Content-Type", "application/activity+json")
|
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
|
// 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,
|
// normally the router would populate these params from the path values,
|
||||||
// but because we're calling the function directly, we need to set them manually.
|
// but because we're calling the function directly, we need to set them manually.
|
||||||
ctx.Params = gin.Params{
|
ctx.Params = gin.Params{
|
||||||
gin.Param{
|
gin.Param{
|
||||||
Key: user.UsernameKey,
|
Key: users.UsernameKey,
|
||||||
Value: receivingAccount.Username,
|
Value: receivingAccount.Username,
|
||||||
},
|
},
|
||||||
}
|
}
|
|
@ -16,7 +16,7 @@
|
||||||
along with this program. If not, see <http://www.gnu.org/licenses/>.
|
along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||||
*/
|
*/
|
||||||
|
|
||||||
package user
|
package users
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"encoding/json"
|
"encoding/json"
|
||||||
|
@ -27,7 +27,7 @@ import (
|
||||||
"strings"
|
"strings"
|
||||||
|
|
||||||
"github.com/gin-gonic/gin"
|
"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/gtserror"
|
||||||
)
|
)
|
||||||
|
|
||||||
|
@ -90,17 +90,17 @@ func (m *Module) OutboxGETHandler(c *gin.Context) {
|
||||||
requestedUsername := strings.ToLower(c.Param(UsernameKey))
|
requestedUsername := strings.ToLower(c.Param(UsernameKey))
|
||||||
if requestedUsername == "" {
|
if requestedUsername == "" {
|
||||||
err := errors.New("no username specified in request")
|
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
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
format, err := api.NegotiateAccept(c, api.HTMLOrActivityPubHeaders...)
|
format, err := apiutil.NegotiateAccept(c, apiutil.HTMLOrActivityPubHeaders...)
|
||||||
if err != nil {
|
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
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
if format == string(api.TextHTML) {
|
if format == string(apiutil.TextHTML) {
|
||||||
// redirect to the user's profile
|
// redirect to the user's profile
|
||||||
c.Redirect(http.StatusSeeOther, "/@"+requestedUsername)
|
c.Redirect(http.StatusSeeOther, "/@"+requestedUsername)
|
||||||
return
|
return
|
||||||
|
@ -111,7 +111,7 @@ func (m *Module) OutboxGETHandler(c *gin.Context) {
|
||||||
i, err := strconv.ParseBool(pageString)
|
i, err := strconv.ParseBool(pageString)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
err := fmt.Errorf("error parsing %s: %s", PageKey, err)
|
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
|
return
|
||||||
}
|
}
|
||||||
page = i
|
page = i
|
||||||
|
@ -129,15 +129,15 @@ func (m *Module) OutboxGETHandler(c *gin.Context) {
|
||||||
maxID = maxIDString
|
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 {
|
if errWithCode != nil {
|
||||||
api.ErrorHandler(c, errWithCode, m.processor.InstanceGet)
|
apiutil.ErrorHandler(c, errWithCode, m.processor.InstanceGet)
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
b, err := json.Marshal(resp)
|
b, err := json.Marshal(resp)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
api.ErrorHandler(c, gtserror.NewErrorInternalError(err), m.processor.InstanceGet)
|
apiutil.ErrorHandler(c, gtserror.NewErrorInternalError(err), m.processor.InstanceGet)
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
|
@ -16,7 +16,7 @@
|
||||||
along with this program. If not, see <http://www.gnu.org/licenses/>.
|
along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||||
*/
|
*/
|
||||||
|
|
||||||
package user_test
|
package users_test
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"context"
|
"context"
|
||||||
|
@ -30,7 +30,7 @@ import (
|
||||||
"github.com/stretchr/testify/suite"
|
"github.com/stretchr/testify/suite"
|
||||||
"github.com/superseriousbusiness/activity/streams"
|
"github.com/superseriousbusiness/activity/streams"
|
||||||
"github.com/superseriousbusiness/activity/streams/vocab"
|
"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/concurrency"
|
||||||
"github.com/superseriousbusiness/gotosocial/internal/messages"
|
"github.com/superseriousbusiness/gotosocial/internal/messages"
|
||||||
"github.com/superseriousbusiness/gotosocial/testrig"
|
"github.com/superseriousbusiness/gotosocial/testrig"
|
||||||
|
@ -55,13 +55,13 @@ func (suite *OutboxGetTestSuite) TestGetOutbox() {
|
||||||
ctx.Request.Header.Set("Date", signedRequest.DateHeader)
|
ctx.Request.Header.Set("Date", signedRequest.DateHeader)
|
||||||
|
|
||||||
// we need to pass the context through signature check first to set appropriate values on it
|
// 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,
|
// normally the router would populate these params from the path values,
|
||||||
// but because we're calling the function directly, we need to set them manually.
|
// but because we're calling the function directly, we need to set them manually.
|
||||||
ctx.Params = gin.Params{
|
ctx.Params = gin.Params{
|
||||||
gin.Param{
|
gin.Param{
|
||||||
Key: user.UsernameKey,
|
Key: users.UsernameKey,
|
||||||
Value: targetAccount.Username,
|
Value: targetAccount.Username,
|
||||||
},
|
},
|
||||||
}
|
}
|
||||||
|
@ -102,7 +102,7 @@ func (suite *OutboxGetTestSuite) TestGetOutboxFirstPage() {
|
||||||
federator := testrig.NewTestFederator(suite.db, tc, suite.storage, suite.mediaManager, fedWorker)
|
federator := testrig.NewTestFederator(suite.db, tc, suite.storage, suite.mediaManager, fedWorker)
|
||||||
emailSender := testrig.NewEmailSender("../../../../web/template/", nil)
|
emailSender := testrig.NewEmailSender("../../../../web/template/", nil)
|
||||||
processor := testrig.NewTestProcessor(suite.db, suite.storage, federator, emailSender, suite.mediaManager, clientWorker, fedWorker)
|
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())
|
suite.NoError(processor.Start())
|
||||||
|
|
||||||
// setup request
|
// setup request
|
||||||
|
@ -114,13 +114,13 @@ func (suite *OutboxGetTestSuite) TestGetOutboxFirstPage() {
|
||||||
ctx.Request.Header.Set("Date", signedRequest.DateHeader)
|
ctx.Request.Header.Set("Date", signedRequest.DateHeader)
|
||||||
|
|
||||||
// we need to pass the context through signature check first to set appropriate values on it
|
// 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,
|
// normally the router would populate these params from the path values,
|
||||||
// but because we're calling the function directly, we need to set them manually.
|
// but because we're calling the function directly, we need to set them manually.
|
||||||
ctx.Params = gin.Params{
|
ctx.Params = gin.Params{
|
||||||
gin.Param{
|
gin.Param{
|
||||||
Key: user.UsernameKey,
|
Key: users.UsernameKey,
|
||||||
Value: targetAccount.Username,
|
Value: targetAccount.Username,
|
||||||
},
|
},
|
||||||
}
|
}
|
||||||
|
@ -161,7 +161,7 @@ func (suite *OutboxGetTestSuite) TestGetOutboxNextPage() {
|
||||||
federator := testrig.NewTestFederator(suite.db, tc, suite.storage, suite.mediaManager, fedWorker)
|
federator := testrig.NewTestFederator(suite.db, tc, suite.storage, suite.mediaManager, fedWorker)
|
||||||
emailSender := testrig.NewEmailSender("../../../../web/template/", nil)
|
emailSender := testrig.NewEmailSender("../../../../web/template/", nil)
|
||||||
processor := testrig.NewTestProcessor(suite.db, suite.storage, federator, emailSender, suite.mediaManager, clientWorker, fedWorker)
|
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())
|
suite.NoError(processor.Start())
|
||||||
|
|
||||||
// setup request
|
// setup request
|
||||||
|
@ -173,17 +173,17 @@ func (suite *OutboxGetTestSuite) TestGetOutboxNextPage() {
|
||||||
ctx.Request.Header.Set("Date", signedRequest.DateHeader)
|
ctx.Request.Header.Set("Date", signedRequest.DateHeader)
|
||||||
|
|
||||||
// we need to pass the context through signature check first to set appropriate values on it
|
// 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,
|
// normally the router would populate these params from the path values,
|
||||||
// but because we're calling the function directly, we need to set them manually.
|
// but because we're calling the function directly, we need to set them manually.
|
||||||
ctx.Params = gin.Params{
|
ctx.Params = gin.Params{
|
||||||
gin.Param{
|
gin.Param{
|
||||||
Key: user.UsernameKey,
|
Key: users.UsernameKey,
|
||||||
Value: targetAccount.Username,
|
Value: targetAccount.Username,
|
||||||
},
|
},
|
||||||
gin.Param{
|
gin.Param{
|
||||||
Key: user.MaxIDKey,
|
Key: users.MaxIDKey,
|
||||||
Value: "01F8MHAMCHF6Y650WCRSCP4WMY",
|
Value: "01F8MHAMCHF6Y650WCRSCP4WMY",
|
||||||
},
|
},
|
||||||
}
|
}
|
|
@ -16,7 +16,7 @@
|
||||||
along with this program. If not, see <http://www.gnu.org/licenses/>.
|
along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||||
*/
|
*/
|
||||||
|
|
||||||
package user
|
package users
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"encoding/json"
|
"encoding/json"
|
||||||
|
@ -25,7 +25,7 @@ import (
|
||||||
"strings"
|
"strings"
|
||||||
|
|
||||||
"github.com/gin-gonic/gin"
|
"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/gtserror"
|
||||||
)
|
)
|
||||||
|
|
||||||
|
@ -39,31 +39,31 @@ func (m *Module) PublicKeyGETHandler(c *gin.Context) {
|
||||||
requestedUsername := strings.ToLower(c.Param(UsernameKey))
|
requestedUsername := strings.ToLower(c.Param(UsernameKey))
|
||||||
if requestedUsername == "" {
|
if requestedUsername == "" {
|
||||||
err := errors.New("no username specified in request")
|
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
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
format, err := api.NegotiateAccept(c, api.HTMLOrActivityPubHeaders...)
|
format, err := apiutil.NegotiateAccept(c, apiutil.HTMLOrActivityPubHeaders...)
|
||||||
if err != nil {
|
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
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
if format == string(api.TextHTML) {
|
if format == string(apiutil.TextHTML) {
|
||||||
// redirect to the user's profile
|
// redirect to the user's profile
|
||||||
c.Redirect(http.StatusSeeOther, "/@"+requestedUsername)
|
c.Redirect(http.StatusSeeOther, "/@"+requestedUsername)
|
||||||
return
|
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 {
|
if errWithCode != nil {
|
||||||
api.ErrorHandler(c, errWithCode, m.processor.InstanceGet)
|
apiutil.ErrorHandler(c, errWithCode, m.processor.InstanceGet)
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
b, err := json.Marshal(resp)
|
b, err := json.Marshal(resp)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
api.ErrorHandler(c, gtserror.NewErrorInternalError(err), m.processor.InstanceGet)
|
apiutil.ErrorHandler(c, gtserror.NewErrorInternalError(err), m.processor.InstanceGet)
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
|
@ -16,7 +16,7 @@
|
||||||
along with this program. If not, see <http://www.gnu.org/licenses/>.
|
along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||||
*/
|
*/
|
||||||
|
|
||||||
package user
|
package users
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"encoding/json"
|
"encoding/json"
|
||||||
|
@ -27,7 +27,7 @@ import (
|
||||||
"strings"
|
"strings"
|
||||||
|
|
||||||
"github.com/gin-gonic/gin"
|
"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/gtserror"
|
||||||
)
|
)
|
||||||
|
|
||||||
|
@ -97,7 +97,7 @@ func (m *Module) StatusRepliesGETHandler(c *gin.Context) {
|
||||||
requestedUsername := strings.ToLower(c.Param(UsernameKey))
|
requestedUsername := strings.ToLower(c.Param(UsernameKey))
|
||||||
if requestedUsername == "" {
|
if requestedUsername == "" {
|
||||||
err := errors.New("no username specified in request")
|
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
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -105,17 +105,17 @@ func (m *Module) StatusRepliesGETHandler(c *gin.Context) {
|
||||||
requestedStatusID := strings.ToUpper(c.Param(StatusIDKey))
|
requestedStatusID := strings.ToUpper(c.Param(StatusIDKey))
|
||||||
if requestedStatusID == "" {
|
if requestedStatusID == "" {
|
||||||
err := errors.New("no status id specified in request")
|
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
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
format, err := api.NegotiateAccept(c, api.HTMLOrActivityPubHeaders...)
|
format, err := apiutil.NegotiateAccept(c, apiutil.HTMLOrActivityPubHeaders...)
|
||||||
if err != nil {
|
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
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
if format == string(api.TextHTML) {
|
if format == string(apiutil.TextHTML) {
|
||||||
// redirect to the status
|
// redirect to the status
|
||||||
c.Redirect(http.StatusSeeOther, "/@"+requestedUsername+"/statuses/"+requestedStatusID)
|
c.Redirect(http.StatusSeeOther, "/@"+requestedUsername+"/statuses/"+requestedStatusID)
|
||||||
return
|
return
|
||||||
|
@ -126,7 +126,7 @@ func (m *Module) StatusRepliesGETHandler(c *gin.Context) {
|
||||||
i, err := strconv.ParseBool(pageString)
|
i, err := strconv.ParseBool(pageString)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
err := fmt.Errorf("error parsing %s: %s", PageKey, err)
|
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
|
return
|
||||||
}
|
}
|
||||||
page = i
|
page = i
|
||||||
|
@ -138,7 +138,7 @@ func (m *Module) StatusRepliesGETHandler(c *gin.Context) {
|
||||||
i, err := strconv.ParseBool(onlyOtherAccountsString)
|
i, err := strconv.ParseBool(onlyOtherAccountsString)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
err := fmt.Errorf("error parsing %s: %s", OnlyOtherAccountsKey, err)
|
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
|
return
|
||||||
}
|
}
|
||||||
onlyOtherAccounts = i
|
onlyOtherAccounts = i
|
||||||
|
@ -150,15 +150,15 @@ func (m *Module) StatusRepliesGETHandler(c *gin.Context) {
|
||||||
minID = minIDString
|
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 {
|
if errWithCode != nil {
|
||||||
api.ErrorHandler(c, errWithCode, m.processor.InstanceGet)
|
apiutil.ErrorHandler(c, errWithCode, m.processor.InstanceGet)
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
b, err := json.Marshal(resp)
|
b, err := json.Marshal(resp)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
api.ErrorHandler(c, gtserror.NewErrorInternalError(err), m.processor.InstanceGet)
|
apiutil.ErrorHandler(c, gtserror.NewErrorInternalError(err), m.processor.InstanceGet)
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
|
@ -16,7 +16,7 @@
|
||||||
along with this program. If not, see <http://www.gnu.org/licenses/>.
|
along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||||
*/
|
*/
|
||||||
|
|
||||||
package user_test
|
package users_test
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"context"
|
"context"
|
||||||
|
@ -32,7 +32,7 @@ import (
|
||||||
"github.com/stretchr/testify/suite"
|
"github.com/stretchr/testify/suite"
|
||||||
"github.com/superseriousbusiness/activity/streams"
|
"github.com/superseriousbusiness/activity/streams"
|
||||||
"github.com/superseriousbusiness/activity/streams/vocab"
|
"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/concurrency"
|
||||||
"github.com/superseriousbusiness/gotosocial/internal/messages"
|
"github.com/superseriousbusiness/gotosocial/internal/messages"
|
||||||
"github.com/superseriousbusiness/gotosocial/testrig"
|
"github.com/superseriousbusiness/gotosocial/testrig"
|
||||||
|
@ -58,17 +58,17 @@ func (suite *RepliesGetTestSuite) TestGetReplies() {
|
||||||
ctx.Request.Header.Set("Date", signedRequest.DateHeader)
|
ctx.Request.Header.Set("Date", signedRequest.DateHeader)
|
||||||
|
|
||||||
// we need to pass the context through signature check first to set appropriate values on it
|
// 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,
|
// normally the router would populate these params from the path values,
|
||||||
// but because we're calling the function directly, we need to set them manually.
|
// but because we're calling the function directly, we need to set them manually.
|
||||||
ctx.Params = gin.Params{
|
ctx.Params = gin.Params{
|
||||||
gin.Param{
|
gin.Param{
|
||||||
Key: user.UsernameKey,
|
Key: users.UsernameKey,
|
||||||
Value: targetAccount.Username,
|
Value: targetAccount.Username,
|
||||||
},
|
},
|
||||||
gin.Param{
|
gin.Param{
|
||||||
Key: user.StatusIDKey,
|
Key: users.StatusIDKey,
|
||||||
Value: targetStatus.ID,
|
Value: targetStatus.ID,
|
||||||
},
|
},
|
||||||
}
|
}
|
||||||
|
@ -111,7 +111,7 @@ func (suite *RepliesGetTestSuite) TestGetRepliesNext() {
|
||||||
federator := testrig.NewTestFederator(suite.db, tc, suite.storage, suite.mediaManager, fedWorker)
|
federator := testrig.NewTestFederator(suite.db, tc, suite.storage, suite.mediaManager, fedWorker)
|
||||||
emailSender := testrig.NewEmailSender("../../../../web/template/", nil)
|
emailSender := testrig.NewEmailSender("../../../../web/template/", nil)
|
||||||
processor := testrig.NewTestProcessor(suite.db, suite.storage, federator, emailSender, suite.mediaManager, clientWorker, fedWorker)
|
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())
|
suite.NoError(processor.Start())
|
||||||
|
|
||||||
// setup request
|
// setup request
|
||||||
|
@ -123,17 +123,17 @@ func (suite *RepliesGetTestSuite) TestGetRepliesNext() {
|
||||||
ctx.Request.Header.Set("Date", signedRequest.DateHeader)
|
ctx.Request.Header.Set("Date", signedRequest.DateHeader)
|
||||||
|
|
||||||
// we need to pass the context through signature check first to set appropriate values on it
|
// 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,
|
// normally the router would populate these params from the path values,
|
||||||
// but because we're calling the function directly, we need to set them manually.
|
// but because we're calling the function directly, we need to set them manually.
|
||||||
ctx.Params = gin.Params{
|
ctx.Params = gin.Params{
|
||||||
gin.Param{
|
gin.Param{
|
||||||
Key: user.UsernameKey,
|
Key: users.UsernameKey,
|
||||||
Value: targetAccount.Username,
|
Value: targetAccount.Username,
|
||||||
},
|
},
|
||||||
gin.Param{
|
gin.Param{
|
||||||
Key: user.StatusIDKey,
|
Key: users.StatusIDKey,
|
||||||
Value: targetStatus.ID,
|
Value: targetStatus.ID,
|
||||||
},
|
},
|
||||||
}
|
}
|
||||||
|
@ -179,7 +179,7 @@ func (suite *RepliesGetTestSuite) TestGetRepliesLast() {
|
||||||
federator := testrig.NewTestFederator(suite.db, tc, suite.storage, suite.mediaManager, fedWorker)
|
federator := testrig.NewTestFederator(suite.db, tc, suite.storage, suite.mediaManager, fedWorker)
|
||||||
emailSender := testrig.NewEmailSender("../../../../web/template/", nil)
|
emailSender := testrig.NewEmailSender("../../../../web/template/", nil)
|
||||||
processor := testrig.NewTestProcessor(suite.db, suite.storage, federator, emailSender, suite.mediaManager, clientWorker, fedWorker)
|
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())
|
suite.NoError(processor.Start())
|
||||||
|
|
||||||
// setup request
|
// setup request
|
||||||
|
@ -191,17 +191,17 @@ func (suite *RepliesGetTestSuite) TestGetRepliesLast() {
|
||||||
ctx.Request.Header.Set("Date", signedRequest.DateHeader)
|
ctx.Request.Header.Set("Date", signedRequest.DateHeader)
|
||||||
|
|
||||||
// we need to pass the context through signature check first to set appropriate values on it
|
// 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,
|
// normally the router would populate these params from the path values,
|
||||||
// but because we're calling the function directly, we need to set them manually.
|
// but because we're calling the function directly, we need to set them manually.
|
||||||
ctx.Params = gin.Params{
|
ctx.Params = gin.Params{
|
||||||
gin.Param{
|
gin.Param{
|
||||||
Key: user.UsernameKey,
|
Key: users.UsernameKey,
|
||||||
Value: targetAccount.Username,
|
Value: targetAccount.Username,
|
||||||
},
|
},
|
||||||
gin.Param{
|
gin.Param{
|
||||||
Key: user.StatusIDKey,
|
Key: users.StatusIDKey,
|
||||||
Value: targetStatus.ID,
|
Value: targetStatus.ID,
|
||||||
},
|
},
|
||||||
}
|
}
|
|
@ -16,7 +16,7 @@
|
||||||
along with this program. If not, see <http://www.gnu.org/licenses/>.
|
along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||||
*/
|
*/
|
||||||
|
|
||||||
package user
|
package users
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"encoding/json"
|
"encoding/json"
|
||||||
|
@ -25,7 +25,7 @@ import (
|
||||||
"strings"
|
"strings"
|
||||||
|
|
||||||
"github.com/gin-gonic/gin"
|
"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/gtserror"
|
||||||
)
|
)
|
||||||
|
|
||||||
|
@ -35,7 +35,7 @@ func (m *Module) StatusGETHandler(c *gin.Context) {
|
||||||
requestedUsername := strings.ToLower(c.Param(UsernameKey))
|
requestedUsername := strings.ToLower(c.Param(UsernameKey))
|
||||||
if requestedUsername == "" {
|
if requestedUsername == "" {
|
||||||
err := errors.New("no username specified in request")
|
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
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -43,31 +43,31 @@ func (m *Module) StatusGETHandler(c *gin.Context) {
|
||||||
requestedStatusID := strings.ToUpper(c.Param(StatusIDKey))
|
requestedStatusID := strings.ToUpper(c.Param(StatusIDKey))
|
||||||
if requestedStatusID == "" {
|
if requestedStatusID == "" {
|
||||||
err := errors.New("no status id specified in request")
|
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
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
format, err := api.NegotiateAccept(c, api.HTMLOrActivityPubHeaders...)
|
format, err := apiutil.NegotiateAccept(c, apiutil.HTMLOrActivityPubHeaders...)
|
||||||
if err != nil {
|
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
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
if format == string(api.TextHTML) {
|
if format == string(apiutil.TextHTML) {
|
||||||
// redirect to the status
|
// redirect to the status
|
||||||
c.Redirect(http.StatusSeeOther, "/@"+requestedUsername+"/statuses/"+requestedStatusID)
|
c.Redirect(http.StatusSeeOther, "/@"+requestedUsername+"/statuses/"+requestedStatusID)
|
||||||
return
|
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 {
|
if errWithCode != nil {
|
||||||
api.ErrorHandler(c, errWithCode, m.processor.InstanceGet)
|
apiutil.ErrorHandler(c, errWithCode, m.processor.InstanceGet)
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
b, err := json.Marshal(resp)
|
b, err := json.Marshal(resp)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
api.ErrorHandler(c, gtserror.NewErrorInternalError(err), m.processor.InstanceGet)
|
apiutil.ErrorHandler(c, gtserror.NewErrorInternalError(err), m.processor.InstanceGet)
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
|
@ -16,7 +16,7 @@
|
||||||
along with this program. If not, see <http://www.gnu.org/licenses/>.
|
along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||||
*/
|
*/
|
||||||
|
|
||||||
package user_test
|
package users_test
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"context"
|
"context"
|
||||||
|
@ -31,7 +31,7 @@ import (
|
||||||
"github.com/stretchr/testify/suite"
|
"github.com/stretchr/testify/suite"
|
||||||
"github.com/superseriousbusiness/activity/streams"
|
"github.com/superseriousbusiness/activity/streams"
|
||||||
"github.com/superseriousbusiness/activity/streams/vocab"
|
"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"
|
"github.com/superseriousbusiness/gotosocial/testrig"
|
||||||
)
|
)
|
||||||
|
|
||||||
|
@ -55,17 +55,17 @@ func (suite *StatusGetTestSuite) TestGetStatus() {
|
||||||
ctx.Request.Header.Set("Date", signedRequest.DateHeader)
|
ctx.Request.Header.Set("Date", signedRequest.DateHeader)
|
||||||
|
|
||||||
// we need to pass the context through signature check first to set appropriate values on it
|
// 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,
|
// normally the router would populate these params from the path values,
|
||||||
// but because we're calling the function directly, we need to set them manually.
|
// but because we're calling the function directly, we need to set them manually.
|
||||||
ctx.Params = gin.Params{
|
ctx.Params = gin.Params{
|
||||||
gin.Param{
|
gin.Param{
|
||||||
Key: user.UsernameKey,
|
Key: users.UsernameKey,
|
||||||
Value: targetAccount.Username,
|
Value: targetAccount.Username,
|
||||||
},
|
},
|
||||||
gin.Param{
|
gin.Param{
|
||||||
Key: user.StatusIDKey,
|
Key: users.StatusIDKey,
|
||||||
Value: targetStatus.ID,
|
Value: targetStatus.ID,
|
||||||
},
|
},
|
||||||
}
|
}
|
||||||
|
@ -114,17 +114,17 @@ func (suite *StatusGetTestSuite) TestGetStatusLowercase() {
|
||||||
ctx.Request.Header.Set("Date", signedRequest.DateHeader)
|
ctx.Request.Header.Set("Date", signedRequest.DateHeader)
|
||||||
|
|
||||||
// we need to pass the context through signature check first to set appropriate values on it
|
// 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,
|
// normally the router would populate these params from the path values,
|
||||||
// but because we're calling the function directly, we need to set them manually.
|
// but because we're calling the function directly, we need to set them manually.
|
||||||
ctx.Params = gin.Params{
|
ctx.Params = gin.Params{
|
||||||
gin.Param{
|
gin.Param{
|
||||||
Key: user.UsernameKey,
|
Key: users.UsernameKey,
|
||||||
Value: strings.ToLower(targetAccount.Username),
|
Value: strings.ToLower(targetAccount.Username),
|
||||||
},
|
},
|
||||||
gin.Param{
|
gin.Param{
|
||||||
Key: user.StatusIDKey,
|
Key: users.StatusIDKey,
|
||||||
Value: strings.ToLower(targetStatus.ID),
|
Value: strings.ToLower(targetStatus.ID),
|
||||||
},
|
},
|
||||||
}
|
}
|
|
@ -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/>.
|
along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||||
*/
|
*/
|
||||||
|
|
||||||
package user_test
|
package users_test
|
||||||
|
|
||||||
import (
|
import (
|
||||||
|
"github.com/gin-gonic/gin"
|
||||||
"github.com/stretchr/testify/suite"
|
"github.com/stretchr/testify/suite"
|
||||||
"github.com/superseriousbusiness/gotosocial/internal/api/s2s/user"
|
"github.com/superseriousbusiness/gotosocial/internal/api/activitypub/users"
|
||||||
"github.com/superseriousbusiness/gotosocial/internal/api/security"
|
|
||||||
"github.com/superseriousbusiness/gotosocial/internal/concurrency"
|
"github.com/superseriousbusiness/gotosocial/internal/concurrency"
|
||||||
"github.com/superseriousbusiness/gotosocial/internal/db"
|
"github.com/superseriousbusiness/gotosocial/internal/db"
|
||||||
"github.com/superseriousbusiness/gotosocial/internal/email"
|
"github.com/superseriousbusiness/gotosocial/internal/email"
|
||||||
|
@ -29,7 +29,7 @@ import (
|
||||||
"github.com/superseriousbusiness/gotosocial/internal/gtsmodel"
|
"github.com/superseriousbusiness/gotosocial/internal/gtsmodel"
|
||||||
"github.com/superseriousbusiness/gotosocial/internal/media"
|
"github.com/superseriousbusiness/gotosocial/internal/media"
|
||||||
"github.com/superseriousbusiness/gotosocial/internal/messages"
|
"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/processing"
|
||||||
"github.com/superseriousbusiness/gotosocial/internal/storage"
|
"github.com/superseriousbusiness/gotosocial/internal/storage"
|
||||||
"github.com/superseriousbusiness/gotosocial/internal/typeutils"
|
"github.com/superseriousbusiness/gotosocial/internal/typeutils"
|
||||||
|
@ -46,8 +46,6 @@ type UserStandardTestSuite struct {
|
||||||
emailSender email.Sender
|
emailSender email.Sender
|
||||||
processor processing.Processor
|
processor processing.Processor
|
||||||
storage *storage.Driver
|
storage *storage.Driver
|
||||||
oauthServer oauth.Server
|
|
||||||
securityModule *security.Module
|
|
||||||
|
|
||||||
// standard suite models
|
// standard suite models
|
||||||
testTokens map[string]*gtsmodel.Token
|
testTokens map[string]*gtsmodel.Token
|
||||||
|
@ -60,7 +58,9 @@ type UserStandardTestSuite struct {
|
||||||
testBlocks map[string]*gtsmodel.Block
|
testBlocks map[string]*gtsmodel.Block
|
||||||
|
|
||||||
// module being tested
|
// module being tested
|
||||||
userModule *user.Module
|
userModule *users.Module
|
||||||
|
|
||||||
|
signatureCheck gin.HandlerFunc
|
||||||
}
|
}
|
||||||
|
|
||||||
func (suite *UserStandardTestSuite) SetupSuite() {
|
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.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.emailSender = testrig.NewEmailSender("../../../../web/template/", nil)
|
||||||
suite.processor = testrig.NewTestProcessor(suite.db, suite.storage, suite.federator, suite.emailSender, suite.mediaManager, clientWorker, fedWorker)
|
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.userModule = users.New(suite.processor)
|
||||||
suite.oauthServer = testrig.NewTestOauthServer(suite.db)
|
|
||||||
suite.securityModule = security.New(suite.db, suite.oauthServer).(*security.Module)
|
|
||||||
testrig.StandardDBSetup(suite.db, suite.testAccounts)
|
testrig.StandardDBSetup(suite.db, suite.testAccounts)
|
||||||
testrig.StandardStorageSetup(suite.storage, "../../../../testrig/media")
|
testrig.StandardStorageSetup(suite.storage, "../../../../testrig/media")
|
||||||
|
|
||||||
|
suite.signatureCheck = middleware.SignatureCheck(suite.db.IsURIBlocked)
|
||||||
|
|
||||||
suite.NoError(suite.processor.Start())
|
suite.NoError(suite.processor.Start())
|
||||||
}
|
}
|
||||||
|
|
|
@ -16,7 +16,7 @@
|
||||||
along with this program. If not, see <http://www.gnu.org/licenses/>.
|
along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||||
*/
|
*/
|
||||||
|
|
||||||
package user
|
package users
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"encoding/json"
|
"encoding/json"
|
||||||
|
@ -25,7 +25,7 @@ import (
|
||||||
"strings"
|
"strings"
|
||||||
|
|
||||||
"github.com/gin-gonic/gin"
|
"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/gtserror"
|
||||||
)
|
)
|
||||||
|
|
||||||
|
@ -43,31 +43,31 @@ func (m *Module) UsersGETHandler(c *gin.Context) {
|
||||||
requestedUsername := strings.ToLower(c.Param(UsernameKey))
|
requestedUsername := strings.ToLower(c.Param(UsernameKey))
|
||||||
if requestedUsername == "" {
|
if requestedUsername == "" {
|
||||||
err := errors.New("no username specified in request")
|
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
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
format, err := api.NegotiateAccept(c, api.HTMLOrActivityPubHeaders...)
|
format, err := apiutil.NegotiateAccept(c, apiutil.HTMLOrActivityPubHeaders...)
|
||||||
if err != nil {
|
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
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
if format == string(api.TextHTML) {
|
if format == string(apiutil.TextHTML) {
|
||||||
// redirect to the user's profile
|
// redirect to the user's profile
|
||||||
c.Redirect(http.StatusSeeOther, "/@"+requestedUsername)
|
c.Redirect(http.StatusSeeOther, "/@"+requestedUsername)
|
||||||
return
|
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 {
|
if errWithCode != nil {
|
||||||
api.ErrorHandler(c, errWithCode, m.processor.InstanceGet)
|
apiutil.ErrorHandler(c, errWithCode, m.processor.InstanceGet)
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
b, err := json.Marshal(resp)
|
b, err := json.Marshal(resp)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
api.ErrorHandler(c, gtserror.NewErrorInternalError(err), m.processor.InstanceGet)
|
apiutil.ErrorHandler(c, gtserror.NewErrorInternalError(err), m.processor.InstanceGet)
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
|
@ -16,7 +16,7 @@
|
||||||
along with this program. If not, see <http://www.gnu.org/licenses/>.
|
along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||||
*/
|
*/
|
||||||
|
|
||||||
package user_test
|
package users_test
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"context"
|
"context"
|
||||||
|
@ -30,8 +30,8 @@ import (
|
||||||
"github.com/stretchr/testify/suite"
|
"github.com/stretchr/testify/suite"
|
||||||
"github.com/superseriousbusiness/activity/streams"
|
"github.com/superseriousbusiness/activity/streams"
|
||||||
"github.com/superseriousbusiness/activity/streams/vocab"
|
"github.com/superseriousbusiness/activity/streams/vocab"
|
||||||
|
"github.com/superseriousbusiness/gotosocial/internal/api/activitypub/users"
|
||||||
apimodel "github.com/superseriousbusiness/gotosocial/internal/api/model"
|
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/internal/oauth"
|
||||||
"github.com/superseriousbusiness/gotosocial/testrig"
|
"github.com/superseriousbusiness/gotosocial/testrig"
|
||||||
)
|
)
|
||||||
|
@ -55,13 +55,13 @@ func (suite *UserGetTestSuite) TestGetUser() {
|
||||||
ctx.Request.Header.Set("Date", signedRequest.DateHeader)
|
ctx.Request.Header.Set("Date", signedRequest.DateHeader)
|
||||||
|
|
||||||
// we need to pass the context through signature check first to set appropriate values on it
|
// 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,
|
// normally the router would populate these params from the path values,
|
||||||
// but because we're calling the function directly, we need to set them manually.
|
// but because we're calling the function directly, we need to set them manually.
|
||||||
ctx.Params = gin.Params{
|
ctx.Params = gin.Params{
|
||||||
gin.Param{
|
gin.Param{
|
||||||
Key: user.UsernameKey,
|
Key: users.UsernameKey,
|
||||||
Value: targetAccount.Username,
|
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.
|
// 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.
|
// This is needed by remote instances for authenticating delete requests and stuff like that.
|
||||||
func (suite *UserGetTestSuite) TestGetUserPublicKeyDeleted() {
|
func (suite *UserGetTestSuite) TestGetUserPublicKeyDeleted() {
|
||||||
userModule := user.New(suite.processor).(*user.Module)
|
userModule := users.New(suite.processor)
|
||||||
targetAccount := suite.testAccounts["local_account_1"]
|
targetAccount := suite.testAccounts["local_account_1"]
|
||||||
|
|
||||||
// first delete the account, as though zork had deleted himself
|
// 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)
|
ctx.Request.Header.Set("Date", signedRequest.DateHeader)
|
||||||
|
|
||||||
// we need to pass the context through signature check first to set appropriate values on it
|
// 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,
|
// normally the router would populate these params from the path values,
|
||||||
// but because we're calling the function directly, we need to set them manually.
|
// but because we're calling the function directly, we need to set them manually.
|
||||||
ctx.Params = gin.Params{
|
ctx.Params = gin.Params{
|
||||||
gin.Param{
|
gin.Param{
|
||||||
Key: user.UsernameKey,
|
Key: users.UsernameKey,
|
||||||
Value: targetAccount.Username,
|
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
|
|
||||||
}
|
|
|
@ -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),
|
||||||
|
}
|
||||||
|
}
|
|
@ -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 @@ package auth_test
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"bytes"
|
"bytes"
|
||||||
"context"
|
|
||||||
"fmt"
|
"fmt"
|
||||||
"net/http/httptest"
|
"net/http/httptest"
|
||||||
|
|
||||||
|
@ -28,7 +27,7 @@ import (
|
||||||
"github.com/gin-contrib/sessions/memstore"
|
"github.com/gin-contrib/sessions/memstore"
|
||||||
"github.com/gin-gonic/gin"
|
"github.com/gin-gonic/gin"
|
||||||
"github.com/stretchr/testify/suite"
|
"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/concurrency"
|
||||||
"github.com/superseriousbusiness/gotosocial/internal/config"
|
"github.com/superseriousbusiness/gotosocial/internal/config"
|
||||||
"github.com/superseriousbusiness/gotosocial/internal/db"
|
"github.com/superseriousbusiness/gotosocial/internal/db"
|
||||||
|
@ -37,10 +36,9 @@ import (
|
||||||
"github.com/superseriousbusiness/gotosocial/internal/gtsmodel"
|
"github.com/superseriousbusiness/gotosocial/internal/gtsmodel"
|
||||||
"github.com/superseriousbusiness/gotosocial/internal/media"
|
"github.com/superseriousbusiness/gotosocial/internal/media"
|
||||||
"github.com/superseriousbusiness/gotosocial/internal/messages"
|
"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/oidc"
|
||||||
"github.com/superseriousbusiness/gotosocial/internal/processing"
|
"github.com/superseriousbusiness/gotosocial/internal/processing"
|
||||||
"github.com/superseriousbusiness/gotosocial/internal/router"
|
|
||||||
"github.com/superseriousbusiness/gotosocial/internal/storage"
|
"github.com/superseriousbusiness/gotosocial/internal/storage"
|
||||||
"github.com/superseriousbusiness/gotosocial/testrig"
|
"github.com/superseriousbusiness/gotosocial/testrig"
|
||||||
)
|
)
|
||||||
|
@ -54,7 +52,6 @@ type AuthStandardTestSuite struct {
|
||||||
processor processing.Processor
|
processor processing.Processor
|
||||||
emailSender email.Sender
|
emailSender email.Sender
|
||||||
idp oidc.IDP
|
idp oidc.IDP
|
||||||
oauthServer oauth.Server
|
|
||||||
|
|
||||||
// standard suite models
|
// standard suite models
|
||||||
testTokens map[string]*gtsmodel.Token
|
testTokens map[string]*gtsmodel.Token
|
||||||
|
@ -90,17 +87,10 @@ func (suite *AuthStandardTestSuite) SetupTest() {
|
||||||
suite.db = testrig.NewTestDB()
|
suite.db = testrig.NewTestDB()
|
||||||
suite.storage = testrig.NewInMemoryStorage()
|
suite.storage = testrig.NewInMemoryStorage()
|
||||||
suite.mediaManager = testrig.NewTestMediaManager(suite.db, suite.storage)
|
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.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.emailSender = testrig.NewEmailSender("../../../web/template/", nil)
|
||||||
suite.processor = testrig.NewTestProcessor(suite.db, suite.storage, suite.federator, suite.emailSender, suite.mediaManager, clientWorker, fedWorker)
|
suite.processor = testrig.NewTestProcessor(suite.db, suite.storage, suite.federator, suite.emailSender, suite.mediaManager, clientWorker, fedWorker)
|
||||||
|
suite.authModule = auth.New(suite.db, suite.processor, suite.idp)
|
||||||
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)
|
|
||||||
testrig.StandardDBSetup(suite.db, suite.testAccounts)
|
testrig.StandardDBSetup(suite.db, suite.testAccounts)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -114,7 +104,7 @@ func (suite *AuthStandardTestSuite) newContext(requestMethod string, requestPath
|
||||||
ctx, engine := testrig.CreateGinTestContext(recorder, nil)
|
ctx, engine := testrig.CreateGinTestContext(recorder, nil)
|
||||||
|
|
||||||
// load templates into the engine
|
// load templates into the engine
|
||||||
testrig.ConfigureTemplatesWithGin(engine, "../../../../web/template")
|
testrig.ConfigureTemplatesWithGin(engine, "../../../web/template")
|
||||||
|
|
||||||
// create the request
|
// create the request
|
||||||
protocol := config.GetProtocol()
|
protocol := config.GetProtocol()
|
||||||
|
@ -131,7 +121,7 @@ func (suite *AuthStandardTestSuite) newContext(requestMethod string, requestPath
|
||||||
|
|
||||||
// trigger the session middleware on the context
|
// trigger the session middleware on the context
|
||||||
store := memstore.NewStore(make([]byte, 32), make([]byte, 32))
|
store := memstore.NewStore(make([]byte, 32), make([]byte, 32))
|
||||||
store.Options(router.SessionOptions())
|
store.Options(middleware.SessionOptions())
|
||||||
sessionMiddleware := sessions.Sessions("gotosocial-localhost", store)
|
sessionMiddleware := sessions.Sessions("gotosocial-localhost", store)
|
||||||
sessionMiddleware(ctx)
|
sessionMiddleware(ctx)
|
||||||
|
|
|
@ -27,8 +27,8 @@ import (
|
||||||
"github.com/gin-contrib/sessions"
|
"github.com/gin-contrib/sessions"
|
||||||
"github.com/gin-gonic/gin"
|
"github.com/gin-gonic/gin"
|
||||||
"github.com/google/uuid"
|
"github.com/google/uuid"
|
||||||
"github.com/superseriousbusiness/gotosocial/internal/api"
|
apimodel "github.com/superseriousbusiness/gotosocial/internal/api/model"
|
||||||
"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/config"
|
||||||
"github.com/superseriousbusiness/gotosocial/internal/db"
|
"github.com/superseriousbusiness/gotosocial/internal/db"
|
||||||
"github.com/superseriousbusiness/gotosocial/internal/gtserror"
|
"github.com/superseriousbusiness/gotosocial/internal/gtserror"
|
||||||
|
@ -42,8 +42,8 @@ import (
|
||||||
func (m *Module) AuthorizeGETHandler(c *gin.Context) {
|
func (m *Module) AuthorizeGETHandler(c *gin.Context) {
|
||||||
s := sessions.Default(c)
|
s := sessions.Default(c)
|
||||||
|
|
||||||
if _, err := api.NegotiateAccept(c, api.HTMLAcceptHeaders...); err != nil {
|
if _, err := apiutil.NegotiateAccept(c, apiutil.HTMLAcceptHeaders...); 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
|
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.
|
// If it's not set, then we don't know yet who the user is, so we need to redirect them to the sign in page.
|
||||||
userID, ok := s.Get(sessionUserID).(string)
|
userID, ok := s.Get(sessionUserID).(string)
|
||||||
if !ok || userID == "" {
|
if !ok || userID == "" {
|
||||||
form := &model.OAuthAuthorize{}
|
form := &apimodel.OAuthAuthorize{}
|
||||||
if err := c.ShouldBind(form); err != nil {
|
if err := c.ShouldBind(form); err != nil {
|
||||||
m.clearSession(s)
|
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
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
if errWithCode := saveAuthFormToSession(s, form); errWithCode != nil {
|
if errWithCode := saveAuthFormToSession(s, form); errWithCode != nil {
|
||||||
m.clearSession(s)
|
m.clearSession(s)
|
||||||
api.ErrorHandler(c, errWithCode, m.processor.InstanceGet)
|
apiutil.ErrorHandler(c, errWithCode, m.processor.InstanceGet)
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
c.Redirect(http.StatusSeeOther, AuthSignInPath)
|
c.Redirect(http.StatusSeeOther, "/auth"+AuthSignInPath)
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -73,7 +73,7 @@ func (m *Module) AuthorizeGETHandler(c *gin.Context) {
|
||||||
if !ok || clientID == "" {
|
if !ok || clientID == "" {
|
||||||
m.clearSession(s)
|
m.clearSession(s)
|
||||||
err := fmt.Errorf("key %s was not found in session", sessionClientID)
|
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
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -87,7 +87,7 @@ func (m *Module) AuthorizeGETHandler(c *gin.Context) {
|
||||||
} else {
|
} else {
|
||||||
errWithCode = gtserror.NewErrorInternalError(err, safe, oauth.HelpfulAdvice)
|
errWithCode = gtserror.NewErrorInternalError(err, safe, oauth.HelpfulAdvice)
|
||||||
}
|
}
|
||||||
api.ErrorHandler(c, errWithCode, m.processor.InstanceGet)
|
apiutil.ErrorHandler(c, errWithCode, m.processor.InstanceGet)
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -101,7 +101,7 @@ func (m *Module) AuthorizeGETHandler(c *gin.Context) {
|
||||||
} else {
|
} else {
|
||||||
errWithCode = gtserror.NewErrorInternalError(err, safe, oauth.HelpfulAdvice)
|
errWithCode = gtserror.NewErrorInternalError(err, safe, oauth.HelpfulAdvice)
|
||||||
}
|
}
|
||||||
api.ErrorHandler(c, errWithCode, m.processor.InstanceGet)
|
apiutil.ErrorHandler(c, errWithCode, m.processor.InstanceGet)
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -115,7 +115,7 @@ func (m *Module) AuthorizeGETHandler(c *gin.Context) {
|
||||||
} else {
|
} else {
|
||||||
errWithCode = gtserror.NewErrorInternalError(err, safe, oauth.HelpfulAdvice)
|
errWithCode = gtserror.NewErrorInternalError(err, safe, oauth.HelpfulAdvice)
|
||||||
}
|
}
|
||||||
api.ErrorHandler(c, errWithCode, m.processor.InstanceGet)
|
apiutil.ErrorHandler(c, errWithCode, m.processor.InstanceGet)
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -128,7 +128,7 @@ func (m *Module) AuthorizeGETHandler(c *gin.Context) {
|
||||||
if !ok || redirect == "" {
|
if !ok || redirect == "" {
|
||||||
m.clearSession(s)
|
m.clearSession(s)
|
||||||
err := fmt.Errorf("key %s was not found in session", sessionRedirectURI)
|
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
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -136,13 +136,13 @@ func (m *Module) AuthorizeGETHandler(c *gin.Context) {
|
||||||
if !ok || scope == "" {
|
if !ok || scope == "" {
|
||||||
m.clearSession(s)
|
m.clearSession(s)
|
||||||
err := fmt.Errorf("key %s was not found in session", sessionScope)
|
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
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
instance, errWithCode := m.processor.InstanceGet(c.Request.Context(), config.GetHost())
|
instance, errWithCode := m.processor.InstanceGet(c.Request.Context(), config.GetHost())
|
||||||
if errWithCode != nil {
|
if errWithCode != nil {
|
||||||
api.ErrorHandler(c, errWithCode, m.processor.InstanceGet)
|
apiutil.ErrorHandler(c, errWithCode, m.processor.InstanceGet)
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -206,7 +206,7 @@ func (m *Module) AuthorizePOSTHandler(c *gin.Context) {
|
||||||
|
|
||||||
if len(errs) != 0 {
|
if len(errs) != 0 {
|
||||||
errs = append(errs, oauth.HelpfulAdvice)
|
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
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -220,7 +220,7 @@ func (m *Module) AuthorizePOSTHandler(c *gin.Context) {
|
||||||
} else {
|
} else {
|
||||||
errWithCode = gtserror.NewErrorInternalError(err, safe, oauth.HelpfulAdvice)
|
errWithCode = gtserror.NewErrorInternalError(err, safe, oauth.HelpfulAdvice)
|
||||||
}
|
}
|
||||||
api.ErrorHandler(c, errWithCode, m.processor.InstanceGet)
|
apiutil.ErrorHandler(c, errWithCode, m.processor.InstanceGet)
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -234,7 +234,7 @@ func (m *Module) AuthorizePOSTHandler(c *gin.Context) {
|
||||||
} else {
|
} else {
|
||||||
errWithCode = gtserror.NewErrorInternalError(err, safe, oauth.HelpfulAdvice)
|
errWithCode = gtserror.NewErrorInternalError(err, safe, oauth.HelpfulAdvice)
|
||||||
}
|
}
|
||||||
api.ErrorHandler(c, errWithCode, m.processor.InstanceGet)
|
apiutil.ErrorHandler(c, errWithCode, m.processor.InstanceGet)
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -263,13 +263,13 @@ func (m *Module) AuthorizePOSTHandler(c *gin.Context) {
|
||||||
}
|
}
|
||||||
|
|
||||||
if errWithCode := m.processor.OAuthHandleAuthorizeRequest(c.Writer, c.Request); errWithCode != nil {
|
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,
|
// saveAuthFormToSession checks the given OAuthAuthorize form,
|
||||||
// and stores the values in the form into the session.
|
// 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 {
|
if form == nil {
|
||||||
err := errors.New("OAuthAuthorize form was nil")
|
err := errors.New("OAuthAuthorize form was nil")
|
||||||
return gtserror.NewErrorBadRequest(err, err.Error(), oauth.HelpfulAdvice)
|
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) {
|
func ensureUserIsAuthorizedOrRedirect(ctx *gin.Context, user *gtsmodel.User, account *gtsmodel.Account) (redirected bool) {
|
||||||
if user.ConfirmedAt.IsZero() {
|
if user.ConfirmedAt.IsZero() {
|
||||||
ctx.Redirect(http.StatusSeeOther, CheckYourEmailPath)
|
ctx.Redirect(http.StatusSeeOther, "/auth"+AuthCheckYourEmailPath)
|
||||||
redirected = true
|
redirected = true
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
if !*user.Approved {
|
if !*user.Approved {
|
||||||
ctx.Redirect(http.StatusSeeOther, WaitForApprovalPath)
|
ctx.Redirect(http.StatusSeeOther, "/auth"+AuthWaitForApprovalPath)
|
||||||
redirected = true
|
redirected = true
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
if *user.Disabled || !account.SuspendedAt.IsZero() {
|
if *user.Disabled || !account.SuspendedAt.IsZero() {
|
||||||
ctx.Redirect(http.StatusSeeOther, AccountDisabledPath)
|
ctx.Redirect(http.StatusSeeOther, "/auth"+AuthAccountDisabledPath)
|
||||||
redirected = true
|
redirected = true
|
||||||
return
|
return
|
||||||
}
|
}
|
|
@ -9,7 +9,7 @@ import (
|
||||||
|
|
||||||
"github.com/gin-contrib/sessions"
|
"github.com/gin-contrib/sessions"
|
||||||
"github.com/stretchr/testify/suite"
|
"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/internal/gtsmodel"
|
||||||
"github.com/superseriousbusiness/gotosocial/testrig"
|
"github.com/superseriousbusiness/gotosocial/testrig"
|
||||||
)
|
)
|
||||||
|
@ -34,7 +34,7 @@ func (suite *AuthAuthorizeTestSuite) TestAccountAuthorizeHandler() {
|
||||||
return []string{"confirmed_at"}
|
return []string{"confirmed_at"}
|
||||||
},
|
},
|
||||||
expectedStatusCode: http.StatusSeeOther,
|
expectedStatusCode: http.StatusSeeOther,
|
||||||
expectedLocationHeader: auth.CheckYourEmailPath,
|
expectedLocationHeader: "/auth" + auth.AuthCheckYourEmailPath,
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
description: "user has their email confirmed but is not approved",
|
description: "user has their email confirmed but is not approved",
|
||||||
|
@ -44,7 +44,7 @@ func (suite *AuthAuthorizeTestSuite) TestAccountAuthorizeHandler() {
|
||||||
return []string{"confirmed_at", "email"}
|
return []string{"confirmed_at", "email"}
|
||||||
},
|
},
|
||||||
expectedStatusCode: http.StatusSeeOther,
|
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",
|
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"}
|
return []string{"confirmed_at", "email", "approved", "disabled"}
|
||||||
},
|
},
|
||||||
expectedStatusCode: http.StatusSeeOther,
|
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",
|
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"}
|
return []string{"confirmed_at", "email", "approved", "disabled"}
|
||||||
},
|
},
|
||||||
expectedStatusCode: http.StatusSeeOther,
|
expectedStatusCode: http.StatusSeeOther,
|
||||||
expectedLocationHeader: auth.AccountDisabledPath,
|
expectedLocationHeader: "/auth" + auth.AuthAccountDisabledPath,
|
||||||
},
|
},
|
||||||
}
|
}
|
||||||
|
|
|
@ -29,7 +29,7 @@ import (
|
||||||
"github.com/gin-contrib/sessions"
|
"github.com/gin-contrib/sessions"
|
||||||
"github.com/gin-gonic/gin"
|
"github.com/gin-gonic/gin"
|
||||||
"github.com/google/uuid"
|
"github.com/google/uuid"
|
||||||
"github.com/superseriousbusiness/gotosocial/internal/api"
|
apiutil "github.com/superseriousbusiness/gotosocial/internal/api/util"
|
||||||
"github.com/superseriousbusiness/gotosocial/internal/config"
|
"github.com/superseriousbusiness/gotosocial/internal/config"
|
||||||
"github.com/superseriousbusiness/gotosocial/internal/db"
|
"github.com/superseriousbusiness/gotosocial/internal/db"
|
||||||
"github.com/superseriousbusiness/gotosocial/internal/gtserror"
|
"github.com/superseriousbusiness/gotosocial/internal/gtserror"
|
||||||
|
@ -47,6 +47,12 @@ type extraInfo struct {
|
||||||
|
|
||||||
// CallbackGETHandler parses a token from an external auth provider.
|
// CallbackGETHandler parses a token from an external auth provider.
|
||||||
func (m *Module) CallbackGETHandler(c *gin.Context) {
|
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)
|
s := sessions.Default(c)
|
||||||
|
|
||||||
// check the query vs session state parameter to mitigate csrf
|
// check the query vs session state parameter to mitigate csrf
|
||||||
|
@ -56,7 +62,7 @@ func (m *Module) CallbackGETHandler(c *gin.Context) {
|
||||||
if returnedInternalState == "" {
|
if returnedInternalState == "" {
|
||||||
m.clearSession(s)
|
m.clearSession(s)
|
||||||
err := fmt.Errorf("%s parameter not found on callback query", callbackStateParam)
|
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
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -65,14 +71,14 @@ func (m *Module) CallbackGETHandler(c *gin.Context) {
|
||||||
if !ok {
|
if !ok {
|
||||||
m.clearSession(s)
|
m.clearSession(s)
|
||||||
err := fmt.Errorf("key %s was not found in session", sessionInternalState)
|
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
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
if returnedInternalState != savedInternalState {
|
if returnedInternalState != savedInternalState {
|
||||||
m.clearSession(s)
|
m.clearSession(s)
|
||||||
err := errors.New("mismatch between callback state and saved state")
|
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
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -81,14 +87,14 @@ func (m *Module) CallbackGETHandler(c *gin.Context) {
|
||||||
if code == "" {
|
if code == "" {
|
||||||
m.clearSession(s)
|
m.clearSession(s)
|
||||||
err := fmt.Errorf("%s parameter not found on callback query", callbackCodeParam)
|
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
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
claims, errWithCode := m.idp.HandleCallback(c.Request.Context(), code)
|
claims, errWithCode := m.idp.HandleCallback(c.Request.Context(), code)
|
||||||
if errWithCode != nil {
|
if errWithCode != nil {
|
||||||
m.clearSession(s)
|
m.clearSession(s)
|
||||||
api.ErrorHandler(c, errWithCode, m.processor.InstanceGet)
|
apiutil.ErrorHandler(c, errWithCode, m.processor.InstanceGet)
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -98,7 +104,7 @@ func (m *Module) CallbackGETHandler(c *gin.Context) {
|
||||||
if !ok || clientID == "" {
|
if !ok || clientID == "" {
|
||||||
m.clearSession(s)
|
m.clearSession(s)
|
||||||
err := fmt.Errorf("key %s was not found in session", sessionClientID)
|
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
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -112,21 +118,21 @@ func (m *Module) CallbackGETHandler(c *gin.Context) {
|
||||||
} else {
|
} else {
|
||||||
errWithCode = gtserror.NewErrorInternalError(err, safe, oauth.HelpfulAdvice)
|
errWithCode = gtserror.NewErrorInternalError(err, safe, oauth.HelpfulAdvice)
|
||||||
}
|
}
|
||||||
api.ErrorHandler(c, errWithCode, m.processor.InstanceGet)
|
apiutil.ErrorHandler(c, errWithCode, m.processor.InstanceGet)
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
user, errWithCode := m.fetchUserForClaims(c.Request.Context(), claims, net.IP(c.ClientIP()), app.ID)
|
user, errWithCode := m.fetchUserForClaims(c.Request.Context(), claims, net.IP(c.ClientIP()), app.ID)
|
||||||
if errWithCode != nil {
|
if errWithCode != nil {
|
||||||
m.clearSession(s)
|
m.clearSession(s)
|
||||||
api.ErrorHandler(c, errWithCode, m.processor.InstanceGet)
|
apiutil.ErrorHandler(c, errWithCode, m.processor.InstanceGet)
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
if user == nil {
|
if user == nil {
|
||||||
// no user exists yet - let's ask them for their preferred username
|
// no user exists yet - let's ask them for their preferred username
|
||||||
instance, errWithCode := m.processor.InstanceGet(c.Request.Context(), config.GetHost())
|
instance, errWithCode := m.processor.InstanceGet(c.Request.Context(), config.GetHost())
|
||||||
if errWithCode != nil {
|
if errWithCode != nil {
|
||||||
api.ErrorHandler(c, errWithCode, m.processor.InstanceGet)
|
apiutil.ErrorHandler(c, errWithCode, m.processor.InstanceGet)
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -135,7 +141,7 @@ func (m *Module) CallbackGETHandler(c *gin.Context) {
|
||||||
s.Set(sessionAppID, app.ID)
|
s.Set(sessionAppID, app.ID)
|
||||||
if err := s.Save(); err != nil {
|
if err := s.Save(); err != nil {
|
||||||
m.clearSession(s)
|
m.clearSession(s)
|
||||||
api.ErrorHandler(c, gtserror.NewErrorInternalError(err), m.processor.InstanceGet)
|
apiutil.ErrorHandler(c, gtserror.NewErrorInternalError(err), m.processor.InstanceGet)
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
c.HTML(http.StatusOK, "finalize.tmpl", gin.H{
|
c.HTML(http.StatusOK, "finalize.tmpl", gin.H{
|
||||||
|
@ -148,10 +154,10 @@ func (m *Module) CallbackGETHandler(c *gin.Context) {
|
||||||
s.Set(sessionUserID, user.ID)
|
s.Set(sessionUserID, user.ID)
|
||||||
if err := s.Save(); err != nil {
|
if err := s.Save(); err != nil {
|
||||||
m.clearSession(s)
|
m.clearSession(s)
|
||||||
api.ErrorHandler(c, gtserror.NewErrorInternalError(err), m.processor.InstanceGet)
|
apiutil.ErrorHandler(c, gtserror.NewErrorInternalError(err), m.processor.InstanceGet)
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
c.Redirect(http.StatusFound, OauthAuthorizePath)
|
c.Redirect(http.StatusFound, "/oauth"+OauthAuthorizePath)
|
||||||
}
|
}
|
||||||
|
|
||||||
// FinalizePOSTHandler registers the user after additional data has been provided
|
// FinalizePOSTHandler registers the user after additional data has been provided
|
||||||
|
@ -161,7 +167,7 @@ func (m *Module) FinalizePOSTHandler(c *gin.Context) {
|
||||||
form := &extraInfo{}
|
form := &extraInfo{}
|
||||||
if err := c.ShouldBind(form); err != nil {
|
if err := c.ShouldBind(form); err != nil {
|
||||||
m.clearSession(s)
|
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
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -169,7 +175,7 @@ func (m *Module) FinalizePOSTHandler(c *gin.Context) {
|
||||||
validationError := func(err error) {
|
validationError := func(err error) {
|
||||||
instance, errWithCode := m.processor.InstanceGet(c.Request.Context(), config.GetHost())
|
instance, errWithCode := m.processor.InstanceGet(c.Request.Context(), config.GetHost())
|
||||||
if errWithCode != nil {
|
if errWithCode != nil {
|
||||||
api.ErrorHandler(c, errWithCode, m.processor.InstanceGet)
|
apiutil.ErrorHandler(c, errWithCode, m.processor.InstanceGet)
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
c.HTML(http.StatusOK, "finalize.tmpl", gin.H{
|
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
|
// see if the username is still available
|
||||||
usernameAvailable, err := m.db.IsUsernameAvailable(c.Request.Context(), form.Username)
|
usernameAvailable, err := m.db.IsUsernameAvailable(c.Request.Context(), form.Username)
|
||||||
if err != nil {
|
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
|
return
|
||||||
}
|
}
|
||||||
if !usernameAvailable {
|
if !usernameAvailable {
|
||||||
|
@ -201,7 +207,7 @@ func (m *Module) FinalizePOSTHandler(c *gin.Context) {
|
||||||
appID, ok := s.Get(sessionAppID).(string)
|
appID, ok := s.Get(sessionAppID).(string)
|
||||||
if !ok {
|
if !ok {
|
||||||
err := fmt.Errorf("key %s was not found in session", sessionAppID)
|
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
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -209,7 +215,7 @@ func (m *Module) FinalizePOSTHandler(c *gin.Context) {
|
||||||
claims, ok := s.Get(sessionClaims).(*oidc.Claims)
|
claims, ok := s.Get(sessionClaims).(*oidc.Claims)
|
||||||
if !ok {
|
if !ok {
|
||||||
err := fmt.Errorf("key %s was not found in session", sessionClaims)
|
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
|
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)
|
user, errWithCode := m.createUserFromOIDC(c.Request.Context(), claims, form, net.IP(c.ClientIP()), appID)
|
||||||
if errWithCode != nil {
|
if errWithCode != nil {
|
||||||
m.clearSession(s)
|
m.clearSession(s)
|
||||||
api.ErrorHandler(c, errWithCode, m.processor.InstanceGet)
|
apiutil.ErrorHandler(c, errWithCode, m.processor.InstanceGet)
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
s.Delete(sessionClaims)
|
s.Delete(sessionClaims)
|
||||||
|
@ -225,10 +231,10 @@ func (m *Module) FinalizePOSTHandler(c *gin.Context) {
|
||||||
s.Set(sessionUserID, user.ID)
|
s.Set(sessionUserID, user.ID)
|
||||||
if err := s.Save(); err != nil {
|
if err := s.Save(); err != nil {
|
||||||
m.clearSession(s)
|
m.clearSession(s)
|
||||||
api.ErrorHandler(c, gtserror.NewErrorInternalError(err), m.processor.InstanceGet)
|
apiutil.ErrorHandler(c, gtserror.NewErrorInternalError(err), m.processor.InstanceGet)
|
||||||
return
|
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) {
|
func (m *Module) fetchUserForClaims(ctx context.Context, claims *oidc.Claims, ip net.IP, appID string) (*gtsmodel.User, gtserror.WithCode) {
|
|
@ -26,8 +26,8 @@ import (
|
||||||
|
|
||||||
"github.com/gin-contrib/sessions"
|
"github.com/gin-contrib/sessions"
|
||||||
"github.com/gin-gonic/gin"
|
"github.com/gin-gonic/gin"
|
||||||
"github.com/superseriousbusiness/gotosocial/internal/api"
|
apimodel "github.com/superseriousbusiness/gotosocial/internal/api/model"
|
||||||
"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/config"
|
||||||
"github.com/superseriousbusiness/gotosocial/internal/db"
|
"github.com/superseriousbusiness/gotosocial/internal/db"
|
||||||
"github.com/superseriousbusiness/gotosocial/internal/gtserror"
|
"github.com/superseriousbusiness/gotosocial/internal/gtserror"
|
||||||
|
@ -38,16 +38,18 @@ func (m *Module) OobHandler(c *gin.Context) {
|
||||||
host := config.GetHost()
|
host := config.GetHost()
|
||||||
instance, errWithCode := m.processor.InstanceGet(c.Request.Context(), host)
|
instance, errWithCode := m.processor.InstanceGet(c.Request.Context(), host)
|
||||||
if errWithCode != nil {
|
if errWithCode != nil {
|
||||||
api.ErrorHandler(c, errWithCode, m.processor.InstanceGet)
|
apiutil.ErrorHandler(c, errWithCode, m.processor.InstanceGet)
|
||||||
return
|
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")
|
oobToken := c.Query("code")
|
||||||
if oobToken == "" {
|
if oobToken == "" {
|
||||||
err := errors.New("no 'code' query value provided in callback redirect")
|
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
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -67,7 +69,7 @@ func (m *Module) OobHandler(c *gin.Context) {
|
||||||
|
|
||||||
if len(errs) != 0 {
|
if len(errs) != 0 {
|
||||||
errs = append(errs, oauth.HelpfulAdvice)
|
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
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -81,7 +83,7 @@ func (m *Module) OobHandler(c *gin.Context) {
|
||||||
} else {
|
} else {
|
||||||
errWithCode = gtserror.NewErrorInternalError(err, safe, oauth.HelpfulAdvice)
|
errWithCode = gtserror.NewErrorInternalError(err, safe, oauth.HelpfulAdvice)
|
||||||
}
|
}
|
||||||
api.ErrorHandler(c, errWithCode, instanceGet)
|
apiutil.ErrorHandler(c, errWithCode, instanceGet)
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -95,7 +97,7 @@ func (m *Module) OobHandler(c *gin.Context) {
|
||||||
} else {
|
} else {
|
||||||
errWithCode = gtserror.NewErrorInternalError(err, safe, oauth.HelpfulAdvice)
|
errWithCode = gtserror.NewErrorInternalError(err, safe, oauth.HelpfulAdvice)
|
||||||
}
|
}
|
||||||
api.ErrorHandler(c, errWithCode, instanceGet)
|
apiutil.ErrorHandler(c, errWithCode, instanceGet)
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
|
@ -26,7 +26,7 @@ import (
|
||||||
|
|
||||||
"github.com/gin-contrib/sessions"
|
"github.com/gin-contrib/sessions"
|
||||||
"github.com/gin-gonic/gin"
|
"github.com/gin-gonic/gin"
|
||||||
"github.com/superseriousbusiness/gotosocial/internal/api"
|
apiutil "github.com/superseriousbusiness/gotosocial/internal/api/util"
|
||||||
"github.com/superseriousbusiness/gotosocial/internal/config"
|
"github.com/superseriousbusiness/gotosocial/internal/config"
|
||||||
"github.com/superseriousbusiness/gotosocial/internal/gtserror"
|
"github.com/superseriousbusiness/gotosocial/internal/gtserror"
|
||||||
"github.com/superseriousbusiness/gotosocial/internal/oauth"
|
"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.
|
// 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.
|
// 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) {
|
func (m *Module) SignInGETHandler(c *gin.Context) {
|
||||||
if _, err := api.NegotiateAccept(c, api.HTMLAcceptHeaders...); err != nil {
|
if _, err := apiutil.NegotiateAccept(c, apiutil.HTMLAcceptHeaders...); 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
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
if m.idp == nil {
|
if !config.GetOIDCEnabled() {
|
||||||
instance, errWithCode := m.processor.InstanceGet(c.Request.Context(), config.GetHost())
|
instance, errWithCode := m.processor.InstanceGet(c.Request.Context(), config.GetHost())
|
||||||
if errWithCode != nil {
|
if errWithCode != nil {
|
||||||
api.ErrorHandler(c, errWithCode, m.processor.InstanceGet)
|
apiutil.ErrorHandler(c, errWithCode, m.processor.InstanceGet)
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -71,7 +71,7 @@ func (m *Module) SignInGETHandler(c *gin.Context) {
|
||||||
if !ok {
|
if !ok {
|
||||||
m.clearSession(s)
|
m.clearSession(s)
|
||||||
err := fmt.Errorf("key %s was not found in session", sessionInternalState)
|
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
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -87,7 +87,7 @@ func (m *Module) SignInPOSTHandler(c *gin.Context) {
|
||||||
form := &login{}
|
form := &login{}
|
||||||
if err := c.ShouldBind(form); err != nil {
|
if err := c.ShouldBind(form); err != nil {
|
||||||
m.clearSession(s)
|
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
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -95,17 +95,17 @@ func (m *Module) SignInPOSTHandler(c *gin.Context) {
|
||||||
if errWithCode != nil {
|
if errWithCode != nil {
|
||||||
// don't clear session here, so the user can just press back and try again
|
// don't clear session here, so the user can just press back and try again
|
||||||
// if they accidentally gave the wrong password or something
|
// if they accidentally gave the wrong password or something
|
||||||
api.ErrorHandler(c, errWithCode, m.processor.InstanceGet)
|
apiutil.ErrorHandler(c, errWithCode, m.processor.InstanceGet)
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
s.Set(sessionUserID, userid)
|
s.Set(sessionUserID, userid)
|
||||||
if err := s.Save(); err != nil {
|
if err := s.Save(); err != nil {
|
||||||
err := fmt.Errorf("error saving user id onto session: %s", err)
|
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.
|
// ValidatePassword takes an email address and a password.
|
|
@ -22,7 +22,7 @@ import (
|
||||||
"net/http"
|
"net/http"
|
||||||
"net/url"
|
"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/gtserror"
|
||||||
"github.com/superseriousbusiness/gotosocial/internal/oauth"
|
"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
|
// 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.
|
// 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) {
|
func (m *Module) TokenPOSTHandler(c *gin.Context) {
|
||||||
if _, err := api.NegotiateAccept(c, api.JSONAcceptHeaders...); err != nil {
|
if _, err := apiutil.NegotiateAccept(c, apiutil.JSONAcceptHeaders...); 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
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -50,7 +50,7 @@ func (m *Module) TokenPOSTHandler(c *gin.Context) {
|
||||||
|
|
||||||
form := &tokenRequestForm{}
|
form := &tokenRequestForm{}
|
||||||
if err := c.ShouldBind(form); err != nil {
|
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
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -99,13 +99,13 @@ func (m *Module) TokenPOSTHandler(c *gin.Context) {
|
||||||
}
|
}
|
||||||
|
|
||||||
if len(help) != 0 {
|
if len(help) != 0 {
|
||||||
api.OAuthErrorHandler(c, gtserror.NewErrorBadRequest(oauth.InvalidRequest(), help...))
|
apiutil.OAuthErrorHandler(c, gtserror.NewErrorBadRequest(oauth.InvalidRequest(), help...))
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
token, errWithCode := m.processor.OAuthHandleTokenRequest(c.Request)
|
token, errWithCode := m.processor.OAuthHandleTokenRequest(c.Request)
|
||||||
if errWithCode != nil {
|
if errWithCode != nil {
|
||||||
api.OAuthErrorHandler(c, errWithCode)
|
apiutil.OAuthErrorHandler(c, errWithCode)
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
|
@ -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/>.
|
along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||||
*/
|
*/
|
||||||
|
|
||||||
package account_test
|
package accounts_test
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"bytes"
|
"bytes"
|
||||||
|
@ -26,7 +26,7 @@ import (
|
||||||
|
|
||||||
"github.com/gin-gonic/gin"
|
"github.com/gin-gonic/gin"
|
||||||
"github.com/stretchr/testify/suite"
|
"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/concurrency"
|
||||||
"github.com/superseriousbusiness/gotosocial/internal/config"
|
"github.com/superseriousbusiness/gotosocial/internal/config"
|
||||||
"github.com/superseriousbusiness/gotosocial/internal/db"
|
"github.com/superseriousbusiness/gotosocial/internal/db"
|
||||||
|
@ -62,7 +62,7 @@ type AccountStandardTestSuite struct {
|
||||||
testStatuses map[string]*gtsmodel.Status
|
testStatuses map[string]*gtsmodel.Status
|
||||||
|
|
||||||
// module being tested
|
// module being tested
|
||||||
accountModule *account.Module
|
accountsModule *accounts.Module
|
||||||
}
|
}
|
||||||
|
|
||||||
func (suite *AccountStandardTestSuite) SetupSuite() {
|
func (suite *AccountStandardTestSuite) SetupSuite() {
|
||||||
|
@ -89,7 +89,7 @@ func (suite *AccountStandardTestSuite) SetupTest() {
|
||||||
suite.sentEmails = make(map[string]string)
|
suite.sentEmails = make(map[string]string)
|
||||||
suite.emailSender = testrig.NewEmailSender("../../../../web/template/", suite.sentEmails)
|
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.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.StandardDBSetup(suite.db, nil)
|
||||||
testrig.StandardStorageSetup(suite.storage, "../../../../testrig/media")
|
testrig.StandardStorageSetup(suite.storage, "../../../../testrig/media")
|
||||||
|
|
|
@ -16,7 +16,7 @@
|
||||||
along with this program. If not, see <http://www.gnu.org/licenses/>.
|
along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||||
*/
|
*/
|
||||||
|
|
||||||
package account
|
package accounts
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"errors"
|
"errors"
|
||||||
|
@ -24,8 +24,8 @@ import (
|
||||||
"net/http"
|
"net/http"
|
||||||
|
|
||||||
"github.com/gin-gonic/gin"
|
"github.com/gin-gonic/gin"
|
||||||
"github.com/superseriousbusiness/gotosocial/internal/api"
|
apimodel "github.com/superseriousbusiness/gotosocial/internal/api/model"
|
||||||
"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/config"
|
||||||
"github.com/superseriousbusiness/gotosocial/internal/gtserror"
|
"github.com/superseriousbusiness/gotosocial/internal/gtserror"
|
||||||
"github.com/superseriousbusiness/gotosocial/internal/oauth"
|
"github.com/superseriousbusiness/gotosocial/internal/oauth"
|
||||||
|
@ -73,23 +73,23 @@ import (
|
||||||
func (m *Module) AccountCreatePOSTHandler(c *gin.Context) {
|
func (m *Module) AccountCreatePOSTHandler(c *gin.Context) {
|
||||||
authed, err := oauth.Authed(c, true, true, false, false)
|
authed, err := oauth.Authed(c, true, true, false, false)
|
||||||
if err != nil {
|
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
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
if _, err := api.NegotiateAccept(c, api.JSONAcceptHeaders...); err != nil {
|
if _, err := apiutil.NegotiateAccept(c, apiutil.JSONAcceptHeaders...); 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
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
form := &model.AccountCreateRequest{}
|
form := &apimodel.AccountCreateRequest{}
|
||||||
if err := c.ShouldBind(form); err != nil {
|
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
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
if err := validateCreateAccount(form); err != nil {
|
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
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -97,14 +97,14 @@ func (m *Module) AccountCreatePOSTHandler(c *gin.Context) {
|
||||||
signUpIP := net.ParseIP(clientIP)
|
signUpIP := net.ParseIP(clientIP)
|
||||||
if signUpIP == nil {
|
if signUpIP == nil {
|
||||||
err := errors.New("ip address could not be parsed from request")
|
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
|
return
|
||||||
}
|
}
|
||||||
form.IP = signUpIP
|
form.IP = signUpIP
|
||||||
|
|
||||||
ti, errWithCode := m.processor.AccountCreate(c.Request.Context(), authed, form)
|
ti, errWithCode := m.processor.AccountCreate(c.Request.Context(), authed, form)
|
||||||
if errWithCode != nil {
|
if errWithCode != nil {
|
||||||
api.ErrorHandler(c, errWithCode, m.processor.InstanceGet)
|
apiutil.ErrorHandler(c, errWithCode, m.processor.InstanceGet)
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -113,7 +113,7 @@ func (m *Module) AccountCreatePOSTHandler(c *gin.Context) {
|
||||||
|
|
||||||
// validateCreateAccount checks through all the necessary prerequisites for creating a new account,
|
// 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.
|
// 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 {
|
if form == nil {
|
||||||
return errors.New("form was nil")
|
return errors.New("form was nil")
|
||||||
}
|
}
|
|
@ -16,4 +16,4 @@
|
||||||
// along with this program. If not, see <http://www.gnu.org/licenses/>.
|
// 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/>.
|
along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||||
*/
|
*/
|
||||||
|
|
||||||
package account
|
package accounts
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"errors"
|
"errors"
|
||||||
"net/http"
|
"net/http"
|
||||||
|
|
||||||
"github.com/gin-gonic/gin"
|
"github.com/gin-gonic/gin"
|
||||||
"github.com/superseriousbusiness/gotosocial/internal/api"
|
apimodel "github.com/superseriousbusiness/gotosocial/internal/api/model"
|
||||||
"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/gtserror"
|
||||||
"github.com/superseriousbusiness/gotosocial/internal/oauth"
|
"github.com/superseriousbusiness/gotosocial/internal/oauth"
|
||||||
)
|
)
|
||||||
|
@ -68,26 +68,26 @@ import (
|
||||||
func (m *Module) AccountDeletePOSTHandler(c *gin.Context) {
|
func (m *Module) AccountDeletePOSTHandler(c *gin.Context) {
|
||||||
authed, err := oauth.Authed(c, true, true, true, true)
|
authed, err := oauth.Authed(c, true, true, true, true)
|
||||||
if err != nil {
|
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
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
form := &model.AccountDeleteRequest{}
|
form := &apimodel.AccountDeleteRequest{}
|
||||||
if err := c.ShouldBind(&form); err != nil {
|
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
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
if form.Password == "" {
|
if form.Password == "" {
|
||||||
err = errors.New("no password provided in account delete request")
|
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
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
form.DeleteOriginID = authed.Account.ID
|
form.DeleteOriginID = authed.Account.ID
|
||||||
|
|
||||||
if errWithCode := m.processor.AccountDeleteLocal(c.Request.Context(), authed, form); errWithCode != nil {
|
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
|
return
|
||||||
}
|
}
|
||||||
|
|
|
@ -16,7 +16,7 @@
|
||||||
along with this program. If not, see <http://www.gnu.org/licenses/>.
|
along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||||
*/
|
*/
|
||||||
|
|
||||||
package account_test
|
package accounts_test
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"net/http"
|
"net/http"
|
||||||
|
@ -24,7 +24,7 @@ import (
|
||||||
"testing"
|
"testing"
|
||||||
|
|
||||||
"github.com/stretchr/testify/suite"
|
"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"
|
"github.com/superseriousbusiness/gotosocial/testrig"
|
||||||
)
|
)
|
||||||
|
|
||||||
|
@ -45,10 +45,10 @@ func (suite *AccountDeleteTestSuite) TestAccountDeletePOSTHandler() {
|
||||||
}
|
}
|
||||||
bodyBytes := requestBody.Bytes()
|
bodyBytes := requestBody.Bytes()
|
||||||
recorder := httptest.NewRecorder()
|
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
|
// call the handler
|
||||||
suite.accountModule.AccountDeletePOSTHandler(ctx)
|
suite.accountsModule.AccountDeletePOSTHandler(ctx)
|
||||||
|
|
||||||
// 1. we should have Accepted because our request was valid
|
// 1. we should have Accepted because our request was valid
|
||||||
suite.Equal(http.StatusAccepted, recorder.Code)
|
suite.Equal(http.StatusAccepted, recorder.Code)
|
||||||
|
@ -67,10 +67,10 @@ func (suite *AccountDeleteTestSuite) TestAccountDeletePOSTHandlerWrongPassword()
|
||||||
}
|
}
|
||||||
bodyBytes := requestBody.Bytes()
|
bodyBytes := requestBody.Bytes()
|
||||||
recorder := httptest.NewRecorder()
|
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
|
// call the handler
|
||||||
suite.accountModule.AccountDeletePOSTHandler(ctx)
|
suite.accountsModule.AccountDeletePOSTHandler(ctx)
|
||||||
|
|
||||||
// 1. we should have Forbidden because we supplied the wrong password
|
// 1. we should have Forbidden because we supplied the wrong password
|
||||||
suite.Equal(http.StatusForbidden, recorder.Code)
|
suite.Equal(http.StatusForbidden, recorder.Code)
|
||||||
|
@ -87,10 +87,10 @@ func (suite *AccountDeleteTestSuite) TestAccountDeletePOSTHandlerNoPassword() {
|
||||||
}
|
}
|
||||||
bodyBytes := requestBody.Bytes()
|
bodyBytes := requestBody.Bytes()
|
||||||
recorder := httptest.NewRecorder()
|
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
|
// call the handler
|
||||||
suite.accountModule.AccountDeletePOSTHandler(ctx)
|
suite.accountsModule.AccountDeletePOSTHandler(ctx)
|
||||||
|
|
||||||
// 1. we should have StatusBadRequest because our request was invalid
|
// 1. we should have StatusBadRequest because our request was invalid
|
||||||
suite.Equal(http.StatusBadRequest, recorder.Code)
|
suite.Equal(http.StatusBadRequest, recorder.Code)
|
|
@ -16,14 +16,14 @@
|
||||||
along with this program. If not, see <http://www.gnu.org/licenses/>.
|
along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||||
*/
|
*/
|
||||||
|
|
||||||
package account
|
package accounts
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"errors"
|
"errors"
|
||||||
"net/http"
|
"net/http"
|
||||||
|
|
||||||
"github.com/gin-gonic/gin"
|
"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/gtserror"
|
||||||
"github.com/superseriousbusiness/gotosocial/internal/oauth"
|
"github.com/superseriousbusiness/gotosocial/internal/oauth"
|
||||||
)
|
)
|
||||||
|
@ -69,25 +69,25 @@ import (
|
||||||
func (m *Module) AccountGETHandler(c *gin.Context) {
|
func (m *Module) AccountGETHandler(c *gin.Context) {
|
||||||
authed, err := oauth.Authed(c, true, true, true, true)
|
authed, err := oauth.Authed(c, true, true, true, true)
|
||||||
if err != nil {
|
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
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
if _, err := api.NegotiateAccept(c, api.JSONAcceptHeaders...); err != nil {
|
if _, err := apiutil.NegotiateAccept(c, apiutil.JSONAcceptHeaders...); 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
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
targetAcctID := c.Param(IDKey)
|
targetAcctID := c.Param(IDKey)
|
||||||
if targetAcctID == "" {
|
if targetAcctID == "" {
|
||||||
err := errors.New("no account id specified")
|
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
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
acctInfo, errWithCode := m.processor.AccountGet(c.Request.Context(), authed, targetAcctID)
|
acctInfo, errWithCode := m.processor.AccountGet(c.Request.Context(), authed, targetAcctID)
|
||||||
if errWithCode != nil {
|
if errWithCode != nil {
|
||||||
api.ErrorHandler(c, errWithCode, m.processor.InstanceGet)
|
apiutil.ErrorHandler(c, errWithCode, m.processor.InstanceGet)
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
|
@ -16,17 +16,13 @@
|
||||||
along with this program. If not, see <http://www.gnu.org/licenses/>.
|
along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||||
*/
|
*/
|
||||||
|
|
||||||
package account
|
package accounts
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"net/http"
|
"net/http"
|
||||||
"strings"
|
|
||||||
|
|
||||||
"github.com/gin-gonic/gin"
|
"github.com/gin-gonic/gin"
|
||||||
"github.com/superseriousbusiness/gotosocial/internal/api"
|
|
||||||
"github.com/superseriousbusiness/gotosocial/internal/processing"
|
"github.com/superseriousbusiness/gotosocial/internal/processing"
|
||||||
|
|
||||||
"github.com/superseriousbusiness/gotosocial/internal/router"
|
|
||||||
)
|
)
|
||||||
|
|
||||||
const (
|
const (
|
||||||
|
@ -49,8 +45,8 @@ const (
|
||||||
|
|
||||||
// IDKey is the key to use for retrieving account ID in requests
|
// IDKey is the key to use for retrieving account ID in requests
|
||||||
IDKey = "id"
|
IDKey = "id"
|
||||||
// BasePath is the base API path for this module
|
// BasePath is the base API path for this module, excluding the 'api' prefix
|
||||||
BasePath = "/api/v1/accounts"
|
BasePath = "/v1/accounts"
|
||||||
// BasePathWithID is the base path for this module with the ID key
|
// BasePathWithID is the base path for this module with the ID key
|
||||||
BasePathWithID = BasePath + "/:" + IDKey
|
BasePathWithID = BasePath + "/:" + IDKey
|
||||||
// VerifyPath is for verifying account credentials
|
// VerifyPath is for verifying account credentials
|
||||||
|
@ -77,65 +73,47 @@ const (
|
||||||
DeleteAccountPath = BasePath + "/delete"
|
DeleteAccountPath = BasePath + "/delete"
|
||||||
)
|
)
|
||||||
|
|
||||||
// Module implements the ClientAPIModule interface for account-related actions
|
|
||||||
type Module struct {
|
type Module struct {
|
||||||
processor processing.Processor
|
processor processing.Processor
|
||||||
}
|
}
|
||||||
|
|
||||||
// New returns a new account module
|
func New(processor processing.Processor) *Module {
|
||||||
func New(processor processing.Processor) api.ClientModule {
|
|
||||||
return &Module{
|
return &Module{
|
||||||
processor: processor,
|
processor: processor,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// Route attaches all routes from this module to the given router
|
func (m *Module) Route(attachHandler func(method string, path string, f ...gin.HandlerFunc) gin.IRoutes) {
|
||||||
func (m *Module) Route(r router.Router) error {
|
|
||||||
// create account
|
// create account
|
||||||
r.AttachHandler(http.MethodPost, BasePath, m.AccountCreatePOSTHandler)
|
attachHandler(http.MethodPost, BasePath, m.AccountCreatePOSTHandler)
|
||||||
|
|
||||||
// delete account
|
|
||||||
r.AttachHandler(http.MethodPost, DeleteAccountPath, m.AccountDeletePOSTHandler)
|
|
||||||
|
|
||||||
// get account
|
// 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
|
// modify account
|
||||||
r.AttachHandler(http.MethodPatch, BasePathWithID, m.muxHandler)
|
attachHandler(http.MethodPatch, UpdateCredentialsPath, m.AccountUpdateCredentialsPATCHHandler)
|
||||||
|
|
||||||
// get account's statuses
|
// get account's statuses
|
||||||
r.AttachHandler(http.MethodGet, GetStatusesPath, m.AccountStatusesGETHandler)
|
attachHandler(http.MethodGet, GetStatusesPath, m.AccountStatusesGETHandler)
|
||||||
|
|
||||||
// get following or followers
|
// get following or followers
|
||||||
r.AttachHandler(http.MethodGet, GetFollowersPath, m.AccountFollowersGETHandler)
|
attachHandler(http.MethodGet, GetFollowersPath, m.AccountFollowersGETHandler)
|
||||||
r.AttachHandler(http.MethodGet, GetFollowingPath, m.AccountFollowingGETHandler)
|
attachHandler(http.MethodGet, GetFollowingPath, m.AccountFollowingGETHandler)
|
||||||
|
|
||||||
// get relationship with account
|
// get relationship with account
|
||||||
r.AttachHandler(http.MethodGet, GetRelationshipsPath, m.AccountRelationshipsGETHandler)
|
attachHandler(http.MethodGet, GetRelationshipsPath, m.AccountRelationshipsGETHandler)
|
||||||
|
|
||||||
// follow or unfollow account
|
// follow or unfollow account
|
||||||
r.AttachHandler(http.MethodPost, FollowPath, m.AccountFollowPOSTHandler)
|
attachHandler(http.MethodPost, FollowPath, m.AccountFollowPOSTHandler)
|
||||||
r.AttachHandler(http.MethodPost, UnfollowPath, m.AccountUnfollowPOSTHandler)
|
attachHandler(http.MethodPost, UnfollowPath, m.AccountUnfollowPOSTHandler)
|
||||||
|
|
||||||
// block or unblock account
|
// block or unblock account
|
||||||
r.AttachHandler(http.MethodPost, BlockPath, m.AccountBlockPOSTHandler)
|
attachHandler(http.MethodPost, BlockPath, m.AccountBlockPOSTHandler)
|
||||||
r.AttachHandler(http.MethodPost, UnblockPath, m.AccountUnblockPOSTHandler)
|
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)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
}
|
|
@ -16,7 +16,7 @@
|
||||||
along with this program. If not, see <http://www.gnu.org/licenses/>.
|
along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||||
*/
|
*/
|
||||||
|
|
||||||
package account
|
package accounts
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"errors"
|
"errors"
|
||||||
|
@ -25,8 +25,8 @@ import (
|
||||||
"strconv"
|
"strconv"
|
||||||
|
|
||||||
"github.com/gin-gonic/gin"
|
"github.com/gin-gonic/gin"
|
||||||
"github.com/superseriousbusiness/gotosocial/internal/api"
|
apimodel "github.com/superseriousbusiness/gotosocial/internal/api/model"
|
||||||
"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/gtserror"
|
||||||
"github.com/superseriousbusiness/gotosocial/internal/oauth"
|
"github.com/superseriousbusiness/gotosocial/internal/oauth"
|
||||||
)
|
)
|
||||||
|
@ -138,33 +138,33 @@ import (
|
||||||
func (m *Module) AccountUpdateCredentialsPATCHHandler(c *gin.Context) {
|
func (m *Module) AccountUpdateCredentialsPATCHHandler(c *gin.Context) {
|
||||||
authed, err := oauth.Authed(c, true, true, true, true)
|
authed, err := oauth.Authed(c, true, true, true, true)
|
||||||
if err != nil {
|
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
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
if _, err := api.NegotiateAccept(c, api.JSONAcceptHeaders...); err != nil {
|
if _, err := apiutil.NegotiateAccept(c, apiutil.JSONAcceptHeaders...); 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
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
form, err := parseUpdateAccountForm(c)
|
form, err := parseUpdateAccountForm(c)
|
||||||
if err != nil {
|
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
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
acctSensitive, errWithCode := m.processor.AccountUpdate(c.Request.Context(), authed, form)
|
acctSensitive, errWithCode := m.processor.AccountUpdate(c.Request.Context(), authed, form)
|
||||||
if errWithCode != nil {
|
if errWithCode != nil {
|
||||||
api.ErrorHandler(c, errWithCode, m.processor.InstanceGet)
|
apiutil.ErrorHandler(c, errWithCode, m.processor.InstanceGet)
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
c.JSON(http.StatusOK, acctSensitive)
|
c.JSON(http.StatusOK, acctSensitive)
|
||||||
}
|
}
|
||||||
|
|
||||||
func parseUpdateAccountForm(c *gin.Context) (*model.UpdateCredentialsRequest, error) {
|
func parseUpdateAccountForm(c *gin.Context) (*apimodel.UpdateCredentialsRequest, error) {
|
||||||
form := &model.UpdateCredentialsRequest{
|
form := &apimodel.UpdateCredentialsRequest{
|
||||||
Source: &model.UpdateSource{},
|
Source: &apimodel.UpdateSource{},
|
||||||
}
|
}
|
||||||
|
|
||||||
if err := c.ShouldBind(&form); err != nil {
|
if err := c.ShouldBind(&form); err != nil {
|
|
@ -16,7 +16,7 @@
|
||||||
along with this program. If not, see <http://www.gnu.org/licenses/>.
|
along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||||
*/
|
*/
|
||||||
|
|
||||||
package account_test
|
package accounts_test
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"context"
|
"context"
|
||||||
|
@ -27,7 +27,7 @@ import (
|
||||||
"testing"
|
"testing"
|
||||||
|
|
||||||
"github.com/stretchr/testify/suite"
|
"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"
|
apimodel "github.com/superseriousbusiness/gotosocial/internal/api/model"
|
||||||
"github.com/superseriousbusiness/gotosocial/testrig"
|
"github.com/superseriousbusiness/gotosocial/testrig"
|
||||||
)
|
)
|
||||||
|
@ -50,10 +50,10 @@ func (suite *AccountUpdateTestSuite) TestAccountUpdateCredentialsPATCHHandler()
|
||||||
}
|
}
|
||||||
bodyBytes := requestBody.Bytes()
|
bodyBytes := requestBody.Bytes()
|
||||||
recorder := httptest.NewRecorder()
|
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
|
// call the handler
|
||||||
suite.accountModule.AccountUpdateCredentialsPATCHHandler(ctx)
|
suite.accountsModule.AccountUpdateCredentialsPATCHHandler(ctx)
|
||||||
|
|
||||||
// 1. we should have OK because our request was valid
|
// 1. we should have OK because our request was valid
|
||||||
suite.Equal(http.StatusOK, recorder.Code)
|
suite.Equal(http.StatusOK, recorder.Code)
|
||||||
|
@ -89,10 +89,10 @@ func (suite *AccountUpdateTestSuite) TestAccountUpdateCredentialsPATCHHandlerUnl
|
||||||
}
|
}
|
||||||
bodyBytes1 := requestBody1.Bytes()
|
bodyBytes1 := requestBody1.Bytes()
|
||||||
recorder1 := httptest.NewRecorder()
|
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
|
// call the handler
|
||||||
suite.accountModule.AccountUpdateCredentialsPATCHHandler(ctx1)
|
suite.accountsModule.AccountUpdateCredentialsPATCHHandler(ctx1)
|
||||||
|
|
||||||
// 1. we should have OK because our request was valid
|
// 1. we should have OK because our request was valid
|
||||||
suite.Equal(http.StatusOK, recorder1.Code)
|
suite.Equal(http.StatusOK, recorder1.Code)
|
||||||
|
@ -125,10 +125,10 @@ func (suite *AccountUpdateTestSuite) TestAccountUpdateCredentialsPATCHHandlerUnl
|
||||||
}
|
}
|
||||||
bodyBytes2 := requestBody2.Bytes()
|
bodyBytes2 := requestBody2.Bytes()
|
||||||
recorder2 := httptest.NewRecorder()
|
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
|
// call the handler
|
||||||
suite.accountModule.AccountUpdateCredentialsPATCHHandler(ctx2)
|
suite.accountsModule.AccountUpdateCredentialsPATCHHandler(ctx2)
|
||||||
|
|
||||||
// 1. we should have OK because our request was valid
|
// 1. we should have OK because our request was valid
|
||||||
suite.Equal(http.StatusOK, recorder1.Code)
|
suite.Equal(http.StatusOK, recorder1.Code)
|
||||||
|
@ -170,10 +170,10 @@ func (suite *AccountUpdateTestSuite) TestAccountUpdateCredentialsPATCHHandlerGet
|
||||||
}
|
}
|
||||||
bodyBytes := requestBody.Bytes()
|
bodyBytes := requestBody.Bytes()
|
||||||
recorder := httptest.NewRecorder()
|
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
|
// call the handler
|
||||||
suite.accountModule.AccountUpdateCredentialsPATCHHandler(ctx)
|
suite.accountsModule.AccountUpdateCredentialsPATCHHandler(ctx)
|
||||||
|
|
||||||
// 1. we should have OK because our request was valid
|
// 1. we should have OK because our request was valid
|
||||||
suite.Equal(http.StatusOK, recorder.Code)
|
suite.Equal(http.StatusOK, recorder.Code)
|
||||||
|
@ -212,10 +212,10 @@ func (suite *AccountUpdateTestSuite) TestAccountUpdateCredentialsPATCHHandlerTwo
|
||||||
}
|
}
|
||||||
bodyBytes := requestBody.Bytes()
|
bodyBytes := requestBody.Bytes()
|
||||||
recorder := httptest.NewRecorder()
|
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
|
// call the handler
|
||||||
suite.accountModule.AccountUpdateCredentialsPATCHHandler(ctx)
|
suite.accountsModule.AccountUpdateCredentialsPATCHHandler(ctx)
|
||||||
|
|
||||||
// 1. we should have OK because our request was valid
|
// 1. we should have OK because our request was valid
|
||||||
suite.Equal(http.StatusOK, recorder.Code)
|
suite.Equal(http.StatusOK, recorder.Code)
|
||||||
|
@ -266,10 +266,10 @@ func (suite *AccountUpdateTestSuite) TestAccountUpdateCredentialsPATCHHandlerWit
|
||||||
}
|
}
|
||||||
bodyBytes := requestBody.Bytes()
|
bodyBytes := requestBody.Bytes()
|
||||||
recorder := httptest.NewRecorder()
|
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
|
// call the handler
|
||||||
suite.accountModule.AccountUpdateCredentialsPATCHHandler(ctx)
|
suite.accountsModule.AccountUpdateCredentialsPATCHHandler(ctx)
|
||||||
|
|
||||||
// 1. we should have OK because our request was valid
|
// 1. we should have OK because our request was valid
|
||||||
suite.Equal(http.StatusOK, recorder.Code)
|
suite.Equal(http.StatusOK, recorder.Code)
|
||||||
|
@ -308,10 +308,10 @@ func (suite *AccountUpdateTestSuite) TestAccountUpdateCredentialsPATCHHandlerEmp
|
||||||
// set up the request
|
// set up the request
|
||||||
bodyBytes := []byte{}
|
bodyBytes := []byte{}
|
||||||
recorder := httptest.NewRecorder()
|
recorder := httptest.NewRecorder()
|
||||||
ctx := suite.newContext(recorder, http.MethodPatch, bodyBytes, account.UpdateCredentialsPath, "")
|
ctx := suite.newContext(recorder, http.MethodPatch, bodyBytes, accounts.UpdateCredentialsPath, "")
|
||||||
|
|
||||||
// call the handler
|
// call the handler
|
||||||
suite.accountModule.AccountUpdateCredentialsPATCHHandler(ctx)
|
suite.accountsModule.AccountUpdateCredentialsPATCHHandler(ctx)
|
||||||
|
|
||||||
// 1. we should have OK because our request was valid
|
// 1. we should have OK because our request was valid
|
||||||
suite.Equal(http.StatusBadRequest, recorder.Code)
|
suite.Equal(http.StatusBadRequest, recorder.Code)
|
||||||
|
@ -343,10 +343,10 @@ func (suite *AccountUpdateTestSuite) TestAccountUpdateCredentialsPATCHHandlerUpd
|
||||||
}
|
}
|
||||||
bodyBytes := requestBody.Bytes()
|
bodyBytes := requestBody.Bytes()
|
||||||
recorder := httptest.NewRecorder()
|
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
|
// call the handler
|
||||||
suite.accountModule.AccountUpdateCredentialsPATCHHandler(ctx)
|
suite.accountsModule.AccountUpdateCredentialsPATCHHandler(ctx)
|
||||||
|
|
||||||
// 1. we should have OK because our request was valid
|
// 1. we should have OK because our request was valid
|
||||||
suite.Equal(http.StatusOK, recorder.Code)
|
suite.Equal(http.StatusOK, recorder.Code)
|
||||||
|
@ -385,10 +385,10 @@ func (suite *AccountUpdateTestSuite) TestAccountUpdateCredentialsPATCHHandlerUpd
|
||||||
}
|
}
|
||||||
bodyBytes := requestBody.Bytes()
|
bodyBytes := requestBody.Bytes()
|
||||||
recorder := httptest.NewRecorder()
|
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
|
// call the handler
|
||||||
suite.accountModule.AccountUpdateCredentialsPATCHHandler(ctx)
|
suite.accountsModule.AccountUpdateCredentialsPATCHHandler(ctx)
|
||||||
|
|
||||||
// 1. we should have OK because our request was valid
|
// 1. we should have OK because our request was valid
|
||||||
suite.Equal(http.StatusOK, recorder.Code)
|
suite.Equal(http.StatusOK, recorder.Code)
|
||||||
|
@ -430,10 +430,10 @@ func (suite *AccountUpdateTestSuite) TestAccountUpdateCredentialsPATCHHandlerUpd
|
||||||
}
|
}
|
||||||
bodyBytes := requestBody.Bytes()
|
bodyBytes := requestBody.Bytes()
|
||||||
recorder := httptest.NewRecorder()
|
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
|
// call the handler
|
||||||
suite.accountModule.AccountUpdateCredentialsPATCHHandler(ctx)
|
suite.accountsModule.AccountUpdateCredentialsPATCHHandler(ctx)
|
||||||
|
|
||||||
suite.Equal(http.StatusBadRequest, recorder.Code)
|
suite.Equal(http.StatusBadRequest, recorder.Code)
|
||||||
|
|
|
@ -16,13 +16,13 @@
|
||||||
along with this program. If not, see <http://www.gnu.org/licenses/>.
|
along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||||
*/
|
*/
|
||||||
|
|
||||||
package account
|
package accounts
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"net/http"
|
"net/http"
|
||||||
|
|
||||||
"github.com/gin-gonic/gin"
|
"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/gtserror"
|
||||||
"github.com/superseriousbusiness/gotosocial/internal/oauth"
|
"github.com/superseriousbusiness/gotosocial/internal/oauth"
|
||||||
)
|
)
|
||||||
|
@ -59,18 +59,18 @@ import (
|
||||||
func (m *Module) AccountVerifyGETHandler(c *gin.Context) {
|
func (m *Module) AccountVerifyGETHandler(c *gin.Context) {
|
||||||
authed, err := oauth.Authed(c, true, true, true, true)
|
authed, err := oauth.Authed(c, true, true, true, true)
|
||||||
if err != nil {
|
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
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
if _, err := api.NegotiateAccept(c, api.JSONAcceptHeaders...); err != nil {
|
if _, err := apiutil.NegotiateAccept(c, apiutil.JSONAcceptHeaders...); 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
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
acctSensitive, errWithCode := m.processor.AccountGet(c.Request.Context(), authed, authed.Account.ID)
|
acctSensitive, errWithCode := m.processor.AccountGet(c.Request.Context(), authed, authed.Account.ID)
|
||||||
if errWithCode != nil {
|
if errWithCode != nil {
|
||||||
api.ErrorHandler(c, errWithCode, m.processor.InstanceGet)
|
apiutil.ErrorHandler(c, errWithCode, m.processor.InstanceGet)
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
|
@ -16,7 +16,7 @@
|
||||||
along with this program. If not, see <http://www.gnu.org/licenses/>.
|
along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||||
*/
|
*/
|
||||||
|
|
||||||
package account_test
|
package accounts_test
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"encoding/json"
|
"encoding/json"
|
||||||
|
@ -28,7 +28,7 @@ import (
|
||||||
|
|
||||||
"github.com/stretchr/testify/assert"
|
"github.com/stretchr/testify/assert"
|
||||||
"github.com/stretchr/testify/suite"
|
"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"
|
apimodel "github.com/superseriousbusiness/gotosocial/internal/api/model"
|
||||||
"github.com/superseriousbusiness/gotosocial/internal/gtsmodel"
|
"github.com/superseriousbusiness/gotosocial/internal/gtsmodel"
|
||||||
)
|
)
|
||||||
|
@ -42,10 +42,10 @@ func (suite *AccountVerifyTestSuite) TestAccountVerifyGet() {
|
||||||
|
|
||||||
// set up the request
|
// set up the request
|
||||||
recorder := httptest.NewRecorder()
|
recorder := httptest.NewRecorder()
|
||||||
ctx := suite.newContext(recorder, http.MethodGet, nil, account.VerifyPath, "")
|
ctx := suite.newContext(recorder, http.MethodGet, nil, accounts.VerifyPath, "")
|
||||||
|
|
||||||
// call the handler
|
// call the handler
|
||||||
suite.accountModule.AccountVerifyGETHandler(ctx)
|
suite.accountsModule.AccountVerifyGETHandler(ctx)
|
||||||
|
|
||||||
// 1. we should have OK because our request was valid
|
// 1. we should have OK because our request was valid
|
||||||
suite.Equal(http.StatusOK, recorder.Code)
|
suite.Equal(http.StatusOK, recorder.Code)
|
|
@ -16,14 +16,14 @@
|
||||||
along with this program. If not, see <http://www.gnu.org/licenses/>.
|
along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||||
*/
|
*/
|
||||||
|
|
||||||
package account
|
package accounts
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"errors"
|
"errors"
|
||||||
"net/http"
|
"net/http"
|
||||||
|
|
||||||
"github.com/gin-gonic/gin"
|
"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/gtserror"
|
||||||
"github.com/superseriousbusiness/gotosocial/internal/oauth"
|
"github.com/superseriousbusiness/gotosocial/internal/oauth"
|
||||||
)
|
)
|
||||||
|
@ -69,25 +69,25 @@ import (
|
||||||
func (m *Module) AccountBlockPOSTHandler(c *gin.Context) {
|
func (m *Module) AccountBlockPOSTHandler(c *gin.Context) {
|
||||||
authed, err := oauth.Authed(c, true, true, true, true)
|
authed, err := oauth.Authed(c, true, true, true, true)
|
||||||
if err != nil {
|
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
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
if _, err := api.NegotiateAccept(c, api.JSONAcceptHeaders...); err != nil {
|
if _, err := apiutil.NegotiateAccept(c, apiutil.JSONAcceptHeaders...); 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
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
targetAcctID := c.Param(IDKey)
|
targetAcctID := c.Param(IDKey)
|
||||||
if targetAcctID == "" {
|
if targetAcctID == "" {
|
||||||
err := errors.New("no account id specified")
|
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
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
relationship, errWithCode := m.processor.AccountBlockCreate(c.Request.Context(), authed, targetAcctID)
|
relationship, errWithCode := m.processor.AccountBlockCreate(c.Request.Context(), authed, targetAcctID)
|
||||||
if errWithCode != nil {
|
if errWithCode != nil {
|
||||||
api.ErrorHandler(c, errWithCode, m.processor.InstanceGet)
|
apiutil.ErrorHandler(c, errWithCode, m.processor.InstanceGet)
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
|
@ -16,7 +16,7 @@
|
||||||
along with this program. If not, see <http://www.gnu.org/licenses/>.
|
along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||||
*/
|
*/
|
||||||
|
|
||||||
package account_test
|
package accounts_test
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"fmt"
|
"fmt"
|
||||||
|
@ -29,7 +29,7 @@ import (
|
||||||
"github.com/gin-gonic/gin"
|
"github.com/gin-gonic/gin"
|
||||||
"github.com/stretchr/testify/assert"
|
"github.com/stretchr/testify/assert"
|
||||||
"github.com/stretchr/testify/suite"
|
"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/internal/oauth"
|
||||||
"github.com/superseriousbusiness/gotosocial/testrig"
|
"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.SessionAuthorizedToken, oauth.DBTokenToToken(suite.testTokens["local_account_1"]))
|
||||||
ctx.Set(oauth.SessionAuthorizedApplication, suite.testApplications["application_1"])
|
ctx.Set(oauth.SessionAuthorizedApplication, suite.testApplications["application_1"])
|
||||||
ctx.Set(oauth.SessionAuthorizedUser, suite.testUsers["local_account_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{
|
ctx.Params = gin.Params{
|
||||||
gin.Param{
|
gin.Param{
|
||||||
Key: account.IDKey,
|
Key: accounts.IDKey,
|
||||||
Value: testAcct.ID,
|
Value: testAcct.ID,
|
||||||
},
|
},
|
||||||
}
|
}
|
||||||
|
|
||||||
suite.accountModule.AccountBlockPOSTHandler(ctx)
|
suite.accountsModule.AccountBlockPOSTHandler(ctx)
|
||||||
|
|
||||||
// 1. status should be Not Acceptable due to attempted self-block
|
// 1. status should be Not Acceptable due to attempted self-block
|
||||||
suite.Equal(http.StatusNotAcceptable, recorder.Code)
|
suite.Equal(http.StatusNotAcceptable, recorder.Code)
|
|
@ -16,15 +16,15 @@
|
||||||
along with this program. If not, see <http://www.gnu.org/licenses/>.
|
along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||||
*/
|
*/
|
||||||
|
|
||||||
package account
|
package accounts
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"errors"
|
"errors"
|
||||||
"net/http"
|
"net/http"
|
||||||
|
|
||||||
"github.com/gin-gonic/gin"
|
"github.com/gin-gonic/gin"
|
||||||
"github.com/superseriousbusiness/gotosocial/internal/api"
|
apimodel "github.com/superseriousbusiness/gotosocial/internal/api/model"
|
||||||
"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/gtserror"
|
||||||
"github.com/superseriousbusiness/gotosocial/internal/oauth"
|
"github.com/superseriousbusiness/gotosocial/internal/oauth"
|
||||||
)
|
)
|
||||||
|
@ -91,32 +91,32 @@ import (
|
||||||
func (m *Module) AccountFollowPOSTHandler(c *gin.Context) {
|
func (m *Module) AccountFollowPOSTHandler(c *gin.Context) {
|
||||||
authed, err := oauth.Authed(c, true, true, true, true)
|
authed, err := oauth.Authed(c, true, true, true, true)
|
||||||
if err != nil {
|
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
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
if _, err := api.NegotiateAccept(c, api.JSONAcceptHeaders...); err != nil {
|
if _, err := apiutil.NegotiateAccept(c, apiutil.JSONAcceptHeaders...); 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
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
targetAcctID := c.Param(IDKey)
|
targetAcctID := c.Param(IDKey)
|
||||||
if targetAcctID == "" {
|
if targetAcctID == "" {
|
||||||
err := errors.New("no account id specified")
|
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
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
form := &model.AccountFollowRequest{}
|
form := &apimodel.AccountFollowRequest{}
|
||||||
if err := c.ShouldBind(form); err != nil {
|
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
|
return
|
||||||
}
|
}
|
||||||
form.ID = targetAcctID
|
form.ID = targetAcctID
|
||||||
|
|
||||||
relationship, errWithCode := m.processor.AccountFollowCreate(c.Request.Context(), authed, form)
|
relationship, errWithCode := m.processor.AccountFollowCreate(c.Request.Context(), authed, form)
|
||||||
if errWithCode != nil {
|
if errWithCode != nil {
|
||||||
api.ErrorHandler(c, errWithCode, m.processor.InstanceGet)
|
apiutil.ErrorHandler(c, errWithCode, m.processor.InstanceGet)
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
|
@ -16,7 +16,7 @@
|
||||||
along with this program. If not, see <http://www.gnu.org/licenses/>.
|
along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||||
*/
|
*/
|
||||||
|
|
||||||
package account_test
|
package accounts_test
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"fmt"
|
"fmt"
|
||||||
|
@ -29,7 +29,7 @@ import (
|
||||||
"github.com/gin-gonic/gin"
|
"github.com/gin-gonic/gin"
|
||||||
"github.com/stretchr/testify/assert"
|
"github.com/stretchr/testify/assert"
|
||||||
"github.com/stretchr/testify/suite"
|
"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/internal/oauth"
|
||||||
"github.com/superseriousbusiness/gotosocial/testrig"
|
"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.SessionAuthorizedToken, oauth.DBTokenToToken(suite.testTokens["local_account_1"]))
|
||||||
ctx.Set(oauth.SessionAuthorizedApplication, suite.testApplications["application_1"])
|
ctx.Set(oauth.SessionAuthorizedApplication, suite.testApplications["application_1"])
|
||||||
ctx.Set(oauth.SessionAuthorizedUser, suite.testUsers["local_account_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{
|
ctx.Params = gin.Params{
|
||||||
gin.Param{
|
gin.Param{
|
||||||
Key: account.IDKey,
|
Key: accounts.IDKey,
|
||||||
Value: testAcct.ID,
|
Value: testAcct.ID,
|
||||||
},
|
},
|
||||||
}
|
}
|
||||||
|
|
||||||
// call the handler
|
// call the handler
|
||||||
suite.accountModule.AccountFollowPOSTHandler(ctx)
|
suite.accountsModule.AccountFollowPOSTHandler(ctx)
|
||||||
|
|
||||||
// 1. status should be Not Acceptable due to self-follow attempt
|
// 1. status should be Not Acceptable due to self-follow attempt
|
||||||
suite.Equal(http.StatusNotAcceptable, recorder.Code)
|
suite.Equal(http.StatusNotAcceptable, recorder.Code)
|
|
@ -16,14 +16,14 @@
|
||||||
along with this program. If not, see <http://www.gnu.org/licenses/>.
|
along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||||
*/
|
*/
|
||||||
|
|
||||||
package account
|
package accounts
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"errors"
|
"errors"
|
||||||
"net/http"
|
"net/http"
|
||||||
|
|
||||||
"github.com/gin-gonic/gin"
|
"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/gtserror"
|
||||||
"github.com/superseriousbusiness/gotosocial/internal/oauth"
|
"github.com/superseriousbusiness/gotosocial/internal/oauth"
|
||||||
)
|
)
|
||||||
|
@ -72,25 +72,25 @@ import (
|
||||||
func (m *Module) AccountFollowersGETHandler(c *gin.Context) {
|
func (m *Module) AccountFollowersGETHandler(c *gin.Context) {
|
||||||
authed, err := oauth.Authed(c, true, true, true, true)
|
authed, err := oauth.Authed(c, true, true, true, true)
|
||||||
if err != nil {
|
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
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
if _, err := api.NegotiateAccept(c, api.JSONAcceptHeaders...); err != nil {
|
if _, err := apiutil.NegotiateAccept(c, apiutil.JSONAcceptHeaders...); 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
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
targetAcctID := c.Param(IDKey)
|
targetAcctID := c.Param(IDKey)
|
||||||
if targetAcctID == "" {
|
if targetAcctID == "" {
|
||||||
err := errors.New("no account id specified")
|
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
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
followers, errWithCode := m.processor.AccountFollowersGet(c.Request.Context(), authed, targetAcctID)
|
followers, errWithCode := m.processor.AccountFollowersGet(c.Request.Context(), authed, targetAcctID)
|
||||||
if errWithCode != nil {
|
if errWithCode != nil {
|
||||||
api.ErrorHandler(c, errWithCode, m.processor.InstanceGet)
|
apiutil.ErrorHandler(c, errWithCode, m.processor.InstanceGet)
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
|
@ -16,14 +16,14 @@
|
||||||
along with this program. If not, see <http://www.gnu.org/licenses/>.
|
along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||||
*/
|
*/
|
||||||
|
|
||||||
package account
|
package accounts
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"errors"
|
"errors"
|
||||||
"net/http"
|
"net/http"
|
||||||
|
|
||||||
"github.com/gin-gonic/gin"
|
"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/gtserror"
|
||||||
"github.com/superseriousbusiness/gotosocial/internal/oauth"
|
"github.com/superseriousbusiness/gotosocial/internal/oauth"
|
||||||
)
|
)
|
||||||
|
@ -72,25 +72,25 @@ import (
|
||||||
func (m *Module) AccountFollowingGETHandler(c *gin.Context) {
|
func (m *Module) AccountFollowingGETHandler(c *gin.Context) {
|
||||||
authed, err := oauth.Authed(c, true, true, true, true)
|
authed, err := oauth.Authed(c, true, true, true, true)
|
||||||
if err != nil {
|
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
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
if _, err := api.NegotiateAccept(c, api.JSONAcceptHeaders...); err != nil {
|
if _, err := apiutil.NegotiateAccept(c, apiutil.JSONAcceptHeaders...); 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
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
targetAcctID := c.Param(IDKey)
|
targetAcctID := c.Param(IDKey)
|
||||||
if targetAcctID == "" {
|
if targetAcctID == "" {
|
||||||
err := errors.New("no account id specified")
|
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
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
following, errWithCode := m.processor.AccountFollowingGet(c.Request.Context(), authed, targetAcctID)
|
following, errWithCode := m.processor.AccountFollowingGet(c.Request.Context(), authed, targetAcctID)
|
||||||
if errWithCode != nil {
|
if errWithCode != nil {
|
||||||
api.ErrorHandler(c, errWithCode, m.processor.InstanceGet)
|
apiutil.ErrorHandler(c, errWithCode, m.processor.InstanceGet)
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,12 +1,12 @@
|
||||||
package account
|
package accounts
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"errors"
|
"errors"
|
||||||
"net/http"
|
"net/http"
|
||||||
|
|
||||||
"github.com/gin-gonic/gin"
|
"github.com/gin-gonic/gin"
|
||||||
"github.com/superseriousbusiness/gotosocial/internal/api"
|
apimodel "github.com/superseriousbusiness/gotosocial/internal/api/model"
|
||||||
"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/gtserror"
|
||||||
"github.com/superseriousbusiness/gotosocial/internal/oauth"
|
"github.com/superseriousbusiness/gotosocial/internal/oauth"
|
||||||
)
|
)
|
||||||
|
@ -57,12 +57,12 @@ import (
|
||||||
func (m *Module) AccountRelationshipsGETHandler(c *gin.Context) {
|
func (m *Module) AccountRelationshipsGETHandler(c *gin.Context) {
|
||||||
authed, err := oauth.Authed(c, true, true, true, true)
|
authed, err := oauth.Authed(c, true, true, true, true)
|
||||||
if err != nil {
|
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
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
if _, err := api.NegotiateAccept(c, api.JSONAcceptHeaders...); err != nil {
|
if _, err := apiutil.NegotiateAccept(c, apiutil.JSONAcceptHeaders...); 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
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -72,18 +72,18 @@ func (m *Module) AccountRelationshipsGETHandler(c *gin.Context) {
|
||||||
id := c.Query("id")
|
id := c.Query("id")
|
||||||
if id == "" {
|
if id == "" {
|
||||||
err = errors.New("no account id(s) specified in query")
|
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
|
return
|
||||||
}
|
}
|
||||||
targetAccountIDs = append(targetAccountIDs, id)
|
targetAccountIDs = append(targetAccountIDs, id)
|
||||||
}
|
}
|
||||||
|
|
||||||
relationships := []model.Relationship{}
|
relationships := []apimodel.Relationship{}
|
||||||
|
|
||||||
for _, targetAccountID := range targetAccountIDs {
|
for _, targetAccountID := range targetAccountIDs {
|
||||||
r, errWithCode := m.processor.AccountRelationshipGet(c.Request.Context(), authed, targetAccountID)
|
r, errWithCode := m.processor.AccountRelationshipGet(c.Request.Context(), authed, targetAccountID)
|
||||||
if errWithCode != nil {
|
if errWithCode != nil {
|
||||||
api.ErrorHandler(c, errWithCode, m.processor.InstanceGet)
|
apiutil.ErrorHandler(c, errWithCode, m.processor.InstanceGet)
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
relationships = append(relationships, *r)
|
relationships = append(relationships, *r)
|
|
@ -16,7 +16,7 @@
|
||||||
along with this program. If not, see <http://www.gnu.org/licenses/>.
|
along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||||
*/
|
*/
|
||||||
|
|
||||||
package account
|
package accounts
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"errors"
|
"errors"
|
||||||
|
@ -25,7 +25,7 @@ import (
|
||||||
"strconv"
|
"strconv"
|
||||||
|
|
||||||
"github.com/gin-gonic/gin"
|
"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/gtserror"
|
||||||
"github.com/superseriousbusiness/gotosocial/internal/oauth"
|
"github.com/superseriousbusiness/gotosocial/internal/oauth"
|
||||||
)
|
)
|
||||||
|
@ -133,19 +133,19 @@ import (
|
||||||
func (m *Module) AccountStatusesGETHandler(c *gin.Context) {
|
func (m *Module) AccountStatusesGETHandler(c *gin.Context) {
|
||||||
authed, err := oauth.Authed(c, false, false, false, false)
|
authed, err := oauth.Authed(c, false, false, false, false)
|
||||||
if err != nil {
|
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
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
if _, err := api.NegotiateAccept(c, api.JSONAcceptHeaders...); err != nil {
|
if _, err := apiutil.NegotiateAccept(c, apiutil.JSONAcceptHeaders...); 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
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
targetAcctID := c.Param(IDKey)
|
targetAcctID := c.Param(IDKey)
|
||||||
if targetAcctID == "" {
|
if targetAcctID == "" {
|
||||||
err := errors.New("no account id specified")
|
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
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -155,7 +155,7 @@ func (m *Module) AccountStatusesGETHandler(c *gin.Context) {
|
||||||
i, err := strconv.ParseInt(limitString, 10, 32)
|
i, err := strconv.ParseInt(limitString, 10, 32)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
err := fmt.Errorf("error parsing %s: %s", LimitKey, err)
|
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
|
return
|
||||||
}
|
}
|
||||||
limit = int(i)
|
limit = int(i)
|
||||||
|
@ -167,7 +167,7 @@ func (m *Module) AccountStatusesGETHandler(c *gin.Context) {
|
||||||
i, err := strconv.ParseBool(excludeRepliesString)
|
i, err := strconv.ParseBool(excludeRepliesString)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
err := fmt.Errorf("error parsing %s: %s", ExcludeRepliesKey, err)
|
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
|
return
|
||||||
}
|
}
|
||||||
excludeReplies = i
|
excludeReplies = i
|
||||||
|
@ -179,7 +179,7 @@ func (m *Module) AccountStatusesGETHandler(c *gin.Context) {
|
||||||
i, err := strconv.ParseBool(excludeReblogsString)
|
i, err := strconv.ParseBool(excludeReblogsString)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
err := fmt.Errorf("error parsing %s: %s", ExcludeReblogsKey, err)
|
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
|
return
|
||||||
}
|
}
|
||||||
excludeReblogs = i
|
excludeReblogs = i
|
||||||
|
@ -203,7 +203,7 @@ func (m *Module) AccountStatusesGETHandler(c *gin.Context) {
|
||||||
i, err := strconv.ParseBool(pinnedString)
|
i, err := strconv.ParseBool(pinnedString)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
err := fmt.Errorf("error parsing %s: %s", PinnedKey, err)
|
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
|
return
|
||||||
}
|
}
|
||||||
pinnedOnly = i
|
pinnedOnly = i
|
||||||
|
@ -215,7 +215,7 @@ func (m *Module) AccountStatusesGETHandler(c *gin.Context) {
|
||||||
i, err := strconv.ParseBool(mediaOnlyString)
|
i, err := strconv.ParseBool(mediaOnlyString)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
err := fmt.Errorf("error parsing %s: %s", OnlyMediaKey, err)
|
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
|
return
|
||||||
}
|
}
|
||||||
mediaOnly = i
|
mediaOnly = i
|
||||||
|
@ -227,7 +227,7 @@ func (m *Module) AccountStatusesGETHandler(c *gin.Context) {
|
||||||
i, err := strconv.ParseBool(publicOnlyString)
|
i, err := strconv.ParseBool(publicOnlyString)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
err := fmt.Errorf("error parsing %s: %s", OnlyPublicKey, err)
|
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
|
return
|
||||||
}
|
}
|
||||||
publicOnly = i
|
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)
|
resp, errWithCode := m.processor.AccountStatusesGet(c.Request.Context(), authed, targetAcctID, limit, excludeReplies, excludeReblogs, maxID, minID, pinnedOnly, mediaOnly, publicOnly)
|
||||||
if errWithCode != nil {
|
if errWithCode != nil {
|
||||||
api.ErrorHandler(c, errWithCode, m.processor.InstanceGet)
|
apiutil.ErrorHandler(c, errWithCode, m.processor.InstanceGet)
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
|
@ -16,7 +16,7 @@
|
||||||
along with this program. If not, see <http://www.gnu.org/licenses/>.
|
along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||||
*/
|
*/
|
||||||
|
|
||||||
package account_test
|
package accounts_test
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"encoding/json"
|
"encoding/json"
|
||||||
|
@ -29,7 +29,7 @@ import (
|
||||||
"github.com/gin-gonic/gin"
|
"github.com/gin-gonic/gin"
|
||||||
"github.com/stretchr/testify/assert"
|
"github.com/stretchr/testify/assert"
|
||||||
"github.com/stretchr/testify/suite"
|
"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"
|
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 := 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{
|
ctx.Params = gin.Params{
|
||||||
gin.Param{
|
gin.Param{
|
||||||
Key: account.IDKey,
|
Key: accounts.IDKey,
|
||||||
Value: targetAccount.ID,
|
Value: targetAccount.ID,
|
||||||
},
|
},
|
||||||
}
|
}
|
||||||
|
|
||||||
// call the handler
|
// call the handler
|
||||||
suite.accountModule.AccountStatusesGETHandler(ctx)
|
suite.accountsModule.AccountStatusesGETHandler(ctx)
|
||||||
|
|
||||||
// 1. we should have OK because our request was valid
|
// 1. we should have OK because our request was valid
|
||||||
suite.Equal(http.StatusOK, recorder.Code)
|
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 := 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{
|
ctx.Params = gin.Params{
|
||||||
gin.Param{
|
gin.Param{
|
||||||
Key: account.IDKey,
|
Key: accounts.IDKey,
|
||||||
Value: targetAccount.ID,
|
Value: targetAccount.ID,
|
||||||
},
|
},
|
||||||
}
|
}
|
||||||
|
|
||||||
// call the handler
|
// call the handler
|
||||||
suite.accountModule.AccountStatusesGETHandler(ctx)
|
suite.accountsModule.AccountStatusesGETHandler(ctx)
|
||||||
|
|
||||||
// 1. we should have OK because our request was valid
|
// 1. we should have OK because our request was valid
|
||||||
suite.Equal(http.StatusOK, recorder.Code)
|
suite.Equal(http.StatusOK, recorder.Code)
|
|
@ -16,14 +16,14 @@
|
||||||
along with this program. If not, see <http://www.gnu.org/licenses/>.
|
along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||||
*/
|
*/
|
||||||
|
|
||||||
package account
|
package accounts
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"errors"
|
"errors"
|
||||||
"net/http"
|
"net/http"
|
||||||
|
|
||||||
"github.com/gin-gonic/gin"
|
"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/gtserror"
|
||||||
"github.com/superseriousbusiness/gotosocial/internal/oauth"
|
"github.com/superseriousbusiness/gotosocial/internal/oauth"
|
||||||
)
|
)
|
||||||
|
@ -70,25 +70,25 @@ import (
|
||||||
func (m *Module) AccountUnblockPOSTHandler(c *gin.Context) {
|
func (m *Module) AccountUnblockPOSTHandler(c *gin.Context) {
|
||||||
authed, err := oauth.Authed(c, true, true, true, true)
|
authed, err := oauth.Authed(c, true, true, true, true)
|
||||||
if err != nil {
|
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
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
if _, err := api.NegotiateAccept(c, api.JSONAcceptHeaders...); err != nil {
|
if _, err := apiutil.NegotiateAccept(c, apiutil.JSONAcceptHeaders...); 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
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
targetAcctID := c.Param(IDKey)
|
targetAcctID := c.Param(IDKey)
|
||||||
if targetAcctID == "" {
|
if targetAcctID == "" {
|
||||||
err := errors.New("no account id specified")
|
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
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
relationship, errWithCode := m.processor.AccountBlockRemove(c.Request.Context(), authed, targetAcctID)
|
relationship, errWithCode := m.processor.AccountBlockRemove(c.Request.Context(), authed, targetAcctID)
|
||||||
if errWithCode != nil {
|
if errWithCode != nil {
|
||||||
api.ErrorHandler(c, errWithCode, m.processor.InstanceGet)
|
apiutil.ErrorHandler(c, errWithCode, m.processor.InstanceGet)
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
|
@ -16,14 +16,14 @@
|
||||||
along with this program. If not, see <http://www.gnu.org/licenses/>.
|
along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||||
*/
|
*/
|
||||||
|
|
||||||
package account
|
package accounts
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"errors"
|
"errors"
|
||||||
"net/http"
|
"net/http"
|
||||||
|
|
||||||
"github.com/gin-gonic/gin"
|
"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/gtserror"
|
||||||
"github.com/superseriousbusiness/gotosocial/internal/oauth"
|
"github.com/superseriousbusiness/gotosocial/internal/oauth"
|
||||||
)
|
)
|
||||||
|
@ -70,25 +70,25 @@ import (
|
||||||
func (m *Module) AccountUnfollowPOSTHandler(c *gin.Context) {
|
func (m *Module) AccountUnfollowPOSTHandler(c *gin.Context) {
|
||||||
authed, err := oauth.Authed(c, true, true, true, true)
|
authed, err := oauth.Authed(c, true, true, true, true)
|
||||||
if err != nil {
|
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
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
if _, err := api.NegotiateAccept(c, api.JSONAcceptHeaders...); err != nil {
|
if _, err := apiutil.NegotiateAccept(c, apiutil.JSONAcceptHeaders...); 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
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
targetAcctID := c.Param(IDKey)
|
targetAcctID := c.Param(IDKey)
|
||||||
if targetAcctID == "" {
|
if targetAcctID == "" {
|
||||||
err := errors.New("no account id specified")
|
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
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
relationship, errWithCode := m.processor.AccountFollowRemove(c.Request.Context(), authed, targetAcctID)
|
relationship, errWithCode := m.processor.AccountFollowRemove(c.Request.Context(), authed, targetAcctID)
|
||||||
if errWithCode != nil {
|
if errWithCode != nil {
|
||||||
api.ErrorHandler(c, errWithCode, m.processor.InstanceGet)
|
apiutil.ErrorHandler(c, errWithCode, m.processor.InstanceGet)
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
|
@ -24,8 +24,8 @@ import (
|
||||||
"net/http"
|
"net/http"
|
||||||
|
|
||||||
"github.com/gin-gonic/gin"
|
"github.com/gin-gonic/gin"
|
||||||
"github.com/superseriousbusiness/gotosocial/internal/api"
|
apimodel "github.com/superseriousbusiness/gotosocial/internal/api/model"
|
||||||
"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/gtserror"
|
||||||
"github.com/superseriousbusiness/gotosocial/internal/oauth"
|
"github.com/superseriousbusiness/gotosocial/internal/oauth"
|
||||||
)
|
)
|
||||||
|
@ -85,38 +85,38 @@ import (
|
||||||
func (m *Module) AccountActionPOSTHandler(c *gin.Context) {
|
func (m *Module) AccountActionPOSTHandler(c *gin.Context) {
|
||||||
authed, err := oauth.Authed(c, true, true, true, true)
|
authed, err := oauth.Authed(c, true, true, true, true)
|
||||||
if err != nil {
|
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
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
if !*authed.User.Admin {
|
if !*authed.User.Admin {
|
||||||
err := fmt.Errorf("user %s not an admin", authed.User.ID)
|
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
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
form := &model.AdminAccountActionRequest{}
|
form := &apimodel.AdminAccountActionRequest{}
|
||||||
if err := c.ShouldBind(form); err != nil {
|
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
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
if form.Type == "" {
|
if form.Type == "" {
|
||||||
err := errors.New("no type specified")
|
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
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
targetAcctID := c.Param(IDKey)
|
targetAcctID := c.Param(IDKey)
|
||||||
if targetAcctID == "" {
|
if targetAcctID == "" {
|
||||||
err := errors.New("no account id specified")
|
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
|
return
|
||||||
}
|
}
|
||||||
form.TargetAccountID = targetAcctID
|
form.TargetAccountID = targetAcctID
|
||||||
|
|
||||||
if errWithCode := m.processor.AdminAccountAction(c.Request.Context(), authed, form); errWithCode != nil {
|
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
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -21,14 +21,13 @@ package admin
|
||||||
import (
|
import (
|
||||||
"net/http"
|
"net/http"
|
||||||
|
|
||||||
"github.com/superseriousbusiness/gotosocial/internal/api"
|
"github.com/gin-gonic/gin"
|
||||||
"github.com/superseriousbusiness/gotosocial/internal/processing"
|
"github.com/superseriousbusiness/gotosocial/internal/processing"
|
||||||
"github.com/superseriousbusiness/gotosocial/internal/router"
|
|
||||||
)
|
)
|
||||||
|
|
||||||
const (
|
const (
|
||||||
// BasePath is the base API path for this module.
|
// BasePath is the base API path for this module, excluding the api prefix
|
||||||
BasePath = "/api/v1/admin"
|
BasePath = "/v1/admin"
|
||||||
// EmojiPath is used for posting/deleting custom emojis.
|
// EmojiPath is used for posting/deleting custom emojis.
|
||||||
EmojiPath = BasePath + "/custom_emojis"
|
EmojiPath = BasePath + "/custom_emojis"
|
||||||
// EmojiPathWithID is used for interacting with a single emoji.
|
// EmojiPathWithID is used for interacting with a single emoji.
|
||||||
|
@ -68,32 +67,28 @@ const (
|
||||||
DomainQueryKey = "domain"
|
DomainQueryKey = "domain"
|
||||||
)
|
)
|
||||||
|
|
||||||
// Module implements the ClientAPIModule interface for admin-related actions (reports, emojis, etc)
|
|
||||||
type Module struct {
|
type Module struct {
|
||||||
processor processing.Processor
|
processor processing.Processor
|
||||||
}
|
}
|
||||||
|
|
||||||
// New returns a new admin module
|
func New(processor processing.Processor) *Module {
|
||||||
func New(processor processing.Processor) api.ClientModule {
|
|
||||||
return &Module{
|
return &Module{
|
||||||
processor: processor,
|
processor: processor,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// Route attaches all routes from this module to the given router
|
func (m *Module) Route(attachHandler func(method string, path string, f ...gin.HandlerFunc) gin.IRoutes) {
|
||||||
func (m *Module) Route(r router.Router) error {
|
attachHandler(http.MethodPost, EmojiPath, m.EmojiCreatePOSTHandler)
|
||||||
r.AttachHandler(http.MethodPost, EmojiPath, m.EmojiCreatePOSTHandler)
|
attachHandler(http.MethodGet, EmojiPath, m.EmojisGETHandler)
|
||||||
r.AttachHandler(http.MethodGet, EmojiPath, m.EmojisGETHandler)
|
attachHandler(http.MethodDelete, EmojiPathWithID, m.EmojiDELETEHandler)
|
||||||
r.AttachHandler(http.MethodDelete, EmojiPathWithID, m.EmojiDELETEHandler)
|
attachHandler(http.MethodGet, EmojiPathWithID, m.EmojiGETHandler)
|
||||||
r.AttachHandler(http.MethodGet, EmojiPathWithID, m.EmojiGETHandler)
|
attachHandler(http.MethodPatch, EmojiPathWithID, m.EmojiPATCHHandler)
|
||||||
r.AttachHandler(http.MethodPatch, EmojiPathWithID, m.EmojiPATCHHandler)
|
attachHandler(http.MethodPost, DomainBlocksPath, m.DomainBlocksPOSTHandler)
|
||||||
r.AttachHandler(http.MethodPost, DomainBlocksPath, m.DomainBlocksPOSTHandler)
|
attachHandler(http.MethodGet, DomainBlocksPath, m.DomainBlocksGETHandler)
|
||||||
r.AttachHandler(http.MethodGet, DomainBlocksPath, m.DomainBlocksGETHandler)
|
attachHandler(http.MethodGet, DomainBlocksPathWithID, m.DomainBlockGETHandler)
|
||||||
r.AttachHandler(http.MethodGet, DomainBlocksPathWithID, m.DomainBlockGETHandler)
|
attachHandler(http.MethodDelete, DomainBlocksPathWithID, m.DomainBlockDELETEHandler)
|
||||||
r.AttachHandler(http.MethodDelete, DomainBlocksPathWithID, m.DomainBlockDELETEHandler)
|
attachHandler(http.MethodPost, AccountsActionPath, m.AccountActionPOSTHandler)
|
||||||
r.AttachHandler(http.MethodPost, AccountsActionPath, m.AccountActionPOSTHandler)
|
attachHandler(http.MethodPost, MediaCleanupPath, m.MediaCleanupPOSTHandler)
|
||||||
r.AttachHandler(http.MethodPost, MediaCleanupPath, m.MediaCleanupPOSTHandler)
|
attachHandler(http.MethodPost, MediaRefetchPath, m.MediaRefetchPOSTHandler)
|
||||||
r.AttachHandler(http.MethodPost, MediaRefetchPath, m.MediaRefetchPOSTHandler)
|
attachHandler(http.MethodGet, EmojiCategoriesPath, m.EmojiCategoriesGETHandler)
|
||||||
r.AttachHandler(http.MethodGet, EmojiCategoriesPath, m.EmojiCategoriesGETHandler)
|
|
||||||
return nil
|
|
||||||
}
|
}
|
||||||
|
|
|
@ -93,7 +93,7 @@ func (suite *AdminStandardTestSuite) SetupTest() {
|
||||||
suite.sentEmails = make(map[string]string)
|
suite.sentEmails = make(map[string]string)
|
||||||
suite.emailSender = testrig.NewEmailSender("../../../../web/template/", suite.sentEmails)
|
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.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.StandardDBSetup(suite.db, nil)
|
||||||
testrig.StandardStorageSetup(suite.storage, "../../../../testrig/media")
|
testrig.StandardStorageSetup(suite.storage, "../../../../testrig/media")
|
||||||
}
|
}
|
||||||
|
|
|
@ -25,8 +25,8 @@ import (
|
||||||
"strconv"
|
"strconv"
|
||||||
|
|
||||||
"github.com/gin-gonic/gin"
|
"github.com/gin-gonic/gin"
|
||||||
"github.com/superseriousbusiness/gotosocial/internal/api"
|
apimodel "github.com/superseriousbusiness/gotosocial/internal/api/model"
|
||||||
"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/gtserror"
|
||||||
"github.com/superseriousbusiness/gotosocial/internal/oauth"
|
"github.com/superseriousbusiness/gotosocial/internal/oauth"
|
||||||
)
|
)
|
||||||
|
@ -126,18 +126,18 @@ import (
|
||||||
func (m *Module) DomainBlocksPOSTHandler(c *gin.Context) {
|
func (m *Module) DomainBlocksPOSTHandler(c *gin.Context) {
|
||||||
authed, err := oauth.Authed(c, true, true, true, true)
|
authed, err := oauth.Authed(c, true, true, true, true)
|
||||||
if err != nil {
|
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
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
if !*authed.User.Admin {
|
if !*authed.User.Admin {
|
||||||
err := fmt.Errorf("user %s not an admin", authed.User.ID)
|
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
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
if _, err := api.NegotiateAccept(c, api.JSONAcceptHeaders...); err != nil {
|
if _, err := apiutil.NegotiateAccept(c, apiutil.JSONAcceptHeaders...); 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
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -147,21 +147,21 @@ func (m *Module) DomainBlocksPOSTHandler(c *gin.Context) {
|
||||||
i, err := strconv.ParseBool(importString)
|
i, err := strconv.ParseBool(importString)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
err := fmt.Errorf("error parsing %s: %s", ImportQueryKey, err)
|
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
|
return
|
||||||
}
|
}
|
||||||
imp = i
|
imp = i
|
||||||
}
|
}
|
||||||
|
|
||||||
form := &model.DomainBlockCreateRequest{}
|
form := &apimodel.DomainBlockCreateRequest{}
|
||||||
if err := c.ShouldBind(form); err != nil {
|
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
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
if err := validateCreateDomainBlock(form, imp); err != nil {
|
if err := validateCreateDomainBlock(form, imp); err != nil {
|
||||||
err := fmt.Errorf("error validating form: %s", err)
|
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
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -169,7 +169,7 @@ func (m *Module) DomainBlocksPOSTHandler(c *gin.Context) {
|
||||||
// we're importing multiple blocks
|
// we're importing multiple blocks
|
||||||
domainBlocks, errWithCode := m.processor.AdminDomainBlocksImport(c.Request.Context(), authed, form)
|
domainBlocks, errWithCode := m.processor.AdminDomainBlocksImport(c.Request.Context(), authed, form)
|
||||||
if errWithCode != nil {
|
if errWithCode != nil {
|
||||||
api.ErrorHandler(c, errWithCode, m.processor.InstanceGet)
|
apiutil.ErrorHandler(c, errWithCode, m.processor.InstanceGet)
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
c.JSON(http.StatusOK, domainBlocks)
|
c.JSON(http.StatusOK, domainBlocks)
|
||||||
|
@ -179,13 +179,13 @@ func (m *Module) DomainBlocksPOSTHandler(c *gin.Context) {
|
||||||
// we're just creating one block
|
// we're just creating one block
|
||||||
domainBlock, errWithCode := m.processor.AdminDomainBlockCreate(c.Request.Context(), authed, form)
|
domainBlock, errWithCode := m.processor.AdminDomainBlockCreate(c.Request.Context(), authed, form)
|
||||||
if errWithCode != nil {
|
if errWithCode != nil {
|
||||||
api.ErrorHandler(c, errWithCode, m.processor.InstanceGet)
|
apiutil.ErrorHandler(c, errWithCode, m.processor.InstanceGet)
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
c.JSON(http.StatusOK, domainBlock)
|
c.JSON(http.StatusOK, domainBlock)
|
||||||
}
|
}
|
||||||
|
|
||||||
func validateCreateDomainBlock(form *model.DomainBlockCreateRequest, imp bool) error {
|
func validateCreateDomainBlock(form *apimodel.DomainBlockCreateRequest, imp bool) error {
|
||||||
if imp {
|
if imp {
|
||||||
if form.Domains.Size == 0 {
|
if form.Domains.Size == 0 {
|
||||||
return errors.New("import was specified but list of domains is empty")
|
return errors.New("import was specified but list of domains is empty")
|
||||||
|
|
|
@ -24,7 +24,7 @@ import (
|
||||||
"net/http"
|
"net/http"
|
||||||
|
|
||||||
"github.com/gin-gonic/gin"
|
"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/gtserror"
|
||||||
"github.com/superseriousbusiness/gotosocial/internal/oauth"
|
"github.com/superseriousbusiness/gotosocial/internal/oauth"
|
||||||
)
|
)
|
||||||
|
@ -72,31 +72,31 @@ import (
|
||||||
func (m *Module) DomainBlockDELETEHandler(c *gin.Context) {
|
func (m *Module) DomainBlockDELETEHandler(c *gin.Context) {
|
||||||
authed, err := oauth.Authed(c, true, true, true, true)
|
authed, err := oauth.Authed(c, true, true, true, true)
|
||||||
if err != nil {
|
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
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
if !*authed.User.Admin {
|
if !*authed.User.Admin {
|
||||||
err := fmt.Errorf("user %s not an admin", authed.User.ID)
|
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
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
if _, err := api.NegotiateAccept(c, api.JSONAcceptHeaders...); err != nil {
|
if _, err := apiutil.NegotiateAccept(c, apiutil.JSONAcceptHeaders...); 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
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
domainBlockID := c.Param(IDKey)
|
domainBlockID := c.Param(IDKey)
|
||||||
if domainBlockID == "" {
|
if domainBlockID == "" {
|
||||||
err := errors.New("no domain block id specified")
|
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
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
domainBlock, errWithCode := m.processor.AdminDomainBlockDelete(c.Request.Context(), authed, domainBlockID)
|
domainBlock, errWithCode := m.processor.AdminDomainBlockDelete(c.Request.Context(), authed, domainBlockID)
|
||||||
if errWithCode != nil {
|
if errWithCode != nil {
|
||||||
api.ErrorHandler(c, errWithCode, m.processor.InstanceGet)
|
apiutil.ErrorHandler(c, errWithCode, m.processor.InstanceGet)
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -25,7 +25,7 @@ import (
|
||||||
"strconv"
|
"strconv"
|
||||||
|
|
||||||
"github.com/gin-gonic/gin"
|
"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/gtserror"
|
||||||
"github.com/superseriousbusiness/gotosocial/internal/oauth"
|
"github.com/superseriousbusiness/gotosocial/internal/oauth"
|
||||||
)
|
)
|
||||||
|
@ -73,25 +73,25 @@ import (
|
||||||
func (m *Module) DomainBlockGETHandler(c *gin.Context) {
|
func (m *Module) DomainBlockGETHandler(c *gin.Context) {
|
||||||
authed, err := oauth.Authed(c, true, true, true, true)
|
authed, err := oauth.Authed(c, true, true, true, true)
|
||||||
if err != nil {
|
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
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
if !*authed.User.Admin {
|
if !*authed.User.Admin {
|
||||||
err := fmt.Errorf("user %s not an admin", authed.User.ID)
|
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
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
if _, err := api.NegotiateAccept(c, api.JSONAcceptHeaders...); err != nil {
|
if _, err := apiutil.NegotiateAccept(c, apiutil.JSONAcceptHeaders...); 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
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
domainBlockID := c.Param(IDKey)
|
domainBlockID := c.Param(IDKey)
|
||||||
if domainBlockID == "" {
|
if domainBlockID == "" {
|
||||||
err := errors.New("no domain block id specified")
|
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
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -101,7 +101,7 @@ func (m *Module) DomainBlockGETHandler(c *gin.Context) {
|
||||||
i, err := strconv.ParseBool(exportString)
|
i, err := strconv.ParseBool(exportString)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
err := fmt.Errorf("error parsing %s: %s", ExportQueryKey, err)
|
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
|
return
|
||||||
}
|
}
|
||||||
export = i
|
export = i
|
||||||
|
@ -109,7 +109,7 @@ func (m *Module) DomainBlockGETHandler(c *gin.Context) {
|
||||||
|
|
||||||
domainBlock, errWithCode := m.processor.AdminDomainBlockGet(c.Request.Context(), authed, domainBlockID, export)
|
domainBlock, errWithCode := m.processor.AdminDomainBlockGet(c.Request.Context(), authed, domainBlockID, export)
|
||||||
if errWithCode != nil {
|
if errWithCode != nil {
|
||||||
api.ErrorHandler(c, errWithCode, m.processor.InstanceGet)
|
apiutil.ErrorHandler(c, errWithCode, m.processor.InstanceGet)
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -24,7 +24,7 @@ import (
|
||||||
"strconv"
|
"strconv"
|
||||||
|
|
||||||
"github.com/gin-gonic/gin"
|
"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/gtserror"
|
||||||
"github.com/superseriousbusiness/gotosocial/internal/oauth"
|
"github.com/superseriousbusiness/gotosocial/internal/oauth"
|
||||||
)
|
)
|
||||||
|
@ -78,18 +78,18 @@ import (
|
||||||
func (m *Module) DomainBlocksGETHandler(c *gin.Context) {
|
func (m *Module) DomainBlocksGETHandler(c *gin.Context) {
|
||||||
authed, err := oauth.Authed(c, true, true, true, true)
|
authed, err := oauth.Authed(c, true, true, true, true)
|
||||||
if err != nil {
|
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
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
if !*authed.User.Admin {
|
if !*authed.User.Admin {
|
||||||
err := fmt.Errorf("user %s not an admin", authed.User.ID)
|
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
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
if _, err := api.NegotiateAccept(c, api.JSONAcceptHeaders...); err != nil {
|
if _, err := apiutil.NegotiateAccept(c, apiutil.JSONAcceptHeaders...); 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
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -99,7 +99,7 @@ func (m *Module) DomainBlocksGETHandler(c *gin.Context) {
|
||||||
i, err := strconv.ParseBool(exportString)
|
i, err := strconv.ParseBool(exportString)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
err := fmt.Errorf("error parsing %s: %s", ExportQueryKey, err)
|
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
|
return
|
||||||
}
|
}
|
||||||
export = i
|
export = i
|
||||||
|
@ -107,7 +107,7 @@ func (m *Module) DomainBlocksGETHandler(c *gin.Context) {
|
||||||
|
|
||||||
domainBlocks, errWithCode := m.processor.AdminDomainBlocksGet(c.Request.Context(), authed, export)
|
domainBlocks, errWithCode := m.processor.AdminDomainBlocksGet(c.Request.Context(), authed, export)
|
||||||
if errWithCode != nil {
|
if errWithCode != nil {
|
||||||
api.ErrorHandler(c, errWithCode, m.processor.InstanceGet)
|
apiutil.ErrorHandler(c, errWithCode, m.processor.InstanceGet)
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -23,7 +23,7 @@ import (
|
||||||
"net/http"
|
"net/http"
|
||||||
|
|
||||||
"github.com/gin-gonic/gin"
|
"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/gtserror"
|
||||||
"github.com/superseriousbusiness/gotosocial/internal/oauth"
|
"github.com/superseriousbusiness/gotosocial/internal/oauth"
|
||||||
)
|
)
|
||||||
|
@ -69,24 +69,24 @@ import (
|
||||||
func (m *Module) EmojiCategoriesGETHandler(c *gin.Context) {
|
func (m *Module) EmojiCategoriesGETHandler(c *gin.Context) {
|
||||||
authed, err := oauth.Authed(c, true, true, true, true)
|
authed, err := oauth.Authed(c, true, true, true, true)
|
||||||
if err != nil {
|
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
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
if !*authed.User.Admin {
|
if !*authed.User.Admin {
|
||||||
err := fmt.Errorf("user %s not an admin", authed.User.ID)
|
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
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
if _, err := api.NegotiateAccept(c, api.JSONAcceptHeaders...); err != nil {
|
if _, err := apiutil.NegotiateAccept(c, apiutil.JSONAcceptHeaders...); 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
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
categories, errWithCode := m.processor.AdminEmojiCategoriesGet(c.Request.Context())
|
categories, errWithCode := m.processor.AdminEmojiCategoriesGet(c.Request.Context())
|
||||||
if errWithCode != nil {
|
if errWithCode != nil {
|
||||||
api.ErrorHandler(c, errWithCode, m.processor.InstanceGet)
|
apiutil.ErrorHandler(c, errWithCode, m.processor.InstanceGet)
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -24,8 +24,8 @@ import (
|
||||||
"net/http"
|
"net/http"
|
||||||
|
|
||||||
"github.com/gin-gonic/gin"
|
"github.com/gin-gonic/gin"
|
||||||
"github.com/superseriousbusiness/gotosocial/internal/api"
|
apimodel "github.com/superseriousbusiness/gotosocial/internal/api/model"
|
||||||
"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/config"
|
||||||
"github.com/superseriousbusiness/gotosocial/internal/gtserror"
|
"github.com/superseriousbusiness/gotosocial/internal/gtserror"
|
||||||
"github.com/superseriousbusiness/gotosocial/internal/oauth"
|
"github.com/superseriousbusiness/gotosocial/internal/oauth"
|
||||||
|
@ -100,42 +100,42 @@ import (
|
||||||
func (m *Module) EmojiCreatePOSTHandler(c *gin.Context) {
|
func (m *Module) EmojiCreatePOSTHandler(c *gin.Context) {
|
||||||
authed, err := oauth.Authed(c, true, true, true, true)
|
authed, err := oauth.Authed(c, true, true, true, true)
|
||||||
if err != nil {
|
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
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
if !*authed.User.Admin {
|
if !*authed.User.Admin {
|
||||||
err := fmt.Errorf("user %s not an admin", authed.User.ID)
|
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
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
if _, err := api.NegotiateAccept(c, api.JSONAcceptHeaders...); err != nil {
|
if _, err := apiutil.NegotiateAccept(c, apiutil.JSONAcceptHeaders...); 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
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
form := &model.EmojiCreateRequest{}
|
form := &apimodel.EmojiCreateRequest{}
|
||||||
if err := c.ShouldBind(form); err != nil {
|
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
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
if err := validateCreateEmoji(form); err != nil {
|
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
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
apiEmoji, errWithCode := m.processor.AdminEmojiCreate(c.Request.Context(), authed, form)
|
apiEmoji, errWithCode := m.processor.AdminEmojiCreate(c.Request.Context(), authed, form)
|
||||||
if errWithCode != nil {
|
if errWithCode != nil {
|
||||||
api.ErrorHandler(c, errWithCode, m.processor.InstanceGet)
|
apiutil.ErrorHandler(c, errWithCode, m.processor.InstanceGet)
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
c.JSON(http.StatusOK, apiEmoji)
|
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 {
|
if form.Image == nil || form.Image.Size == 0 {
|
||||||
return errors.New("no emoji given")
|
return errors.New("no emoji given")
|
||||||
}
|
}
|
||||||
|
|
|
@ -24,7 +24,7 @@ import (
|
||||||
"net/http"
|
"net/http"
|
||||||
|
|
||||||
"github.com/gin-gonic/gin"
|
"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/gtserror"
|
||||||
"github.com/superseriousbusiness/gotosocial/internal/oauth"
|
"github.com/superseriousbusiness/gotosocial/internal/oauth"
|
||||||
)
|
)
|
||||||
|
@ -78,31 +78,31 @@ import (
|
||||||
func (m *Module) EmojiDELETEHandler(c *gin.Context) {
|
func (m *Module) EmojiDELETEHandler(c *gin.Context) {
|
||||||
authed, err := oauth.Authed(c, true, true, true, true)
|
authed, err := oauth.Authed(c, true, true, true, true)
|
||||||
if err != nil {
|
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
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
if !*authed.User.Admin {
|
if !*authed.User.Admin {
|
||||||
err := fmt.Errorf("user %s not an admin", authed.User.ID)
|
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
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
if _, err := api.NegotiateAccept(c, api.JSONAcceptHeaders...); err != nil {
|
if _, err := apiutil.NegotiateAccept(c, apiutil.JSONAcceptHeaders...); 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
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
emojiID := c.Param(IDKey)
|
emojiID := c.Param(IDKey)
|
||||||
if emojiID == "" {
|
if emojiID == "" {
|
||||||
err := errors.New("no emoji id specified")
|
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
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
emoji, errWithCode := m.processor.AdminEmojiDelete(c.Request.Context(), authed, emojiID)
|
emoji, errWithCode := m.processor.AdminEmojiDelete(c.Request.Context(), authed, emojiID)
|
||||||
if errWithCode != nil {
|
if errWithCode != nil {
|
||||||
api.ErrorHandler(c, errWithCode, m.processor.InstanceGet)
|
apiutil.ErrorHandler(c, errWithCode, m.processor.InstanceGet)
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -24,7 +24,7 @@ import (
|
||||||
"net/http"
|
"net/http"
|
||||||
|
|
||||||
"github.com/gin-gonic/gin"
|
"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/gtserror"
|
||||||
"github.com/superseriousbusiness/gotosocial/internal/oauth"
|
"github.com/superseriousbusiness/gotosocial/internal/oauth"
|
||||||
)
|
)
|
||||||
|
@ -68,31 +68,31 @@ import (
|
||||||
func (m *Module) EmojiGETHandler(c *gin.Context) {
|
func (m *Module) EmojiGETHandler(c *gin.Context) {
|
||||||
authed, err := oauth.Authed(c, true, true, true, true)
|
authed, err := oauth.Authed(c, true, true, true, true)
|
||||||
if err != nil {
|
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
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
if !*authed.User.Admin {
|
if !*authed.User.Admin {
|
||||||
err := fmt.Errorf("user %s not an admin", authed.User.ID)
|
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
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
if _, err := api.NegotiateAccept(c, api.JSONAcceptHeaders...); err != nil {
|
if _, err := apiutil.NegotiateAccept(c, apiutil.JSONAcceptHeaders...); 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
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
emojiID := c.Param(IDKey)
|
emojiID := c.Param(IDKey)
|
||||||
if emojiID == "" {
|
if emojiID == "" {
|
||||||
err := errors.New("no emoji id specified")
|
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
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
emoji, errWithCode := m.processor.AdminEmojiGet(c.Request.Context(), authed, emojiID)
|
emoji, errWithCode := m.processor.AdminEmojiGet(c.Request.Context(), authed, emojiID)
|
||||||
if errWithCode != nil {
|
if errWithCode != nil {
|
||||||
api.ErrorHandler(c, errWithCode, m.processor.InstanceGet)
|
apiutil.ErrorHandler(c, errWithCode, m.processor.InstanceGet)
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -25,7 +25,7 @@ import (
|
||||||
"strings"
|
"strings"
|
||||||
|
|
||||||
"github.com/gin-gonic/gin"
|
"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/config"
|
||||||
"github.com/superseriousbusiness/gotosocial/internal/db"
|
"github.com/superseriousbusiness/gotosocial/internal/db"
|
||||||
"github.com/superseriousbusiness/gotosocial/internal/gtserror"
|
"github.com/superseriousbusiness/gotosocial/internal/gtserror"
|
||||||
|
@ -125,18 +125,18 @@ import (
|
||||||
func (m *Module) EmojisGETHandler(c *gin.Context) {
|
func (m *Module) EmojisGETHandler(c *gin.Context) {
|
||||||
authed, err := oauth.Authed(c, true, true, true, true)
|
authed, err := oauth.Authed(c, true, true, true, true)
|
||||||
if err != nil {
|
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
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
if !*authed.User.Admin {
|
if !*authed.User.Admin {
|
||||||
err := fmt.Errorf("user %s not an admin", authed.User.ID)
|
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
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
if _, err := api.NegotiateAccept(c, api.JSONAcceptHeaders...); err != nil {
|
if _, err := apiutil.NegotiateAccept(c, apiutil.JSONAcceptHeaders...); 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
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -149,7 +149,7 @@ func (m *Module) EmojisGETHandler(c *gin.Context) {
|
||||||
i, err := strconv.ParseInt(limitString, 10, 32)
|
i, err := strconv.ParseInt(limitString, 10, 32)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
err := fmt.Errorf("error parsing %s: %s", LimitKey, err)
|
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
|
return
|
||||||
}
|
}
|
||||||
limit = int(i)
|
limit = int(i)
|
||||||
|
@ -177,7 +177,7 @@ func (m *Module) EmojisGETHandler(c *gin.Context) {
|
||||||
shortcode = strings.Trim(filter[10:], ":") // remove any errant ":"
|
shortcode = strings.Trim(filter[10:], ":") // remove any errant ":"
|
||||||
default:
|
default:
|
||||||
err := fmt.Errorf("filter %s not recognized; accepted values are 'domain:[domain]', 'disabled', 'enabled', 'shortcode:[shortcode]'", filter)
|
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
|
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)
|
resp, errWithCode := m.processor.AdminEmojisGet(c.Request.Context(), authed, domain, includeDisabled, includeEnabled, shortcode, maxShortcodeDomain, minShortcodeDomain, limit)
|
||||||
if errWithCode != nil {
|
if errWithCode != nil {
|
||||||
api.ErrorHandler(c, errWithCode, m.processor.InstanceGet)
|
apiutil.ErrorHandler(c, errWithCode, m.processor.InstanceGet)
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -25,8 +25,8 @@ import (
|
||||||
"strings"
|
"strings"
|
||||||
|
|
||||||
"github.com/gin-gonic/gin"
|
"github.com/gin-gonic/gin"
|
||||||
"github.com/superseriousbusiness/gotosocial/internal/api"
|
apimodel "github.com/superseriousbusiness/gotosocial/internal/api/model"
|
||||||
"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/config"
|
||||||
"github.com/superseriousbusiness/gotosocial/internal/gtserror"
|
"github.com/superseriousbusiness/gotosocial/internal/gtserror"
|
||||||
"github.com/superseriousbusiness/gotosocial/internal/oauth"
|
"github.com/superseriousbusiness/gotosocial/internal/oauth"
|
||||||
|
@ -123,42 +123,42 @@ import (
|
||||||
func (m *Module) EmojiPATCHHandler(c *gin.Context) {
|
func (m *Module) EmojiPATCHHandler(c *gin.Context) {
|
||||||
authed, err := oauth.Authed(c, true, true, true, true)
|
authed, err := oauth.Authed(c, true, true, true, true)
|
||||||
if err != nil {
|
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
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
if !*authed.User.Admin {
|
if !*authed.User.Admin {
|
||||||
err := fmt.Errorf("user %s not an admin", authed.User.ID)
|
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
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
if _, err := api.NegotiateAccept(c, api.JSONAcceptHeaders...); err != nil {
|
if _, err := apiutil.NegotiateAccept(c, apiutil.JSONAcceptHeaders...); 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
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
emojiID := c.Param(IDKey)
|
emojiID := c.Param(IDKey)
|
||||||
if emojiID == "" {
|
if emojiID == "" {
|
||||||
err := errors.New("no emoji id specified")
|
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
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
form := &model.EmojiUpdateRequest{}
|
form := &apimodel.EmojiUpdateRequest{}
|
||||||
if err := c.ShouldBind(form); err != nil {
|
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
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
if err := validateUpdateEmoji(form); err != nil {
|
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
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
emoji, errWithCode := m.processor.AdminEmojiUpdate(c.Request.Context(), emojiID, form)
|
emoji, errWithCode := m.processor.AdminEmojiUpdate(c.Request.Context(), emojiID, form)
|
||||||
if errWithCode != nil {
|
if errWithCode != nil {
|
||||||
api.ErrorHandler(c, errWithCode, m.processor.InstanceGet)
|
apiutil.ErrorHandler(c, errWithCode, m.processor.InstanceGet)
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -166,14 +166,14 @@ func (m *Module) EmojiPATCHHandler(c *gin.Context) {
|
||||||
}
|
}
|
||||||
|
|
||||||
// do a first pass on the form here
|
// 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
|
// check + normalize update type so we don't need
|
||||||
// to do this trimming + lowercasing again later
|
// to do this trimming + lowercasing again later
|
||||||
switch strings.TrimSpace(strings.ToLower(string(form.Type))) {
|
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
|
// no params required for this one, so don't bother checking
|
||||||
form.Type = model.EmojiUpdateDisable
|
form.Type = apimodel.EmojiUpdateDisable
|
||||||
case string(model.EmojiUpdateCopy):
|
case string(apimodel.EmojiUpdateCopy):
|
||||||
// need at least a valid shortcode when doing a copy
|
// need at least a valid shortcode when doing a copy
|
||||||
if form.Shortcode == nil {
|
if form.Shortcode == nil {
|
||||||
return errors.New("emoji action type was 'copy' but no shortcode was provided")
|
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
|
form.Type = apimodel.EmojiUpdateCopy
|
||||||
case string(model.EmojiUpdateModify):
|
case string(apimodel.EmojiUpdateModify):
|
||||||
// need either image or category name for modify
|
// need either image or category name for modify
|
||||||
hasImage := form.Image != nil && form.Image.Size != 0
|
hasImage := form.Image != nil && form.Image.Size != 0
|
||||||
hasCategoryName := form.CategoryName != nil
|
hasCategoryName := form.CategoryName != nil
|
||||||
|
@ -212,7 +212,7 @@ func validateUpdateEmoji(form *model.EmojiUpdateRequest) error {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
form.Type = model.EmojiUpdateModify
|
form.Type = apimodel.EmojiUpdateModify
|
||||||
default:
|
default:
|
||||||
return errors.New("emoji action type must be one of 'disable', 'copy', 'modify'")
|
return errors.New("emoji action type must be one of 'disable', 'copy', 'modify'")
|
||||||
}
|
}
|
||||||
|
|
|
@ -23,8 +23,8 @@ import (
|
||||||
"net/http"
|
"net/http"
|
||||||
|
|
||||||
"github.com/gin-gonic/gin"
|
"github.com/gin-gonic/gin"
|
||||||
"github.com/superseriousbusiness/gotosocial/internal/api"
|
apimodel "github.com/superseriousbusiness/gotosocial/internal/api/model"
|
||||||
"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/config"
|
||||||
"github.com/superseriousbusiness/gotosocial/internal/gtserror"
|
"github.com/superseriousbusiness/gotosocial/internal/gtserror"
|
||||||
"github.com/superseriousbusiness/gotosocial/internal/oauth"
|
"github.com/superseriousbusiness/gotosocial/internal/oauth"
|
||||||
|
@ -71,19 +71,19 @@ import (
|
||||||
func (m *Module) MediaCleanupPOSTHandler(c *gin.Context) {
|
func (m *Module) MediaCleanupPOSTHandler(c *gin.Context) {
|
||||||
authed, err := oauth.Authed(c, true, true, true, true)
|
authed, err := oauth.Authed(c, true, true, true, true)
|
||||||
if err != nil {
|
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
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
if !*authed.User.Admin {
|
if !*authed.User.Admin {
|
||||||
err := fmt.Errorf("user %s not an admin", authed.User.ID)
|
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
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
form := &model.MediaCleanupRequest{}
|
form := &apimodel.MediaCleanupRequest{}
|
||||||
if err := c.ShouldBind(form); err != nil {
|
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
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -98,7 +98,7 @@ func (m *Module) MediaCleanupPOSTHandler(c *gin.Context) {
|
||||||
}
|
}
|
||||||
|
|
||||||
if errWithCode := m.processor.AdminMediaPrune(c.Request.Context(), remoteCacheDays); errWithCode != nil {
|
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
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -23,7 +23,7 @@ import (
|
||||||
"net/http"
|
"net/http"
|
||||||
|
|
||||||
"github.com/gin-gonic/gin"
|
"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/gtserror"
|
||||||
"github.com/superseriousbusiness/gotosocial/internal/oauth"
|
"github.com/superseriousbusiness/gotosocial/internal/oauth"
|
||||||
)
|
)
|
||||||
|
@ -74,18 +74,18 @@ import (
|
||||||
func (m *Module) MediaRefetchPOSTHandler(c *gin.Context) {
|
func (m *Module) MediaRefetchPOSTHandler(c *gin.Context) {
|
||||||
authed, err := oauth.Authed(c, true, true, true, true)
|
authed, err := oauth.Authed(c, true, true, true, true)
|
||||||
if err != nil {
|
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
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
if !*authed.User.Admin {
|
if !*authed.User.Admin {
|
||||||
err := fmt.Errorf("user %s not an admin", authed.User.ID)
|
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
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
if errWithCode := m.processor.AdminMediaRefetch(c.Request.Context(), authed, c.Query(DomainQueryKey)); errWithCode != nil {
|
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
|
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/>.
|
along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||||
*/
|
*/
|
||||||
|
|
||||||
package app
|
package apps
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"fmt"
|
"fmt"
|
||||||
"net/http"
|
"net/http"
|
||||||
|
|
||||||
"github.com/gin-gonic/gin"
|
"github.com/gin-gonic/gin"
|
||||||
"github.com/superseriousbusiness/gotosocial/internal/api"
|
apimodel "github.com/superseriousbusiness/gotosocial/internal/api/model"
|
||||||
"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/gtserror"
|
||||||
"github.com/superseriousbusiness/gotosocial/internal/oauth"
|
"github.com/superseriousbusiness/gotosocial/internal/oauth"
|
||||||
)
|
)
|
||||||
|
@ -77,48 +77,48 @@ const (
|
||||||
func (m *Module) AppsPOSTHandler(c *gin.Context) {
|
func (m *Module) AppsPOSTHandler(c *gin.Context) {
|
||||||
authed, err := oauth.Authed(c, false, false, false, false)
|
authed, err := oauth.Authed(c, false, false, false, false)
|
||||||
if err != nil {
|
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
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
if _, err := api.NegotiateAccept(c, api.JSONAcceptHeaders...); err != nil {
|
if _, err := apiutil.NegotiateAccept(c, apiutil.JSONAcceptHeaders...); 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
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
form := &model.ApplicationCreateRequest{}
|
form := &apimodel.ApplicationCreateRequest{}
|
||||||
if err := c.ShouldBind(form); err != nil {
|
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
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
if len([]rune(form.ClientName)) > formFieldLen {
|
if len([]rune(form.ClientName)) > formFieldLen {
|
||||||
err := fmt.Errorf("client_name must be less than %d characters", 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
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
if len([]rune(form.RedirectURIs)) > formRedirectLen {
|
if len([]rune(form.RedirectURIs)) > formRedirectLen {
|
||||||
err := fmt.Errorf("redirect_uris must be less than %d characters", 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
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
if len([]rune(form.Scopes)) > formFieldLen {
|
if len([]rune(form.Scopes)) > formFieldLen {
|
||||||
err := fmt.Errorf("scopes must be less than %d characters", 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
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
if len([]rune(form.Website)) > formFieldLen {
|
if len([]rune(form.Website)) > formFieldLen {
|
||||||
err := fmt.Errorf("website must be less than %d characters", 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
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
apiApp, errWithCode := m.processor.AppCreate(c.Request.Context(), authed, form)
|
apiApp, errWithCode := m.processor.AppCreate(c.Request.Context(), authed, form)
|
||||||
if errWithCode != nil {
|
if errWithCode != nil {
|
||||||
api.ErrorHandler(c, errWithCode, m.processor.InstanceGet)
|
apiutil.ErrorHandler(c, errWithCode, m.processor.InstanceGet)
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
|
@ -16,33 +16,28 @@
|
||||||
along with this program. If not, see <http://www.gnu.org/licenses/>.
|
along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||||
*/
|
*/
|
||||||
|
|
||||||
package app
|
package apps
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"net/http"
|
"net/http"
|
||||||
|
|
||||||
"github.com/superseriousbusiness/gotosocial/internal/api"
|
"github.com/gin-gonic/gin"
|
||||||
"github.com/superseriousbusiness/gotosocial/internal/processing"
|
"github.com/superseriousbusiness/gotosocial/internal/processing"
|
||||||
"github.com/superseriousbusiness/gotosocial/internal/router"
|
|
||||||
)
|
)
|
||||||
|
|
||||||
// BasePath is the base path for this api module
|
// BasePath is the base path for this api module, excluding the api prefix
|
||||||
const BasePath = "/api/v1/apps"
|
const BasePath = "/v1/apps"
|
||||||
|
|
||||||
// Module implements the ClientAPIModule interface for requests relating to registering/removing applications
|
|
||||||
type Module struct {
|
type Module struct {
|
||||||
processor processing.Processor
|
processor processing.Processor
|
||||||
}
|
}
|
||||||
|
|
||||||
// New returns a new auth module
|
func New(processor processing.Processor) *Module {
|
||||||
func New(processor processing.Processor) api.ClientModule {
|
|
||||||
return &Module{
|
return &Module{
|
||||||
processor: processor,
|
processor: processor,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// Route satisfies the RESTAPIModule interface
|
func (m *Module) Route(attachHandler func(method string, path string, f ...gin.HandlerFunc) gin.IRoutes) {
|
||||||
func (m *Module) Route(s router.Router) error {
|
attachHandler(http.MethodPost, BasePath, m.AppsPOSTHandler)
|
||||||
s.AttachHandler(http.MethodPost, BasePath, m.AppsPOSTHandler)
|
|
||||||
return nil
|
|
||||||
}
|
}
|
|
@ -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 @@ package blocks
|
||||||
import (
|
import (
|
||||||
"net/http"
|
"net/http"
|
||||||
|
|
||||||
"github.com/superseriousbusiness/gotosocial/internal/api"
|
"github.com/gin-gonic/gin"
|
||||||
"github.com/superseriousbusiness/gotosocial/internal/processing"
|
"github.com/superseriousbusiness/gotosocial/internal/processing"
|
||||||
"github.com/superseriousbusiness/gotosocial/internal/router"
|
|
||||||
)
|
)
|
||||||
|
|
||||||
const (
|
const (
|
||||||
// BasePath is the base URI path for serving favourites
|
// BasePath is the base URI path for serving blocks, minus the api prefix.
|
||||||
BasePath = "/api/v1/blocks"
|
BasePath = "/v1/blocks"
|
||||||
|
|
||||||
// MaxIDKey is the url query for setting a max ID to return
|
// MaxIDKey is the url query for setting a max ID to return
|
||||||
MaxIDKey = "max_id"
|
MaxIDKey = "max_id"
|
||||||
|
@ -38,20 +37,16 @@ const (
|
||||||
LimitKey = "limit"
|
LimitKey = "limit"
|
||||||
)
|
)
|
||||||
|
|
||||||
// Module implements the ClientAPIModule interface for everything relating to viewing blocks
|
|
||||||
type Module struct {
|
type Module struct {
|
||||||
processor processing.Processor
|
processor processing.Processor
|
||||||
}
|
}
|
||||||
|
|
||||||
// New returns a new blocks module
|
func New(processor processing.Processor) *Module {
|
||||||
func New(processor processing.Processor) api.ClientModule {
|
|
||||||
return &Module{
|
return &Module{
|
||||||
processor: processor,
|
processor: processor,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// Route attaches all routes from this module to the given router
|
func (m *Module) Route(attachHandler func(method string, path string, f ...gin.HandlerFunc) gin.IRoutes) {
|
||||||
func (m *Module) Route(r router.Router) error {
|
attachHandler(http.MethodGet, BasePath, m.BlocksGETHandler)
|
||||||
r.AttachHandler(http.MethodGet, BasePath, m.BlocksGETHandler)
|
|
||||||
return nil
|
|
||||||
}
|
}
|
||||||
|
|
|
@ -24,7 +24,7 @@ import (
|
||||||
"strconv"
|
"strconv"
|
||||||
|
|
||||||
"github.com/gin-gonic/gin"
|
"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/gtserror"
|
||||||
"github.com/superseriousbusiness/gotosocial/internal/oauth"
|
"github.com/superseriousbusiness/gotosocial/internal/oauth"
|
||||||
)
|
)
|
||||||
|
@ -96,12 +96,12 @@ import (
|
||||||
func (m *Module) BlocksGETHandler(c *gin.Context) {
|
func (m *Module) BlocksGETHandler(c *gin.Context) {
|
||||||
authed, err := oauth.Authed(c, true, true, true, true)
|
authed, err := oauth.Authed(c, true, true, true, true)
|
||||||
if err != nil {
|
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
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
if _, err := api.NegotiateAccept(c, api.JSONAcceptHeaders...); err != nil {
|
if _, err := apiutil.NegotiateAccept(c, apiutil.JSONAcceptHeaders...); 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
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -123,7 +123,7 @@ func (m *Module) BlocksGETHandler(c *gin.Context) {
|
||||||
i, err := strconv.ParseInt(limitString, 10, 32)
|
i, err := strconv.ParseInt(limitString, 10, 32)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
err := fmt.Errorf("error parsing %s: %s", LimitKey, err)
|
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
|
return
|
||||||
}
|
}
|
||||||
limit = int(i)
|
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)
|
resp, errWithCode := m.processor.BlocksGet(c.Request.Context(), authed, maxID, sinceID, limit)
|
||||||
if errWithCode != nil {
|
if errWithCode != nil {
|
||||||
api.ErrorHandler(c, errWithCode, m.processor.InstanceGet)
|
apiutil.ErrorHandler(c, errWithCode, m.processor.InstanceGet)
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -21,9 +21,8 @@ package bookmarks
|
||||||
import (
|
import (
|
||||||
"net/http"
|
"net/http"
|
||||||
|
|
||||||
"github.com/superseriousbusiness/gotosocial/internal/api"
|
"github.com/gin-gonic/gin"
|
||||||
"github.com/superseriousbusiness/gotosocial/internal/processing"
|
"github.com/superseriousbusiness/gotosocial/internal/processing"
|
||||||
"github.com/superseriousbusiness/gotosocial/internal/router"
|
|
||||||
)
|
)
|
||||||
|
|
||||||
const (
|
const (
|
||||||
|
@ -31,20 +30,16 @@ const (
|
||||||
BasePath = "/api/v1/bookmarks"
|
BasePath = "/api/v1/bookmarks"
|
||||||
)
|
)
|
||||||
|
|
||||||
// Module implements the ClientAPIModule interface for everything related to bookmarks
|
|
||||||
type Module struct {
|
type Module struct {
|
||||||
processor processing.Processor
|
processor processing.Processor
|
||||||
}
|
}
|
||||||
|
|
||||||
// New returns a new emoji module
|
func New(processor processing.Processor) *Module {
|
||||||
func New(processor processing.Processor) api.ClientModule {
|
|
||||||
return &Module{
|
return &Module{
|
||||||
processor: processor,
|
processor: processor,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// Route attaches all routes from this module to the given router
|
func (m *Module) Route(attachHandler func(method string, path string, f ...gin.HandlerFunc) gin.IRoutes) {
|
||||||
func (m *Module) Route(r router.Router) error {
|
attachHandler(http.MethodGet, BasePath, m.BookmarksGETHandler)
|
||||||
r.AttachHandler(http.MethodGet, BasePath, m.BookmarksGETHandler)
|
|
||||||
return nil
|
|
||||||
}
|
}
|
||||||
|
|
|
@ -29,7 +29,7 @@ import (
|
||||||
|
|
||||||
"github.com/stretchr/testify/suite"
|
"github.com/stretchr/testify/suite"
|
||||||
"github.com/superseriousbusiness/gotosocial/internal/api/client/bookmarks"
|
"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/api/model"
|
||||||
"github.com/superseriousbusiness/gotosocial/internal/concurrency"
|
"github.com/superseriousbusiness/gotosocial/internal/concurrency"
|
||||||
"github.com/superseriousbusiness/gotosocial/internal/db"
|
"github.com/superseriousbusiness/gotosocial/internal/db"
|
||||||
|
@ -67,7 +67,7 @@ type BookmarkTestSuite struct {
|
||||||
testFollows map[string]*gtsmodel.Follow
|
testFollows map[string]*gtsmodel.Follow
|
||||||
|
|
||||||
// module being tested
|
// module being tested
|
||||||
statusModule *status.Module
|
statusModule *statuses.Module
|
||||||
bookmarkModule *bookmarks.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.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.emailSender = testrig.NewEmailSender("../../../../web/template/", nil)
|
||||||
suite.processor = testrig.NewTestProcessor(suite.db, suite.storage, suite.federator, suite.emailSender, suite.mediaManager, clientWorker, fedWorker)
|
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.statusModule = statuses.New(suite.processor)
|
||||||
suite.bookmarkModule = bookmarks.New(suite.processor).(*bookmarks.Module)
|
suite.bookmarkModule = bookmarks.New(suite.processor)
|
||||||
|
|
||||||
suite.NoError(suite.processor.Start())
|
suite.NoError(suite.processor.Start())
|
||||||
}
|
}
|
||||||
|
@ -123,7 +123,7 @@ func (suite *BookmarkTestSuite) TestGetBookmark() {
|
||||||
ctx.Set(oauth.SessionAuthorizedToken, oauthToken)
|
ctx.Set(oauth.SessionAuthorizedToken, oauthToken)
|
||||||
ctx.Set(oauth.SessionAuthorizedUser, suite.testUsers["local_account_1"])
|
ctx.Set(oauth.SessionAuthorizedUser, suite.testUsers["local_account_1"])
|
||||||
ctx.Set(oauth.SessionAuthorizedAccount, suite.testAccounts["local_account_1"])
|
ctx.Set(oauth.SessionAuthorizedAccount, suite.testAccounts["local_account_1"])
|
||||||
ctx.Request = httptest.NewRequest(http.MethodPost, fmt.Sprintf("http://localhost:8080%s", strings.Replace(status.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")
|
ctx.Request.Header.Set("accept", "application/json")
|
||||||
|
|
||||||
suite.bookmarkModule.BookmarksGETHandler(ctx)
|
suite.bookmarkModule.BookmarksGETHandler(ctx)
|
||||||
|
|
|
@ -6,7 +6,7 @@ import (
|
||||||
"strconv"
|
"strconv"
|
||||||
|
|
||||||
"github.com/gin-gonic/gin"
|
"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/gtserror"
|
||||||
"github.com/superseriousbusiness/gotosocial/internal/oauth"
|
"github.com/superseriousbusiness/gotosocial/internal/oauth"
|
||||||
)
|
)
|
||||||
|
@ -56,12 +56,12 @@ const (
|
||||||
func (m *Module) BookmarksGETHandler(c *gin.Context) {
|
func (m *Module) BookmarksGETHandler(c *gin.Context) {
|
||||||
authed, err := oauth.Authed(c, true, true, true, true)
|
authed, err := oauth.Authed(c, true, true, true, true)
|
||||||
if err != nil {
|
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
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
if _, err := api.NegotiateAccept(c, api.JSONAcceptHeaders...); err != nil {
|
if _, err := apiutil.NegotiateAccept(c, apiutil.JSONAcceptHeaders...); 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
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -71,7 +71,7 @@ func (m *Module) BookmarksGETHandler(c *gin.Context) {
|
||||||
i, err := strconv.ParseInt(limitString, 10, 64)
|
i, err := strconv.ParseInt(limitString, 10, 64)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
err := fmt.Errorf("error parsing %s: %s", LimitKey, err)
|
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
|
return
|
||||||
}
|
}
|
||||||
limit = int(i)
|
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)
|
resp, errWithCode := m.processor.BookmarksGet(c.Request.Context(), authed, maxID, minID, limit)
|
||||||
if errWithCode != nil {
|
if errWithCode != nil {
|
||||||
api.ErrorHandler(c, errWithCode, m.processor.InstanceGet)
|
apiutil.ErrorHandler(c, errWithCode, m.processor.InstanceGet)
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
if errWithCode != nil {
|
if errWithCode != nil {
|
||||||
api.ErrorHandler(c, errWithCode, m.processor.InstanceGet)
|
apiutil.ErrorHandler(c, errWithCode, m.processor.InstanceGet)
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -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)
|
||||||
|
}
|
|
@ -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 @@ package favourites
|
||||||
import (
|
import (
|
||||||
"net/http"
|
"net/http"
|
||||||
|
|
||||||
"github.com/superseriousbusiness/gotosocial/internal/api"
|
"github.com/gin-gonic/gin"
|
||||||
"github.com/superseriousbusiness/gotosocial/internal/processing"
|
"github.com/superseriousbusiness/gotosocial/internal/processing"
|
||||||
"github.com/superseriousbusiness/gotosocial/internal/router"
|
|
||||||
)
|
)
|
||||||
|
|
||||||
const (
|
const (
|
||||||
// BasePath is the base URI path for serving favourites
|
// BasePath is the base URI path for serving favourites, minus the 'api' prefix
|
||||||
BasePath = "/api/v1/favourites"
|
BasePath = "/v1/favourites"
|
||||||
|
|
||||||
// MaxIDKey is the url query for setting a max status ID to return
|
// MaxIDKey is the url query for setting a max status ID to return
|
||||||
MaxIDKey = "max_id"
|
MaxIDKey = "max_id"
|
||||||
|
@ -42,20 +41,16 @@ const (
|
||||||
LocalKey = "local"
|
LocalKey = "local"
|
||||||
)
|
)
|
||||||
|
|
||||||
// Module implements the ClientAPIModule interface for everything relating to viewing favourites
|
|
||||||
type Module struct {
|
type Module struct {
|
||||||
processor processing.Processor
|
processor processing.Processor
|
||||||
}
|
}
|
||||||
|
|
||||||
// New returns a new favourites module
|
func New(processor processing.Processor) *Module {
|
||||||
func New(processor processing.Processor) api.ClientModule {
|
|
||||||
return &Module{
|
return &Module{
|
||||||
processor: processor,
|
processor: processor,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// Route attaches all routes from this module to the given router
|
func (m *Module) Route(attachHandler func(method string, path string, f ...gin.HandlerFunc) gin.IRoutes) {
|
||||||
func (m *Module) Route(r router.Router) error {
|
attachHandler(http.MethodGet, BasePath, m.FavouritesGETHandler)
|
||||||
r.AttachHandler(http.MethodGet, BasePath, m.FavouritesGETHandler)
|
|
||||||
return nil
|
|
||||||
}
|
}
|
||||||
|
|
|
@ -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.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.emailSender = testrig.NewEmailSender("../../../../web/template/", nil)
|
||||||
suite.processor = testrig.NewTestProcessor(suite.db, suite.storage, suite.federator, suite.emailSender, suite.mediaManager, clientWorker, fedWorker)
|
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())
|
suite.NoError(suite.processor.Start())
|
||||||
}
|
}
|
||||||
|
|
|
@ -6,7 +6,7 @@ import (
|
||||||
"strconv"
|
"strconv"
|
||||||
|
|
||||||
"github.com/gin-gonic/gin"
|
"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/gtserror"
|
||||||
"github.com/superseriousbusiness/gotosocial/internal/oauth"
|
"github.com/superseriousbusiness/gotosocial/internal/oauth"
|
||||||
)
|
)
|
||||||
|
@ -78,12 +78,12 @@ import (
|
||||||
func (m *Module) FavouritesGETHandler(c *gin.Context) {
|
func (m *Module) FavouritesGETHandler(c *gin.Context) {
|
||||||
authed, err := oauth.Authed(c, true, true, true, true)
|
authed, err := oauth.Authed(c, true, true, true, true)
|
||||||
if err != nil {
|
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
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
if _, err := api.NegotiateAccept(c, api.JSONAcceptHeaders...); err != nil {
|
if _, err := apiutil.NegotiateAccept(c, apiutil.JSONAcceptHeaders...); 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
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -105,7 +105,7 @@ func (m *Module) FavouritesGETHandler(c *gin.Context) {
|
||||||
i, err := strconv.ParseInt(limitString, 10, 32)
|
i, err := strconv.ParseInt(limitString, 10, 32)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
err := fmt.Errorf("error parsing %s: %s", LimitKey, err)
|
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
|
return
|
||||||
}
|
}
|
||||||
limit = int(i)
|
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)
|
resp, errWithCode := m.processor.FavedTimelineGet(c.Request.Context(), authed, maxID, minID, limit)
|
||||||
if errWithCode != nil {
|
if errWithCode != nil {
|
||||||
api.ErrorHandler(c, errWithCode, m.processor.InstanceGet)
|
apiutil.ErrorHandler(c, errWithCode, m.processor.InstanceGet)
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -21,30 +21,25 @@ package filter
|
||||||
import (
|
import (
|
||||||
"net/http"
|
"net/http"
|
||||||
|
|
||||||
"github.com/superseriousbusiness/gotosocial/internal/api"
|
"github.com/gin-gonic/gin"
|
||||||
"github.com/superseriousbusiness/gotosocial/internal/processing"
|
"github.com/superseriousbusiness/gotosocial/internal/processing"
|
||||||
"github.com/superseriousbusiness/gotosocial/internal/router"
|
|
||||||
)
|
)
|
||||||
|
|
||||||
const (
|
const (
|
||||||
// BasePath is the base path for serving the filter API
|
// BasePath is the base path for serving the filters API, minus the 'api' prefix
|
||||||
BasePath = "/api/v1/filters"
|
BasePath = "/v1/filters"
|
||||||
)
|
)
|
||||||
|
|
||||||
// Module implements the ClientAPIModule interface for every related to filters
|
|
||||||
type Module struct {
|
type Module struct {
|
||||||
processor processing.Processor
|
processor processing.Processor
|
||||||
}
|
}
|
||||||
|
|
||||||
// New returns a new filter module
|
func New(processor processing.Processor) *Module {
|
||||||
func New(processor processing.Processor) api.ClientModule {
|
|
||||||
return &Module{
|
return &Module{
|
||||||
processor: processor,
|
processor: processor,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// Route attaches all routes from this module to the given router
|
func (m *Module) Route(attachHandler func(method string, path string, f ...gin.HandlerFunc) gin.IRoutes) {
|
||||||
func (m *Module) Route(r router.Router) error {
|
attachHandler(http.MethodGet, BasePath, m.FiltersGETHandler)
|
||||||
r.AttachHandler(http.MethodGet, BasePath, m.FiltersGETHandler)
|
|
||||||
return nil
|
|
||||||
}
|
}
|
|
@ -4,7 +4,7 @@ import (
|
||||||
"net/http"
|
"net/http"
|
||||||
|
|
||||||
"github.com/gin-gonic/gin"
|
"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/gtserror"
|
||||||
"github.com/superseriousbusiness/gotosocial/internal/oauth"
|
"github.com/superseriousbusiness/gotosocial/internal/oauth"
|
||||||
)
|
)
|
||||||
|
@ -12,12 +12,12 @@ import (
|
||||||
// FiltersGETHandler returns a list of filters set by/for the authed account
|
// FiltersGETHandler returns a list of filters set by/for the authed account
|
||||||
func (m *Module) FiltersGETHandler(c *gin.Context) {
|
func (m *Module) FiltersGETHandler(c *gin.Context) {
|
||||||
if _, err := oauth.Authed(c, true, true, true, true); err != nil {
|
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
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
if _, err := api.NegotiateAccept(c, api.JSONAcceptHeaders...); err != nil {
|
if _, err := apiutil.NegotiateAccept(c, apiutil.JSONAcceptHeaders...); 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
|
return
|
||||||
}
|
}
|
||||||
|
|
|
@ -16,14 +16,14 @@
|
||||||
along with this program. If not, see <http://www.gnu.org/licenses/>.
|
along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||||
*/
|
*/
|
||||||
|
|
||||||
package followrequest
|
package followrequests
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"errors"
|
"errors"
|
||||||
"net/http"
|
"net/http"
|
||||||
|
|
||||||
"github.com/gin-gonic/gin"
|
"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/gtserror"
|
||||||
"github.com/superseriousbusiness/gotosocial/internal/oauth"
|
"github.com/superseriousbusiness/gotosocial/internal/oauth"
|
||||||
)
|
)
|
||||||
|
@ -72,25 +72,25 @@ import (
|
||||||
func (m *Module) FollowRequestAuthorizePOSTHandler(c *gin.Context) {
|
func (m *Module) FollowRequestAuthorizePOSTHandler(c *gin.Context) {
|
||||||
authed, err := oauth.Authed(c, true, true, true, true)
|
authed, err := oauth.Authed(c, true, true, true, true)
|
||||||
if err != nil {
|
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
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
if _, err := api.NegotiateAccept(c, api.JSONAcceptHeaders...); err != nil {
|
if _, err := apiutil.NegotiateAccept(c, apiutil.JSONAcceptHeaders...); 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
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
originAccountID := c.Param(IDKey)
|
originAccountID := c.Param(IDKey)
|
||||||
if originAccountID == "" {
|
if originAccountID == "" {
|
||||||
err := errors.New("no account id specified")
|
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
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
relationship, errWithCode := m.processor.FollowRequestAccept(c.Request.Context(), authed, originAccountID)
|
relationship, errWithCode := m.processor.FollowRequestAccept(c.Request.Context(), authed, originAccountID)
|
||||||
if errWithCode != nil {
|
if errWithCode != nil {
|
||||||
api.ErrorHandler(c, errWithCode, m.processor.InstanceGet)
|
apiutil.ErrorHandler(c, errWithCode, m.processor.InstanceGet)
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
|
@ -16,7 +16,7 @@
|
||||||
along with this program. If not, see <http://www.gnu.org/licenses/>.
|
along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||||
*/
|
*/
|
||||||
|
|
||||||
package followrequest_test
|
package followrequests_test
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"context"
|
"context"
|
||||||
|
@ -30,7 +30,7 @@ import (
|
||||||
"github.com/gin-gonic/gin"
|
"github.com/gin-gonic/gin"
|
||||||
"github.com/stretchr/testify/assert"
|
"github.com/stretchr/testify/assert"
|
||||||
"github.com/stretchr/testify/suite"
|
"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"
|
"github.com/superseriousbusiness/gotosocial/internal/gtsmodel"
|
||||||
)
|
)
|
||||||
|
|
||||||
|
@ -60,7 +60,7 @@ func (suite *AuthorizeTestSuite) TestAuthorize() {
|
||||||
|
|
||||||
ctx.Params = gin.Params{
|
ctx.Params = gin.Params{
|
||||||
gin.Param{
|
gin.Param{
|
||||||
Key: followrequest.IDKey,
|
Key: followrequests.IDKey,
|
||||||
Value: requestingAccount.ID,
|
Value: requestingAccount.ID,
|
||||||
},
|
},
|
||||||
}
|
}
|
||||||
|
@ -90,7 +90,7 @@ func (suite *AuthorizeTestSuite) TestAuthorizeNoFR() {
|
||||||
|
|
||||||
ctx.Params = gin.Params{
|
ctx.Params = gin.Params{
|
||||||
gin.Param{
|
gin.Param{
|
||||||
Key: followrequest.IDKey,
|
Key: followrequests.IDKey,
|
||||||
Value: requestingAccount.ID,
|
Value: requestingAccount.ID,
|
||||||
},
|
},
|
||||||
}
|
}
|
|
@ -16,21 +16,20 @@
|
||||||
along with this program. If not, see <http://www.gnu.org/licenses/>.
|
along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||||
*/
|
*/
|
||||||
|
|
||||||
package followrequest
|
package followrequests
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"net/http"
|
"net/http"
|
||||||
|
|
||||||
"github.com/superseriousbusiness/gotosocial/internal/api"
|
"github.com/gin-gonic/gin"
|
||||||
"github.com/superseriousbusiness/gotosocial/internal/processing"
|
"github.com/superseriousbusiness/gotosocial/internal/processing"
|
||||||
"github.com/superseriousbusiness/gotosocial/internal/router"
|
|
||||||
)
|
)
|
||||||
|
|
||||||
const (
|
const (
|
||||||
// IDKey is for account IDs
|
// IDKey is for account IDs
|
||||||
IDKey = "id"
|
IDKey = "id"
|
||||||
// BasePath is the base path for serving the follow request API
|
// BasePath is the base path for serving the follow request API, minus the 'api' prefix
|
||||||
BasePath = "/api/v1/follow_requests"
|
BasePath = "/v1/follow_requests"
|
||||||
// BasePathWithID is just the base path with the ID key in it.
|
// 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.
|
// Use this anywhere you need to know the ID of the account that owns the follow request being queried.
|
||||||
BasePathWithID = BasePath + "/:" + IDKey
|
BasePathWithID = BasePath + "/:" + IDKey
|
||||||
|
@ -40,22 +39,18 @@ const (
|
||||||
RejectPath = BasePathWithID + "/reject"
|
RejectPath = BasePathWithID + "/reject"
|
||||||
)
|
)
|
||||||
|
|
||||||
// Module implements the ClientAPIModule interface
|
|
||||||
type Module struct {
|
type Module struct {
|
||||||
processor processing.Processor
|
processor processing.Processor
|
||||||
}
|
}
|
||||||
|
|
||||||
// New returns a new follow request module
|
func New(processor processing.Processor) *Module {
|
||||||
func New(processor processing.Processor) api.ClientModule {
|
|
||||||
return &Module{
|
return &Module{
|
||||||
processor: processor,
|
processor: processor,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// Route attaches all routes from this module to the given router
|
func (m *Module) Route(attachHandler func(method string, path string, f ...gin.HandlerFunc) gin.IRoutes) {
|
||||||
func (m *Module) Route(r router.Router) error {
|
attachHandler(http.MethodGet, BasePath, m.FollowRequestGETHandler)
|
||||||
r.AttachHandler(http.MethodGet, BasePath, m.FollowRequestGETHandler)
|
attachHandler(http.MethodPost, AuthorizePath, m.FollowRequestAuthorizePOSTHandler)
|
||||||
r.AttachHandler(http.MethodPost, AuthorizePath, m.FollowRequestAuthorizePOSTHandler)
|
attachHandler(http.MethodPost, RejectPath, m.FollowRequestRejectPOSTHandler)
|
||||||
r.AttachHandler(http.MethodPost, RejectPath, m.FollowRequestRejectPOSTHandler)
|
|
||||||
return nil
|
|
||||||
}
|
}
|
|
@ -16,7 +16,7 @@
|
||||||
along with this program. If not, see <http://www.gnu.org/licenses/>.
|
along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||||
*/
|
*/
|
||||||
|
|
||||||
package followrequest_test
|
package followrequests_test
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"bytes"
|
"bytes"
|
||||||
|
@ -25,7 +25,7 @@ import (
|
||||||
|
|
||||||
"github.com/gin-gonic/gin"
|
"github.com/gin-gonic/gin"
|
||||||
"github.com/stretchr/testify/suite"
|
"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/concurrency"
|
||||||
"github.com/superseriousbusiness/gotosocial/internal/config"
|
"github.com/superseriousbusiness/gotosocial/internal/config"
|
||||||
"github.com/superseriousbusiness/gotosocial/internal/db"
|
"github.com/superseriousbusiness/gotosocial/internal/db"
|
||||||
|
@ -59,7 +59,7 @@ type FollowRequestStandardTestSuite struct {
|
||||||
testStatuses map[string]*gtsmodel.Status
|
testStatuses map[string]*gtsmodel.Status
|
||||||
|
|
||||||
// module being tested
|
// module being tested
|
||||||
followRequestModule *followrequest.Module
|
followRequestModule *followrequests.Module
|
||||||
}
|
}
|
||||||
|
|
||||||
func (suite *FollowRequestStandardTestSuite) SetupSuite() {
|
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.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.emailSender = testrig.NewEmailSender("../../../../web/template/", nil)
|
||||||
suite.processor = testrig.NewTestProcessor(suite.db, suite.storage, suite.federator, suite.emailSender, suite.mediaManager, clientWorker, fedWorker)
|
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.StandardDBSetup(suite.db, nil)
|
||||||
testrig.StandardStorageSetup(suite.storage, "../../../../testrig/media")
|
testrig.StandardStorageSetup(suite.storage, "../../../../testrig/media")
|
||||||
|
|
|
@ -16,13 +16,13 @@
|
||||||
along with this program. If not, see <http://www.gnu.org/licenses/>.
|
along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||||
*/
|
*/
|
||||||
|
|
||||||
package followrequest
|
package followrequests
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"net/http"
|
"net/http"
|
||||||
|
|
||||||
"github.com/gin-gonic/gin"
|
"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/gtserror"
|
||||||
"github.com/superseriousbusiness/gotosocial/internal/oauth"
|
"github.com/superseriousbusiness/gotosocial/internal/oauth"
|
||||||
)
|
)
|
||||||
|
@ -74,18 +74,18 @@ import (
|
||||||
func (m *Module) FollowRequestGETHandler(c *gin.Context) {
|
func (m *Module) FollowRequestGETHandler(c *gin.Context) {
|
||||||
authed, err := oauth.Authed(c, true, true, true, true)
|
authed, err := oauth.Authed(c, true, true, true, true)
|
||||||
if err != nil {
|
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
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
if _, err := api.NegotiateAccept(c, api.JSONAcceptHeaders...); err != nil {
|
if _, err := apiutil.NegotiateAccept(c, apiutil.JSONAcceptHeaders...); 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
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
accts, errWithCode := m.processor.FollowRequestsGet(c.Request.Context(), authed)
|
accts, errWithCode := m.processor.FollowRequestsGet(c.Request.Context(), authed)
|
||||||
if errWithCode != nil {
|
if errWithCode != nil {
|
||||||
api.ErrorHandler(c, errWithCode, m.processor.InstanceGet)
|
apiutil.ErrorHandler(c, errWithCode, m.processor.InstanceGet)
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
|
@ -16,7 +16,7 @@
|
||||||
along with this program. If not, see <http://www.gnu.org/licenses/>.
|
along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||||
*/
|
*/
|
||||||
|
|
||||||
package followrequest_test
|
package followrequests_test
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"context"
|
"context"
|
|
@ -16,14 +16,14 @@
|
||||||
along with this program. If not, see <http://www.gnu.org/licenses/>.
|
along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||||
*/
|
*/
|
||||||
|
|
||||||
package followrequest
|
package followrequests
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"errors"
|
"errors"
|
||||||
"net/http"
|
"net/http"
|
||||||
|
|
||||||
"github.com/gin-gonic/gin"
|
"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/gtserror"
|
||||||
"github.com/superseriousbusiness/gotosocial/internal/oauth"
|
"github.com/superseriousbusiness/gotosocial/internal/oauth"
|
||||||
)
|
)
|
||||||
|
@ -70,25 +70,25 @@ import (
|
||||||
func (m *Module) FollowRequestRejectPOSTHandler(c *gin.Context) {
|
func (m *Module) FollowRequestRejectPOSTHandler(c *gin.Context) {
|
||||||
authed, err := oauth.Authed(c, true, true, true, true)
|
authed, err := oauth.Authed(c, true, true, true, true)
|
||||||
if err != nil {
|
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
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
if _, err := api.NegotiateAccept(c, api.JSONAcceptHeaders...); err != nil {
|
if _, err := apiutil.NegotiateAccept(c, apiutil.JSONAcceptHeaders...); 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
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
originAccountID := c.Param(IDKey)
|
originAccountID := c.Param(IDKey)
|
||||||
if originAccountID == "" {
|
if originAccountID == "" {
|
||||||
err := errors.New("no account id specified")
|
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
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
relationship, errWithCode := m.processor.FollowRequestReject(c.Request.Context(), authed, originAccountID)
|
relationship, errWithCode := m.processor.FollowRequestReject(c.Request.Context(), authed, originAccountID)
|
||||||
if errWithCode != nil {
|
if errWithCode != nil {
|
||||||
api.ErrorHandler(c, errWithCode, m.processor.InstanceGet)
|
apiutil.ErrorHandler(c, errWithCode, m.processor.InstanceGet)
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
|
@ -16,7 +16,7 @@
|
||||||
along with this program. If not, see <http://www.gnu.org/licenses/>.
|
along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||||
*/
|
*/
|
||||||
|
|
||||||
package followrequest_test
|
package followrequests_test
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"context"
|
"context"
|
||||||
|
@ -30,7 +30,7 @@ import (
|
||||||
"github.com/gin-gonic/gin"
|
"github.com/gin-gonic/gin"
|
||||||
"github.com/stretchr/testify/assert"
|
"github.com/stretchr/testify/assert"
|
||||||
"github.com/stretchr/testify/suite"
|
"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"
|
"github.com/superseriousbusiness/gotosocial/internal/gtsmodel"
|
||||||
)
|
)
|
||||||
|
|
||||||
|
@ -60,7 +60,7 @@ func (suite *RejectTestSuite) TestReject() {
|
||||||
|
|
||||||
ctx.Params = gin.Params{
|
ctx.Params = gin.Params{
|
||||||
gin.Param{
|
gin.Param{
|
||||||
Key: followrequest.IDKey,
|
Key: followrequests.IDKey,
|
||||||
Value: requestingAccount.ID,
|
Value: requestingAccount.ID,
|
||||||
},
|
},
|
||||||
}
|
}
|
Some files were not shown because too many files have changed in this diff Show More
Loading…
Reference in New Issue